linux 信号机制、生命

以下部分内容来自于亚嵌课件


一、信号及信号来源
信号本质
信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。
信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进
程有哪些事情发生了。信号机制经过 POSIX 实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。信号来源
信号事件的发生有两个来源:硬件来源(比如我们按下了键盘或者其它硬件故障);软件来

源,最常用发送信号的系统函数是 kill, raise, alarm 和 setitimer 以及 sigqueue 函数,软件来源还包括一些非法运算等操作。


二、信号的种类
可以从两个不同的分类角度对信号进行分类:(1)可靠性方面:可靠信号与不可靠信号;

(2)与时间的关系上:实时信号与非实时信号。


1、可靠信号与不可靠信号

 "不可靠信号"

Linux 信号机制基本上是从 Unix 系统中继承过来的。早期 Unix 系统中的信号机制比较简单

和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做 "不可
靠信号",信号值小于 SIGRTMIN(Red hat 7.2 中,SIGRTMIN=32,SIGRTMAX=63)的信号
都是不可靠信号。这就是"不可靠信号"的来源。它的主要问题是:
• 进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致
对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结
尾再一次调用 signal(),重新安装该信号。
• 信号可能丢失,后面将对此详细阐述。
因此,早期 unix 下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。
Linux 支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处理函数后,不
必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,Linux 下的

不可靠信号问题主要指的是信号可能丢失。

"可靠信号"

随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。所以,后来出现的
各种 Unix 版本分别在这方面进行了研究,力图实现"可靠信号"。由于原来定义的信号已有
许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可
靠信号,这些信号支持排队,不会丢失。同时,信号的发送和安装也出现了新版本:信号发
送函数 sigqueue()及信号安装函数 sigaction()。POSIX.4 对可靠信号机制做了标准化。但是,
POSIX 只对可靠信号机制应具有的功能以及信号机制的对外接口做了标准化,对信号机制的实现没有作具体的规定。
信号值位于 SIGRTMIN 和 SIGRTMAX 之间的信号都是可靠信号,可靠信号克服了信号可
能 丢 失 的 问 题 。 Linux 在 支 持 新 版 本 的 信 号 安 装 函 数 sigation ( ) 以 及 信 号 发 送 函 数
sigqueue()的同时,仍然支持早期的 signal()信号安装函数,支持信号发送函数 kill()。
sigaction 安装的信号就是可靠的。
注:不要有这样的误解:由 sigqueue()发送、事实上,可靠
信号是指后来添加的新信号(信号值位于 SIGRTMIN 及 SIGRTMAX 之间);不可靠信号
是信号值小于 SIGRTMIN 的信号。信号的可靠与不可靠只与信号值有关,与信号的发送及
安装函数无关。目前 linux 中的 signal()是通过 sigation() 函数实现的,因此,即使通过
signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由
signal()安装的实时信号支持排队,同样不会丢失。
对于目前 linux 的两个信号安装函数:signal()及 sigaction()来说,它们都不能把 SIGRTMIN 以
前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对
SIGRTMIN 以后的信号都支持排队。
这两个函数的最大区别在于,经过 sigaction 安装的信号
都能传递信息给信号处理函数(对所有信号这一点都成立),而经过 signal 安装的信号却

不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。


2、实时信号与非实时信号
早 期 Unix 系 统 只 定 义 了 32 种 信 号 , Ret hat7.2 支 持 64 种 信 号 , 编 号 0-
63(SIGRTMIN=31,SIGRTMAX=63),将来可能进一步增加,这需要得到内核的支持。 32前
种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。
如按键盘的 CTRL ^C 时,会产生 SIGINT 信号,对该信号的默认反应就是进程终止。
32 个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都
后被接收。实时信号是 POSIX 标准的一部分,可用于应用进程。

非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。


