信号是传送给进程的一种事件通知,生成信号的事件有三大类:
1. 程序错误:除零,非法内存访问…
2. 外部信号:终端Ctrl-C产生SGINT信号,定时器到期产生SIGALRM…
3. 显示请求:kill函数允许进程发送任何信号给其他进程或进程组。
信号生成既可以是同步的(信号与程序中的某个具体操作相关并在那个操作同时产生),也可以是异步的。通常程序错误生成信号为同步的,进程显式请求给自己的信号也是同步的。
外部事件总是异步的,来自其他进程的显示请求也是异步的。
信号发生时,我们可以告诉unix内核采取下面三种动作中的任一种:
1. 忽略信号:大部分信号可被忽略,除SIGSTOP和SIGKILL信号外(这是超级用户杀掉或停掉任意进程的手段)。
2. 捕获信号:注册信号处理函数,它对产生的特定信号做处理。
3. 让信号默认动作起作用:unix内核定义的默认动作,有5种情况:
a) 流产abort:终止进程并产生core文件。
b) 终止stop:终止进程但不生成core文件。
c) 忽略:忽略信号。
d) 挂起suspend:挂起进程。
e) 继续continue:若进程是挂起的,则resume进程,否则忽略此信号。
任意时刻,进程可以为信号指定动作。
信号处理涉及两个过程,生成与交付。
信号生成出现在事件发生时,此时内核检查接收进程的相关数据结构,此结构中记录了信号的布局,悬挂信号集和处理动作。如果信号是要被忽略的,内核不做任何动作就返回。否则,将此信号加入悬挂信号集合中。(悬挂信号集合通常用位串表示,每位对应一个信号,内核无法记录同一信号的多个实例)。
如果进程处于可中断的睡眠状态,并且该信号非阻塞,内核唤醒进程。被唤醒进程一旦运行则在返回用户态前优先处理悬挂信号,当有悬挂信号并且非阻塞时,内核查看是否有处理句柄,如果没有注册句柄,则采取默认动作(通常为终止进程)。如果有句柄,则将此信号加入阻塞信号屏蔽中。
最后内核安排进程返回到用户态并执行信号句柄,同时保证句柄执行完时,进程从被中断处代码执行。
由异步事件产生的信号可能在任一条指令后发生,当信号句柄完成时,进程从中断之处起执行。如果信号是在进程处于系统调用期间到达的,内核通常abort此系统调用并返回错误码EINTR。
进程可以有选择的阻塞信号交付,当一个被阻塞的信号生成时,如果进程指定的动作为默认或者捕获,则此信号一直悬挂于该进程直到对此信号的阻塞放开,或者信号动作改为忽略。 系统对阻塞信号的判定是在信号交付时而非生成时,这样可以允许进程在信号被交付前改变信号动作。
每个进程有一个阻塞信号屏蔽,它定义当前被阻塞交付的那些信号。可认为它是一个位串,每位对应一个信号。如果某信号对应的位被设置,则该信号当前阻塞,进程可调用
sigprocmask函数来检查或设置屏蔽。
程序错误类信号:默认动作使进程流产,产生core文件。
SIGABRT: 调用abort函数生成的信号。
SIGFPE: 浮点计算错误。
SIGILL: 非法指令错误。
SIGBUS/SIGSEGV: 硬件错误-非法地址访问。
SIGEMT: 硬件错误
SIGSYS: 非法系统调用。
SIGTRAP: 硬件错误(通常为断点指令)。
程序终止类信号:默认动作使进程终止,我们通常要处理这类信号,做一些清理工作,句柄函数应在结束时为此信号指定默认动作,然后再次生成该信号,使得程序终止。
SIGHUP:终端断开连接时,生成此信号给控制进程。
SIGINT:Ctrl-C或Delete按下时,由终端驱动生成,并发送给前台进程组中的所有进程。
SIGKILL:使程序立即终止,不能被捕获或忽略,也不能被阻塞。
SIGQUIT:Ctrl-\,如SIGINT,并且产生core。
SIGTERM:该信号使程序终止,但是可以阻塞、捕获、忽略。
闹钟类信号:通知定时器到期,默认动作是终止程序,但通常会设置句柄。
SIGALRM:alarm/setitimer函数设置定时到期后,会产生此信号。
SIGPROF:
SIGVTALRM:
I/O类信号:通知进程在描述字上发生了感兴趣事件,支持信号驱动IO。
SIGIO: fd准备执行输入输出时发送此信号。
SIGPOLL:异步I/O信号。
SIGURG:网络收到带外数据时可选择生成此信号。
作业控制类信号:
SIGCHLD: 进程终止或停止时会向其父进程发送该信号,默认动作为忽略。
SIGCONT: 使停止的进程恢复运行。
SIGSTOP: 停止进程。
SIGTSTP/SIGTTIN/SIGTTOU:
操作错误类信号:默认动作终止程序。
SIGPIPE: 管道破裂。
SIGXCPU/SIGXFSZ:
signal函数:
void (* signal(int sig, void (*func)(int)))(int);
sig指明是哪一种信号。
func指明动作:SIG_DFL, SIG_IGN,或者信号句柄地址。
当信号发生时,如果func指向信号句柄,系统在将控制转往句柄前,先将该信号动作置为DFL,或者阻塞该信号直到句柄完成。
Signal函数返回值指向前一次有效动作指针:SIG_DFL,SIG_IGN,或信号地址,这提供了恢复信号动作的机制。 如果signal调用出错,返回SIG_ERR并设置errno。
进程初启时的信号动作:
fork:继承父进程的动作
exec:所有信号动作要么是忽略要么是默认。
不可靠信号:
早期版本Unix中使用signal,每当信号交付时,其动作总是由系统重置为默认动作,因此为了使信号句柄执行期间,仍能对同一信号后续做反应,需要再次调用signal。
Catch_Signal(){
// 如果第二次信号刚好在此时发生,将导致进程终止core掉。
signal(SIGQUIT,Catch_Signal);
}
Main(){
signal(SIGQUIT, Catch_Signal);
…
}
使用signal的另一个问题是,对于信号,进程要么忽略,要么捕获,无法在一段时间内阻塞信号(推迟信号的交付),为了克服signal兼容性问题,现代unix均实现了POSIX定义的sigaction函数,该函数采用一个sigaction结构,除定义信号交付时要采取的动作外,还包含其他一些动作控制信息。
sigaction还允许调用进程检测或指定与特定信号相关的动作。
int sigaction(int sig, const struct sigaction* act, struct sigaction* oact);
sig指定信号-除SIGKILL和SIGSTOP。
如果act为NULL,则不改变信号动作,只查询当前动作。
成功返回0,失败则不安装新信号动作,返回-1,设置errno。
struct sigaction{
void (*sa_handler)(int); // 同signal的第二参数func。
void (*sa_sigaction)(int, siginfo_t*, void*); // 仅当flags设置SA_SIGINFO起作用。
sigset_t sa_mask; // 指明信号执行期间要阻塞的一组信号,除此之外导致信号句柄执
行的信号也自动阻塞,除非指定了SA_NODEFER。信号句柄正常返回时,屏蔽恢复到原先状态。
int sa_flags; //
};
sa_flags是一个位串,可以通过或运算生成。可设置下列标志:
SA_NOCLDSTOP: 只对CHLD信号起作用,子进程暂停时不发信号给父进程。
SA_RESTART: 信号句柄返回时,自动恢复被该信号中断的系统调用。否则该系统调用将中断返回-1,并设置errno为EINTR。
SA_ONSTACK:
SA_RESETHAND:信号句柄入口,系统将重置信号动作为SIG_DFL.
SA_NODEFER:句柄执行期间,不自动阻塞该信号。
SA_NOCLDWAIT:只对CHLD起作用,调用进程的所有子进程在终止时不会成为Zombe。这种情况下,父进程无需要wait子进程,并且子进程终止也不向父进程发SIGCHLD信号。
如果父进程调用wait,将阻塞到所有子进程终止,并返回-1,errno设为ECHILD.
SA_SIGINFO:如果未设置此标志,则信号句柄原型为:
void func(int signo);
如果设置此标志,则句柄原型为:
void func(int signo, singinfo_t* info, void* context);
Info- 解释信号生成的原因。
Context-信号被交付时所中断进程的上下文。
一旦用sigaction为特定信号建立了动作,该动作就一直保持,直到另一次调用sigaction,或者调用exec,或者因设置了SA_RESETHAND导致系统自动改变动作为默认为止。
除了外部中断产生信号外,程序可以显式的调用raise函数给他自己发送信号,或调用kill向自己或其他进程发送信号。
阻塞信号意味着保持该信号并推迟它的交付,可以防止程序中的关键代码被信号中断。
信号集操作:
int sigemptyset(sigset_t* set); // 清空信号集
int sigfillset(sigset_t* set); // 包含所有信号集
sigaddset/sigdelset/sigismember;
sigprocmask用来检测或改变调用进程的信号屏蔽。
int sigprocmask(int how, const sigset_t* set, sigset_t* oset);
How: SIGBLOCK SIG_UNBLOCK SIG_SETMASK
如果调用sigprocmask放开某个信号而导致任何悬挂信号被解除阻塞,则函数返回前,这些信号中至少有一个被交付。
检查悬挂信号:int sigpending(sigset_t* set);
等待信号:int pause(void);
悬挂调用进程直到有一个信号到达。仅当句柄执行并返回时,pause函数才返回:此时返回-1,并设置errno为EINTR。所有其他情况下pause不返回。
如果多个相同信号在信号句柄运行前发给了进程,则句柄只被运行一次。换句话说,默认情况下unix信号是非排队的,只有当实现支持实时信号并且sa_flags设置SA_SIGINFO时,由sigqueue生成的后续信号才排队。
I/O执行期间,有可能到达信号,此时有两种情况:重新开始系统调用还是返回失败.
早期unix特征为,进程执行慢系统调用期间捕获信号时,该调用被中断并设置errno为EINTR。现代unix增加了sa_flags选项SA_RESTART可对单个信号要求自动恢复被中断的系统调用。
原则如下:如果进程阻塞于慢系统调用,并且进程捕获信号且该信号句柄返回,系统调用可能返回EINTR。 虽然有些unix能自动恢复系统调用,但是为了兼容性,我们必须准备慢系统调用返回EINTR,当检测到EINTR,要么重新开始系统调用,要么做其他处理。
Again:
if (n=read(fd,buff, BUFSIZE) < 0) {
if (errno ==EINTR)
goto Again;
else
…}
分享到:
相关推荐
6. **进程管理**:UNIX提供了丰富的进程控制命令,如`ps`(查看进程状态)、`kill`(发送信号到进程)、`nohup`(让程序在后台运行不受挂断影响)等。 7. **权限与用户管理**:UNIX的权限系统基于所有者、组和其他...
在Unix环境中,还会涉及系统调用、信号处理等Unix特定的编程技术。不断地实践和阅读高质量的代码是提高编程能力的关键。同时,积极寻找和解决问题,以及参与开源项目,都能加速学习进程。 总之,Unix下的C语言学习...
7. **进程管理**:Unix系统提供了丰富的进程控制命令,如`ps`(查看进程状态)、`kill`(发送信号给进程)、`nohup`(让进程在后台运行不受登录/注销影响)。 8. **网络和通信**:Unix系统在网络通信方面非常强大,...
《Unix傻瓜书》是一本面向初学者的经典Unix学习指南,旨在通过逐步引导的方式帮助读者深入理解Unix操作系统。这本书以浅显易懂的语言,详细介绍了Unix的基础知识、核心概念以及实用技巧,是学习Unix系统不可多得的...
《Unix操作系统学习手册》是一本全面且深入的指南,旨在帮助读者掌握Unix操作系统的配置与命令使用。Unix系统,作为一种历史悠久、广泛应用于服务器和科研领域的多用户、多任务操作系统,其强大而灵活的命令行界面和...
《Unix学习教程》是一本旨在帮助读者快速掌握Unix操作系统编程的宝贵资料。Unix系统作为一款历史悠久、广泛应用的操作系统,其强大的稳定性和灵活性深受程序员和系统管理员喜爱。通过深入学习Unix,我们可以理解操作...
**UNIX基础学习知识** 在IT领域,UNIX操作系统是一款历史悠久、广泛应用的多用户、多任务操作系统。它以其稳定、高效和可移植性著称,尤其在科研、工程和服务器管理等领域有着广泛的应用。本文将深入探讨UNIX的基础...
接下来,你会学习到进程管理和调度,包括如何使用`ps`查看进程状态,`kill`发送信号终止进程,以及`nohup`让命令在后台持续运行。Unix的管道(pipe)和重定向功能是其强大的工具,可以组合命令并改变输入输出流,...
3. **进程管理**: Unix提供了丰富的进程控制命令,如`ps`查看进程状态,`kill`发送信号终止进程,`nohup`让命令在后台运行不受登录退出影响。 4. **管道和重定向**: Unix的管道`|`可以将一个命令的输出作为另一个...
通过分析这个项目,我们可以学习如何使用Unix的系统调用如`sem_init`, `sem_wait` (P操作), `sem_post` (V操作) 来实现信号量,并解决实际的同步问题。这对于理解和编写多线程或多进程应用程序,尤其是在涉及共享...
通过《UNIX程序员手册》,新手不仅可以学习到UNIX系统的基础知识,还能深入理解其设计哲学和工作原理,为后续的系统级编程和高级应用开发打下坚实基础。这本书不仅是入门工具,更是长期参考资料,值得反复研读和实践...
本学习资料集旨在为初学者提供一个基础的UNIX学习平台,帮助你掌握基本的UNIX命令行操作,以及理解UNIX系统的运行机制。 1. **UNIX系统概述** - UNIX是最早采用分时系统设计的操作系统之一,由Ken Thompson和...
笔记可能涉及了Unix的文件系统结构、进程管理、I/O操作、信号处理、Shell脚本编写等内容。了解这些基础将帮助读者更好地理解和操作Unix环境。 2. **C语言基础**:C语言提供了低级别的内存管理和控制,使得开发者...
作者详细讲解了Unix系统调用接口,包括文件I/O、进程控制、信号、内存映射等核心功能。此外,还涵盖了线程编程、进程间通信和POSIX标准等内容,对理解系统级编程提供了全面的指导。读者通过这本书可以深入了解Unix...
### Unix编程学习指南 #### 一、概述 本指南旨在为初学者提供一套全面而系统的Unix编程学习路径,帮助读者迅速掌握Unix编程的核心概念和技术要点。通过本指南的学习,你可以了解Unix编程的基本流程,掌握关键的...
本学习资料包包含了两份PDF文档:初级Unix 1.pdf和初级Unix 2.pdf,旨在帮助初学者入门UNIX系统。 1. **UNIX历史**:UNIX最初由Ken Thompson和Dennis Ritchie于1969年在AT&T贝尔实验室开发,它是一种分时操作系统,...
这篇"Unix学习笔记"显然是为了帮助初学者掌握Unix的基础知识和操作技巧。 首先,让我们从"Chapter 1"开始,这通常会涵盖Unix的历史、基本概念以及安装。在这一章,你可以期待了解Unix是如何诞生的,它与Linux和类...
书中的内容不仅涵盖了基础的系统调用、文件操作,还包括进程管理、网络编程等高级主题,是学习Unix/Linux系统编程的宝贵资料。光盘资源则为学习过程提供了丰富的实践练习,有助于巩固理论知识,提升实际操作技能。 ...
在深入探讨Unix与C++编程之前,我们先来理解一下这个学习资料的背景。这份资料来自达内,一个知名的IT培训机构,它强调了外企的师资、技术以及品质,这意味着培训内容将按照国际标准进行,适合对进入外企或者追求高...
### Unix系统编程与OCaml语言 #### 引言 本文档提供了一个关于Unix系统编程的入门课程...通过学习本教程,读者不仅可以掌握如何使用OCaml编写高效的Unix应用程序,还能了解到OCaml语言在系统编程领域中的优势和特点。