APUE阅读笔记(八)——进程控制

说明:

1、为什么写的第一篇阅读笔记就是八呢?因为之前看apue并没有写读书笔记,一直看到第八章,也就是标题中的这一章,才发现,不写是不行的,因为当我自己大量的接受新知识的时候,会遗忘的很厉害,也就是传说中的前学后忘。
2、之前差的那些章节,准备回过头再看一遍,然后写点笔记
3、这是一个笔记,笔记中尽可能详细的记录了我的理解,可能跟你的理解不一样,欢迎赐教。

一、进程ID

1、pid和pid复用

 系统为了管理方便,为每一个进程分配了一个非负整数,作为进程的ID,我们称之为pid(Process ID)。既然是ID,具有标识性,也就是意味着,每一个进程的ID都是不能重复的。可分配的ID数目是有限的,在保证不出现重复的ID的同时又要实现系统可以创建无限个进程(当然这是一个理想的状态,实际上创建无限个进程的阻碍并不只有ID不够分配),所以理所当然的ID是可以复用的。
 ID复用是随便分配一个没有使用的pid新进程吗?并不是这样的,这个很好理解,举一个例子就明白了。假设有这么一个场景:用户刚刚杀死了一个pid是1223的进程,此时新建了一个进程,如果是随便分配的,那碰巧新进程分配的pid就是1223,用户杀死了以后,ps -a 一看,咦,这个进程怎么还在呢?这就造成了一个困扰。所以,ID并不是随便分配的,而且还要避免分配一个最近刚刚结束的进程。

2、特殊的进程

pid是0的进程是交换进程,也称作是闲逛进程。
pid是1的进程是init进程,所有孤儿进程的父亲。

二、fork和vfork函数

  fork函数被用来新建一个进程,fork函数执行完了以后,父进程和子进程一起运行下面的代码,这也就是我们常常说的fork函数会返回两次。我们知道C语言函数并不能返回两次,在fork这个系统调用运行完了以后,子进程新建完成,被改为就绪状态插入就绪队列。这时候,已经是两个进程了,我们假设父进程先运行,父进程fork返回子进程的pid,然后接着执行下面的代码。父进程时间片用完,触发调度,子进程被选中。子进程fork返回0,运行下面的代码(父子进程共享文本段,子进程的栈空间是复制父进程的)。这就是看似返回两次,实际又不是两次的奥义,在上面的假设中,子进程没有执行exec族函数。

 fork结束后产生了子进程,子进程和父进程运行的先后顺序是未知的,所以前面我们举例子里面是假设父进程先运行。子进程具有自己的地址空间,并且子进程运行在自己的地址空间中。也就是说,子进程中修改了一个变量的值,父进程读出这个值的内容是没有改变的。
在前面分析do_fork的博文中提到了,子进程的资源基本上都是复制父进程的。

子进程复制父进程的基本上有:
* 父进程打开的文件描述符
* 父进程的uid、gid、euid、egid
* 环境
* 存储映像
* 资源限制

子进程独有的是:
* 进程id
* 未决信号集
vfork和fork的区别:
1、vfork保证子进程先运行
2、vfork函数创建新进程,并不会父进程的地址空间完全复制到子进程的地址空间中。因为vfork结束以后回直接调用exec函数。如果vfork结束以后并没有调用exec函数,子进程会在父进程的地址空间中运行。

三、函数exit

 进程的结束分为正常结束和异常结束。

正常结束有五种:
* main函数执行return
* 调用exit函数
* 调用_exit函数
* 进程最后一个线程调用pthread_exit函数
* 进程的最后一个线程执行return

三种异常结束有:
* 调用abort函数
* 进程被信号杀死
* 进程对取消做出响应(??不理解)

进程结束操作:
* 回收空间
* 回收pid
* 退出状态返回给父进程
* 所有的子进程被init进程收养

exit函数和_exit函数区别:
* exit函数是标准I/O提供的退出函数,_exit函数的系统提供的
* 调用exit函数退出进程打开的所有文件都被关闭,所有流被刷新,而_exit函数并不会刷新流
* exit函最终调用_exit函数(这里不理解,exit函数不是ISO C的库函数吗,应该具有可移植性,怎么能调用Unix的系统调用来实现自己的功能呢?)

僵尸进程和孤儿进程:
* 僵尸进程:子进程已经退出,父进程忽略了子进程的退出信号或者是父进程执行其他任务并没有为子进程“收尸”,子进程就变成了僵尸进程。
* 孤儿进程:父进程先于子进程结束,子进程变成了孤儿进程。孤儿进程被init进程收养。

四、函数wait

 子进程结束以后会发送给父进程SIGCHLD信号,表示子进程已经结束了,父进程来进行相应的操作。我们希望每一个进程结束以后都能通知系统其退出状态,所以就引出了wait函数。
 wait函数是父进程用来等待子进程退出的函数,常用的有wait和waitpid这两个函数(函数声明原型参见man手册),下面详细解释一下这两个函数的用途以及不同。
 先说wait函数,父进程调用wait函数,阻塞自身等待子进程的退出,如果父进程有多个子进程,只要有任何一个子进程退出,父进程阻塞态转为就绪态。
 和wait函数相比,waitpid函数就灵活多了。waitpid函数使得父进程不用阻塞自身等待子进程退出并且waitpid函数支持等待一个指定pid的子进程退出,waitpid也可以根据最后一个参数option进行等待相同的组id进程结束之类的操作,详情参见man手册。

问题解答

1、为什么部分库函数可以调用系统调用的同时保持可移植性。
 我们以exit函数为例,查阅资料发现exit函数和_exit函数都是调用了sys_exit系统调用。exit库函数是gcc(此处编译器举例是gcc)提供的函数,gcc帮我们屏蔽了不同的系统之间系统调用的差异,封装成统一的接口。因此如果把调用了exit的程序移植到windows上运行,也是行得通的(当然!!!),这也是为什么我们安装编译器的时候需要选择系统的原因。
 这里注意是部分库函数调用了系统调用实现自己的功能,并不是全部。
 那么,是不是针对不同的系统,要实现不同的编译器呢?哈哈,你忘了POSIX标准了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值