在探讨进程概念这个内容之前,必须先对冯诺依曼体系结构以及操作系统这俩个概念做一个简单的了解和认识。
冯诺依曼体系结构是绝大多数现代计算机的硬件体系结构,其认为现代计算机应该包含如下图所示的五大硬件体系结构。
冯诺依曼体系结构的俩大特点:
操作系统其实也是一款软件,是安装在计算机上的一个程序。操作系统包括内核(进程管理+内存管理+文件管理+驱动管理…)以及其他程序(外部应用)。
操作系统可以实现与硬件的交互,管理所有的软硬件资源,且为用户程序提供了一个良好的执行环境。在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件。
刚才说操作系统是一款管理所有的软硬件的软件,那么其到底是怎么管理的呢?很简单,举个例子,比如学校的管理体系,可简单看成是校长–>院领导–>辅导员–>学生这样的管理体系。那么,以学生为例,学校把学生的信息录入学籍,将学生描述了起来,再通过学籍管理系统组织起来。此后,如果某学生的信息发生改变,只需要改变关于他的描述信息(学籍)即完成了对该学生的管理。
而操作系统也是这样,总的来说,就是通过描述+组织的方式来进行管理。
操作系统管理硬件的方式为:先用struct结构体将硬件描述起来,再用链表或者其他高效的数据结构将它们组织起来,而下面要说的进程同理。
从户用层面上来看,进程就是一个运行中的程序。
而从操作系统的角度来看,进程则是描述一个程序运行过程的描述信息,也就是下面要说的进程控制块-PCB。在linux中,PCB就是一个名为task_stauct的结构体。
PCB(process control block),也叫进程控制块,linux中进程信息被放在这样一个叫进程控制块的数据结构中,可以理解为属性的集合。Linux下描述进程的结构体叫做task_struct,这个结构体在内核中的sched.h头文件中。路径为/usr/src/kernels/3.10.0-1062.18.1.el7.x86_64/include/linux(kernels下为内核版本,每个人可能不同)。
task_ struct内容分类
ls /proc命令:
/proc这个文件夹中保存着系统中所有的进程信息,以进程号命(PID)名,如要查看具体的进程,则再/proc/之后跟具体的进程号即可,如
ls /proc/1 //查看进程号为1的进程信息
ps aux命令:
查看进程的具体状态信息,常与grep搭配使用,来查看某一个进程的状态信息。
其中第二列即为当前进程的进程号,即进程PID。
ps -ef命令:
与ps aux 类似,也是查看进程的一些相关信息,只不过内容略有区别。
其中第二列和第三列的内容分别表示当前进程的PID和当前进程父进程的PID。
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。进程状态用于描述操作系统应该去如何处理进程,描述一个进程什么时候能够干什么,是否能被CPU调度运行等。
通过观察内核中关于进程状态的源码,我们可以知道linux中总共有如下几种进程状态:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
概念及产生原因:子进程先于父进程退出,但父进程没有关注子进程的退出状态,导致资源没有被完全释放,此时子进程进入僵尸状态,同时子进程也成为了僵尸进程。
通过以下代码和相关命令进行查看和验证:
#include
#include
#include
int main()
{
pid_t pid = fork(); //创建子进程
if (pid < 0) {
perror("fork");
return -1;
}
else if (pid == 0) {
//子进程
printf("I'm child,pid is %d\n", getpid());
exit(0); //子进程先退出
}
else {
//父进程
sleep(20);
printf("I'm father,pid is %d\n", getpid());
}
return 0;
}
启动俩个终端,一个终端运行当前程序,另一个终端通过ps aux命令查看进程信息:
可以看出,进程号为29444的子进程成为了一个僵尸进程。
僵尸进程的危害:
产生资源泄露,僵尸进程的PCB未被释放,可能会导致正常的进程无法运行。
孤儿进程:父进程先于子进程退出,子进程就会成为孤儿进程。
特性:让出终端,子进程进入系统后台运行,其父进程成为1号进程,孤儿进程会被1号进程领养。
通过以下代码和相关命令进行查看和验证:
#include
#include
#include
int main()
{
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}
else if (pid == 0) {
//child
while (1) {
sleep(1);
}
printf("I'm child,pid is %d\n", getpid());
}
else {
//parent
sleep(20);
printf("I'm parent,pid is %d\n", getpid());
exit(0);
}
return 0;
}
父进程会在20秒中后先于子进程退出,在父进程退出前使用ps-ef查看进程信息:
父进程退出后再次ps-ef:
可以看出父进程退出后,子进程的父进程成为了一号进程,而并非之前的30624。
需要注意的是,僵尸进程会产生危害(资源泄露),但孤儿进程并不会,而是被1号进程领养(回收)。