三、进程对信号的响应
进程可以通过三种方式来响应一个信号:(1)忽略信号,即对信号不做任何处理,其中,
有两个信号不能忽略:SIGKILL 及 SIGSTOP;(2)捕捉信号。
定义信号处理函数,当信号
发生时,执行相应的处理函数;(3)执行缺省操作,Linux 对每种信号都规定了默认操作,
详细情况请参考[2]以及其它资料。注意,进程对实时信号的缺省反应是进程终止。

Linux 究竟采用上述三种方式的哪一个来响应信号,取决于传递给相应 API 函数的参数。


四、信号的发送

发送信号的主要函数有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及 abort()。


1、kill()

#include <sys/types.h>

#include <signal.h>
int kill(pid_t pid,int signo)
参数 pid 的值
pid>0
pid=0
信号的接收进程
进程 ID 为 pid 的进程
同一个进程组的进程
pid<0 pid!=-1
进程组 ID 为 -pid 的所有进程
pid=-1
除发送进程自身外,所有进程 ID 大于 1 的进程
Sinno 是信号值,当为 0 时(即空信号),实际不发送任何信号,但照常进行错误检查,因
此,可用于检查目标进程是否存在,以及当前进程是否具有向目标发送信号的权限 (root
权限的进程可以向任何进程发送信号,非 root 权限的进程只能向属于同一个 session 或者同
一个用户的进程发送信号)。
Kill()最常用于 pid>0 时的信号发送,调用成功返回 0; 否则,返回 -1。
注:对于 pid<0 时的
情况 ,对 于哪 些进 程将 接受 信号 ,各 种版 本说 法不 一, 其实 很 简 单, 参阅 内核 源码

kernal/signal.c 即可,上表中的规则是参考 red hat 7.2。


2、raise()
#include <signal.h>
int raise(int signo)

向进程本身发送信号,参数为即将发送的信号值。调用成功返回 0;否则,返回 -1。


3、sigqueue()
#include <sys/types.h>
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval val)
调用成功返回 0;否则,返回 -1。
sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然也支持前 32
种),支持信号带有参数,与函数 sigaction()配合使用。
sigqueue 的第一个参数是指定接收信号的进程 ID,第二个参数确定即将发送的信号,第三
个参数是一个联合数据结构 union sigval,指定了信号传递的参数,即通常所说的 4 字节值。
typedef union sigval {
int sival_int;
void *sival_ptr;
}sigval_t;
sigqueue()比 kill()传递了更多的附加信息,但 sigqueue()只能向一个进程发送信号,而不能
发送信号给一个进程组。如果 signo=0,将会执行错误检查,但实际上不发送任何信号,0
值信号可用于检查 pid 的有效性以及当前进程是否有权限向目标进程发送信号。
在调用 sigqueue 时,sigval_t 指定的信息会拷贝到 3 参数信号处理函数(3 参数信号处理函
数指的是信号处理函数由 sigaction 安装,并设定了 sa_sigaction 指针,稍后将阐述)的
siginfo_t 结构中,这样信号处理函数就可以处理这些信息了。由于 sigqueue 系统调用支持发
送带参数信号,所以比 kill()系统调用的功能要灵活和强大得多。
注:sigqueue()发送非实时信号时,第三个参数包含的信息仍然能够传递给信号处理函数;
sigqueue()发送非实时信号时,仍然不支持排队,即在信号处理函数执行过程中到来的

所有相同信号,都被合并为一个信号

4、alarm()
#include <unistd.h>
unsigned int alarm(unsigned int seconds)
专门为 SIGALRM 信号而设,在指定的时间 seconds 秒后,将向进程本身发送 SIGALRM 信
号,又称为闹钟时间。进程调用 alarm 后,任何以前的 alarm()调用都将无效。如果参数
seconds 为零,那么进程内将不再包含任何闹钟时间。
返回值,如果调用 alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩

余时间,否则返回 0。


