本次实验目的是学习进程fork函数的使用以及注意事项。
实验环境为ubuntu16.04 编译器是gcc 5.4
废话不多说,先上代码了。一会解释。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
int main()
{
printf("before fork pid = %d\n",getpid());
pid_t pid;
pid = fork();
if(pid == -1)
{
ERR_EXIT("fork error");
}
if(pid > 0)
{
printf("this is pareent pid = % d childpid = %d\n",getpid(),pid);
}else if(pid == 0)
{
printf("this is child = %d parentpid = %d \n",getpid(),getppid());
}
return 0;
}
解释一下主函数下第一行代码,
printf("before fork pid = %d\n",getpid());
这行代码是打印当前程序得进程号,getpid()是获取当前程序得进程号,并且打印出来。想要查看getpid函数可以man 2 getpid 一下。
pid = fork();
这句代码是复制进程的函数,
当复制进程成功,子进程返回0,父进程返回子进程的ID,失败返回-1.
这又有下面的代码
if(pid == -1)
{
ERR_EXIT("fork error");
}
if(pid > 0)
{
printf("this is pareent pid = % d childpid = %d\n",getpid(),pid);
}else if(pid == 0)
{
printf("this is child = %d parentpid = %d \n",getpid(),getppid());
}
如果pid == -1 打印错误信息。如果pid > 0 打印父进程ID和pid(也就是子进程ID)如果 pid == 0 打印子进程ID和父进程ID
其中getppid函数是获取父进程ID的函数
这个是打印结果,很奇怪。
第一个奇怪点,打印了三行。
按以前的思维pid只有一种状态要么大于0要么等于0,怎么会执行两者都执行了。这个说起了就是fork函数的关系, fork函数创建了一个子进程。那么 pid == 0 的代码是要在子进程中执行的,pid > 0是要在父进程中执行的。可以理解为CPU同一时间干了两件事,事实上并不是这样,事实上是cpu切换进程来实现的,比如干一件事干了1ms切换到第二件事干了1ms又切换到第一件事,这样来回切换,给人的感觉就是cpu在同时干两件事。这就是为什么可以输出三行。
fork函数一次调用两次返回。两个是在各自的地址空间返回的。
还有一个奇怪的地方那就是为什么子进程中的父进程是1 不应是7807吗?这个是因为父进程先执行完,执行完的进程及退出了,吧子进程托孤给1号进程。所以就出现这个现象,假如子进程先执行完,父进程在执行完就不会这样了。下面我们可以让父进程睡眠1S试试,这样父进程就不会比子进程先执行完。
先上代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
int main()
{
printf("before fork pid = %d\n",getpid());
pid_t pid;
pid = fork();
if(pid == -1)
{
ERR_EXIT("fork error");
}
if(pid > 0)
{
sleep(1);
printf("this is pareent pid = % d childpid = %d\n",getpid(),pid);
}else if(pid == 0)
{
printf("this is child = %d parentpid = %d \n",getpid(),getppid());
}
return 0;
}
看结果:
本次代码只在上次代码基础上增加了一行代码:
那就是sleep(1);
这行代码的意思是睡眠1s的意思,延迟父进程的时间。
僵尸进程:子进程先退出,父进程尚未查询子进程退出状态,子进程就是僵尸状态。
修改sleep(100)子进程为8029 父进程为8028
查看子进程用 ps -ef 查看
其中<defunct>的意思就是僵尸进程。
如何避免僵尸进程:
只需要添加一个信号即可,
#include <signal.h>
在main函数下添加
signal(SIGCHLD,SIG_IGN);
即可避免僵尸进程;
上代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
int main()
{
signal(SIGCHLD,SIG_IGN);
printf("before fork pid = %d\n",getpid());
pid_t pid;
pid = fork();
if(pid == -1)
{
ERR_EXIT("fork error");
}
if(pid > 0)
{
sleep(100);
printf("this is pareent pid = % d childpid = %d\n",getpid(),pid);
}else if(pid == 0)
{
printf("this is child = %d parentpid = %d \n",getpid(),getppid());
}
return 0;
}
这就是结果僵尸进程已经不见了。
总结:
父进程先退出,子进程成为孤儿进程。子进程先退出,父进程没有察觉到,子进程会成为僵尸进程。