1.进程创建
- 分配新的内存块和内核数据结构给子进程
- 将父进程部分的数据结构拷贝至子进程
- 添加子进程到系统进程列表中
- fork返回,开始调度器调度
创建子进程除了fork还有vfork:
- vfork 用于创建一个子进程,而子进程和父进行共享地址空间,fork的子进程有独立的地址空间
- vfork保证子进程先运行,在它调用exec或exit(return不行)之后父进程才可能被调度运行。vfork也有两个返回值
2.进程终止
进程提出场景:
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码没跑完,异常终止(while(1)永远跑不完,只能ctrl +异常终止,absort,kill pid)
进程常见退出方法:
正常终止:(可以通过echo $?查看进程退出码,只保存离它最近的退出码)
- 从main返回
- 调用exit
- _exit(系统调用,强制退出)
return只有在main函数中才会退出进程,参数表示退出码,而exit()在任何时候使用都会退出进程,参数表示退出码,退出码表示进程退出信息,除了exit()可以退出进程,_exit()也可以,但_exit()是强制退出,而exit()退出时还会做些收尾工作,像刷新缓冲区等
异常退出:
- ctrl +c,信号终止
- absort
- kill pi
3.进程等待(子进程必须被等待)
进程等待的重要性:
- 子进程退出,父进程不管不顾,就可能造成僵尸进程,进而造成内存泄漏
- 我们要知道父进程派给子进程的任务完成情况,如:子进程运行完成,结果是对还是不对,或者是否正常退出等
- 父进程通过进程等待的方式回收子进程资源,获取子进程退出信息
等待方法:
(1)pid_t wait(int *status) // 阻塞,直到有一个子进程死亡,回收返回
注:wait(NULL)可以回收僵尸进程,但是只能回收一个
需要了解的宏:
- WEXITSTATUS(status):得到子进程的退出码
- WIFEXITED(status):如果正常退出,返回真 两个经常搭配使用,先判断后使用
- WIFSIGNALED(status):如果是信号令其死亡,返回真
- WIERMSIG(status):获得杀死进程的的信号
(2)pid_t wait(pid_t pid , int *status , int options);//第一个参数为要等待的子进程pid;如果是-1,表示等待任意子进程,第三个参数默认为0;waitpid()也可以回收僵尸进程
返回值:当正常返回的时候waitpid返回收集到的子进程的pd,如果被设置为WNOHANG,返回0,出错返回-1
参数pid:
等待情况:
- 如果子进程已经退出,调用wait/waitpid,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞
- 如果子进程不存在,则立即出错返回
想想在一个shell终端,我们敲下ls命令后,ls命令是怎样被执行的呢,很容易想到,ls的代码开始肯定在磁盘上,那么它是如何及加载到内存然后被执行的呢,这里就用到了进程替换
如果shell终端直接将ls的代码替换它自己,那么ls执行失败,它自己也会崩掉,所以它一定是创建了一个子进程,自己就指向原代码,让ls的代码将子进程的代码替换,子进程执行ls的代码
注:进程替换并没有创建新进程,所以pid不变
// 用file指定的程序替换当前进程
int execvp(const char *file, // 可执行程序的名字
char *const argv[]); // main函数的命令行参数
成功,没有返回值,失败,返回-1
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *filename, char *const argv[],
char *const envp[]);
p : PATH
v : vector
l : list
e : environment
execvp第一个参数为替换文件,第二个参数为main函数的命令行参数,第一个是替换程序的可执行名,必须以NULL结尾
小案例
(1)mysystem
这里就是用fork创建子进程,子进程用execvp进程替换,父进程用waitpid阻塞等待子进程
(2)myshell