5、setitimer()
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer()比 alarm 功能强大,支持 3 种类型的定时器:
• ITIMER_REAL: 设定绝对时间;经过指定的时间后,内核将发送 SIGALRM 信号
给本进程;
• ITIMER_VIRTUAL 设 定 程 序 执 行 时 间 ; 经 过 指 定 的 时 间 后 , 内 核 将 发 送
SIGVTALRM 信号给本进程;
• ITIMER_PROF 设定进程执行以及内核因本进程而消耗的时间和,经过指定的时间
后,内核将发送 ITIMER_VIRTUAL 信号给本进程;
Setitimer() 第 一 个 参 数 which 指 定 定 时 器 类 型 ( 上 面 三 种 之 一 ) ; 第 二 个 参 数 是 结 构
itimerval 的一个实例,结构 itimerval 形式见附录 1。第三个参数可不做处理。

Setitimer()调用成功返回 0,否则返回-1。


6、abort()
#include <stdlib.h>
void abort(void);
向进程发送 SIGABORT 信号,默认情况下进程会异常退出,当然可定义自己的信号处理函
数。即使 SIGABORT 被进程设置为阻塞信号,调用 abort()后,SIGABORT 仍然能被进程接

收。该函数无返回值。


五、信号的安装(设置信号关联动作)
如果进程要处理某一信号,那么就要在进程中安装该信号。
安装信号主要用来确定信号值及
进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进
程时,将执行何种操作。
linux 主要有两个函数实现信号的安装:signal()、sigaction()。其中 signal()在可靠信号系统调
用的基础上实现, 是库函数。
它只有两个参数,不支持信号传递信息,主要是用于前 32 种非
实时信号的安装;而 sigaction() 是较新的函数(由两个系统调用实现: sys_signal 以及
sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue() 系统调用配合
sigaction()优于 signal()主要体现在支持
使用,当然,sigaction()同样支持非实时信号的安装。
信号带有参数。




一、信号生命周期
从信号发送到信号处理函数§的执行完毕对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三
个重要的阶段,这三个阶段由四个重要事件来刻画:

信号诞生;信号在进程中注册完毕;信号在进程中的注销完毕;信号处理函数§执行完毕。
相邻两个事件§的时间间隔构成信号生
命周期的一个阶段。§
下面阐述四个事件§的实际意义:

1 信号"诞生"。信号的诞生指的是触发信号的事件§发生(如检测到硬件异常、定时器超时以及调用信号发送函数§kill()或 sigqueue()等)。


2 信号在目标进程中"注册";进程的 task_struct 结构中有关于本进程中未决信号的数据成员:
struct sigpending pending:
struct sigpending{
struct sigqueue *head, **tail;
sigset_t signal;
};
第三个成员是进程中所有未决信号集,第一、第二个成员分别指向一个 sigqueue 类型的结构链(称之为"未决信号信息链")的首尾,信息链中的每个 sigqueue 结构刻画一个特定信号
所携带的信息,并指向下一个 sigqueue 结构:
struct sigqueue{
struct sigqueue *next;
siginfo_t info;
}
信号在进程中注册指的就是信号值加入到进程的未决信号集中( sigpending 结构的第二个成员 sigset_t signal),并且信号所携带的信息被保留到未决信号信息链的某个 sigqueue 结构中。只要信号在进程的未决信号集中,表明进程已经知道这些信号的存在,但还没来得及处理,或者该信号被进程阻塞。
注:
当一个实时信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一
次,因此,信号不会丢失,因此,实时信号又叫做"可靠信号"。
这意味着同一个实时信号可以在同一个进程的未决信号信息链中占有多个 sigqueue 结构(进程每收到一个实时信号,都会为它分配一个结构来登记该信号信息,并把该结构添加在未决信号链尾,即所有诞生的实时信号都会在目标进程中注册);当一个非实时信号发送给一个进程时,如果该信号已经在进程中注册,则该信号将被丢弃造成信号丢失。因此,非实时信号又叫做"不可靠信号"。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个 sigqueue 结构(一个非实时信号诞生后,(1)、如果
发现相同的信号已经在目标结构中注册,则不再注册,对于进程来说,相当于不知道本次
信号发生,信号丢失;(2)、如果进程的未决信号中没有相同信号,则在进程中注册自己)。


3 信号在进程中的注销。在目标进程执行过程中,会检测是否有信号等待处理(每次
从系统空间返回到用户空间时都做这样的检查)。如果存在未决信号等待处理且该信号
没有被进程阻塞,则在运行相应的信号处理函数§前,进程会把信号在未决信号链中占
有的结构卸掉。是否将信号从进程未决信号集中删除对于实时与非实时信号是不同的。
对于非实时信号来说,由于在未决信号信息链中最多只占用一个 sigqueue 结构,因此
该结构被释放后,应该把信号在进程未决信号集中删除(信号注销完毕);而对于实
时信号来说,可能在未决信号信息链中占用多个 sigqueue 结构,因此应该针对占用
sigqueue 结构的数目区别对待:如果只占用一个 sigqueue 结构(进程只收到该信号一
次),则应该把信号在进程的未决信号集中删除(信号注销完毕)。否则,不应该在进
程的未决信号集中删除该信号(信号注销完毕)。
进程在执行信号相应处理函数§之前,首先要把信号在进程中注销。进程注销信号后,立即执行相应的信号处理函数§,执行完毕后,信


4 信号生命终止。
号的本次发送对进程的影响彻底结束。
注:
1)信号注册与否,与发送信号的 函数(如 kill()或 sigqueue()等)以及信号安装 函数§
(signal()及 sigaction())无关,只与信号值有关(信号值小于 SIGRTMIN 的信号最多只注
册一次,信号值在 SIGRTMIN 及 SIGRTMAX 之间的信号,只要被进程接收到就被注册)。
2)在信号被注销到相应的信号处理函数§执行完毕这段时间内,如果进程又收到同一信号
多次,则对实时信号来说,每一次都会在进程中注册;而对于非实时信号来说,无论收到
多少次信号,都会视为只收到一个信号,只在进程中注册一次。


