fork可以在linux中创建子进程。先看man手册里面的东西:
函数的作用的创建一个进程,这个函数会返回两次,可能有三种不同的返回值。
1. 出错返回-1
2. 返回0,表示是子进程
3. 返回大于0,表示是父进程
下面的一个简单的实例:
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h> #define CHILDCOUNT 1 int main()
{
pid_t fpid[CHILDCOUNT ];
int i = ;
for(i = ; i < CHILDCOUNT; i++)
{
fpid[i] = fork();
if(fpid[i] < )
{
perror("fork");
return -;
}
else if(fpid[i] == )
{
printf("This is child process, id:%d, my father:%d\n", getpid(), getppid());
getchar();
return ;
}
else
{
printf("This is parent process, id:%d\n", getpid());
}
} getchar();
printf("main() ------\n");
return ;
}
补充一点知识
- fork出来的子进程复制了父进程的内存空间(处理代码区都复制了),和父进程共享代码区
- fork之后,父子进程谁先执行不确定
- 子进程中新定义的变量和父进程没有任何关系
- 子进程也会复制父进程文件描述符,但是不会复制文件表。而是共用一个offset
- 如果父进程先挂了,子进程就变成孤儿进程了,它爹就会变成1号进程
- 子进程挂了会给父进程发信号(SIGCHLD),如果父进程收到该信号没有及时处理。子进程就变成僵尸进程了,直到父进程处理了该信号或父进程也退出了。
如何避免僵尸进程的出现?
如之前所说,父进程应该及时处理子进程发出来的信号,并且去获取子进程的退出码。
在父进程中使用wait或waitpid参数等待子进程退出。
也可以在收到子进程退出的信号时用wait或waitpid等待。
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h> void sig_handle(int sig)
{
int ret = ;
printf("%s() +++\n", __func__);
#if 0
wait(NULL);
#else
while(ret = waitpid(-, NULL, WNOHANG))
{
if(ret < )
{
printf("waitpid failed\n");
}
else
{
printf("waitpid success\n");
break;
}
}
#endif
} int main()
{
signal(SIGCHLD, sig_handle); // 标记A
pid_t fpid[];
int i = ;
for(i = ; i < ; i++)
{
fpid[i] = fork();
if(fpid[i] < )
{
perror("fork");
return -;
}
else if(fpid[i] == )
{
printf("This is child process, id:%d, my father:%d\n", getpid(), getppid());
return ;
}
else
{
printf("This is parent process, id:%d\n", getpid());
}
} getchar();
printf("main() ------\n");
return ;
}
假如没有signal(SIGCHLD, sig_handle);这个语句,函数运行时,5个子进程马上就结束了(父进程不能退出了)。这时候去查看可以看到有5个僵尸进程。
如下:
xcy@xcy-virtual-machine:~/test/sock4$ ps -ef | grep test
xcy : pts/ :: ./test
xcy : pts/ :: [test] <defunct>
xcy : pts/ :: [test] <defunct>
xcy : pts/ :: [test] <defunct>
xcy : pts/ :: [test] <defunct>
xcy : pts/ :: [test] <defunct>
xcy : pts/ :: grep --color=auto test
xcy@xcy-virtual-machine:~/test/sock4$
加上那句话就没有了
xcy@xcy-virtual-machine:~/test/sock4$ ps -ef | grep test
xcy : pts/ :: ./test
xcy : pts/ :: grep --color=auto test
xcy@xcy-virtual-machine:~/test/sock4$
再来一些有意思的东西
我们知道fork会出现两个进程,两个进程都会往下执行,看下面的代码:
#include<stdio.h>
#include<unistd.h>
int main()
{
fork();
fork();
fork();
printf("+\n");
}
这样就相当于最开始主进程创建两个进程A和B。
A又创建两个进程A1,A2。B又创建两个进程B1,B2。
最后,A1,A2,B1,B2都创建两个进程,所以最后会有8个进程,+会打印8次。
这样就想细胞的二次分裂一样,3次分裂周期之后就有了8个细胞。
再来看个更难的:
#include<stdio.h>
#include<unistd.h> void fun()
{
fork();
// fork() || fork() && fork(); // 第一个fork返回大于0,后面两个就不执行了
fork() && fork() || fork();
fork();
printf("+\n");
} int main()
{
fun2();
}
这样一共能创建几个进程呢,+会打印几次呢。我们先一步一步来分析,假定所有的fork都会成功:
第一次分裂成A和B。A进程创建的进程和B创建的进程数目肯定是一样的。
就先像下面这样分析:
fork() && fork() || fork(); // line 1
fork(); // line 2
要注意&&运算符和||运算符:
对于&&来说:第一个表达式如果为0,后面的表达式就不用算了
对于||来说:第一个表达式如果为1,后面的表达式也不用算了。
为了更好分析,上面的fork一次标号为fork1,fork2,fork3,fork4。
先看line1。fork1会返回一个非0,和一个0.
- 1.返回0,则直接进入line2,fork4创建两个进程
- 2.返回大于0,执行fork2。
- 2.1 fork2返回0,则需要执行fork3,再接着执行fork4.这里会创建4个进程
- 2.2 fork2返回大于0,也会执行fork3和fork4。相当于也创建4个进程。
综上,第一次的A进程会产生10个进程,同理,B也会有10个进程。所以一共会有20个进程。