apue学习第十二天——进程控制1(第八章)

我们开始第八章啦,process control。要说UNIX的进程控制,当然首先说进程的创建、执行和终止啦,对应的也就是fork, exec, exit。那么我们首先来说fork。

要说fork,当然先看定义啦:

#include <unistd.h>
pid_t fork(void);
    /*Returns: 0 in child, process ID of child in parent, −1 on error*/
其实我们从定义看不出来什么,只是知道如果返回0那就代表当前执行的是子进程,返回process ID(>0)代表当前执行的是父进程,返回-1当然是错误啦。其实,初学者(包括我)看定义,只是模糊的知道个概念,所以细节,就要看如下代码啦(fork代码1):

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

int main(void)
{
	pid_t pid;
	char *message;
	int n;
	pid = fork();
	if (pid < 0) {
		perror("fork failed");
		exit(1);
	}
	if (pid == 0) {
		message = "This is the child\n";
		n = 6;
	} else {
		message = "This is the parent\n";
		n = 3;
	}
	for(; n > 0; n--) {
		printf("%s", message);
		sleep(1);
	}
	return 0;
}
往往初学者的困惑就在if(pid == 0)之后,前面的很容易懂,我提两点:

(1) pid_t是fork返回的格式,用就行了; (2) pid<0的话,说明子进程没创建成功,当然相应的errno也会被设置。

那好,要想懂后面的东西,直接下面的运行过程图:


我们没有直接看运行结果,直接来看过程图,就是为了通过分析过程来导出结果,最后再验证结果对不对。我们来看分析:

(1) 父进程初始化,它有一个非负整型的唯一ID,有PCB(包括ID,进程状态,内存信息,fd table等等)。

(2) 执行到了fork,那么该进程就会创建它的子进程。怎么创建呢?子进程获得父进程数据空间、堆、栈的copy,父子进程不共享这些东西,那共享什么呢?共享.text段(当然在后面file sharing也会共享file tables,先不提这个)。那子进程是不是在创建时立即获得父进程的这些信息的copy呢?不是,有种Copy-On-Write(COW)技术,初始exec时把这数据段、堆栈设为只读并对父子进程共享,谁要修改这些值的时候再copy。

或者可以这样说,这个时候父子进程看起来完全一样,fork刚进kernel,还没有返回;

(3) “调用一次,返回两次”。来吧,现在有两个一模一样的进程都等待fork从kernel中返回。先返回哪个呢?不知道。这个取决于内核的调度算法。我们知道的是,如果fork返回了值,那么它给父进程返回的是刚创建的子进程ID(>0),给子进程返回的是0;

(4) 我们说了,哪个先返回,哪个先执行,取决于调度算法,我们不知道。好吧,那就假设(!)父进程先返回吧!那么它当然执行else里面的东西:初始化message = "This is the parent"; n = 3; 好啦,这个过程其实很快,我们假设(!)子进程还没返回,那么就继续执行,进入for语句,执行第一个printf,完毕后sleep(1)。

(5) 那好,这1秒钟对计算机来说很长了,假设(!)这时候子进程的pid==0也返回啦。在父进程等待的1秒钟里,它执行到printf,并也是sleep(1)。

(6) 所以,结果很有可能是这样的:父子进程交替执行,直到结束。

我们有很多假设(!),所以执行顺序也有很大的不确定性。来,看实际运行的结果:

$ ./a.out 
This is the child
This is the parent
This is the child
This is the parent
This is the child
This is the parent
This is the child
$ This is the child
This is the child
啊~差不多吧,只是在这个结果中,fork先返回了子进程。但是!哎?倒数第二行是怎么回事?那个$在那里怎么那么碍眼。原因嘛,是这样的:

首先,这个程序是在shell下运行的吧?那么这个parent的父进程是谁?对啦!是shell!parent执行完最后一个printf之后,当然要exit,虽然此时它的子进程还没有执行完呢,但shell可不知道这个最小的子进程的存在,所以就直接输出$符号啦!(其实这个时候shell又可以接受其它命令正常工作了)但这个失去了爸比的子进程该怎么办呢?它被init(pID=1)收养(inherited)啦,我们后边会说到这个“孤儿院”进程和另外一个重要的swapper。如果此时打印这个被收养的子进程的父ID(getppid),就会发现它是1啦!

那么,OK,fork代码1我们分析完毕。


好困啊,回去睡觉去,明天接着写。







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值