wblyuyang

博客园 首页 新随笔 联系 订阅 管理

进程的状态迁移:
 三个基本状态:就绪态、运行态、睡眠态。如下图:

 图二。

进程的终止:
 自愿终止 和 被迫终止。
 自愿终止指的是应用程序中主动调用了执行退出过程的系统调用而终止,这个可以通过函数 exit 来做到,其

接口头文件与原型如下:
 #include <stdlib.h>
 void exit(int status); 
 
 被迫终止指的是应用程序中没有主动调用退出进程的系统调用而被内核强制终止的情形。

 当一个进程终止时,内核会通知其父进程。在父进程进行处理前,这个进程成为所谓的僵尸进程,它所占的各

种资源已经被回收,但进程描述符仍然存在,以便父进程获取它的退出状态。父进程可以用 wait 函数或 waitpid 函

数获取子进程的退出状态,它们的接口头文件及原型如下:
 #include <sys/types.h>
 #inclde <sys/wait.h>
 pid_t wait(int *status);
 pid_t waitpid(pid_t pid, int *status, int options);

 调用 wait 函数使当前进程阻塞直到它的某个子进程退出,这时返回值是退出的子进程的 PID,而参数

status 指向的整数被设为子进程的退出状态(或称返回值)。如果有错误发生则返回 -1。
 waitpid 函数则提供了比 wait 函数更为精细的控制,其各个参数及返回值含义如下:
 ◆ pid:指定要等待的子进程的 PID。
 ◆ status:用于获取子进程的退出状态。
 ◆ options:等待的参数,可以改变函数的行为。
 ◆ 返回值:退出的子进程的 PID,返回 -1 表示有错误发生。

 其中 pid 参数有如下多种用法:
 ◆ pid > 0 ,表示某个特定的子进程。
 ◆ pid = 0 ,表示任何进程组 ID 等于当前进程的子进程。
 ◆ pid = -1 ,表示任何子进程。
 ◆ pid < -1 ,表示任何进程组 ID 等于 -pid 的子进程。

 options 参数的一个常用值是 WNOHANG,它表示函数以非阻塞的方式执行,即如果没有子进程退出则对函数的

调用立刻以错误状态返回。

 对 wait 函数的调用 wait(&status) 实际上等价于:
 waitpid(-1, &status, 0);

 如果一个进程没有子进程,则对上述函数的调用会立刻以错误状态返回,并且变量 errno 的值被设为 ECHILD


 如果父进程在子进程退出前就已经退出,这时称子进程为孤儿进程。在进程退出时,内核会检视它的子进程并

使这些子进程成为 1 号进程的子进程,也就是说,子进程被 init 进程“收养”。


进程与信号:
  
 信号是内核提供的一种异步消息机制,主要用于内核对进程发送异步通知事件,可以理解为对进程的执行流程

的一个“软中断”。
 信号总是由内核递交给进程,但从应用程序的角度讲,信号的来源是多种多样的,如:
 ◆ 当进程在一个没有打开的管道上等待时,内核发出 SIGPIPE 信号。
 ◆ 进程在 Shell 中前台执行时,用户按下 Ctrl+C 组合键,将向进程发送 SIGINT 信号。
 ◆ 用户使用 kill 命令向某个进程发送信号。
 ◆ 进程访问非法的内存地址时内核向其发送“段错误”信号 SIGSEGV。
 ◆ 一个进程使用系统调用向另一个进程发送信号。
 ◆ 发生各种运行异常时,内核将向进程发送 SIGFPE 信号。

Linux 中的信号处理机制:
 在内核对进程管理的 PCB信息块中有若干个字节,其中每个比特位用于表示某个信号是否发生。当需要向某个

进程发送一个特定的信号时,就将其 PCB 信息中对应的比特位置为 1。但是对信号的处理并不立刻发生,内核会在从

内核态返回用户态时对当前进程的 PCB 中表示信号的数据进行检查,如果有信号发生,则内核会修改当前进程栈中的

信息,使得返回用户态后首先执行与信号绑定的处理函数,然后再从当前进程被中断或进行系统调用的地方继续执行。
 
关于信号的一些概念:
◆ 发送信号
 给某个进程发送信号实质上就是讲其 PCB 中的对应比特位置 1.当然,这个操作只能由内核来进行,但应用程

序可以通过系统调用间接的完成这个操作。
◆ 信号屏蔽
 每个进程都有一个用来描述哪些信号将被屏蔽的信号集,成为信号掩码,如果某个信号在进程的信号掩码中,

则发生到进程的这种信号将会被屏蔽。屏蔽只是延迟信号的到达,信号会在解除屏蔽后继续传递。在信号处理函数和程

序的其他部分共享全局变量时,一般会在访问变量前屏蔽信号,以免在访问过程中发生信号而导致同步问题,访问完毕

后再解除对信号的屏蔽。
◆ 忽略信号
 忽略信号就是指当进程收到某个信号时直接丢弃,对进程的执行没有影响。但有些信号是不能忽略的,如

SIGKILL 和 SIGSTOP 信号。
◆ 捕捉信号
 对每个信号来说,系统都有默认的信号处理函数。如果程序中用自己定义的函数取代了默认的信号处理函数,

则称为捕捉了这个信号。 


发送信号:
 信号实际上是由一个正整数代表的,但为了使用方便,每个信号都定义了一个名称,代表一种特殊的含义。这

些信号的定义在系统头文件 signal.h 中。

 向一个进程发送喜欢可以使用 kill 命令,默认情况下 kill 命令发送 SIGTERM 信号以试图终止一个进程。
 #include <sys/types.h>
 #include <signal.h>
 int kill(pid_t pid, int sig);
  
 ◆ pid > 0:表示目标进程的 PID。
 ◆ pid = 0:表示与当前进程同组的所有进程。
 ◆ pid = -1:表示所有当前进程有发送信号权限的其他进程。
 ◆ pid < -1:表示所有处于进程组 -pid 中的进程。

 sig 参数则是要发送的信号。函数返回 0 则表示信号发送成功,如果信号发送失败,则函数返回 -1。

 注意的是,发送信号的进程需要有必要的权限,一般情况下,发送信号的进程与接收信号的进程应属于相同的

用户。root 可以发送信号到任何进程。

 进程可以使用 raise 函数给自己发送信号:
 #include <signal.h>
 int raise(int sig);

 使用 alarm 函数可以在过一段时间后向进程自己发送 SIGALRM 信号,其接口头文件及原型如下:
 #include <unistd.h>
 unsigned int alarm(unsigned int seconds);
 seconds 参数表示一个以秒为单位的超时时间。超过这段时间后,内核将自动向进程自己发送 SIGALRM 信号

。如果在指定时间超时前再次调用 alarm 函数,则新的时间值将覆盖旧的,并且从当前时间开始重新计算。如果指定

的时间值为 0,则表示取消信号的发送。
 alarm 函数的返回值表示前一次对 alarm 函数的调用还剩多少秒就要超时,如果返回 0 则说明没有调用过

alarm 函数或前一次调用 alarm 函数后已超时。
 利用 alarm 函数可以实现定时操作:
 #include <unistd.h>

 int main()
 {
  alarm(1);
  for(; ;);
 }

 使用 alarm 函数设置了 1 秒后发送 SIGALRM 信号,而 SIGALRM 信号的默认操作是退出进程,所以尽管随后

是一个死循环,进程仍然能够退出。

posted on 2012-11-11 22:09  wblyuyang  阅读(1331)  评论(0编辑  收藏  举报