fork函数:
fork函数用于创建一个新进程,称为子进程,它与调用它的进程同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。子进程使用相同的pc(程序计数器),相同的CPU寄存器。
它不需要参数并返回一个整数值,下面是fork()返回的不同值:
负值:创建子进程失败。
零:返回到新创建的子进程。
正值:返回父母或来电者。该值包含新创建的子进程的进程ID
我们来看一个程序:
#include<stdio.h>
#include<unistd.h>
int main()
{
pid_t pid ;
pid = fork(); //fork一个进程
int count = 0 ;
if(pid == 0) { //pid为0,
printf("this is child process, pid is %d\n",getpid());//getpid返回的是当前进程的PID
count++;
printf("count = %d\n",count);
}
else if(pid > 0) {
printf("返回值是%d\n", pid);
printf("this is father process, pid is %d\n",getpid());
count++;
printf("count = %d\n",count);
}
else {
fprintf(stderr,"ERROR:fork() failed!\n");
}
return 0;
}
其输出结果是:
可以发现0的返回值对应子进程,大于零且刚好是子进程的pid的返回值是父进程。
再来看一个程序:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
/* 允许建立的子进程个数最大值 */
#define MAX_CHILD_NUMBER 10
/* 子进程睡眠时间 */
#define SLEEP_INTERVAL 2
int proc_number=0; /* 子进程的自编号,从0开始 */
void do_something();
main(int argc, char* argv[])
{
int child_proc_number = MAX_CHILD_NUMBER; /* 子进程个数 */
int i, ch;
pid_t child_pid;
pid_t pid[10]={0}; /* 存放每个子进程的id */
if (argc > 1)
{
/* 命令行参数中的第一个参数表示建立几个子进程,最多10个 */
child_proc_number = atoi(argv[1]); //将字符串转换成整数
child_proc_number = (child_proc_number > 10) ? 10 : child_proc_number;
}
for (i=0; i<child_proc_number; i++)
{
/* 建立child_proc_number个子进程*/
child_pid = fork() ;
if(child_pid == 0){
proc_number = i ;
do_something() ;
break ;
}
* 父进程把子进程的id保存到pid[i] */
pid[i] = child_pid ;
}
/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
* 输入q退出 */
while ((ch = getchar()) != 'q')
{
if (isdigit(ch))
{
/* 向pid[ch-'0']发信号SIGTERM,杀死该子进程 */
kill(pid[ch-'0'], SIGTERM) ;
}
}
/* 杀死本组的所有进程 */
kill(0, SIGTERM) ;
return;
}
void do_something()
{
for(;;)
{
/* 打印子进程自编号。为清晰,在每个号码前加“号码+3”个空格
* 比如号码是1,就打印" 1" */
printf("This is process No.%*d\n",
proc_number+3,
proc_number);
sleep(2); /* 主动阻塞两秒钟,也给其他进程执行的机会 */
}
}
看不懂没关系,我一步一步说:
前面的宏定义和声明就不看了,一直到第一个for循环,其实都是一些赋值语句,你只需要明白下面的这个for循环要创建child_proc_number个子进程。
for (i=0; i<child_proc_number; i++)
{
/* 建立child_proc_number个子进程*/
child_pid = fork() ;
if(child_pid == 0){
proc_number = i ;
do_something() ;
break ;
}
* 父进程把子进程的id保存到pid[i] */
pid[i] = child_pid ;
}
当执行到for循环时,我们用一个child_pid接受返回值,若其值为0则子进程执行if条件里的语句(我在语句结束时加了个break,一会再说),那么它会执行下面的两句代码:
proc_number = i ;
do_something() ;
我们知道proc_number是一个全局变量,我们将i,也就是创建的第几个子进程的进程号赋值给了proc_number,下一句是一个函数do_something():
void do_something()
{
for(;;)
{
/* 打印子进程自编号。为清晰,在每个号码前加“号码+3”个空格
* 比如号码是1,就打印" 1" */
printf("This is process No.%*d\n",
proc_number+3,
proc_number);
sleep(2); /* 主动阻塞两秒钟,也给其他进程执行的机会 */
}
}
可以发现,这是一个死循环,子进程不断的打印它的进程号,而一共有child_proc_number个子进程被创建而且它们都执行这段代码,只是其中的proc_number(进程号)不一样。看完子进程我们回到第一个for循环:
for (i=0; i<child_proc_number; i++)
{
/* 建立child_proc_number个子进程*/
child_pid = fork() ;
if(child_pid == 0){
proc_number = i ;
do_something() ;
break ;
}
* 父进程把子进程的id保存到pid[i] */
pid[i] = child_pid ;
}
可以发现父进程每创建一个子进程,会将其child_pid(系统内部标识--pid)保存在数组pid[]内,当创建了child_proc_number个子进程后循环结束,父进程执行如下代码:
/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
* 输入q退出 */
while ((ch = getchar()) != 'q')
{
if (isdigit(ch))
{
/* 向pid[ch-'0']发信号SIGTERM,杀死该子进程 */
kill(pid[ch-'0'], SIGTERM) ;
}
}
/* 杀死本组的所有进程 */
kill(0, SIGTERM) ;
return;
接下来的代码比较好理解,键盘接受用户的一个输入,杀死对应的一个子进程,若输入1则杀死1号进程,若输入q则退出系统,kill()函数是杀死子进程的函数,接收参数为子进程pid时杀死对应子进程,接收0时杀死本组的所有子进程,若输入q,则会传0杀死本组所有子进程,以防多余的资源占用。
程序分析到这其实我们已经可以预测这个程序的功能了:
child_proc_number个子进程每隔两秒打印打印一次自己的进程号,而父进程等待用户杀死进程的输入,若按1则杀死1号进程,则一号进程不会再打印自己的pid,若按q则退出系统,程序结束。
我们来看实际运行结果:
从图中我们发现基本与我们预想的一致,但还可以发现进程们的运行顺序是无序的。
讲完以上部分,我再来说说那个if语句里的break是什么作用?
部分人问,这个break根本没有用为啥还要加在程序里?
我们接着看:
for (i=0; i<child_proc_number; i++)
{
/* 建立child_proc_number个子进程*/
child_pid = fork() ;
if(child_pid == 0){
proc_number = i ;
do_something() ;
break ;
}
* 父进程把子进程的id保存到pid[i] */
pid[i] = child_pid ;
}
我们设想,若子进程执行的do_someing()函数是这样的:
void do_something(){
printf("I'm child process No.%d\n", proc_number);
}
程序会变成什么样?
我们来分析:
主进程创建了第一个子进程后,这个子进程会执行do_something,do_something只打印一个进程号后结束返回主函数,若是没有break此时的子进程会执行和父进程相同的代码,它同样会创建子进程,所以为了避免创建过多的子进程(这些是删不掉的),我加了一个break跳出循环,这就是break的作用。
最后有一个小问题,如果不加break,会创建多少个子进程?(滑稽)
这个留给大家来分析0.0
来源:CSDN
作者:dms2017
链接:https://blog.csdn.net/dms2017/article/details/103479770