码迷,mamicode.com
首页 > 其他好文 > 详细

进程篇(3: 基本进程控制:进程的退出)--请参照本博客“操作系统”专栏

时间:2014-06-02 05:53:08      阅读:286      评论:0      收藏:0      [点我收藏+]

标签:des   c   style   class   blog   code   

1. exit函数:

进程的五种正常的结束方式:

  • 在main函数中执行return语句,这等效于exit;
  • 调用exit函数。此函数由ISO C定义,其操作包括运行各终止处理程序,然后关闭所有标准I/O流等。
  • 调用_exit或_Exit函数,ISO C定义了_Exit函数,目的是为了为进程提供一种无需运行终止处理程序和信号处理程序而终止的方法。并不处理标准I/O流!
  • 进程的最后一个线程在其启动例程中执行返回语句,然后该进程以终止状态0返回。
  • 进程的最后一个线程调用pthread_exit函数。

进程的三种非正常的结束方式:

  • 调用abort。它产生SIGABRT信号,这是下一种异常终止的一个特例。
  • 当进程接收到某些信号时,信号可以由进程自身,其他进程或内核产生。比如进程越过其地址空间访问存储单元或除以0,内核就会为该进程产生相应的信号!
  • 最后一个线程对"取消"(cancellation)请求做出响应。按系统默认,"取消"以延迟方式发生:一个线程要求取消另一个线程,一段时间后,目标线程终止。

不管进程如何终止,最后都会执行内核中的同一段代码:这段代码为进程关闭所有的相应的打开描述符,释放它所用的存储器等。

终止进程的父进程可以用wait和waitpid取得其终止状态。

 

对于父进程已经终止的进程,它们的父进程都被改为init进程,称这些进程被init进程领养。其操作过程如下:

在一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正要终止进程的子进程,如果是,那么将该进程的父进程的id更改为1(init进程ID)。

这种进程保证了每个进程都有一个父进程。

 

另外一个问题是:如果子进程在父进程之前终止,那么父进程如何如何得到子进程的终止状态呢?

回答: 内核为每个终止进程保存了一定量的信息,所以当终止进程的父进程调用wait和waitpid,时可以得到这些信息。这些信息至少包括:进程ID,该进程的终止状态,

以及该进程使用CPU时间总量。在UNIX术语中,一个已经终止,但是其父进程尚未对其进行善后处理(终止进程的相关信息,释放它们仍然占用的资源)的进程被称为僵死进程

bubuko.com,布布扣

上图是一个典型的UNIX/LINUX 进程状态模型。

 

2. wait 和 waitpid 函数:

  当一个进程正常或异常终止的时候,内核就会向其父进程发送SIGCHLD信号。因为子进程的终止是个异步事件(可以在父进程运行的任何时候发生),所以这种信号也是内核向父进程发的异步通知。父进程有两种处理这个信号的方式: 1)忽略该信号; 2) 提供一个该信号发生时即被调用执行的函数(信号处理程序)。

  调用wait或waitpid的进程可能会发生的情况:

  • 如果所有的子进程都还在运行,那么这个调用进程阻塞;
  • 如果一个子进程已经终止,正在等待其父进程获取其终止状态,则取得该子进程的终止状态之后立即返回;
  • 如果它没有任何子进程,则立即出错返回;

下面是linux manpage 中对wait函数族的描述:

bubuko.com,布布扣
NAME
       wait, waitpid, waitid - wait for process to change state

SYNOPSIS
       #include <sys/types.h>
       #include <sys/wait.h>

       pid_t wait(int *status);

       pid_t waitpid(pid_t pid, int *status, int options);

       int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
                       /* This is the glibc and POSIX interface; see
                          NOTES for information on the raw system call. */

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       waitid():
           _SVID_SOURCE || _XOPEN_SOURCE >= 500 ||
           _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED
           || /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L

