介绍:
进程是程序执行的实体,可将其理解为程序。比如:当我们使用文本编辑器Notepad应用程序来编写一篇文章时,此时,Notepad应用程序就被加载到了内存中,并且它占用的资源(如内存、CPU等)也得到了分配。在这个过程中,Notepad应用程序就是一个进程,此进程在使用过程将不断被执行,当关闭Notepad应用程序,此进程占用的资源将释放,进程结束。而操作系统则是负责调度和管理这个进程的实体,它为进程分配资源,并在进程结束后回收资源。也就是说,操作系统负责管理进程。
进程控制块
进程控制块的简称为PCB,是操作系统中用于管理进程的数据结构。它包含了进程的所有状态信息,可以理解为进程属性的集合,用来描述一个进程的所有属性,是进程存在的唯一标志。
Linux下的PCB(进程控制块)是 task_struct 结构体,此结构体是Linux内核的一种数据结构。而task_struct 结构体中的数据如下:
task_ struct内容分类 标示符: 描述本进程的唯一标示符,用来区别其他进程。 状态: 任务状态,退出代码,退出信号等。 优先级: 相对于其他进程的优先级。 程序计数器: 程序中即将被执行的下一条指令的地址。 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。 I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。 其他信息
使用ps axj指令可查看进程详细信息。其中,PID对应的就是进程的标识符,PPID对应的是父进程的标识符。标识符是用来描述本进程的唯一标示符,用来区别其他进程。
标识符函数
在写程序时,要想观察此进程的PID可在代码中使用 getpid() 函数,此函数可获得进程的PID,返回类型为pid_t(就是int),即谁调用getpid,就获取谁的PID。对应的还有 getppid() 函数,获取该进程的父进程标识符,返回类型为pid_t(就是int)。
这里要说明一下,每一次启动程序时,对应的PID都会发生变化,PPID一般不会发生变化,除非您通过某种方式改变了父进程。
在Linux中,当您启动一个程序时,该程序的PPID通常是被启动该程序的shell进程的PID,即该进程的PPID对应的就是bash或其他shell进程。可以说每启动的一个进程都是bash或shell的子进程。因此说每一次启动进程时,PID都会变化,PPID一般不会变化,除非我们将父进程也改变。
[zhu@zhujunhao day21]$ ll total 16 -rw-rw-r-- 1 zhu zhu 305 Dec 21 17:07 code.cpp -rwxrwxr-x 1 zhu zhu 9176 Dec 21 17:07 code.exe [zhu@zhujunhao day21]$ cat code.cpp //程序的源代码 #include <iostream> #include <sys/types.h> #include <unistd.h> using namespace std; int main() { pid_t pid = getpid(); pid_t ppid = getppid(); sleep(3); cout << "该进程对应的PID: " << pid << endl; sleep(3); cout << "该进程对应的PPID: " << ppid << endl; return 0; } [zhu@zhujunhao day21]$ ./code.exe 该进程对应的PID: 2248 该进程对应的PPID: 1381 /*下面我们删除程序的进程,再次编译运行后可发现,该进程的PID发生了变化,PPID没有改变。该程序的PPID就是bash或shell进程*/ [zhu@zhujunhao day21]$ rm -f code.exe [zhu@zhujunhao day21]$ ll total 4 -rw-rw-r-- 1 zhu zhu 305 Dec 21 17:07 code.cpp [zhu@zhujunhao day21]$ g++ -o code.exe code.cpp [zhu@zhujunhao day21]$ ./code.exe 该进程对应的PID: 2257 该进程对应的PPID: 1381
/proc目录
下面,我们了解一下 /proc目录。/proc目录提供了一种访问系统内核内部数据结构、改变内核设置的机制。用户和应用程序可以通过访问/proc目录中的文件和子目录,来获取系统的信息,并可以改变内核的某些参数。
其中,/proc/[pid]目录提供了访问和操作进程信息的接口,此接口提供指定进程的各种详细信息,可方便地了解和控制进程的状态和行为。
这里要说明一下,当使用指令 ll /proc/[pid] 进行详细查看时,对应的cwd是当前进程启动的工作目录,即进程启动时所在的工作目录。exe对应的是可执行程序的工作目录,即进程运行的程序。当修改当前进程或可执行文件的工作目录时,对应的cwd或exe会发生变化。
[zhu@zhujunhao day21]$ ll /proc/1381 ........ lrwxrwxrwx 1 zhu zhu 0 Dec 21 16:54 cwd -> /home/zhu/day21 //进程启动的工作目录 ........ lrwxrwxrwx 1 zhu zhu 0 Dec 21 16:54 exe -> /usr/bin/bash //可执行程序的工作目录
创建进程fork初识
fork函数(没有参数):创建子进程,在fork之后,代码会形成分支形成两个,即代码共享,其中一个是该函数创建的一个子进程。其中,该函数返回值为pid_t,给创建的子进程返回0,父进程返回子进程的PID。
[zhujunhao@bogon code]$ cat test.cpp //源代码 #include <iostream> #include <sys/types.h> #include <unistd.h> using namespace std; int main() { cout << "PID: " << getpid() << endl; int ret = fork(); cout << "ret = fork(): " << ret << endl; sleep(1); return 0; } [zhujunhao@bogon code]$ ./test.exe //执行源代码的可执行程序 PID: 9655 ret = fork(): 9656 ret = fork(): 0
此函数通常跟 if 条件连用,分别使父子进程执行不同的功能。
[zhujunhao@bogon code]$ cat test.cpp //源代码 #include <iostream> #include <sys/types.h> #include <unistd.h> using namespace std; int main() { cout << "PID: " << getpid() << endl; int ret = fork(); if (ret == 0) { cout << "I am Child: PID " << getpid() << " PPID: " << getppid() << endl; } else { cout << "I am father: PID " << getpid() << " PPID: " << getppid() << endl; } sleep(1); return 0; } [zhujunhao@bogon code]$ ./test.exe //可执行程序 PID: 10418 I am father: PID 10418 PPID: 3038 I am Child: PID 10419 PPID: 10418
在执行进程的过程中,若不想执行该进程,通常使用 kill -9 [pid]命令强制终止指定进程的执行。关闭该进程后,不会影响该进程的子进程或父进程,即进程(任意进程,包括父子进程)之间是有独立性的,互相是不能影响,可放心使用。