这里我们来看看第一个问题
问题:
这里我们可以得到 结束时状态值 status 为 256 ,这里为什么不是 exit()中传入的参数 呢?
如果我们修改子进程中exit 函数传入的值,我们可以发现得到的 status 值 为 传入值乘以 256.
这让我很费解,为什么不直接记录状态值而是要乘以256呢(表面上看来是这样的)?
我们同样可以查找函数源码来找到答案
通过查找 waitpid、_exit、以及WIFEXITED(status)和WEXITSTATUS(status)函数我们可以发现,在实际使用中,status 保存的
虽然是进程结束时的状态,但是,这个状态并不是就只有这一种情况下我们才会使用到。
观察 wait.h 文件
这里面包涵的宏就不止 WIFEXITED 这一个,还有WIFSIGNALED(status),WIFSTOPPED(status),WIFCONTINUED(status)
等,如果直接记录exit的传入参数值的话,就会有很大的局限性,并且很多时候我们不关心进程的退出状态,但是系统或者程序却要
关心,因此这样的做法应该是有它原来设计的时候的打算的。
通过观察,我们可以看到,WIFEXITED 宏做的事情其实就是 把status 和 0x7f 位与然后判断结果是否等于0,这样除非我们传入的exit 的参数为0 否则它的执行结果一定不会是0。而我们需要知道非正常退出的时候只需要判断 WIFEXITED 返回值是否为0就可以了,如不是0 我们则可以 通过 WEXITSTATUS 宏来获取 退出时候的状态值。
这个宏 我们看到这里执行的是 和 0xff00 位与然后向右移动8位,其实这里做的就是取s 的高八位值。原先我们看到的状态值被乘以256 的作用就能在这里体现出来了。因为乘以256其实就是想到与把s 的值 乘以2^8 就相当于向左移8位,这样我们就知道原来 status 的值并不是被乘以了256 而是被左移的8位。
下面是一个测试程序 测试这WEXITSTATUS 宏的:
我们同样可以把 WEXITSTATUS 改成 WIFEXITED 宏 这里我就不写了。
关于问题二
问题: 明明printf 函数在 fork函数之前 为什么 Test 被 打印了两次?
如果我们 把 fork 函数改成 vfork 是什么结果?
如果我们 在第一个printf函数里增加 ‘/n’ 又是什么结果?
为什么Test 被打印了两次呢? 其实原因很简单,printf 是遇到/n 才输出并清空缓冲区的。
观察测试程序执行过程:
首先 父进程中的 Test 被放入缓冲区
接着 生成子进程(注意,这里使用的fork 函数,因此父进程中的缓冲区也被复制到子进程,因此子进程缓冲区中也有 Test 字符),子进程执行printf 的时候遇到 '/n' 于是输出缓冲区的内容
所以输出的是 Test child exit 1.(注意这里有换行符)
同时执行父进程的时候,原先缓冲区的Test并未被清空(因为子进程中缓冲区和父进程中的不是同一个),这样父进程执行prinft 函数的时候 遇到 '/n'也输出自己缓冲区的内容
所以输出的是 Test father exit 1
这样就解释了为什么 Test 被打印了两次。
接着我们把fork 函数改成 vfork 函数结果是什么呢?
还是执行过后我们看结果吧下面是我的执行结果:
Testchild exit 1.
father exit 1
那为什么是这个结果呢?第二个输出为什么没有Test 呢? 原因在于,子进程使用的是vfork 生成的,并没有完全复制了父进程的数据段,而是采用了和父进程读写互斥的方法来使用数据段,因此在子进程执行了printf 之后缓冲区就被清空了,因此轮到父进程执行printf 的时候,缓冲区是没有Test 的。这是vfork和fork 的区别之一。
在第一个printf函数里增加 ‘/n’ 又是什么结果?
我想看到这里所有人都可以不用执行程序就能分析出结果,对了结果就是:
Test
child exit 1.
father exit 1
这个结果也是和我们预料到的结果是一样的。
总结:
对于很多程序,我们在写的时候往往都会忽略一些细节,这些细节往往就是我们程序产生莫名其妙的错误的原因。
在读C缺陷和陷阱的时候,让我深刻体会到了这一点。在这里,我们通过一些我们常常困惑不解的例子来达到一样的效果。
请记住 细节决定成败。