进程控制
文章目录
- 进程控制
- 进程创建
- 进程的终止
- wait()和waitpd()
- 僵尸进程
- 孤儿进程
进程创建
程序在执行的过程中,可能会创建多个进程,创建进程称为父进程,新的进程称为子进程,每个新的进程也可以创建其他进程,从而形成进程树
大多数操作系统(UNIX,Linux,Windows) 对进程的识别采用的唯一的进程标识符,每个进程都有一个唯一的pid 他可以用作索引,以便访问内核中的进程的各种属性
这是一个典型的Linux 的进程树,进程init 是作为所有的用户进程的父进程,一旦系统启动,他就可以创建各种用户进程,
一般来说,当一个进程创建子进程时,该进程会需要一定的资源(CPU 时间、内存、文件、I/O 设备)来完成任务,子进程可以从操作系统哪里直接获得资源,也可以只从父进程哪里获得资源子集,父进程可能在子进程间分配资源,(如内存或文件)限制子进程只能使用父进程的资源,可以防止创建过多进程,导致系统超载。
当进程创建新的进程时,会有两种执行可能:
- 父进程和子进程并发执行
- 父进程阻塞,等待某个子进程或者所有子进程执行完
子进程的地址空间也有两种可能:
-
子进程时父进程的复制品(他与父进程有同样的数据和程序)
-
子进程的加载另外一个进程
在一个程序调用系统调用fork() 后,子进程的地址空间会复制父进程的地址空间(此处也会用到虚拟内存的写时拷贝),这种机制允许父子进程间的轻松通信
父子进程都会执行fork() 之后的指令,唯一不同的是,子进程fork()的返回值为0,父进程则为非0.
在调用fork()以后,有个进程调用了exec(),以用新的程序来取代进程的地址空间,系统调用exec()加载二进制文件文件到内存中(破坏了包含系统调用的原来的内存内容)并进行执行,采用这种方法,这两个进程能互相通信
父进程在调用系统调用wait()能把自己移出就绪队列到阻塞队列中,从就绪态到阻塞态,直到子进程结束。
进程的终止
当进程完成指向的最终语句并且通过系统调用exit() 请求操作系统删除自身时,进程终止
此时,进程可以返回状态值 到父进程(通过系统调用wait)。所有的进程资源,如物理和虚拟内存、打开的文件和I/O缓存区等,会由操作系统释放。
- 如果终止子进程,则父进程需要知道这些子进程的的标识符,因此,当一个进程创建子进程时,则该子进程的标识符需要传递给父进程。父进程会将其存储到他的进程控制块中。
父进程终止子进程的方式有很多 比如:
-
子进程使用了超过他所分配到的一些资源。 这要求父进程有一个检查其子进程的状态的机制。
-
分配给子进程的任务已不再需要。
-
父进程退出,如果父进程终止,那么操作系统不允许子进程继续
有的系统不允许,子进程在父进程终止的情况下继续存在,对于这类系统, 如果一个进程终止(正常或不正常)。那么它的所有子进程也被终止。这种现象,称为 级联终止,通常是由操作系统进行的。
wait()和waitpd()
1、wait的工作原理
(1)子进程结束时,系统向其父进程发送SIGCHILD信号
(2)父进程调用wait函数后阻塞
(3)父进程被SIGCHILD信号唤醒,然后去回收僵尸子进程
(4)父子进程之间是异步的,SIGCHILD信号机制就是为了解决父子进程之间的异步通信问题,让父进程可以及时的去回收僵尸子进程
(5)若父进程没有任何子进程则wait返回错误
2、参数解读
(1)wait的参数status。
- status用来返回子进程结束时的状态,父进程通过wait得到status后就可以知道子进程的一些结束状态信息。
(2)wait的返回值pid_t。
- 这个返回值就是本次wait回收的子进程的PID。当前进程有可能有多个子进程,wait函数阻塞直到其中一个子进程结束wait就会返回,wait的返回值就可以用来判断到底是哪一个子进程本次被回收了。
(3)小结:wait主要是用来回收子进程资源,回收同时还可以得知被回收子进程的pid和退出状态。
(4)WIFEXITED、 WIFSIGNALED、 WEXITSTATUS这几个宏用来获取子进程的退出状态。
- WIFEXITED宏用来判断子进程是否正常终止(return、exit、_exit退出)。
- WIFSIGNALED宏用来判断子进程是否非正常终止(被信号所终止)。
- WEXITSTATUS宏用来得到正常终止情况下的进程返回值。
4、wait和waitpd差别
(1)基本功能一样,都是用来回收子进程的
(2)waitpid可以回收指定PID的子进程
(3)waitpid可以阻塞或者非阻塞两种工作模式
原文链接:https://blog.csdn.net/xuw_xy/article/details/105793619
僵尸进程
当一个进程终止时,操作系统会释放其资源。不过他位于进程表中的信息还是在的,直到父进程调用wait(),这是因为进程表中包含了进程的退出状态。
当一个进程终止,但是其父进程没有调用wait(),这样的进程称为僵尸进程
一旦父进程调用了wait(),僵尸进程的进程标识符和他在父进程进程表中的信息就会释放
原因可见上文中,对于wait()解读 转载部分
孤儿进程
如果父进程没有调用wait()就终止了,以致于子进程成为了孤儿进程。
Linux和UNIX 对于这种情况的处理是:
- 进程init定期调用wait(),以便于收集任何孤儿进程的退出状态,并释放孤儿进程的进程标识符和进程表中的信息。