linux/unix 环境编程-信号总结
2014-03-19 16:03
183 查看
一些疑问:
信号,信号,如果学习信号,我想按照一个人的思维来问自己这么几个问题。
信号是什么,信号怎么产生的,有哪些信号,啥用?
怎么发送信号?
谁来接收信号,接收什么信号?
收到信号了该做些什么?
我的理解:
在unix环境高级编程中指出信号是一种软中断。软中断与硬中断相对应。硬中断是指硬件对CPU的中断,软中断是指中断处理程序中 下半部对内核的中断。根据中断类型的不同就会产生不同的信号(没有查证,暂时这么理解)。内核会将信号发送给当前正在运行的进程。 信号是一种异步事件处理机制。
信号的种类大约40种,不同的系统不一样。都已SIG开头,其中SIGUSR1和SIGUSR2是用户定义信号,可用于应用程序。
信号是由进程发往进程的,包括内核进程,用户进程。发送信号可以使用下面的函数:
kill(pit_t pid, int sig); //kill向进程号为pid的进程发送sig信号
raise(int sig);进程向自己发送sig信号。
另外值得一提的是alarm()函数和pause(void)函数,alarm函数是定时器,比如alarm(5);那么在5秒后会向进程自己发送一个SIGALRM函数。pause(void)函数则是 使进程挂起,直到进程调用了一个信号处理函数,这是pause会返回-1,并置errno为EINTR。pause函数有个缺点。设想如果之前屏蔽掉了部分信号,接着解除,然后调用pause函数。可能在解除之后,就捕捉到了很多信号,这时调用pause可能就再也捕捉不到信号了。所以进程就会一直被挂起。
要解决这种问题就需要将解除屏蔽和进程休眠合为一个原子操作。int sigsuspend(const sigset_t *sigmask);函数就是用来干这种事的。返回-1,并将errno置为EINTR。
一个进程怎么捕获想要处理的信号呢?
首先了解一下信号集这个数据结构。sigset_t 是一个位数多于信号种类个数的整数,这样每一位都可以对应一个信号。 我们可以用sigset_t来表示我们想捕获或不想收到的信号集。
操作sigset_t有4个函数
int sigemptyset(sigset_t *set); //将set各位置0
int sigfillset(sigset_t *set); //将set各位置1
int sigaddset(sigset_t *set, int sig); //将sig位置1
int sigdelset(sigset_t *set , int sig); //将sig位置0
前四个返回值是: 成功返回0,出错返回-1
int sigismember(sigset_t *set, int sig); //判断sig是否在set中。
返回值 1 表示在,0表示不在,-1 表示错误。
有了信号集,我们就要告诉系统是对这个集合感兴趣(捕获),还是不感兴趣(不捕获),可以使用下面的函数。
int sigprocmask(int how, sigset_t *set, sigset_t *old_set);
how 有三个值:
SIG_BLOCK: 该进程最新的信号屏蔽字是 set和原来的屏蔽字的并集
SIG_UNBLOCK:set包含了我们需要 解除阻塞的信号,新的屏蔽字为 原来的与set的补集的交集。
SIG_SETMASK:进程最新的屏蔽字就是set。
上面的屏蔽和阻塞的意思是说进程将不在处理这些信号。
old_set用来保存原先的屏蔽字。
若set为空,则how值无意义。
调用sigprocmask函数之后,在进程的生命中 能捕获的信号就确定了,在重新设置之前。
可以使用sigpending()函数来查看当前进程中有哪些信号被阻塞没有处理。
int sigpending(sigset_t *set);返回当前屏蔽字。
捕获了信号那么我们应该怎么处理呢?
捕捉到信号后,需要调用信号处理例程。我们可以定义自己的信号处理例程,并与相关信号绑定。这样收到该信号后就能调用该例程。实现 信号和例程的绑定,知道的有2种方法。
第一种使用signal函数,原型如下:
void (*signal(int sig, void (*sigfun)(int))) (int);
看上去很难理解,仔细推敲一下。 先只看 signal(int sig, void(*sigfun(int)));这样我们就知道了signal函数的2个参数,一个是要绑定的信号,另外一个是 一个返回值是void,参数只有一个int的函数指针。 这样 我们就明白了 void(*signal())(int),是说signal的返回值也是一个参数是int 无返回值的函数指针,是改变前的信号处理例程。
使用signal函数绑定自定义处理例程是不可靠的,因为信号可能丢失。即如果在处理例程的时候又来了一个信号,那么这个信号就捕获不到了,也就是使用signal不能在信号发生的时候记住它等进程做好准备后再通知。
第二种方法:使用sigaction函数,其功能是检查或修改与指定信号相关联的处理动作。原型如下:
int sigaction(int signo,struct sigaction *act, struct sigaction *oact);
成功返回0,失败返回-1;
struct sigaction的定义如下:
struct sigaction
{
void (*signal_handler)(int); //当flag的值不为 SA_SIGINFO时,将这个函数作为信号的动作。
sigset_t sa_mask; //设置进程的信号屏蔽字,当处理例程运行完毕会将进程信号屏蔽字还原。
int flag; //有多种值,常用有 SA_INTERRUPT,表示被信号中断的系统调用不重启,SA_RESTART表示会重启。
void (*sa_sigaction)(int, siginfo_t, void*); //这个相当于另外一种动作,当flag为SA_SIGINFO时使用,和signal_handler二选一。
};
sa_mask的作用是将暂时设置信号屏蔽字,这样当我们处理完毕被屏蔽的信号会被再次发送。这样就不会产生使用signal函数所带来的信号丢失问题。
信号,信号,如果学习信号,我想按照一个人的思维来问自己这么几个问题。
信号是什么,信号怎么产生的,有哪些信号,啥用?
怎么发送信号?
谁来接收信号,接收什么信号?
收到信号了该做些什么?
我的理解:
在unix环境高级编程中指出信号是一种软中断。软中断与硬中断相对应。硬中断是指硬件对CPU的中断,软中断是指中断处理程序中 下半部对内核的中断。根据中断类型的不同就会产生不同的信号(没有查证,暂时这么理解)。内核会将信号发送给当前正在运行的进程。 信号是一种异步事件处理机制。
信号的种类大约40种,不同的系统不一样。都已SIG开头,其中SIGUSR1和SIGUSR2是用户定义信号,可用于应用程序。
信号是由进程发往进程的,包括内核进程,用户进程。发送信号可以使用下面的函数:
kill(pit_t pid, int sig); //kill向进程号为pid的进程发送sig信号
raise(int sig);进程向自己发送sig信号。
另外值得一提的是alarm()函数和pause(void)函数,alarm函数是定时器,比如alarm(5);那么在5秒后会向进程自己发送一个SIGALRM函数。pause(void)函数则是 使进程挂起,直到进程调用了一个信号处理函数,这是pause会返回-1,并置errno为EINTR。pause函数有个缺点。设想如果之前屏蔽掉了部分信号,接着解除,然后调用pause函数。可能在解除之后,就捕捉到了很多信号,这时调用pause可能就再也捕捉不到信号了。所以进程就会一直被挂起。
要解决这种问题就需要将解除屏蔽和进程休眠合为一个原子操作。int sigsuspend(const sigset_t *sigmask);函数就是用来干这种事的。返回-1,并将errno置为EINTR。
一个进程怎么捕获想要处理的信号呢?
首先了解一下信号集这个数据结构。sigset_t 是一个位数多于信号种类个数的整数,这样每一位都可以对应一个信号。 我们可以用sigset_t来表示我们想捕获或不想收到的信号集。
操作sigset_t有4个函数
int sigemptyset(sigset_t *set); //将set各位置0
int sigfillset(sigset_t *set); //将set各位置1
int sigaddset(sigset_t *set, int sig); //将sig位置1
int sigdelset(sigset_t *set , int sig); //将sig位置0
前四个返回值是: 成功返回0,出错返回-1
int sigismember(sigset_t *set, int sig); //判断sig是否在set中。
返回值 1 表示在,0表示不在,-1 表示错误。
有了信号集,我们就要告诉系统是对这个集合感兴趣(捕获),还是不感兴趣(不捕获),可以使用下面的函数。
int sigprocmask(int how, sigset_t *set, sigset_t *old_set);
how 有三个值:
SIG_BLOCK: 该进程最新的信号屏蔽字是 set和原来的屏蔽字的并集
SIG_UNBLOCK:set包含了我们需要 解除阻塞的信号,新的屏蔽字为 原来的与set的补集的交集。
SIG_SETMASK:进程最新的屏蔽字就是set。
上面的屏蔽和阻塞的意思是说进程将不在处理这些信号。
old_set用来保存原先的屏蔽字。
若set为空,则how值无意义。
调用sigprocmask函数之后,在进程的生命中 能捕获的信号就确定了,在重新设置之前。
可以使用sigpending()函数来查看当前进程中有哪些信号被阻塞没有处理。
int sigpending(sigset_t *set);返回当前屏蔽字。
捕获了信号那么我们应该怎么处理呢?
捕捉到信号后,需要调用信号处理例程。我们可以定义自己的信号处理例程,并与相关信号绑定。这样收到该信号后就能调用该例程。实现 信号和例程的绑定,知道的有2种方法。
第一种使用signal函数,原型如下:
void (*signal(int sig, void (*sigfun)(int))) (int);
看上去很难理解,仔细推敲一下。 先只看 signal(int sig, void(*sigfun(int)));这样我们就知道了signal函数的2个参数,一个是要绑定的信号,另外一个是 一个返回值是void,参数只有一个int的函数指针。 这样 我们就明白了 void(*signal())(int),是说signal的返回值也是一个参数是int 无返回值的函数指针,是改变前的信号处理例程。
使用signal函数绑定自定义处理例程是不可靠的,因为信号可能丢失。即如果在处理例程的时候又来了一个信号,那么这个信号就捕获不到了,也就是使用signal不能在信号发生的时候记住它等进程做好准备后再通知。
第二种方法:使用sigaction函数,其功能是检查或修改与指定信号相关联的处理动作。原型如下:
int sigaction(int signo,struct sigaction *act, struct sigaction *oact);
成功返回0,失败返回-1;
struct sigaction的定义如下:
struct sigaction
{
void (*signal_handler)(int); //当flag的值不为 SA_SIGINFO时,将这个函数作为信号的动作。
sigset_t sa_mask; //设置进程的信号屏蔽字,当处理例程运行完毕会将进程信号屏蔽字还原。
int flag; //有多种值,常用有 SA_INTERRUPT,表示被信号中断的系统调用不重启,SA_RESTART表示会重启。
void (*sa_sigaction)(int, siginfo_t, void*); //这个相当于另外一种动作,当flag为SA_SIGINFO时使用,和signal_handler二选一。
};
sa_mask的作用是将暂时设置信号屏蔽字,这样当我们处理完毕被屏蔽的信号会被再次发送。这样就不会产生使用signal函数所带来的信号丢失问题。
相关文章推荐
- UNIX环境高级编程--第十章信号总结
- linux/unix 环境编程-基本I/O总结
- UNIX环境高级编程——IPC总结
- (八) 一起学 Unix 环境高级编程 (APUE) 之 信号
- UNIX环境编程学习笔记(25)——信号处理进阶学习之 sigaction 函数
- UNIX环境高级编程--第十一章线程总结
- unix 环境编程学习笔记------对于信号的一点认识
- unix环境高级编程——线程同步信号
- unix环境高级编程-10(信号)
- unix环境高级编程-alarm、pause()与信号处理函数
- unix环境高级编程之信号篇(一)
- UNIX环境高级编程学习之第十章信号-信号的基本操作(绑定/忽略/默认/发送)
- UNIX环境高级编程——线程和信号
- 高级UNIX环境编程10 信号
- UNIX环境高级编程--第9章总结
- UNIX环境高级编程第10章信号10.3singal函数
- Unix环境编程: 信号
- <<UNIX环境高级编程>>学习总结——第四章:文件和目录
- UNIX环境高级编程——线程和信号
- 一起学 Unix 环境高级编程 (APUE) 之 信号