APUE 第八章 进程管理 子进程领养问题探讨

         终于开始学习APUE的进程管理这章了,其实看书到现在终于发现,我们读一本书,并且确定这本书是好书,然后认真读这本书。但是当前我们已经没有太多时间去逐句逐字读,所以需要我们有所甄别。可以只看自己感兴趣的地方,比如我对进程管理这部分感兴趣,所以可以直接去看进程管理这章,而不用去看文件或者IO。

        下述程序为fork的使用示例。Program List 8-1

#include "apue.h"
int glob = 6;
char buf[] = "a write to stdout\n";
int main()
{
	int	var;
       volatile	pid_t	pid,pid2;

	var = 88;
	if(write(STDOUT_FILENO, buf, sizeof(buf)-1 ) != sizeof(buf)-1 )
		err_sys("write error");
	printf("before fork\n");
	if(((pid = fork()) < 0) )
	err_sys("fork errir");
	else if(pid == 0)
	{
		//printf("the pid = fork() returns %d\n",pid);
		glob++;
		var++;
		//sleep(10);//the child block itself,when its parent dies after sleep for 2s,the child will be an orphan
			  //then will be adobted by the process init 
	}
	else sleep(2);
	printf("pid = %d, glob = %d,var =%d\n",getpid(),glob,var);
	//printf("the pid of parent is getppid = %d\n",getppid());//return the pid of parent of the process
	//printf("the sizeof buf is %d\n",sizeof(buf));
	//printf("the strlen of buf is %d\n",strlen(buf));
	exit(0);
}
有几个比较有意思的地方。

对程序进行一定修改(其实就是不要注释某些语句)。

#include "apue.h"
int glob = 6;
char buf[] = "a write to stdout\n";
int main()
{
	int	var;
       volatile	pid_t	pid,pid2;

	var = 88;
	if(write(STDOUT_FILENO, buf, sizeof(buf)-1 ) != sizeof(buf)-1 )
		err_sys("write error");
	printf("before fork\n");
	if(((pid = fork()) < 0) )
	err_sys("fork errir");
	else if(pid == 0)
	{
		printf("the pid = fork() returns %d\n",pid);
		glob++;
		var++;
		//sleep(10);//the child block itself,when its parent dies after sleep for 2s,the child will be an orphan
			  //then will be adobted by the process init 
	}
	else sleep(2);
	printf("pid = %d, glob = %d,var =%d\n",getpid(),glob,var);
	printf("the pid of parent is getppid = %d\n",getppid());//return the pid of parent of the process
	//printf("the sizeof buf is %d\n",sizeof(buf));
	//printf("the strlen of buf is %d\n",strlen(buf));
	exit(0);
}
         关于sizeof和strlen的区别,在于sizeof计算字符串buf的长度包含最后的NULL字节(其实就是“\0”),而使用strlen则不计算NULL字节。另一个区别就是sizeof在编译时就已经计算缓冲区长度,所以长度固定,而strlen需要一次函数调用(不知道这算什么区别)。

a write to stdout
before fork
the pid = fork() returns 0
pid = 12857, glob = 7,var =89
the pid of parent is getppid = 12856
pid = 12856, glob = 6,var =88
the pid of parent is getppid = 12790

运行结果如上

关于父进程。在exit前加入printf("the pid of parent is getppid = %d\n",getppid());,获得父进程,可以看到其中一个进程的父进程pid是另一个进程的pid,说明这个进程是子进程,而另一个是父进程。父进程的父进程,是这个当前打开的这个terminal的进程,这个terminal是bash程序打开的。

PID TTY      STAT   TIME  MAJFL   TRS   DRS   RSS %MEM COMMAND
  928 tty4     Ss+    0:00      2     0  4612   756  0.0 /sbin/getty -8 38400 tty4
  932 tty5     Ss+    0:00      2     0  4612   756  0.0 /sbin/getty -8 38400 tty5
  944 tty2     Ss+    0:00      1     0  4612   756  0.0 /sbin/getty -8 38400 tty2
  945 tty3     Ss+    0:00      1     0  4612   756  0.0 /sbin/getty -8 38400 tty3
  947 tty6     Ss+    0:00      1     0  4612   756  0.0 /sbin/getty -8 38400 tty6
 1002 tty7     Ss+   11:07    117     0 114036 49700  2.4 /usr/bin/X :0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch -background none
 1181 tty1     Ss+    0:00      0     0  4612   864  0.0 /sbin/getty -8 38400 tty1
12790 pts/3    Ss     0:00      0   876  6767  4044  0.1 bash
12871 pts/3    R+     0:00      0    82  4605   708  0.0 ps -av

程序中,我们通过父进程休眠2s以达到父子两进程不同时输出的效果,如果我让子进程也sleep一段时间呢,我的一个意外发现。

我们看到上一个输出结果中, the pid = fork() returns 0,因为父进程fork后,子进程会返回0,这个0代表的是父进程,因为一个进程可能有n个子进程,但是只会有一个父进程,所以返回0就对了,因此会进入那个if(pid == 0)的区域。如果让子进程也sleep,并且sleep(10)(在var++后加)。

效果是

a write to stdout
before fork
the pid = fork() returns 0
pid = 13503, glob = 6,var =88
the pid of parent is getppid = 12790
pid = 13504, glob = 7,var =89
the pid of parent is getppid = 1

子进程为glob和var自加后的结果,因此后两行是子进程的运行结果,但是这次,子进程返回的getppid是1,而不是父进程pid 12503,这是怎么回事呢。

子进程休眠时间长,也就是说,在子进程苏醒继续执行时,父进程已经执行完毕,不存在了,这个时候子进程就成了孤儿进程,然后会被init进程收养,init的pid是1,这样可以保证linux中所有进程都有父进程。这是一个意外的发现,这个巧合帮助我理解了8.5节中的init进程收留,不然真的不好理解,也算意外之喜。从这里我得到的启示是,可以通过进程休眠使进程停下来,这样对于调试很有帮助;学习时多改改样例程序,会有很多不一样的发现。


在8.4节中,介绍了vfork的用法,但是我的结果不太一样。

#include "apue.h"

int glob = 6;
char buf[] = "a write to stdout\n";

int
main ()
{
  int var;
  volatile pid_t pid, pid2;

var = 88;
  printf ("before vfork\n");

  if (((pid = vfork ()) < 0))
    err_sys ("vfork errir");
  else if (pid == 0)
    {
      glob++;
      var++;
      //printf ("pid = %d, glob = %d,var =%d\n", getpid (), glob, var);
      _exit (0);
    }

  printf ("pid = %d, glob = %d,var =%d\n", getpid (), glob, var);
  exit (0);
}
我的结果是

before vfork
pid = 13615, glob = 7,var =88

和书上的答案不一样的是,书上的var是89,而我的是88。奇怪的是glob自加,说明if中语句是执行了的。将if中的那个printf的注释取消掉,打印出来var=89,说明的确执行。我也不是菜鸟,判断是程序执行中的全局变量与局部变量的问题,在int var;前使var 成为volatile变量,就可以看到打印出来89了。所以我认为是优化问题,查看我的Makefile,果然如此,我的是O2优化级别。改为O1,发现结果仍是88,如果改成无忧化,那就是89了,关于gcc的优化问题,也是值得探究的。在PC编程中有这样的小问题,在avr的gcc编程中也有,感觉优化这个东西,真是个双刃剑。之后我会写一篇优化的blog。




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值