循环创建N个子进程

以循环创建5个进程为例,给出如下代码,分析其错误

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void)
{int i;pid_t pid;printf("xxxxxxxxxxx\n");for (i = 0; i < 5; i++){pid = fork( ); if(pid == -1){printf("process of %u creat process failurely!\n",getpid( ));perror("fork");}else if(pid == 0){printf("I'am %dth child , pid = %u\n", i+1, getpid());}else{printf("I'am parent, pid = %u\n",getpid());}}printf("yyyyyyyyyy\n");return 0;
}

分析:首先在shell中执行该文件时,由终端进程fork产生一个子进程来执行该程序,然后在for循环体中,子进程在创建一个个的孙进程。在上述for循环体中,i=0时,父进程创建了一个子进程,此时父进程与子进程的i都为0(刚fork后两个的i相等,但是以后不一定相等,它们各自独立)。此时有两个进程(父、子进程)都会开始向下执行,即后面的代码都一样的执行,各个进程一直执行到return语句后,各个进程才会自动终止(结束)。上述,在for循环体中创建的子进程,又会在下一次循环中继续去创建子进程,因此最终并不仅仅创建的是5个进程,而是共创建了25-1个进程,总共25个进程。如果循环n次,则总共为2n个进程。

因此,需要在循环的过程,保证子进程不再执行fork ,因此当(fork() == 0)时,子进程应该立即break;才正确(即跳出循环体)。

 

练习:通过命令行参数指定创建进程的个数,如:第1个子进程休眠0秒打印:“我是第1个子进程”;第2个进程休眠1秒打印:“我是第2个子进程”;第3个进程休眠2秒打印:“我是第3个子进程。”

通过该练习掌握框架:循环创建n个子进程,使用循环因子i对创建的子进程加以区分。

//代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char *argv[ ])
{if(argc < 2){printf("./a.out 5\n");exit(1);}int i;pid_t pid;printf("xxxxxxxxxxx\n");long int a = strtol(argv[1],NULL,10);  //将字符串转化为10进制整数for (i = 0; i < a; i++){pid = fork( );  //创建子进程if(pid == -1){printf("process of %u creat process failurely!\n",getpid( ));  perror("fork");}  //判断创建进程是否成功,如果当次循环创建不成功,则不结束该进程,进行下一次循环,再次尝试创建(这样等效于少了一次循环)。else if(pid == 0)  //如果为子进程,则跳出循环{break;}else    //否则(父进程),不执行操作,进入下一次循环;}sleep(i);  //通过i值来区分进程,可见父进程睡眠时间最久,为a秒,最先创建的子进程睡眠时间最少,为0秒。if(i < 5)printf("I'am the %dth child process, the ID = %u\n",i+1,getpid( )); //子进程输出elseprintf("I'am parent process, the ID = %u\n",getpid( ));  //父进程输出return 0;
}

[root@localhost fork]# ./fork_test 5  //shell终端fork产生子进程来运行这一程序

xxxxxxxxxxx 

I'am the 1th child process, the ID = 16507

I'am the 2th child process, the ID = 16508

I'am the 3th child process, the ID = 16509

I'am the 4th child process, the ID = 16510

I'am the 5th child process, the ID = 16511

I'am parent process, the ID = 16506

[root@localhost fork]#

分析:之所以要引入sleep函数,来使各个进程睡眠,是为了确保父进程最后结束(即最后执行return),且越先创建的子进程越先能够结束。而在创建进程后,每个进程的i值都由自己维护,都要从创建处开始执行自己的代码,从而i值发生改变。因此就可以用i来区分是哪一个进程,从而越先创建的进程睡眠时间越少,第i个子进程睡眠时间为i-1秒。下面深度分析sleep函数,代码如下:

//与上面的代码相比,只是去掉了一行内容: sleep(i);  因此不再列出,其执行结果如下:

[root@localhost fork]# ./fork_test 5

xxxxxxxxxxx

I'am parent process, the ID = 16702

I'am the 3th child process, the ID = 16705

[root@localhost fork]# I'am the 1th child process, the ID = 16703

I'am the 2th child process, the ID = 16704