二、信号编程注意事项


5 防止不该丢失的信号丢失。如果对八中所提到的信号生命周期理解深刻的话,很容易知道信号会不会丢失,以及在哪里丢失。

6 程序的可移植性考虑到程序的可移植性,应该尽量采用 POSIX 信号函数,POSIX 信号函数§主要分为两类:
o POSIX        1003.1§信号函数:
Kill()、sigaction()、sigaddset()、sigdelset()、sigemptyset()、
sigfillset()、sigismember()、sigpending()、sigprocmask()、sigsuspend()。
o POSIX 1003.1b 信号函数。POSIX 1003.1b 在信号的实时性方面对 POSIX
1003.1 做了扩展,包括以下三个函数: sigqueue()、sigtimedwait()、sigwaitinfo()。
其中,sigqueue 主要针对信号发送,而 sigtimedwait 及 sigwaitinfo()主要用于取
代 sigsuspend()函数§,后面有相应实例。
#include <signal.h>
int sigwaitinfo(sigset_t *set, siginfo_t *info).
该函数与 sigsuspend()类似,阻塞一个进程直到特定信号发生,但信号到来时不执行信号处
理函数,而是返回信号值。
因此为了避免执行相应的信号处理函数,必须在调用该函数前,
使进程屏蔽掉 set 指向的信号,因此调用该函数§的典型代码是:
sigset_t newmask;
int rcvd_sig;
siginfo_t info;
sigemptyset(&newmask);
sigaddset(&newmask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &newmask, NULL);
rcvd_sig = sigwaitinfo(&newmask, &info)
if (rcvd_sig == -1) {
..
}
调用成功返回信号值,否则返回-1。sigtimedwait()功能相似,只不过增加了一个进程等待的时间。程序的稳定性。