DESCRIPTION
       All of these system calls are used to wait for state changes in a child
       of  the  calling  process, and obtain information about the child whose
       state has changed.(这些函数用来在调用进程中观察子进程状态的变化)  A state change 
is considered to be: the child ter‐minated (子进程终止); the child was stopped by a signal(子进程由于信号而停止); or the child was resumed by a signal(子进程因信号重新启动). In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is not performed, then the terminated child remains in a "zombie" state (see NOTES below). If a child has already changed state, then these calls return immedi‐ ately. Otherwise they block until either a child changes state or a signal handler interrupts the call (assuming that system calls are not automatically restarted using the SA_RESTART flag of sigaction(2)). In the remainder of this page, a child whose state has changed and which has not yet been waited upon by one of these system calls is termed waitable. wait() and waitpid() The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&status) is equivalent to: waitpid(-1, &status, 0); The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state. By default, waitpid() waits only for terminated children, but this behavior is mod‐ ifiable via the options argument, as described below. The value of pid can be: < -1 meaning wait for any child process whose process group ID is equal to the absolute value of pid. -1 meaning wait for any child process. 0 meaning wait for any child process whose process group ID is equal to that of the calling process. > 0 meaning wait for the child whose process ID is equal to the value of pid. The value of options is an OR of zero or more of the following con‐ stants: WNOHANG return immediately if no child has exited. WUNTRACED also return if a child has stopped (but not traced via ptrace(2)). Status for traced children which have stopped is provided even if this option is not specified. WCONTINUED (since Linux 2.6.10) also return if a stopped child has been resumed by delivery of SIGCONT. (For Linux-only options, see below.) If status is not NULL, wait() and waitpid() store status information in the int to which it points. This integer can be inspected with the following macros (which take the integer itself as an argument, not a pointer to it, as is done in wait() and waitpid()!): WIFEXITED(status) returns true if the child terminated normally, that is, by call‐ ing exit(3) or _exit(2), or by returning from main(). WEXITSTATUS(status) returns the exit status of the child. This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main(). This macro should be employed only if WIFEXITED returned true. WIFSIGNALED(status) returns true if the child process was terminated by a signal. WTERMSIG(status) returns the number of the signal that caused the child process to terminate. This macro should be employed only if WIFSIGNALED returned true. WCOREDUMP(status) returns true if the child produced a core dump. This macro should be employed only if WIFSIGNALED returned true. This macro is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS). Only use this enclosed in #ifdef WCOREDUMP ... #endif. WIFSTOPPED(status) returns true if the child process was stopped by delivery of a signal; this is possible only if the call was done using WUN‐ TRACED or when the child is being traced (see ptrace(2)). WSTOPSIG(status) returns the number of the signal which caused the child to stop. This macro should be employed only if WIFSTOPPED returned true. WIFCONTINUED(status) (since Linux 2.6.10) returns true if the child process was resumed by delivery of SIGCONT. waitid() The waitid() system call (available since Linux 2.6.9) provides more precise control over which child state changes to wait for. The idtype and id arguments select the child(ren) to wait for, as fol‐ lows: idtype == P_PID Wait for the child whose process ID matches id. idtype == P_PGID Wait for any child whose process group ID matches id. idtype == P_ALL Wait for any child; id is ignored. The child state changes to wait for are specified by ORing one or more of the following flags in options: WEXITED Wait for children that have terminated. WSTOPPED Wait for children that have been stopped by delivery of a signal. WCONTINUED Wait for (previously stopped) children that have been resumed by delivery of SIGCONT. The following flags may additionally be ORed in options: WNOHANG As for waitpid(). WNOWAIT Leave the child in a waitable state; a later wait call can be used to again retrieve the child status information. Upon successful return, waitid() fills in the following fields of the siginfo_t structure pointed to by infop: si_pid The process ID of the child. si_uid The real user ID of the child. (This field is not set on most other implementations.) si_signo Always set to SIGCHLD. si_status Either the exit status of the child, as given to _exit(2) (or exit(3)), or the signal that caused the child to termi‐ nate, stop, or continue. The si_code field can be used to determine how to interpret this field. si_code Set to one of: CLD_EXITED (child called _exit(2)); CLD_KILLED (child killed by signal); CLD_DUMPED (child killed by signal, and dumped core); CLD_STOPPED (child stopped by signal); CLD_TRAPPED (traced child has trapped); or CLD_CONTINUED (child continued by SIGCONT). If WNOHANG was specified in options and there were no children in a waitable state, then waitid() returns 0 immediately and the state of the siginfo_t structure pointed to by infop is unspecified. To distin‐ guish this case from that where a child was in a waitable state, zero out the si_pid field before the call and check for a nonzero value in this field after the call returns. RETURN VALUE wait(): on success, returns the process ID of the terminated child; on error, -1 is returned. waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more child(ren) speci‐ fied by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned. waitid(): returns 0 on success or if WNOHANG was specified and no child(ren) specified by id has yet changed state; on error, -1 is returned. Each of these calls sets errno to an appropriate value in the case of an error. ERRORS ECHILD (for wait()) The calling process does not have any unwaited-for children. ECHILD (for waitpid() or waitid()) The process specified by pid (wait‐ pid()) or idtype and id (waitid()) does not exist or is not a child of the calling process. (This can happen for ones own child if the action for SIGCHLD is set to SIG_IGN. See also the Linux Notes section about threads.) EINTR WNOHANG was not set and an unblocked signal or a SIGCHLD was caught; see signal(7). EINVAL The options argument was invalid.
bubuko.com,布布扣

