《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: 当软件条件达成,且有进程需要得到此通知

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

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

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

http://blog.csdn.net/alex_my/article/details/39345931

-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


3

-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还是有些疑问,留着。

时间: 2024-09-30 20:08:10

《unix环境高级编程》 读书笔记 (9)的相关文章

UNIX环境高级编程学习笔记(第一章UNIX基础知识)

总所周知,UNIX环境高级编程是一本很经典的书,之前我粗略的看了一遍,感觉理解得不够深入. 听说写博客可以提高自己的水平,因此趁着这个机会我想把它重新看一遍,并把每一章的笔记写在博客里面. 我学习的时候使用的平台是Windows+VMware+debian,使用secureCRT来连接(可以实现多个终端连接). 因为第一章是本书大概的描述,所以第一章的我打算写得详细一点,而且书本的原话占的比例会比较多,重点的东西会用粗体显示出来. 1.1  引言 所有操作系统都为他们所运行的程序提供服务.典型的

Unix环境高级编程学习笔记(五):进程控制

1 getpid函数,getppid函数,得到进程id,得到父进程id #include<unistd.h> pid_t getpid(void) pid_t getppid(void) uid_t getuid(void)得到实际用户id uid_t geteuid(void)得到有效用户id gid_t getgid(void)得到实际组id gid_t getegid(void)得到有效组id 2 fork函数,当前进程创建新进程 #include<unistd.h> pid

《UNIX环境高级编程》笔记——3.文件IO

一.引言 说明几个I/O函数:open.read.write.lseek和close,这些函数都是不带缓冲(不带缓冲,只调用内核的一个系统调用),这些函数不输入ISO C,是POSIX的一部分: 多进程共享资源(包括文件)时,会有很多额外的烦恼,需要对共享资源.原子操作等概念深入理解,需要理解涉及的内核有关数据结构,这些数据结构对理解文件.共享有重要作用: 最后介绍dup.fcntl.sync.fsync和ioctl函数. 二.文件描述符 open或creat文件时,内核--文件描述符fd-->

Unix环境高级编程学习笔记(四):进程环境

1 exit函数与_Exit函数 #include<stdlib.h> void exit(int status) void _Exit(int status) 这两个函数的不同之处在于exit函数先执行清理工作后再进入内核(清理I/O缓冲),_Exit函数直接进入内核 2 atexit函数,登记函数,在exit的时候执行 int atexit(void (* func) (void)); 被登记的函数称为终止处理函数,这些函数的调用顺序与登记顺序相反,如果一个函数被登记多次,也会被调用多次

Unix环境高级编程学习笔记(七):线程

1 线程包含线程ID,一组寄存器的值,栈,调度优先级和策略,信号屏蔽字,errno变量,以及线程私有数据.进程的所有信息对于该进程的所有线程都是共享的,包括可执行程序文本,程序全局内存和堆内存,栈以及文件描述符. 线程可以通过pthread_self函数获得自身线程ID #include<pthread.h> pthread_t pthread_self(void) 新增进程可以通过pthread_create函数创建 #include <pthread.h> int pthrea

Unix环境高级编程学习笔记(三):标准I/O , 系统数据文件和信息

1 标准I/O函数不同于read,write函数,是其在流上进行操作, 当首次调用标准I/O函数时,系统会首先调用malloc,为流创造缓冲区, 2 fopen函数 #include<stdio.h> file * fopen(const char* pathname, const char * restrict name); 打开返回指针,出错返回NULL, type的取指有r(读),w(写),a(追加),r+/w+(读+写),a+(读+写+追加) int fclose(file* fp)

UNIX环境高级编程(阅读笔记)---多线程信号

多线程信号 1.默认情况下,信号将由主进程接收处理,就算信号处理函数是由子线程注册的 2. 每个线程均有自己的信号屏蔽字,可以使用sigprocmask函数来屏蔽某个线程对该信号的响应处理,仅留下需要处理该信号的线程来处理指定的信号. 3. 对某个信号处理函数,以程序执行时最后一次注册的处理函数为准,即在所有的线程里,同一个信号在任何线程里对该信号的处理一定相同 4. 可以使用pthread_kill对指定的线程发送信号 5. 在主进程中对sigmask进行设置后,主进程创建出来的线程将继承主进

《Unix环境高级编程》读书笔记 第7章-进程环境

1. main函数 int main( int argc, char *argv[] ); argc是命令行参数的数目,包括程序名在内 argv是指向参数的各个指针所构成的数组,即指针数组 当内核执行C程序时(使用exec函数),在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址——这是由连接器设置的,而连接器则是由C编译器调用.启动例程从内核取得命令行参数和环境变量值,然后按上述方式调用main函数做好安排. 2. 进程终止 有8种方式使进程终止,其中5种

《Unix环境高级编程》读书笔记 第3章-文件I/O

1. 引言 Unix系统的大多数文件I/O只需用到5个函数:open.read.write.lseek以及close 本章描述的函数经常被称为不带缓冲的I/O.术语不带缓冲指的是在用户的进程中对其不会自动缓冲,每个read和write都调用内核中的一个系统调用.但是,所有磁盘I/O都要经过内核的块缓存区(也称为内核的缓冲区高速缓存).唯一例外的是对原始磁盘设备的I/O. 2. 文件描述符 对于内核而言,所有打开的文件都通过文件描述符引用.文件描述符是一个非负整数,其变化范围是0~OPEN_MAX

《UNIX环境高级编程》读书笔记 —— 文件 I/O

一.打开或创建一个文件 #include <fcntl.h> int open(const char *pathname, int oflag, .../*mode_t mode*/); 返回值:若成功则返回文件描述符,若出错则返回-1 oflag选项: O_RDONLY O_WRONLY O_RDWR 以上三个常量中必须指定一个,且只能指定一个. 以下常量都是可选的: O_APPED     每次写时追加到文件尾 O_CREAT     若文件不存在,则创建 O_EXCL      若同时指