I'am the 5th child process, the ID = 16707

I'am the 4th child process, the ID = 16706

pwd   //正常执行shell中的pwd命令(前面标签已经输出)

/mnt/hgfs/share/01_process_test/fork

[root@localhost fork]#

 

分析:由上可以看出,在没有sleep( )函数的控制下,每个进程的结束先后顺序是随机的,没法控制的。在上述程序执行过程中,总共参与了7个进程:shell终端进程、父进程(由shell终端fork产生)和5个子进程(由父进程fork产生),这7个进程对CPU的抢占是公平的(随机的),无法预测。注意一点:父进程只能够知道其子进程是否结束,而不能直到其孙进程是什么状态,这7个进程共同使用这一个终端,当shell中执行. /fork_test 5时,shell进程会把前台交给其子进程使用,一旦子进程结束(执行了return后),shell进程知道并马上收回前台,并输出[root@localhost fork]# 光标  等待与用户再次交互(此时shell进程放弃了CPU,将自己阻塞起来,等待用户的命令),但是此时那5个子进程并不一定就结束了,因此未结束的进程将会继续占用CPU,直到执行到return并结束。因此,这些进程的输出结果会在 [root@localhost fork]#的后面。CPU1s内可执行上亿条指令,因此睡1s可以绝对保证进程可以按照希望的顺序执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385373.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++ Primer | 19】控制内存分配

1. 测试代码&#xff1a; #include <iostream> #include <new> #include <cstring> #include <cstdlib> using namespace std;void* operator new(size_t size) {cout << "global Override operator new" << endl;if (void* p…

getuid、geteuid、getgid和getegid函数

#include <unistd.h> #include <sys/types.h> uid_t getuid(void); uid_t geteuid(void); 作用&#xff1a;getuid返回当前进程的实际用户ID&#xff1b;geteuid返回当前用户的有效用户ID。这两个总是成功&#xff0c;不会失败。 #include <unistd.h> #…

【第15章】虚函数

一、为什么基类中的析构函数要声明为虚析构函数&#xff1f; 直接的讲&#xff0c;C中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说&#xff0c;如果派生类中申请了内存空间&#xff0c;并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数&am…

进程共享(读时共享写时复制)

父子进程之间在刚fork后。父子相同处: 全局变量、.data、.bbs、.text、栈、堆、环境变量、用户ID、宿主目录&#xff08;进程用户家目录&#xff09;、进程工作目录、信号处理方式等等&#xff0c;即0~3G的用户空间是完全一样的。父子不同处: 1.进程ID 2.fork返回值 3.父进…

【C++ Primer | 08】IO库

一、istringstream类 描述&#xff1a;从流中提取数据&#xff0c;支持 >> 操作 这里字符串可以包括多个单词&#xff0c;单词之间使用空格分开 #include <iostream> #include <sstream> using namespace std; int main() {istringstream istr(&quo…

gdb调试(如何跟踪指定进程)

使用gdb调试的时候&#xff0c;gdb只能跟踪一个进程。可以在fork函数调用之前&#xff0c;通过指令设置gdb调试工具跟踪父进程或者是跟踪子进程。默认跟踪父进程。 set follow-fork-mode child 命令设置gdb在fork之后跟踪子进程。 set follow-fork-mode parent 设置跟踪父进程…

【Leetcode | 01】Backtracking

回溯算法序号题号117. 电话号码的字母组合222. 括号生成 39. 组合总和 40. 组合总和 II 46. 全排列 47. 全排列 II 60. 第k个排列 77. 组合 78. 子集 90. 子集 II 93. 复原IP地址 131. 分割回文串 216. 组合总和 III 306. 累加数 357. 计算各个位数不同的数字个数 401. 二进…

1033. 旧键盘打字(20)

旧键盘上坏了几个键&#xff0c;于是在敲一段文字的时候&#xff0c;对应的字符就不会出现。现在给出应该输入的一段文字、以及坏掉的那些键&#xff0c;打出的结果文字会是怎样&#xff1f; 输入格式&#xff1a; 输入在2行中分别给出坏掉的那些键、以及应该输入的文字。其中对…

