🌟hello,各位读者大大们你们好呀🌟
🍭🍭系列专栏:【Linux初阶】
✒️✒️本篇内容:进程的概念,进程管理初识(描述、管理进程),查看进程的基础方法,获取进程标识符(pid、ppid),fork进程创建(分流应用)
🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-
目录
一、什么是进程
二、进程的管理
1.描述进程-PCB
2.程序控制块(PCB)的管理
三、查看进程
四、通过系统调用获取进程标示符
1.进程和父进程
2.清除进程
五、进程创建 - fork
1.一般进程创建
2.通过系统调用创建进程-fork初识
3. fork的分流应用
一、什么是进程
进程的概念,有很多种不同的说法,我们看的最多的说法之一就是:一个运行起来的(加载到内存) 的程序,被称为进程。进程和程序相比,具有动态属性。
实际上,进程 = 内核数据结构(task_struct)+ 进程对应的磁盘代码
二、进程的管理
首先,我们要清楚一个前提——程序是一个文件,它存储在磁盘中。其次,我们要知道,要执行一个程序需要把程序加载到内存中。
接下来我们还需要了解一些相关的基础知识,来将进程的知识串联起来。
1.描述进程-PCB
- 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
- 课本上称之为PCB(process control block,中文名为程序控制块),Linux操作系统下的PCB是: struct task_struct
- PCB对象是操作系统创建的
我们可以把PCB理解为一种struct结构体或类,专门用于存储加载到内存的各个程序的属性,比如程序的状态、运行的优先级等。
2.程序控制块(PCB)的管理
通过上面的知识,我们知道了内存中有从磁盘加载进来的程序块,每个程序块有对应的PCB,那么问题来了,计算机是如何对加载到内存中的程序进行管理的呢?我们通过下面的图示来理解
- “管理”的执行本质:先描述,再组织;
- 描述:PCB(struct task_struct),对程序信息进行了描述;
- 组织:操作系统通过特定的数据结构,将不同的PCB(程序控制块)及其对应的程序组织(加载到内存的程序块)联系起来,最终实现对程序的管理。
进程 = 内核数据结构(PCB)+ 进程对应的磁盘代码。操作系统可以通过PCB找到对应的磁盘代码,因此,只要我们实现了对PCB的管理,也就相当于实现了进程的管理。
进程组织:所有运行在系统里的进程都以task_struct链表的形式存在内核里。我们可以在内核源代码里找到它。
内存中PCB和程序块的组织形式如下图所示
三、查看进程
在Linux中查看进程的操作,实际上和我们再window上查看任务管理器是差不多的。
ps axj | head -1 && ps axj | grep ‘myproc’ 或
ps axj | head -1 && ps axj | grep 4974(进程id)
- ps axj - 查看系统所有进程;
- head -1(数字1) - 打印标题;
- grep ‘myproc’ - 对除文件myproc外进行行过滤。
进程在被调度运行的时候,进程就具有动态属性
四、通过系统调用获取进程标示符
1.进程和父进程
- 进程id(PID)
- 父进程id(PPID)
gerpid() - 获取当前进程的id(PID)
getppid() - 获取当前进程的父进程的id(PPID)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid: %d\n", getpid());
printf("ppid: %d\n", getppid());
return 0;
}
2.清除进程
kill -9 4974(进程id) #kill -9 表示强制终止退出
五、进程创建 - fork
1.一般进程创建
实际上,我们运行的一个可执行程序,就是一个进程
命令行上启动的进程,在无特殊情况下,它的父进程都是bash。(bash为进程名,它是一个命令行解释器)
通常进程运行都是在子进程下运行,子进程和父进程相互独立但是又数据共享。目的是保护计算机安全(防止进程对系统进行危险操作)。
当程序单线程运行时,进程在子进程下运行。多次运行同一运行程序,子进程id改变,父进程id不变,说明每次运行该程序的子进程变了,但是每个子进程对应的父进程不变。
2.通过系统调用创建进程-fork初识
- 运行 man fork 认识fork(除了起始页,还可通过底部命令行指令:/return val查看返回值);
- fork有两个返回值(如果成功,子进程的pid返回父进程,0返回给子进程;如果失败,返回-1);
- 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)。
接下来我们看下面这段代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
//创建子进程 -- fork是一个函数 -- 函数执行前:只有一个父进程 -- 函数执行后:父进程+子进程
fork();
printf("我是一个进程,pid:%d, ppid: %d\n", getpid(), getppid());
sleep(2);
return 0;
}
3. fork的分流应用
通过文档我们了解到,fork 之后通常要用 if 进行分流【通过返回值不同实现分流】
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
return 1;
}
else if(id == 0)
{
//child
printf("I am child : %d!, id: %d\n", getpid(), id);
}
else
{
//father
printf("I am father : %d!, id: %d\n", getpid(), id);
}
sleep(1);
return 0;
}
结果是,父进程和子进程将会同时运行,因此我们可以得出结论
- fork() 之后,会有父进程+子进程两个进程执行后续代码(拥有多个线程,多个执行流,这就是我们所说的多线程)
- fork后续的代码,被父子进程共享(注意:共享不代表需要一个进程执行所有代码)
- 通过返回值不同,可以让父子进程各自执行后续共享代码的一部分
通过对进程和合理化利用,就可以多进程/多线程运行程序,实现并发式编程!!!
🌹🌹 Linux进程的相关概念大概就讲到这里啦,博主后续会继续更新更多Linux操作系统的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