《unix环境高级编程》 读书笔记 (9)

简介: 目录: http://blog.csdn.net/alex_my/article/details/39346381 signals 1 signal concepts 信号是一种软中断,可以由以下情形触发: -1: 用户按下某些终端键,例如ctrl + D -2: 硬件异常,例如除数为0,无效的内存引用 -3:kill(2), kill(1) -4:

signals


1 signal concepts

信号是一种软中断,可以由以下情形触发:

-1: 用户按下某些终端键,例如ctrl + D
-2: 硬件异常,例如除数为0,无效的内存引用
-3:kill(2), kill(1)
-4: 当软件条件达成,且有进程需要得到此通知

当信号发生时,可以告诉内核进行以下处理:

-1:忽略信号;有两个信号不能被忽略,SIGKILL, SIGSTOP, 不能被忽略的原因是这两个信号为内核和超级用户提供了一条可靠的方法去杀死和暂停进程。当忽略一些来自硬件的异常信号,产生的结果是未定义的,不如引用了无效的内存,除数为0。

-2:抓取信号;在抓取信号之前,首先要定义抓取信号之后要如何处理,SIGKILL和SIGSTOP信号不能被捕捉。关于抓取信号处理的可以参考以下前面章节第五节--程序用例3,链接如下:

-3:默认处理信号;每一个信号都有一个默认的处理,大部分处理方式都是终止进程。如果想看各个的处理方式,可以在终端上输入man 7 signal(就不翻译成中文了,直接更明了),以下列出备查。

Signal     Value     Action   Comment
──────────────────────────────────────────────────────────────────────
SIGHUP        1       Term    Hangup detected on controlling terminal
                                     or death of controlling process
SIGINT        2       Term    Interrupt from keyboard
SIGQUIT       3       Core    Quit from keyboard
SIGILL        4       Core    Illegal Instruction
SIGABRT       6       Core    Abort signal from abort(3)
SIGFPE        8       Core    Floating point exception
SIGKILL       9       Term    Kill signal
SIGSEGV      11       Core    Invalid memory reference
SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                     readers
SIGALRM      14       Term    Timer signal from alarm(2)
SIGTERM      15       Term    Termination signal
SIGUSR1   30,10,16    Term    User-defined signal 1
SIGUSR2   31,12,17    Term    User-defined signal 2
SIGCHLD   20,17,18    Ign     Child stopped or terminated
SIGCONT   19,18,25    Cont    Continue if stopped
SIGSTOP   17,19,23    Stop    Stop process
SIGTSTP   18,20,24    Stop    Stop typed at terminal
SIGTTIN   21,21,26    Stop    Terminal input for background process
SIGTTOU   22,22,27    Stop    Terminal output for background process


SIGBUS      10,7,10     Core    Bus error (bad memory access)
SIGPOLL                 Term    Pollable event (Sys V).
                                       Synonym for SIGIO
SIGPROF     27,27,29    Term    Profiling timer expired
SIGSYS      12,31,12    Core    Bad argument to routine (SVr4)
SIGTRAP        5        Core    Trace/breakpoint trap
SIGURG      16,23,21    Ign     Urgent condition on socket (4.2BSD)
SIGVTALRM   26,26,28    Term    Virtual alarm clock (4.2BSD)
SIGXCPU     24,24,30    Core    CPU time limit exceeded (4.2BSD)
SIGXFSZ     25,25,31    Core    File size limit exceeded (4.2BSD)


SIGIOT         6        Core    IOT trap. A synonym for SIGABRT
SIGEMT       7,-,7      Term
SIGSTKFLT    -,16,-     Term    Stack fault on coprocessor (unused)
SIGIO       23,29,22    Term    I/O now possible (4.2BSD)
SIGCLD       -,-,18     Ign     A synonym for SIGCHLD
SIGPWR      29,30,19    Term    Power failure (System V)
SIGINFO      29,-,-             A synonym for SIGPWR
SIGLOST      -,-,-      Term    File lock lost (unused)
SIGWINCH    28,28,20    Ign     Window resize signal (4.3BSD, Sun)
SIGUNUSED    -,31,-     Core    Synonymous with SIGSYS


2 signal function

#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);

这个signal函数接受两个参数
参数1: 信号编码,也就是第一节中的Signal, 比如SIGCHLD
参数2: 接受信号处理函数指针,函数原型void func(int)

另外,还有三个定义:

#define SIG_ERR (void(*)())-1    // 返回错误值
#define SIG_DFL (void(*)())0     // 忽略参数1所指信号     
#define SIG_IGN (void(*)())1     // 恢复为参数1所指信号的处理方法为默认方法

可以作为参数2或者signal的返回值

程序用例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

static void sig_func(int signo)
{
     printf("\nsigno: %d\n", signo);
     exit(EXIT_SUCCESS);
}

