进程相关的基本概念
1 进程的创建与结束
#include<unistd.h>
pid_t fork(void);
在讨论 fork()
函数之前,有必要先明确父进程和子进程两个概念。除了。号进程(该进程是系统自举时由系统创建的)以外,Linux 系统中的任何一个进程都是由其他进程创建的。创建新进程的进程,即调用 fork()
函数的进程就是父进程,而新创建的进程就是子进程。
fork()
函数不需要参数,返回值是一个进程标识符( PID ) 。对于返回值,有以下3 种情况。
- 对于父进程,fork() 函数返回新创建的子进程的ID。
- 对于子进程,fork() 函数返回0。
- 如果创建出错,则 fork() 函数返回 -1。
2 进程的结束——exit()函数
#include<stdlib.h>
void exit(int status);
在Linux 中如何让一个进程退出(进程退出表示进程即将结束),在 Linux 中进程退出分为正常退出和异常退出两种。
- 正常退出
a. 在 main() 函数中执行 return;
b. 调用 exit() 函数;
c. 调用_exit() 函数。
- 异常退出
a. 调用 abort 函数
b. 进程收到某个信号,而该信号使程序终止。
以上几种退出方式的不同点:
(1)exit 和 return 的区别
- exit 是一个函数,带有参数,exit 执行完后把控制权交给系统。
- return 是函数执行完后的返回,return 执行完后把控制权交给调用函数。
(2)exit 和 abort 的区别
- exit是正常终止进程。
- abort 是异常终止
(3)exit() 和 _exit() 函数
exit
和_exit
函数都是用来终止进程的。当程序执行到exit
和_exit
时,系统无条件地停止剩下所有操作,清除包括 PCB 在内的各种数据结构,并终止本进程的运行。exit()
是在头文件 stdlib.h 中声明,而_exit()
声明在头文件 unistd.h 中声明。exit 中的参数 exit code 为0 时,代表进程正常终止,若为其他值,表示程序执行过程中有错误发生。并且_exit()
执行后立即返回给内核,而exit()
要先执行一些清除操作,然后将控制权交给内核。在调用exit
函数时,其会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数,但不会刷新流( stdin 、stdout 、stderr· · · )的数据。exit 函数是在 exit 函数之上的一个封装, 其会自动调用 exit ,并在调用之前先刷新流数据。exit()
函数与_exit()
函数最大区别就在于exit()
函数在调用exit 系统之前要检查文件的打开情况,把文件缓冲区的内容写回文件。由于Linux 的标准函数库中,有一种被称作“缓冲 I/O ”的操作, 。其特征就是对应每一个打开的文件,在内存中都有一片缓冲区。每次读文件时, 会连续的读出若干条记录,这样在下次读文件时就可以直接从内存的缓冲区中读取;同样,每次写文件的时候也仅仅是写入内存的缓冲区,等满足了一定的条件(如达到了一定数量或遇到特定字符等)后,再将缓冲区中的内容一次性写人文件。这种技术大大增加了文件读写的速度,但也给编程带来了一点儿麻烦。比如有一些数据,理论上应该已经写入了文件,但实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时如果用exit()
函数直接将进程关闭, 缓冲区的数据就会丢失。因此,要想保证数据的完整性,就一定要使用exit()
函数。
3 僵尸进程
孤儿进程,
是指一个父进程退出后,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init 进程(进程号为1 )所收养,并由init 进程对它们完成状态收集工作。
僵尸进程,
是指一个进程使用fork 创建子进程,如果子进程退出,而父进程并没有调用wait
或 waitpid
获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称为僵尸进程。当一个进程完成它的工作终止之后,它的父进程需要调用wait()
或者 waitpid()
系统调用取得子进程的终止状态。
可以这样理解孤儿进程和僵尸进程的区别:孤儿进程是父进程已退出,而子进程未退出;僵尸进程是父进程未退出,而子进程已退出。