OSTEP:5 Interlude: Process API

5 Interlude: Process API

5.1 The fork() System Call

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
  printf("hello world (pid:%d)\n", (int) getpid());
 int rc = fork();
 if (rc < 0) {
 // fork failed
 fprintf(stderr, "fork failed\n");
 exit(1);
 } else if (rc == 0) {
 // child (new process)
 printf("hello, I am child (pid:%d)\n", (int) getpid());
 } else {
 // parent goes down this path (main)
 printf("hello, I am parent of %d (pid:%d)\n",
 rc, (int) getpid());
 }
 return 0;
 }

首先是对这个的编译,unistd.h为Linux/Unix系统中内置头文件,unix std么。所以win是不能编译的,换到linux去了。

(主要是受到CSAPP关于异常控制流的视频启发,突然想道自己还写过一篇关于这个的,fork这个函数就把它看成是系统调用,创建个与父进程拷贝过来几乎一模一样的子进程,除了pid不一样,甚至连当前PC都一样,然后fork对子进程返回值是0,对父进程返回值是子进程的pid,据此就有上面的根据rc判断是父进程还是子进程的控制逻辑了)

把这命名为p1.c,然后用gcc p1.c编译它,会默认输出个a.out,我们输入个./a.out就能执行了

cbh@ubuntu:~/Desktop/Oprator SYstems Three Easy Pieces$ ls
p1.c
cbh@ubuntu:~/Desktop/Oprator SYstems Three Easy Pieces$ gcc p1.c
cbh@ubuntu:~/Desktop/Oprator SYstems Three Easy Pieces$ ls
a.out  p1.c
cbh@ubuntu:~/Desktop/Oprator SYstems Three Easy Pieces$ ./a.out
hello world (pid:10524)
hello, I am parent of 10525 (pid:10524)
hello, I am child (pid:10525)

第一次玩gcc,肯定要好好玩玩的哈哈,后来的那个gcc a.c还真成功了,只不过报了好多错没法运行便是

在这里插入图片描述

此时我们再看看目录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OvRkIKux-1650302986067)(C:\Users\伊莉雅\AppData\Roaming\Typora\typora-user-images\image-20220418101019758.png)]

a.c居然还是惊人的绿色,区别于p1.c

目前玩够了,现在好好的用gcc,后面加个-o 能够重命名为你想要的,否则就自动变成a.out了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OvRkIKux-1650302986067)(C:\Users\伊莉雅\AppData\Roaming\Typora\typora-user-images\image-20220418101019758.png)]

现在问题来了,我们在回头看看代码,标准的if-else if-else 结构,为啥会输出三段?不应该只输出两段么,或者四段啊?

emm首先,fork的英文意思是

n.叉(挖掘用的园艺工具); 餐叉; (道路、河流等的)分岔处; 岔路; 叉状物; 车叉子;
v.分岔; 岔开两条分支; 走岔路中的一条; 叉运; 叉掘;

我们可以把fork()理解成以父进程为模板创建一个子进程,返回值是子进程先不理fork()的具体机理因为我也不懂,这不是这节的重点,我就理解成复制一个和父进程一模一样的子进程包括执行位置。现在输出三段而不是两段或者四段的理由想必大家已经知道了。

作者说有时候子进程会先执行,就是i am child 在 i am parent 前面,可是我试了好多次都没试出来

5.2 The wait() System Call

 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/wait.h>

 int main(int argc, char *argv[]) {
 printf("hello world (pid:%d)\n", (int) getpid());
 int rc = fork();
 if (rc < 0) { // fork failed; exit
 fprintf(stderr, "fork failed\n");
 exit(1);
 } else if (rc == 0) { // child (new process)
 printf("hello, I am child (pid:%d)\n", (int) getpid());
 } else { // parent goes down this path (main)
 int rc_wait = wait(NULL);
 printf("hello, I am parent of %d (rc_wait:%d) (pid:%d)\n",
 rc, rc_wait, (int) getpid());
 }
 return 0;
 }

上面是p2.c的代码,下面我们来试试它

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J2Ja948F-1650302986070)(C:\Users\伊莉雅\AppData\Roaming\Typora\typora-user-images\image-20220418104024910.png)]

好了,加了wait后子进程就先输出了,那,wait(NULL)又是什么意思呢,先放着吧,我们后面再看

5.3 Finally, The exec() System Call

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

 int main(int argc, char *argv[]) {
 printf("hello world (pid:%d)\n", (int) getpid());
 int rc = fork();
 if (rc < 0) { // fork failed; exit
 fprintf(stderr, "fork failed\n");
 exit(1);
 } else if (rc == 0) { // child (new process)
 printf("hello, I am child (pid:%d)\n", (int) getpid());
 char *myargs[3];
 myargs[0] = strdup("wc"); // program: "wc" (word count)
 myargs[1] = strdup("p3.c"); // argument: file to count
 myargs[2] = NULL; // marks end of array
 execvp(myargs[0], myargs); // runs word count
 printf("this shouldn’t print out");
 } else { // parent goes down this path (main)
 int rc_wait = wait(NULL);
 printf("hello, I am parent of %d (rc_wait:%d) (pid:%d)\n",
 rc, rc_wait, (int) getpid());
 }
 return 0;
 }