int main(int argc, char** argv)
{
     if(signal(SIGINT, sig_func) == SIG_ERR)
     {
          printf("signal failed. errno[%d]\t %s\n", errno, strerror(errno));
          exit(EXIT_FAILURE);
     }

     pause();

     exit(EXIT_SUCCESS);
}

输出:

在键盘上同时按下ctrl + c,产生中断信号。

^C
signo: 2



-1:当执行一个程序时,所有信号的状态都是系统默认或者忽略,且信号相应后执行默认动作,直到在程序中做出改变。如同上一节程序用例当中,按下ctrl + c后,默认是终止进程,当在程序中做出改变之后,变成执行函数sig_func了。

-2:当使用fork后,子进程继承父进程的信号处理方式。

-3:当fork后又使用exec后,新的程序中的信号又恢复了默认,因为替换旧的数据空间,堆栈等,使得原来处理函数对于新的程序来说,已经失效。


4 signal sets

#include <signal.h>

int sigemptyset(sigset_t* set);
int sigfillset(sigset_t* set);
int sigaddset(sigset_t* set, int signum);
int sigdelset(sigset_t* set, int signum);
          return 0 on success and -1 on error

int sigismember(const sigset_t* set, int signum);
          return 1 if is signum is a member of set, return 0 if not, -1 on error

以上函数可以从字面意思理解:
sigemptyset: 将set清空
sigfillset : 将所有的信号填充到set中
sigaddset  : 添加指定信号signum到set中
sigdelset  : 从set中移除指定信号signum
sigismember: 特使signum是否是set中的成员

程序用例在下一节


5 sigprocmask

#include <signal.h>

int pthread_sigmask(int how, const sigset_t* restrict set, sigset_t* restrict oset);
int sigprocmask(int how, const sigset_t* restrict set, sigset_t* restrict oset);

sigprocmask: 用于设定信号屏蔽集内信号的处理方式

参数how可选值如下:

SIG_BLOCK  : set中包含了我们所希望添加的阻塞信号

SIG_UNBLOCK: set中包含了我们所希望解除阻塞的信号

SIG_SETMASK: set中为所设置屏蔽的信号

程序用例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

int main(int argc, char** argv)
{
     sigset_t set;
     sigemptyset(&set);
     sigaddset(&set, SIGINT);

     if(sigismember(&set, SIGINT) == 1)
          printf("SIGINT is a member now\n");
     else
          printf("SIGINT is not a member or error occur\n");

     sigdelset(&set, SIGINT);

     if(sigismember(&set, SIGINT) == 0)
          printf("SIGINT is not a member now\n");
     else
          printf("SIGINT is still a member or error occur\n");

     sigaddset(&set, SIGINT);

     sigprocmask(SIG_SETMASK, &set, NULL);

     getchar();

     // try ctrl + c

     return 0;
}

编译运行之后,可以ctrl + c,可以发现无效。
如果将sigprocmask(SIG_SETMASK, &set, NULL)注释,则可以了。


6 sigpending

#include <signal.h>

int sigpending(sigset_t* set);
          return 0 if success and -1 on error.

sigpending: 通过set返回已经通知进程,但被阻塞而挂起的信号。比如阻塞了SIGINT信号一段时间,而在这段时间中产生了这个信号,通知进程,这个信号称之为pending信号。如果产生多个SIGINT信号,在Linux实现中,也只会处理一次。可以通过程序用例观察到。

程序用例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

static void sigfunc(int signo)
{
     printf("signo: %d\n", signo);
}

int main(int argc, char** argv)
{
     sigset_t set, oset, pset;

     if(signal(SIGINT, sigfunc) == SIG_ERR)
     {
          printf("signal failed, error[%d]: %s\n", errno, strerror(errno));
          exit(EXIT_FAILURE);
     }

     sigemptyset(&set);
     sigaddset(&set, SIGINT);
     if(sigprocmask(SIG_BLOCK, &set, &oset) == -1)
     {
          printf("sigprocmask failed, error[%d]: %s\n", errno, strerror(errno));
          exit(EXIT_FAILURE);
     }

     // 在此期ctrl + c,可以多次输入,看会处理几次
     sleep(5);

     if(sigpending(&pset) == -1)
     {
          printf("sigpending failed, error[%d]: %s\n", errno, strerror(errno));
          exit(EXIT_FAILURE);
     }

     if(sigismember(&pset, SIGINT) == 1)
          printf("SIGINT is pending\n");
     else
          printf("SIGINT is not pending\n");

     // 不再屏蔽
     if(sigprocmask(SIG_SETMASK, &oset, NULL) == -1)
     {
          printf("sigprocmask failed2, error[%d]: %s \n", errno, strerror(errno));
          exit(EXIT_FAILURE);
     }

     printf("sleep again\n");
     // ctrl + c, 可以多次输入,看会处理几次
     sleep(5);

     exit(EXIT_SUCCESS);
}

