`
isiqi
  • 浏览: 16069025 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

进程与信号(五)

阅读更多

健壮的信号接口

我们已经讨论了使用signal来发出与捕获信号,因为他们在较为旧的Unix程序中很常见。然而,X/Open与Unix规范推荐了一个更为健壮的用于信号处理的新的编程接口:sigaction。

#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);

sigaction结构定义在signal.h中,这个结构用于定义依据由sig所指定的信号而所要采取的动作,而且这个结构至少有以下几个成员:

void (*) (int) sa_handler /* function, SIG_DFL or SIG_IGN
sigset_t sa_mask /* signals to block in sa_handler
int sa_flags /* signal action modifiers

sigaction函数设置也sig信号相关联的动作。如果oact不为空,sigaction就会将前一个信号动作写入他所指向的位置。如果act为空,这就是sigaction所做的所有事情。如果act不为空,就会设置指定信号的动作。

作为信号,如果成功,sigaction就会返回0,否则返回-1。如果指定的信号不存在或是尝试捕获或忽略不能捕获或是忽略的信号时,错误变量errno就会被设置EINVAL。

在参数act所指向的sigaction结构内部,sa_handler是一个指针,指向当接收到sig信号时所调用的函数。这与我们在前面所见到过的传递给signal的函数func相类似。我们可以在sa_handler域使用特殊值SIG_IGN与SIG_DFL分别表示要忽略此信号或是重新载入默认动作。

sa_mask域指定了一个在sa_handler函数调用之前要添加到进程的信号掩码中信号集合。有一些被屏蔽或是不会传递给进程的信号集合。这就会阻止了我们在前面所看到的在处理器函数运行完成之前接收到信号的情况。使用sa_mask域可以减少竞争条件。

然而,由sigaction所设置的处理器捕获的信号默认情况下并不会重新设置,而如要我们希望获得我们在前面所看到的信号行为,那么就必须设置sa_flags域来包含SA_RESETHAND值。在我们详细深入了解sigaction之前,让我们来使用sigaction来替换信号重新编写ctrlc.c程序。

试验--sigaction

进行如下的修改,从而SIGINT可以被sigaction所解释。我们将这个新程序称之为ctrlc2.c。

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void ouch(int sig)
{

printf(“OUCH! - I got signal %d\n”, sig);
}
int main()
{
struct sigaction act;
act.sa_handler = ouch;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
while(1) {
printf(“Hello World!\n”);
sleep(1);
}
}

当我们运行这个版本的程序时,当我们按下Ctrl+C时我们总会得到一条消息,因为SIGINT信号反复的被sigaction处理。要结束这个程序,我们必须按下Ctrl+-\,这在默认情况下会产生SIGQUIT信号。

$ ./ctrlc2
Hello World!
Hello World!
Hello World!
^C
OUCH! - I got signal 2
Hello World!
Hello World!
^C
OUCH! - I got signal 2
Hello World!
Hello World!
^\
Quit
$

工作原理

这个程序调用sigaction而不是signal来将Ctrl+C(SIGINT)的信号处理器设置为函数ouch。首先需要设置一个包含处理器,信号掩码与标志的sigaction结构。在这个例子中,我们并不需要任何标志,并且使用一个新函数sigemptyset设置一个空的信号掩码。

信号集合

头文件signal.h定义了类型sigset_t以及用来操作信号集合的函数。这个集合用在sigaction结构与其他的函数中,用来依据信号修改进程行为。

#include <signal.h>
int sigaddset(sigset_t *set, int signo);
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigdelset(sigset_t *set, int signo);

这些函数的执行他们的名字所显示的操作。sigemptyset将一个信号集合初始化为一个空集合。sigfillset将一个信号集合初始化为一个包含所有定义的信号的集合。sigaddset与sigdelset由一个信号集合中添加和删除一个指定的信号(signo)。这些函数都会在成功时返回0,而失败时返回-1,并且设置errno变量。如果指定的信号不可用,那么唯一的错误就是EINVAL。

函数sigismember确定一个指定的信号是否是一个信号集合中的成员。如果此信号是信号集合中的一员,函数就会返回1,如果不是则会返回0,而如果信号不可用,则返回-1,并且将errno设置为EINVAL。

#include <signal.h>
int sigismember(sigset_t *set, int signo);

进程信号掩码是通过调用函数sigprocmask来设置或是检测的。信号掩码是当前被屏蔽掉的信号集合,因而并不会当前的进程接收到。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

sigprocmask函数会根据how参数以多种方式来改变进程信号掩码。如果参数set不为空,那么新的信号掩码值可以通过set传入,而前一个信号掩码值被写入信号集合oset。

how参数如下:

SIG_BLOCK set中的信号都被加入到信号掩码中
SIG_SETMASK 由set中设置信号掩码
SIG_UNBLOCK set中的信号由信号掩码中移除

如果set参数是一个空指,那么how参数并不会被使用,而这个调用的唯一作用就是获取当前的信号掩码值并写入oset。

如果函数调用成功,sigprocmask会返回0,如果how参数不可用则返回-1,并且将errno设置为EINVAL。

如果一个信号被一个进程屏蔽,那么他就不会被传输,但是仍保持等待状态。一个程序可以通过调用函数sigpending来他的被屏蔽的信号哪些处理等待状态。

#include <signal.h>
int sigpending(sigset_t *set);

这会输出一个被阻止传输的信号集合并且添加到由set所指向的信号集合中。如果成功会返回0,否则会返回-1,并且设置errno来表示错误。当一个程序需要处理信号并且要控制何时调用处理函数时,这个函数会很有用。

一个进程可以挂起执行直到通过调用sigsuspend传输一个信号。这就是我们在前面常见到的暂停功能的常见形式:

#include <signal.h>
int sigsuspend(const sigset_t *sigmask);

sigsuspend函数使用由sigmask信号集合来替换进程信号掩码,然后挂起执行。他会在信号处理函数执行之后恢复执行。如果接收到的信号终止程序,sigsuspend函数就不会再返回。如果接收的信号没有终止程序,sigsuspend函数会返回-1,并且将errno设置为EINTR。

sigaction标记

用于sigaction中的sigaction结构的sa_flags域也许会包含下列值来修改信号行为:

SA_NOCLDSTOP 当子进程停止时不要生成SIGCHLD信号
SA_RESETHAND 当收到信号时将信号动作重设为SIG_DFL
SA_RESTART 重新启动可中断函数而不是以EINTR报错
SA_NODEFER 当捕获时不要将信号添加到信号掩码中

SA_RESETHAND可以用于当捕获一个信号时自动清除一个信号函数,正如我们在前面所看到的。

程序所用的许多系统调用都是可中断的;也就是说,当他们接收到的一个信号时,他们会返回一个错误,并且将errno设置为EINTR表明函数是因为信号而返回的。这个行为对于使用信号的程序要格外小心。如果在sigaction调用中的sa_flags域被设置为SA_RESTART,那么这个函数就不会被中断,而在信号处理函数被执行时重新启动。

通常而言,当一个信号处理函数正在执行时,所接收到的信号会在信号处理函数执行期间添加到进程的信号掩码中。这是为了阻止后面发生了同样的信号,从而使得信号处理函数再次运行。如果函数不是可重入的,在他完成处理之前为另一个信号的发生所调用,那么前一个就会出现问题。然而,如果调用了SA_NODEFER标记,当接收到这个信号时,信号掩码并不会提示。

信号处理函数可以在执行期间被中断,并且在其他的函数再次调用。当我们返回到的前一个调用时,他可正常运行是很重要的。他不仅是递归(调用自身),而是重入(无问题的进入并再次运行)。在内核中同时处理多个设备的中断服务例程需要是可重入的,因为在相同代码的执行期间也许会有一个更高优先级的中断进入。

下面列出的是X/Open规范认为在一个信号处理器内部是可以安全调用的,他们或者是可重入的或者是自身不会发出信号。

access alarm cfgetispeed cfgetospeed
cfsetispeed cfsetospeed chdir chmod
chown close creat dup2
dup execle execve _exit
fcntl fork fstat getegid
geteuid getgid getgroups getpgrp
getpid getppid getuid kill
link lseek mkdir mkfifo
open pathconf pause pipe
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 tcsetpgrp
time times umask uname
unlink utime wait waitpid
write

通用信号参考

在这一部分,我们会列出Linux与Unix程序默认行为通常需要的信号。

下表所列出的信号默认动作都是使用_exit的进程非正常终止(类似于exit,但是在返回内核之前并没有执行任何清理)。然而,状态可以用于wait,而waitpid可以通过特定的信号来表明非正常的终止。

信号名 描述
SIGALRM 由alarm函数所调用的计时器生成的信号
SIGHUP 通过非连接终端发往控制进程,或是通过终端控制进程发往前台进程
SIGINIT 通常是在终端由Ctrl+C或是配置的中断字符生成的信号
SIGKILL 通常由shell使用来强制终止一个不确定进程,因为这个信号是不能被捕获或是忽略的
SIGPIPE 尝试写入一个没有相应读端管道时生成的信号
SIGTERM 作为一个请求进程完成的请求发送。Unix用于停机的情况来请求系统服务停止。这是由kill命令所发送的默认信号。
SIGUSR1,SIGUSR2 也许会被进程用于彼此通信,可能使得他们报告状态信息

默认情况下,下表中的信号也许会引起非正常终止。另外,与实现相关的动作,例如也许会发生创建核心文件的情况。

信号名 描述
SIGFPE 由浮点算术异常生成的信号
SIGILL 处理器执行了一条非法指令。通常是由一个恶意程序或是不正确的共享内存模块引起的
SIGQUIT 通常是在终端输入Ctrl+\或是配置的退出字符发出的信号
SIGSEGV 段错误,通常是由读写非法内存地址(或者是超出数组边界或是引用非法指针)引起的。覆盖一个局部数组变量并破坏堆栈,从而使得函数返回非法地址,引起SIGSEGV信号。

进程默认情况下接收到下表中的信号时会挂起执行。

信号名 描述
SIGSTOP 停止执行(不能被捕获或是忽略)
SIGTSTP 终端停止信号,通常是由Ctrl+Z引起的
SIGTTIN,SIGTTOU shell用来表明后台作业已经停止,因为他们需要由终端读取或是产生输出

SIGCONT可以使得一个停止进程重新启动,如果被一个并没有停止的进程接收到,则会被忽略。SIGCHLD默认会被忽略。

信号名 描述
SIGCONT 如果停止,继续执行
SIGCHLD 当一个子进程停止或是退出时生成信号

小结

在这一章,我们了解了进程如何成为Linux操作系统的基础部分。我们了解了他们如何启动,终止,查看,并且我们如何使用他们来解决编程问题。我们也了解了可以用于控制运行程序动作的信号,事件。我们了解到所有的Linux进程,包括init,使用与程序可用的相同的系统调用集合。

分享到:
评论

相关推荐

    进程间通信之信号 sinal ) 完整代码

    进程间通信之信号 sinal ) 唯一的异步通信方式 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 sinal 五 消息队列 message queue ) 六 信号量 ...

    操作系统实验-信号量机制实现进程同步

    这是一个利用信号量机制实现进程同步的模拟程序,如你有更好的方法或觉的我的可以修改欢迎交流学习。

    进程同步与互斥:System V 信号量示例代码

    进程同步与互斥:System V 信号量,相关使用教程链接如下: http://blog.csdn.net/tennysonsky/article/details/47811201

    1操作系统实验五.docx

    操作系统实验报告 实验五: Linux 信号量与 P、V操作函数的定义 实验目的 掌握Linux信号量的使用方法和P、V操作函数的定义; 掌握使用P、V操作实现进程之间的同步和互斥的方法; 加深对进程同步互斥概念的理解。 实验...

    linux下的进程管理演示(c语言)

    signal()让父进程捕捉用alarm函数设置时钟的时间段终止时产生的信号,当捕捉到该信号后,父进程使用系统调用Kill()向两个子进程发出信号,子进程捕捉到信号后分别输出子进程被杀死信息后终止,父进程等待两个子...

    进程的管道通讯机制和信号量同步互斥机制。

    编制一个程序,程序中创建5个子进程,代表五位哲学家,然后父进程结束。使用信号量机制解决哲学家进餐问题。当哲学家进餐时,屏幕输出: [进程号] eating! 当哲学家思考时,屏幕输出: [进程号] thinging! 相关的...

    操作系统实验报告_进程同步与互斥.doc

    操作系统实验报告_进程同步与互斥.doc

    进程间通信

    信号 ( sinal ) 五.消息队列 ( message queue ) 六.信号量 ( semophore ) 七.套接字 ( socket ) 进程间通信之无名管道(pipe) 进程间通信之有名管道(fifo) 进程间通信之共享内存 shared memory 进程间通信之...

    进程、管道创建

    创建进程和捕获管道内容。 VC6 + stl

    实验五-信号量的实现和应用.zip

    实验五-信号量的实现和应用,一、问题回答 1.在 pc.c 中去掉所有与信号量有关的代码,再运行程序,执行效果有变化吗?为什么会这样? 答:在去掉与信号量有关的代码后,执行结果Customer的消费数据没有按递增的...

    linux实验五 进程间通信实验

    1、编写一段程序,使用系统调用fork( )创建两个子进程,再用系统调用signal( )让父进 程捕捉键盘上来的中断信号(即按ctrl+c键),当捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到...

    进程间通信之无名管道(pipe) 完整代码

    进程间通信之无名管道(pipe) 注意: 1 只能用于具有亲缘关系的进程之间的通信 2 SIGPIPE信号的处理 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 ...

    操作系统进程间通信五种方式

    操作系统中的进程间通信是指不同进程之间...当另一个进程释放该资源时,它会发出一个信号量,通知其他等待的进程可以访问资源了。 套接字通信 套接字通常是指网络编程中使用的一种通信机制,但也可以用于进程间通信。

    深入理解Linux进程间通信

    Linux环境进程间通信(二): 信号(上) 19 Linux环境进程间通信(二): 信号(下) 30 Linux环境进程间通信(三) 40 Linux环境进程间通信(四) 52 Linux环境进程间通信(五): 共享内存(上) 63 Linux环境进程间...

    进程间通信之有名管道(fifo) 完整代码

    五 消息队列 message queue ) 六 信号量 semophore 七 套接字 socket 以上七种进程间通信的完整代码 皆可以在我的资源列表中获取下载: 资源列表:http: download csdn net user qiulanzhu"&gt;进程间通信之有名...

    进程间通信之消息队列 ( message queue )——完整代码

    五.消息队列 ( message queue ) 六.信号量 ( semophore ) 七.套接字 ( socket ) 以上七种进程间通信的完整代码,皆可以在我的资源列表中获取下载: 资源列表:http://download.csdn.net/user/qiulanzhu

    进程间通信之套接字( socket )——完整代码

    五.消息队列 ( message queue ) 六.信号量 ( semophore ) 七.套接字 ( socket ) 以上七种进程间通信的完整代码,皆可以在我的资源列表中获取下载: 资源列表:http://download.csdn.net/user/qiulanzhu

    实验四、进程同步实验

    抽烟者问题。假设一个系统中有三个抽烟者进程,每个抽烟者不断地...卷起并抽掉一颗烟后会发信号通知供应者,让它继续提供另外的两种材料。这一过 程重复进行。 请用以上介绍的 IPC 同步机制编程,实现该问题要求的功能。

    进程间通信--操作系统实验

    通过本实验了解和掌握进程间通信的相关知识 (1)了解进程通信的基本原理。 (2)了解和熟悉管道通信、消息传送机制、共享存储机制和信号机制。

    进程间通信之共享内存 shared memory 完整代码

    进程间通信之共享内存 shared memory ) 1 效率最高 2 存在竞态 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 sinal 五 消息队列 message queue ) ...

Global site tag (gtag.js) - Google Analytics