上面是代码,下面是运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tVQNH1p-1650302986072)(C:\Users\伊莉雅\AppData\Roaming\Typora\typora-user-images\image-20220418110223947.png)]

简单的分析

好了,两个例子基本也看完了,相信大家也看得差不多了,我们在这姑且再分析一下这个程序的小细节

1.wait(NULL)是干嘛的

用途:等待创建的子进程执行完成后执行

引出的小问题

1.如果有多个子进程,那么返回的值是哪个进程号(PID)?

2.在不使用fork()的情况下,单独使用wait()会怎么样?

2.strdup(“wc”)又是干嘛的

这个函数用途,是新建一个区域,把"wc"复制到这块区域来,然后返回这个区域的指针

自己的尝试

一.多个子进程wait(NULL)的情况

1.多加一个fork()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

 int main(int argc, char *argv[]) {
 printf("hello world (pid:%d)\n", (int) getpid());
 int rc = fork();
 int rc1=fork();	//new add code compaerd with p3.c!!!Pay Attention!!!
 if (rc < 0) { // fork failed; exit
 fprintf(stderr, "fork failed\n");
 exit(1);
 } else if (rc == 0) { // child (new process)
 printf("hello, I am child (pid:%d)\n", (int) getpid());
 char *myargs[3];
 myargs[0] = strdup("wc"); // program: "wc" (word count)
 myargs[1] = strdup("p3.c"); // argument: file to count
 myargs[2] = NULL; // marks end of array
 execvp(myargs[0], myargs); // runs word count
 printf("this shouldn’t print out");
 } else { // parent goes down this path (main)
 int rc_wait = wait(NULL);
 printf("hello, I am parent of %d (rc_wait:%d) (pid:%d)\n",
 rc, rc_wait, (int) getpid());
 }
 return 0;
 }

结果非常有意思

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLKWkuVf-1650302986073)(C:\Users\伊莉雅\AppData\Roaming\Typora\typora-user-images\image-20220419003816410.png)]

父进程(4054)分别创造两个子进程(fork创建4055,其rc为0)(fork1创建4057,其rc为4055),其中进程(4055)又会创造一个子进程4056(因为fork1,其rc也为0),感觉可以用来当考试题恶心人了…

(在后面的分析中可以发现我这段是在胡扯。。。)

为了阐述为什么这个rc为0我做了个小例子,貌似gcc会默认局部变量为0而不是VS中未初始化,注意看这个0输入在哪…在第二行首端,因为我没输出换行符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pjp7KUCA-1650302986074)(C:\Users\伊莉雅\AppData\Roaming\Typora\typora-user-images\image-20220419005104425.png)]

2.直接wait(NULL)

在这里插入图片描述

这回自己学乖了加了个换行符,结果是-1,估计是不用等待就输出-1

3.综合上面两个下

我们还有个问题没解决,wait(NULL)在有多个子进程,那么返回的值是哪个进程号(PID)?让我们来多加几个fork看看结果,子进程调用顺序咱不知道,反正我们可以肯定的是,进程号最小的肯定是父进程

在这里我们加了3个fork(),总共会有7个进程生成
在这里插入图片描述

好了,惊讶的事情来了,我们测试了5个案例(一如既往左边是测试代码),第一组父进程11064与wait(NULL)返回值之差是3,第二组是1,第三组是3,第四组是1,第五组也是1。再给大家补两组好玩的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZxstrOys-1650302986076)(C:\Users\伊莉雅\AppData\Roaming\Typora\typora-user-images\image-20220419011420792.png)]

第一组差是3,但是仔细看看他的位置和含有-1行的位置,是不是感觉很混乱?以为只有1和3?我们再看看第四组,差值是4哈哈,所以wait(NULL)在有多个子进程,那么返回的值是哪个进程号(PID)这个问题感觉很难在实际中得出解答。

下面我们看看我们搜索得到的一些回复

stackoverflow :wait(NULL) will block parent process until any of its children has finished.

博客园 :父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

感觉自己好像懂了点什么,一旦父进程wait能够捕捉到一个子进程变成zombie(僵尸)状态,就触发它销毁它,也得到这个子进程的PID,看上去好像是一一对应的关系,现在我们再来实践下

~~[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EARZ5ZxG-1650302986077)(C:\Users\伊莉雅\AppData\Roaming\Typora\typora-user-images\image-20220419012634177.png)]~~

真的是这样呢,好耶!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值