输出:

第一次sleep时,可以多次输入ctrl + c,5秒结束之后,会显示"SIGINT is pending",且函数sigfunc仅会被调用一次,说明多次输入,在当前的Linux实现中只会被调用一次。
第二次sleep期间,由于SIGINT不再被阻塞,因此输入ctrl + c后,会立即响应。


7 sigaction

#include <signal.h>

int sigaction(int sig, const struct sigaction* restrict act, struct sigaction* restrict oact);

sigaction: 指定或者修改与指定信号相关联的处理动作

struct sigaction:

void(*)(int)                      sa_handler     Pointer to a signal-catching
                                                 function or one of the macros
                                                 SIG_IGN or SIG_DFL.
sigset_t                          sa_mask        Additional set of signals to
                                                 be blocked during execution of
                                                 signal-catching function.
int                               sa_flags       Special flags to affect behav‐
                                                 ior of signal.
void(*)(int, siginfo_t *, void *) sa_sigaction   Pointer to a signal-catching
                                                 function.

sa_handler: 信号处理函数,参数为int,如同signal函数的参数意愿。
sa_sigaction: 信号处理函数,参数有3个。
至于选择sa_handler指向的函数还是sa_sigaction指向的函数,需要依靠sa_flags判断。

sa_flags: 指定信号处理行为,以下值可以使用或组合

SA_NOCLDSTOP: 父进程在子进程暂停或者继续运行时不会收到SIGCHLD信号
SA_ONSTACK  : 如果设置此标志,且使用了sigaltstack设置了备用信号堆栈,则信号会传递给该堆栈中的进程,否则,在当前的进程中
SA_RESETHAND: 信号处理之后,重新设置为默认的处理方式
SA_RESART   : 被信号打断的系统调用自动重新发起
SA_SIGINTO  : 使用sa_sigaction做为处理函数,而不是sa_handler
SA_NOCLDWAIT: 父进程在子进程中退出不会收到SIGCHLD信号,且子进程不会变成僵尸进程
SA_NODEFER  : 使sa_mask设置的屏蔽无效

sa_mask: 在信号处理函数执行过程中,屏蔽哪些信号。注意的是,这个屏蔽仅在信号处理函数执行过程中有效,而不是整个进程。

程序用例:

// test8.9.cc

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

static void sigfunc(int signo)
{
     printf("signo: %d\n", signo);
     sleep(10);
     printf("sleep over\n");
}

static void sigfunc_int(int signo)
{
     printf("SIGINT occur\n");
}

int main(int argc, char** argv)
{
     struct sigaction act;
     act.sa_flags = SA_NODEFER;

     sigemptyset(&act.sa_mask);

     act.sa_handler = sigfunc_int;
     sigaction(SIGINT, &act, NULL);

     act.sa_handler = sigfunc;
     sigaddset(&act.sa_mask, SIGINT);
     if(sigaction(SIGQUIT, &act, NULL) == -1)
     {
          printf("sigaction failed");
          exit(EXIT_FAILURE);
     }

     sleep(20);

     printf("\n");
     return 0;
}

关于sa_mask和SA_NODEFER还是有些疑问,留着。







相关文章
|
Unix Linux C语言
计算机操作系统实验一 Unix/Linux编程开发环境
计算机操作系统实验一 Unix/Linux编程开发环境
167 0
|
6月前
|
Unix
Unix环境高级编程(第三版)中apue.h头文件及其依赖安装教程
Unix环境高级编程(第三版)中apue.h头文件及其依赖安装教程
116 0
|
Unix Shell Python
unix高级编程-fork和execve
unix高级编程-fork和execve
62 0
|
Ubuntu Unix Shell
unix高级编程-fork之后父子进程共享文件
unix高级编程-fork之后父子进程共享文件
64 0
|
Unix Linux
unix高级编程-僵尸进程和孤儿进程
unix高级编程-僵尸进程和孤儿进程
62 0
|
Unix Linux Shell
Unix/Linux环境使用(基础篇)(五)
Unix/Linux环境使用(基础篇)(五)
|
网络协议 安全 Ubuntu
Unix/Linux环境使用(基础篇)(四)
Unix/Linux环境使用(基础篇)(四)
|
Ubuntu Unix Linux
Unix/Linux环境使用(基础篇)(三)
Unix/Linux环境使用(基础篇)(三)
|
安全 Unix Linux
Unix/Linux环境使用(基础篇)(二)
Unix/Linux环境使用(基础篇)(二)
|
Ubuntu 安全 NoSQL
Unix/Linux环境使用(基础篇)(一)
Unix/Linux环境使用(基础篇)(一)