wait函数和waitpid函数的区别在于:

  • 在一个子进程终止前,wait使其调用者阻塞;而waitpid有一个选项,可以使调用者不阻塞。
  • waitpid并不等待其调用之后的第一个终止子进程,它有若干个选项可以控制它所等待的进程。

 下面通过实例来验证wait函数的使用:

bubuko.com,布布扣
 1 #include "apue.h"
 2 #include <sys/wait.h>
 3 
 4 void pr_exit(int status);
 5 
 6 int main(void)
 7 {
 8     pid_t    pid;
 9     int     status;
10 
11     if((pid = fork()) < 0)
12         err_sys("fork error");
13     else if(pid == 0)
14         exit(7);
15 
16     if(wait(&status) != pid)
17         err_sys("wait error");
18     pr_exit(status);
19 
20     if((pid = fork()) < 0)
21         err_sys("fork error");
22     else if(pid == 0)
23         abort();
24 
25     if(wait(&status) != pid)
26         err_sys("wait error");
27     pr_exit(status);
28 
29     
30     if((pid = fork()) < 0)
31         err_sys("fork error");
32     else if(pid == 0)
33         status /= 0;
34 
35     if(wait(&status) != pid)
36         err_sys("wait error");
37     pr_exit(status);
38     exit(0);
39 }
40 
41 void pr_exit(int status)
42 {
43     if(WIFEXITED(status))
44         printf("normal termination, exit status = %d\n",WEXITSTATUS(status));
45     else if(WIFSIGNALED(status))
46         printf("abnormal termination, signal number = %d%s\n",WTERMSIG(status),
47 #ifdef WCOREDUMP
48                 WCOREDUMP(status) ? " (core file generated)" : "");
49 #else
50             "");
51 #endif
52     else if(WIFSTOPPED(status))
53         printf("child stopped, signal number = %d\n",WSTOPSIG(status));
54 }
bubuko.com,布布扣

上面代码运行的结果是:

normal termination, exit status = 7
abnormal termination, signal number = 6
abnormal termination, signal number = 8

 

 

 

进程篇(3: 基本进程控制:进程的退出)--请参照本博客“操作系统”专栏,布布扣,bubuko.com

进程篇(3: 基本进程控制:进程的退出)--请参照本博客“操作系统”专栏

标签:des   c   style   class   blog   code   

原文地址:http://www.cnblogs.com/jiangheng/p/3763716.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!