fork系统调用(执行起点,作业分析)

子进程的执行起点

第一次知道fork()时,就有点疑惑子进程到底是从哪里开始执行的。既然fork之后是相互独立的关系,子进程也该从头开始,但是如果从头开始又会遇到已经在父进程调用过的fork,那这样会一直fork下去没有尽头。

机器不会骗人,在一个fork调用前加一句输出语句我们来看看结果。

    int x = 1;
	printf("before fork x = %d ",x);
	//fflush(stdout);
    pid_t pid = fork();
    if (pid == 0) {
	printf("Child has x = %d\n", ++x);
    } 
    else {
	printf("Parent has x = %d\n", --x);
    }
    printf("Bye from process %d with x = %d\n", getpid(), x);

在这里插入图片描述
输出了2次before fork,于是我怀着子进程从头开始执行的信念改良了一下代码:printf("before fork x = %d \n",x);,再次编译运行结果如下:在这里插入图片描述
我傻了,说好的遇到\n即刻输出呢,第二个before fork被吃了吗?(其实这里代码的确改良了,是我的脑子没改良过来)

我被第一个结果误导的脑子在看到这篇博文后终于纠过来了:https://www.cnblogs.com/QingCHOW/p/4588575.html
这篇博客里有几个点很关键,对我们正确理解fork()也很有帮助:
1、子进程继承了父进程的PC-程序计数器
2、子进程继承了fork()语句执行时当前的环境(包括缓冲区),而不是程序的初始环境。

而我的初始程序出现这样的误导性结果,正是因为没有\n,父进程里的before fork被放在缓冲区里继承给子进程了,所以子进程在遇到\n后会把历史遗留的before fork一并输出来。(为我的莽撞自罚一杯)
所以,子进程是从父进程调用fork后接下来的指令开始执行的。(pc计数器是关键)

CSAPP教材课后题分析(进程图,运行结果)

先把几个题的代码发一下(函数名fork813代表是课后作业8.13题)
(之前在edge浏览器复制代码的时候编辑器崩了,而我还没保存,一切重来,一瓶苦酒入喉)
(果然换个浏览器就没有复制不了长代码的问题了)

#include"csapp.h"
int counter = 1;
pid_t Fork(void)//简单包装一下fork()
{
	pid_t pid;

	if ((pid = fork()) < 0)
		printf("Fork error");
	return pid;
}
void end(void) {//8.18要用
	printf("2"); fflush(stdout);
}

void fork813()
{
	int x = 3;
	if (Fork() != 0)
		printf("x=%d\n", ++x);
	printf("x=%d\n", --x);
	exit(0);

}
void fork814()//这里对于作业里的doit(),剩余代码在main中的对应case里
{
	if (Fork() == 0) {
		Fork();
		printf("hello\n");
		exit(0);
	}		
	return;
}
void fork815()//这里对于作业里的doit(),剩余代码在main中的对应case里
{
	if (Fork() == 0) {
		Fork();
		printf("hello\n");
		return;
	}
	return;
}
void fork816() {

	if (Fork() == 0) {
		counter--;//全局变量,初值在第二行定义了
		printf("in child,counter=%d\n", counter);
		exit(0);
	}
	else
	{
		wait(NULL);
		printf("finaly,counter=%d\n", ++counter);
	}
	exit(0);
}
void fork818() {
	if (Fork() == 0)
		atexit(end);
	if (Fork() == 0) {
		printf("in child:0"); fflush(stdout);
	}else {
		printf("in parent:1"); fflush(stdout);
	}
	exit(0);
}
void fork819(int n) {
	int i;
	for (i = 0; i < n; i++)
		Fork();
	printf("hello\n");
	exit(0);
}
int main(int argc, char *argv[])
{
    int option = 0;
	int n = 4;
    if (argc > 1)
	option = atoi(argv[1]);
    switch(option) {
    case 813: fork813();
	break;
    case 814: fork814();
		printf("hello\n");
		exit(0);
	break;
    case 815: fork815();
		printf("hello\n");
		exit(0);
	break;
   case 816: fork816();
	break;
   case 818: fork818();
	   break;
   case 819: 
			 fork819(n);
	   break;
	default:
	printf("Unknown option %d\n", option);
	break;
    }
    return 0;
}

在这里插入图片描述
8.13到8.15的输出,再对照一下13,14的进程图(随手在题目旁边画的,字丑画质渣见谅)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
根据观察,每个程序,无论进程总数是2个还是2个以上,最后上图这个只出现两次,且第一个头头(我太菜了,我不知道它叫什么)输出的内容就是最开始的父进程(也就是进程图最底下那条线)的输出,后面的一代子进程和二代,三代子进程都算做一个大的子进程在第二个头头输出。(不知道其他系统是不是这个效果)

然后是16,18,19的运行结果。
(16题我脑子又想岔了好一阵儿,总觉得该输出2,时刻谨记:相同但独立的地址空间,独立独立独立)
在这里插入图片描述
接下来详细讲一下8.19吧,其实fork的原理搞清楚了这题也不难,就是第一次做的时候选错了分析方向有点被自己绕晕。
放一下进程图和分析草稿(依然是手写见谅
在这里插入图片描述
子进程等比数列求和加上最原先的的1个祖宗父进程,一共有为2^n个进程,也即hello的输出数量。现在对照运行结果,给n的赋值为4,数了一下的确是16个hello输出,没毛病。

遗留疑问(好的,其实是我眼神的问题)

在这里插入图片描述
在这里插入图片描述
这里有个很迷的地方,就是8.19明明应该有4个进程,输出应该有6个数字,但运行却只有5个数字,检查了一下,少的还就是最祖宗的那个进程,想不通。

没问题了,谁能想到呢,原来真相只有一个,是我眼神不好:在这里插入图片描述
下次没有换行没有空格的输出一定要睁大眼睛。。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值