第二课 关于 exit、_exit、fork、vfork、printf以及wait和waitpid的学习

来源:互联网 发布:c语言解惑刘振安 编辑:程序博客网 时间:2024/04/29 11:13

一、exit _exit

    简单来说,两个函数都是为了要退出某个进程,主要区别在于
        exit  简单的来说就是进程直接退出,并对缓冲区数据结构等做一些善后处理。
这些善后处理一般是刷新I/O缓冲区。 
        _exit 清空各种数据结构 (缓冲区),并不刷新I/O缓冲区。
    传入参数 0 为正常结束,其他数值为异常出错

 

二、fork vfork 区别

 

        fork : 子进程拥有父进程的数据段、堆和栈的副本,(相当于新的一个数据段,堆栈)父进程和子进程共享代码段
       vfork :     共享父进程的数据段,代码段,由于在vfork后经常是跟着一个exec执行一个新的程序不会在用
到原来的地址空间,
       所以vfork的子进程在调用execexit之前是在父进程的空间里运行的。因此对于数据段的读写和父进程
是互斥的。
看别人的精辟分析看到得多了才知道基础的重要性,操作系统的课还好不是白上的。
下面是一位达人的分析我这里引用下:
fork  vfork 函数执行时,操作系统创建一个新的进(子进程),
并且在进程表中相应为它建立一个新的表项。新进程和原有进程的可执行程序是同一个程序;上下文
和数据,绝大部分就是原进程(父进程)的拷贝,但它们是两个相互独立的进程!此时程序寄存器pc
在父、子进程的上下文中都声明,这个进程目前执行到fork调用即将返回(此时子进程不占有CPU,子
进程的pc不是真正保存在寄存器中,而是作为进程上下文保存在进程表中的对应表项内)。对应的进
程返回代码指针指向的下一句。 

 

三、wait waitpid 区别联系

 

函数原型:pid_t waitpid(pid_t pid, int *status, int options)
从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里
有不同的意义。
1.pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的
子进程还没有结束,waitpid就会一直等下去。 
2.pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpidwait的作用一模一样。 
3.pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做
任何理睬。 
4.pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
 
options提供了一些额外的选项来控制waitpid WNOHANGWUNTRACED两个选项,这是两个常数,
可以用"|"运算符把它们连接起来使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
书上说:WNOHANG参数,若由指定的子进程不立即可以用,则waitpid不阻塞,此时返回0
其实我们简单的来说,就是指 使父进程不会阻塞。
WUNTRACED参数,的解释则是:若实现某支持作业控制,则由pid指定的任一进程状态已暂停,且
其状态自暂停以来还未报告过,则返回其状态。
      
简单的说就是:如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。 (子进程的结束状
态返回后存于 status
 
函数原型:pid_t wait(* status)
进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让
它找到了这样一个已经变成僵死的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;
如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
区别和联系:
Wait其实就是经过包装的waitpid 其具体的实现可以通过查阅源码资料发现:
static inline pid_t wait(int * wait_stat)
{
       return waitpid(-1,wait_stat,0);
}

 

四、关于 printf 函数

这里关于格式化的输出我就不多做讨论了,在我的另篇中就有网络搜集的关于格式化输入输出的内容,
有兴趣的可以去看看。但是 LINUX 下的printf 函数 是在 遇到 /n 才从缓冲区读出 数据的,这一点是
非常值得关注的。如果没有注意到经常出来的结果让我们大吃一惊,因为这并不是我们想要的,但是
我们却很难发现问题所在。
看到这里,对于我们外行来说就会有很多问题。例如:
参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果看到的很多程
序对这个子进程是如何死掉的都是不在意的,只想把这个僵死进程消灭掉,因此都会写成这样
  
这很容易让我们忽略 进程退出状态的判断。
这是一个测试程序,是网上抄的,被我稍微改动了下:
 
问题:
这里我们可以得到 结束时状态值 status  256 ,这里为什么不是 exit()中传入的参数 呢?
如果我们修改子进程中exit 函数传入的值,我们可以发现得到的 status   传入值乘以 256.
 这让我很费解,为什么不直接记录状态值而是要乘以256呢?

 

下面一个程序是关于 fork printf 函数 注意点的学习:

 

       执行的结果:

Test child exit 1.

Test father exit 1

       问题: 明明printf 函数在 fork函数之前 为什么 Test 打印了两次?

                 如果我们 fork 函数改成 vfork 是什么结果?

                 如果我们 在第一个printf函数里增加 ‘/n’ 又是什么结果?

 

   

    这一课没有习题,关于这些问题,请关注 第二课问题之详解

原创粉丝点击