1038. 统计同成绩学生(20)

本题要求读入N名学生的成绩&#xff0c;将获得某一给定分数的学生人数输出。 输入格式&#xff1a; 输入在第1行给出不超过105的正整数N&#xff0c;即学生总人数。随后1行给出N名学生的百分制整数成绩&#xff0c;中间以空格分隔。最后1行给出要查询的分数个数K&#xff08;不…

EXEC函数族的一般规律

事实上&#xff0c;只有execve是真正的系统调用&#xff0c;其它五个函数最终都调用execve&#xff0c;所以execve在man手册第2节&#xff0c;其它函数在man手册第3节。这些函数之间的关系如下图所示。

exit与_exit函fork与vfork函数

#include <stdlib.h> void exit(int status); #include <unistd.h> void _exit(int status); exit函数与_exit函数一样&#xff0c;都是系统函数&#xff0c;且都是用来终止一个进程的&#xff0c;无论在程序中的什么位置&#xff0c;只要执行这exit或_exit系统…

1042. 字符统计(20)

请编写程序&#xff0c;找出一段给定文字中出现最频繁的那个英文字母。 输入格式&#xff1a; 输入在一行中给出一个长度不超过1000的字符串。字符串由ASCII码表中任意可见字符及空格组成&#xff0c;至少包含1个英文字母&#xff0c;以回车结束&#xff08;回车不算在内&#…

孤儿进程与僵尸进程

孤儿进程&#xff1a;父进程先于子进程结束&#xff08;遇到return、exit、异常终止等情况时&#xff09;&#xff0c;则子进程成为孤儿进程&#xff0c;子进程的父进程成为init进程&#xff0c;称为init进程领养孤儿进程。可以通过getppid函数来查看孤儿进程的父进程ID&#x…

1043. 输出PATest(20)

给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序&#xff0c;按“PATestPATest....”这样的顺序输出&#xff0c;并忽略其它字符。当然&#xff0c;六种字符的个数不一定是一样多的&#xff0c;若某种字符已经输出完&#xff0c;则余下的字符仍按P…

wait函数

#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); 僵尸进程。进程结束后放弃了几乎所有的内存空间&#xff0c;没有任何可以执行的代码&#xff0c;也不能被调度&#xff0c;仅仅在进程列表中保留着一个位置&#xff0c;记载着该进程的退出…

map与unordered_map

时间复杂度&#xff1a; mapunordered_mapOrderingincreasing orderno orderImplementationSelf balancing BSTHash Tablesearch timelog(n) O(1&#xff09;: 平均水ping O(n&#xff09;&#xff1a;最糟糕情况 Insertion timelog(n) RebalanceSame sa searchDelete timelo…

waitpid函数

#include <sys/wait.h> #include <sys/types.h> pid_t waitpid(pid_t pid, int *status, int options); 作用&#xff1a;同wait&#xff0c;但可指定pid进程清理&#xff0c;可以不阻塞。 waitpid函数的第二个参数int *status跟wait函数的形参一样&#xff0c;…

进程间通信的方法

Linux环境下&#xff0c;进程地址空间相互独立&#xff0c;每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程和进程之间不能相互访问&#xff0c;要交换数据必须通过内核&#xff0c;在内核中开辟一块缓冲区&#xff0c;进…

管道的概念(匿名管道)

管道是一种最基本的IPC机制&#xff0c;作用于有血缘关系的进程之间&#xff0c;完成数据传递。调用pipe系统函数即可创建一个管道。管道有如下特质&#xff1a;1.其本质是一个伪文件&#xff08;实为内核缓冲区&#xff09;&#xff0c;伪文件即不是真正的文件&#xff0c;不占…

1023. 组个最小数 (20)

给定数字0-9各若干个。你可以以任意顺序排列这些数字&#xff0c;但必须全部使用。目标是使得最后得到的数尽可能小&#xff08;注意0不能做首位&#xff09;。例如&#xff1a;给定两个0&#xff0c;两个1&#xff0c;三个5&#xff0c;一个8&#xff0c;我们得到的最小的数就…