进程控制都包含那些内容?
1.进程创建
2.进程终止
3.进程等待
4.进程替换
1.进程创建:
在前面进程概念中关于进程创建已有所了解,下面更详细的分析进城创建。
-
fork()函数
返回值类型:pid_t
1.返回值:
1.子进程中返回0;
2.父进程中返回子进程的pid;
3.失败返回-1 ;
(失败原因:1.子进程数目达到上限;2.内存不够)
2.fork的特点:
1.子进程以父进程为模板拷贝父进程。(写时拷贝)
2.子进程把父进程的PCB复制,稍加修改(pid/ppid)
3.内存指针上下文基本相同。
4.父子进程执行同一份代码。
5.子进程从fork之后开始执行,父子进程执行顺序取决与操作系统的调度。
画图理解fork()之后父子进程执行的过程:
fork()的写时拷贝技术:
通常情况下父子进程代码共享,当有一方向试图写入时,便以写时拷贝的方式双方各执一份副本。- vfork()函数
Linux前期的进程创建函数,缺点比较多,在进程概念中以讲过,这里就不在解释。
2.进程终止:
- 进程退出的3个场景:
1.代码运行完毕,结果正确。
2.代码运行完毕,结果错误。
3.代码异常终止。 - 进程常见退出方式:
1.从main函数返回。
2.调用exit()函数。
3._exit()。
同为进程终止函数exit()和_exit的区别:
1.exit()函数调用通过at_exit()和on_exit()函数定义的清理函数(回调函数)。
2.exit()函数关闭所有打开的流,所有的缓冲数据均被写入。
3.调用_exit();- A: exit()退出、_exit()退出 实例:
- B:return退出
return是一种常见的退出进程方式。执行return n等同于执行exit(n),因为main函数会将return返回值当作为exit的参数。
- A: exit()退出、_exit()退出 实例:
3.进程等待:
-
进程等待的必要:
1.之前介绍过僵尸进程,如果子进程退出,父进程对子进程退出置之不理的话,就会造成内存泄露。
2.一旦成为僵尸进程的话,就会变的刀枪不入,kil -9都没办法,因为不可能杀死一个已经死了的进程。
3.父进程如果不等待子进程的话,就不会获知父进程派给子进程的任务完成的情况。结果是否正确,是否正常退出。
4.父进程通过进程等待获取子进程退出信息,释放子进程资源。 -
进程等待的方式:
1.wait方法:
返回值:pid_t;成功:返回子进程pid;失败:返回-1;
参数:输出型参数,获取子进程退出状态,不关心则可设为NULL。
特点:阻塞型等待。
wait阻塞实例:
2.waitpid方法:
返回值:pid_t类型;成功:返回收集到子进程pid;如果设置WNOHANG,waitpid发现没有已退出的子进程可收集,则返回0;
失败:返回-1;
pid:pid = -1,等待任意一个子进程,与wait等效;pid>0,等待ID与pid相等的子进程。
status:保存进程退出状态。
options:WNOHANG,若pid指定的子进程没有结束,直接返回0,不予以等待。若子进程结束,则返回子进程pid。
出错原因: 不存在子进程。
4.进程替换:
- 进程 替换会用到exec族函数:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<wait.h>
#include<fcntl.h>
//exec族函数使用样例
int main(void)
{
char* const argv[] = {"ps","-ef",NULL};
char* const envp[] = {"PATH=/bin:/usr/bin","TIME=console",NULL};
//1.execl 文件路径l,在文件路径中找,可以是相对路径,也可以是绝对路径
//execl("/bin/ps","ps","-ef",NULL);
//2.execlp 文件名p,在当前路径中找,带环境变量,无需写全路径
//execlp("ps","ps","-ef",NULL);
//3.execle 带e的,需要自己组装环境变量
//execle("/bin/ps","ps","-ef",NULL,envp);
//4.execv,带v的,命令以数组方式传参
//execv("/bin/ps",argv);
//5.execvp,带v带p
//execvp("ps",argv);
//6.execve,带v带e
//execve("/bin/ps",argv,envp);
exit(0);
}
学习了进程控制,我们可以根据bash的原理自己写一个小项目,自己的shell,会在下一篇博客中更新。