7为了增强程序的稳定性,在信号处理函数中应使用可重入函数§。
信号处理程序中应当使用可再入(可重入)函数(注:所谓可重入函数是指一个可以被多
个任务调用的过程,任务在调用时不必担心数据是否会出错)。
因为进程在收到信号后,就
将跳转到信号处理函数去接着执行。
如果信号处理函数中使用了不可重入函数,那么信号处
理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着
执行时,可能会出现不可预料的后果。
不可再入函数在信号处理函数中被视为不安全函数§。
满足下列条件的函数§多数是不可再入的:(1)使用静态的数据结构§,如
getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及 getpwnam()等等;(2)函数
实现时,调用了 malloc()或者 free()函数;(3)实现时使用了标准 I/O 函数的。The Open
Group 视下列函数§为可再入的:
_exit()、
ACCESS§()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、
cfsetospeed()、chdir()、chmod()、chown()、close()、creat()、
dup()、dup2()、execle()、
execve()、fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、geteuid()、getgid()、getgroups()、
getpgrp()、getpid()、getppid()、getuid()、kill( ) 、

link ( ) 、 lseek ( ) 、 mkdir ( ) 、 mkfifo ( ) 、open()、pathconf()、pause()、pipe()、raise()、read()、rename()、rmdir()、setgid ( ) 、

setpgid ( ) 、 setsid ( ) 、 setuid ( ) 、sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、signal()、sigpending()、sigprocmask()、
sigsuspend()、sleep()、stat()、sysconf()tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、tcsendbreak()、tcsetattr(、tcsetpgrptimetimes)、()、()、()、
umask()、uname()、unlink()、utime()、wait()、waitpid()、write()。
即使信号处理函数使用的都是"安全函数",同样要注意进入处理函数时,首先要保存 errno
的值,结束时,再恢复原值。因为,信号处理过程中,errno 值随时可能被改变。另外,
longjmp()以及 siglongjmp()没有被列为可再入函数,因为不能保证紧接着两个函数§的其它
调用是安全的。


三、深入浅出:信号应用实例
linux 下的信号应用,要做的最多只有三件事情:
安装信号(推荐使用 sigaction());
实现三参数信号处理函数§,handler(int signal,struct siginfo *info, void *);
发送信号,推荐使用 sigqueue()。
实际上,对有些信号来说,只要安装信号就足够了(信号处理方式采用缺省或忽略)。其他可能要做的无非是与信号集相关的几种操作。
实例一:信号发送及处理
实现一个信号接收程序 sigreceive(其中信号安装由 sigaction())。

#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*,void*);
int main(int argc,char**argv)
{
struct sigaction act;
int sig;
sig=atoi(argv[1]);
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=new_op;
if(sigaction(sig,&act,NULL) < 0)
{
printf("install sigal error\n");
}
while(1)
{
sleep(200000);
printf("wait for the signal\n");
}
}
void new_op(int signum,siginfo_t *info,void *myact)
{
printf("receive signal %d", signum);
sleep(5);
}

说明,命令行参数为信号值,后台运行 37 &,可获得该进程的 ID,假设为 pid,然后再另一终端上运行 kill -s 37 pid 验证信号的发送接收及处理。同时,可验证信号的排队问题。



父子进程
1 . 父进程可以利用 wait()/ waitpid()等待子进程的结束,避免僵死子进
程的产生,当然也可以循环的 wait()/ watipid()来等待所有的子进程的
结束;最好可以用法是,在子进程结束时,会向父进程发送的
SIGCHLD 信号,父进程通过 signal()/sigaction()来响应子进程的结束.
具体可参考代码如下:


