chapter8进程管理(上)

复习时抓住一个主线:进程创建、执行、终止的过程中发生了哪些事情?

每个进程都有一个非负整型表示的唯一进程ID,每个系统都有一些专用的进程,具体细节因实现而异,ID0的进程通常是调度进程,常常被称为交换进程(swapper)。该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。进程ID1通常是init进程,在自举过程结束时由内核调用。(附:自举(bootstrapping)一词来自于人都是靠自身的“自举”机构站立起来的这一思想。计算机必须具备自举能力将自己所有的元件激活,以便能完成加载操作系统这一目的,然后再由操作系统承担起那些单靠自举代码无法完成的更复杂的任务。自举只有两个功能:加电自检和磁盘引导。 加电自检:当我们按下计算机电源开关时,头几秒钟机器似乎什么反应也没有,其实,这时的计算机正在进行加电自检,以断定它的所有元件都在正确地工作。如果某个元件有故障,显示器上就会出现报警提示信息(如果显示器也不能正常工作,则以一串嘟嘟声来报警)。由于大多数计算机工作非常可靠,加电自检报警非常罕见。 磁盘引导:查找装有操作系统的磁盘驱动器。从磁盘加载操作系统的原因有二:一是操作系统升级简单容易,二是使用户拥有选择操作系统的自由。当以上功能完成时,自举操作就启动一个读写操作系统文件和将它们复制到随机存储器中的过程,此时的机器才是真正意义上的计算机。计算机的启动可以有冷启动和热启动两种方式,它们之间的差别是热启动不进行机器的自检(机器本身配置的检查与测试),当计算机在使用过程中由于某些原因造成死机时,可以对计算机进行热启动处理。)该进程负责在自举内核后启动一个UNIX系统。init通常读与系统有关的初始化文件,并将系统引导到一个状态init进程,绝不会终止,它是一个普通的用户进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户特权运行。

除了进程ID,每个进程还有一些其他的标志符。下列函数返回这些标志符。

#include <unistd.h>

pid_t getpid(void);

//返回值:调用进程的进程ID

pid_t getppid(void);

//返回值:调用进程的父进程ID

uid_t getuid(void);

//返回值:调用进程的实际用户ID

uid_t geteuid(void);

//返回值:调用进程的有效用户ID

gid_t getgid(void);

//返回值:调用进程的实际组ID

gid_t getegid(void);

//返回值:调用进程的有效组ID

注意:这些函数都没有出错返回。


一个现有进程可以通过fork函数创建一个新进程。

#include <unistd.h>

pid_t fork(void);

//返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1

由fork创建的新进程被称为子进程(child process)。fork函数被调用一次,但是返回两次,两次返回的唯一区别是子进程的返回值是0,而父进程的返回值是新子进程的进程ID,进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数可以使进程获得其所有子进程的进程ID。fork使子进程得到返回值0的理由是:每个进程只有一个父进程,可以通过调用getppid,得到该进程的父进程ID(进程ID 0总是由内核交换进程使用,所以一个子进程的进程ID不可能是0)。子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本,可以获得父进程数据空间、堆、栈的副本。注意,这是子进程所拥有的副本。父子进程并不共享这些数据空间部分,父子进程共享正文段。一般来说,fork之后,父子进程的执行顺序是不确定的。这取决于内核所使用的调度算法。

对于程序清单8-1,我们要注意整个程序的执行过程。当写到标准输出时,我们讲buf长度减去1作为输出字节数,这是为了避免将终止null符写出。strlen计算不包含终止null字节的字符串长度,而sizeof则计算包括终止符null的缓冲区长度。两者之间的另一个差别是,使用strlen需要进行一次函数调用,而对于sizeof而言,因为缓冲区已用已知字符串进行了优化,其长度是固定的,所以其长度在编译时确定。另外还要注意fork与I/O函数之间的交互关系。这里要特别注意,结合上一章中的知识加深理解进程的调度执行。fork的一个特性是父进程的所有打开文件描述符都被复制到子进程中,父子进程的每个相同的文件描述符共享一个文件表项。fork有下面两种用法:

(1)一个父进程希望复制自己,使父子进程同时执行不同的代码段。(2)一个进程要执行不同的程序。

进程有5种正常终止方式,前一章已经概述了,至于其终止时的一些细节,还是要参照apue,这里就不详细说了,想不起来的话,就翻书吧。

对于apue中程序清单8-3,为什么我的系统不支持WCOREDUMP宏呢?莫不是我源代码输入有问题。

对于程序清单8-5,调用fork两次以避免僵死进程的程序,还要好好看看,具体是怎么避免的!我们执行这个程序,产生如下结果:

goku@ubuntu:~/apue/chapter8$ ./a.out
goku@ubuntu:~/apue/chapter8$ second child,parent pid=1
waitpid error!: No child processes

有点奇怪,为什么会出现一个waitpid error!跟书本上的运行结果不同。根据程序清单8-6,我们知道,不带缓冲的标准输出,使得输出每个字符时都要调用一次write,这样内核就要在两个进程间进行多次切换,很好的演示了竞争条件。我的程序运行结果如下:

goku@ubuntu:~/apue/chapter8$ ./a.out
output from pareonutt!p
ut from child!
goku@ubuntu:~/apue/chapter8$ ./a.out
output from parent!
goku@ubuntu:~/apue/chapter8$ output from child!
./a.out
output from parento!u
tput from child!

fork函数创建一个子进程后,子进程往往要调用一种exec函数以执行另一种程序。当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用一个全新的程序替换了当前进程的正文 、数据、堆和栈段。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值