文章目录
1、课程目标
- 创建子进程(熟练)
- 父子进程(熟练)
- 结束进程(熟练)
- 小结
2、进程创建-fork
#include <unistd.h>
pid_t fork(void);
创建新的进程,失败时返回-1
成功时父进程返回子进行的进程号,子进程返回0
3、进程创建-fork-示例
pid_t pid;
if ((pid = fork())<0)//进程创建失败
{
perror("fork");//创建错误打印错误信息
return -1;
}
else if (pid == 0)//子进程
{
printf("child process:my pid is %d\n",getpid());
}
else//父进程
{
printf("parent process:my pid is %d\n",getpid());
}
4、父子进程
- 子进程继承了父进程的内容(文本区域即代码段、数据区域即存储变量和进程执行期间的动态分配内存、堆栈内容即活动过程调用的指令和本地变量,但不是完全复制,pid,ppid不同)
- 父子进程都有独立的地址空间、互不影响
- 若父进程先结束:
- 子进程成为孤儿进程、被init进程收养(init进程时内核启动首先启动的用户态进程,进程号1。子进程必须有父进程进行回收)
- 子进程变成后天进程
- 若子进程先结束:
- 父进程如果没有及时回收返回值和退出状态,子进程变成僵尸进程
5、进程-思考
- 问:子进程从何处开始运行?
- 答:子进程从创建进程后下一条指令开始执行。子进程继承了父进程的几乎所有内容,其中包含父进程的pc,父进程在创建子进程后,则将pc复制给了子进程,所以子进程从fork创建进程之后开始执行。子进程并没有执行fork,若子进程也执行fork,则会无线循环创建进程。
- 问:父子进程谁先执行?
- 答:不确定,取决于内核的调度。当父进程在创建子进程的时间片没有用完,通常为父进程先执行。也有可能父进程创建进程时将时间片用完,子进程则先被执行。
- 问:父进程能否多次调用fork?子进程呢?
- 答:取决于程序的是否有需求,可以多次调用进行创建。进程的数量也不会无限大,其和内存大小,占用资源等有关。子进程也可以调用fork,创建孙进程。
6、进程结束-exit/_exit
exit和_exit都是用来正常终止一个进程的,主要区别_exit会立即进入内核,而exit会先执行一些清除工作(包含执行各种终止处理程序,关闭所有标准io口一旦关闭),然后才会进入内核
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
- 调用的传入传入一个参数,int类型,实际只有低八位有效。函数虽然没有返回值但结束后会将参数第八位传给内核。
- exit结束进程时会刷新流的缓冲区,_exit会结束进程但不会刷新。当io口有缓存时,exit可以将数据打印出,而_eixt却不可以打印出。
7、进程结束-exit-示例1
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("this process will exit");//printf讲字符串放在缓冲区中,没有写到终端上。看不到打印结果。
exit(0);//结束当前进程。刷新流,在标准输出缓冲区的的字符串被写到终端上,得以显示。
printf("never be displayed");//进程已结束,不会执行。
}
运行结果:
this process will exit
8、进程结束-exit-示例2
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("using exit\n");//有换行符\n,强制刷新流,将缓冲区刷新到终端上并显示
printf("this is the end");//保存到标准输出流的缓冲区中
exit(0);//结束当前进程,强制硕鑫标准输出流的缓冲区,讲数据写到终端,得以显示。
}
运行结果:
using exit
this is the end
- 若程序中exit()换成_exit(),直接结束进程,不会刷新标准输出流,则无法打印出this is the end。
9、进程小结
- fork创建进程。
- 子父进程的区别。
- 结束进程exit和_exit的区别。