signal(SIGCHLD, sigchld_handler);
void sigchld_handler(int sig)
{
pid_t pid;
int status;
for(; (pid =waitpid(-1, &status, WNOHANG)) >0;)
{
printf("child %d died:%d\n", pid, WEXITSTATUS(status));// pid,
status);
}
//while((pid =waitpid(-1, &status, WNOHANG))>0){}
return;
}
signal(SIGCHLD, sigchld_handler);
void sigchld_handler(int sig)
{
pid_t pid;
int status;
for(; (pid =waitpid(-1, &status, WNOHANG)) >0;)
{
printf("child %d died:%d\n", pid, WEXITSTATUS(status));// pid,
status);
}
//while((pid =waitpid(-1, &status, WNOHANG))>0){}
return;
}


2 . 当父进程结束时,还未结束的子进程就会成为孤儿进程,系统会
把 init 进程作为其父进程;从而使得子进程在父进程结束后,继续
运行.


3 . 对于父子进程共存时,产生相关信号如 SIGINT 时,父子进程都
能接受到此信号,只是先由父进程响应,再由父进程把此信号传递
给子进程;但要注意的地方是:(1)如父进程没有对该信号进程自
定 义 处 理 , 则 父 子 进 程 都 接 受 信 号 的 默 认 处 理 , eg: 在 没 有 对
SIGINT 进程自定义处理时,产生此信号,由父子进程都马上中止;
(2)如父进程自定义了信号处理方法,则子进程一样接受此信号和
其信号处理方法;此时,若子进程成为孤儿进程,则此时的子进程
不会再接受此信号和其信号处理方法.代码如下:

view plaincopy to clipboardprint?
signal(SIGINT, sig_handler);
for(i =0; i<5; i++)
{
if(fork() ==0)
{
printf("child %d\n", getpid());
sleep(5);//此刻,响应 SIGINT 信号,且信号处理完后唤醒进程
printf("after sleep1:child %d\n", getpid());
sleep(5);// 到 此 刻 , 子 进 程 成 为 孤 儿 进 程 , 因 此 不 再 响 应
SIGINT 信号
printf("after sleep2:child %d\n", getpid());
exit(0);
}
}
signal(SIGINT, sig_handler);
for(i =0; i<5; i++)
{
if(fork() ==0)
{
printf("child %d\n", getpid());
sleep(5);//此刻,响应 SIGINT 信号,且信号处理完后唤醒进
程
printf("after sleep1:child %d\n", getpid());
sleep(5);//到此刻,子进程成为孤儿进程,因此不再响应
SIGINT 信号
printf("after sleep2:child %d\n", getpid());
exit(0);
}
}


4 . 在创建子进程时,内核将父进程的很多内容拷贝给子进程,同时
在调用 fork()后,父子进程共享正文部分(其实也相当于拷贝);注
意拷贝两字,所以子进程中修改的东东对父进程没有作用,除非利
用父子进程通信方法;因此,要特别注意子进程的代码编写,如动
态空间的释放问题,内存信号修改问题.代码如下:

gets(buf);
pid =fork();
if(pid ==0)
{
printf("child:");
strcpy(buf, "xiaofeng");
//此处的 buf(为主进程中 buf 的一个副本)与 else(主进程)中的
buf 无关,因为对其修改不会影响主进程中 buf 的内容
}
else
{
printf("parent:");
sleep(5);
}
printf("%s\n", buf);//此处输出结果处决于该语句位于哪个进程(父/子
进程)
gets(buf);
pid =fork();
if(pid ==0)
{
printf("child:");
strcpy(buf, "xiaofeng");
//此处的 buf(为主进程中 buf 的一个副本)与 else(主进程)中
的 buf 无关,因为对其修改不会影响主进程中 buf 的内容
}
else
{
printf("parent:");
sleep(5);
}
printf("%s\n", buf);//此处输出结果处决于该语句位于哪个进程(父
/子进程)

结果:(hello 为 gets(buf)语句)
hello
child:xiaofeng
parent:hello
(1)信号处理有三种模式,一为默认处理方式;二为信号忽略方式;三为自定义信号处理方法;
(2) 当一个进程睡眠在可中断优先级时,进程捕获的信号将中断进程的睡眠



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值