信号学习第一课--基础知识
信号是某些错误条件而引起由shell和终端管理器生成的
signal可以作为进程间传递消息或者修改行为的一种方式,明确的由一个进程传递给另外一个进程
信号可以被生成,捕获,响应,或者忽略。
程序可以使用signal库函数来处理信号
1 #include <signal.h> 2 void (* signal (int sig, void (* func) (int))) (int);
signal是一个带有sig和func两个参数的函数
准备捕获或者忽略的信号由参数sig给出
接收到指定的信号后将要调用的函数由参数func给出
信号处理函数必须有一个int类型的参数也就是接收信号代码
并且返回值是void
signal函数本身也返回一个同类型的函数,也就是先前用来处理这个信号的函数
处理信号的函数可以用下面的两个值代替:
SIG_IGN忽略信号
SIG_DFL恢复默认行为
信号处理实验程序:
1 #include <signal.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 5 void ouch(int sig)//定义ouch函数,截获信号时候处理这个信号 6 { 7 printf("OUCH! - I got signal %d\n", sig);//先打印一句话 8 (void) signal(SIGINT, SIG_DFL);//然后把一般由ctrl+c产生的SIGINT信号的处理方式恢复为默认形式 9 } 10 11 int main() 12 { 13 (void) signal(SIGINT, ouch);//信号处理函数,第一个参数是准备捕获的或者忽略的信号,第二个参数是处理函数 14 //在这里是指终端中断信号,有信号的时候截获,没信号的时候一直执行打招呼 15 while(1) { 16 printf("Hello World!\n");//一秒中循环打印一句招呼 17 sleep(1); 18 }//当再次按下终端中断信号时候,就会起终止程序的作用。 19 }
执行效果:
1 jason@t61:~/c_program/544977-blp3e/chapter11$ ./ctrlc1 2 Hello World! 3 Hello World! 4 Hello World! 5 ^COUCH! - I got signal 2 6 Hello World! 7 Hello World! 8 ^C 9 jason@t61:~/c_program/544977-blp3e/chapter11$ 10 //程序中信号SIGINT的值恰好是2
这是一种旧的信号处理函数,signal函数返回的是先前对指定信号进行处理的信号处理函数的指针
如果未定义信号处理函数,则返回SIG_ERR并设置errno为一个正值
如果给出的是一个无效的信号,或者尝试处理的信号是不可捕获的或者不可忽略的信号
例如SIGKILL信号,errno将被设置为EINVAL
发送信号
1 #include <sys/types.h> 2 #include <signal.h> 3 int kill(pid_t pid, int sig);
这个函数把参数sig给定的信号发送给参数pid给出的进城好所指定的进程,成功时返回0
要想发送一个信号,发送进程必须拥有相应的权限。
这通常意味这两个进程拥有相同的用户ID
也就是说用户只能发送信号给属于自己的进程,而超级用户可以给任何进程发送信号
失败的时候返回-1并设置errno变量
errno设置为EINVAL是因为给定的信号无效
errno设置为EPERM是因为发送进程权限不够
errno设置为ESRCH是因为目标进程不存在
闹钟函数:
1 #inlcude <unistd.h> 2 unsigned int alarm(unsigned int seconds);
调用这个函数的作用是在seconds秒之后安排发送一个SIGALRM信号
由于处理的延时和时间调度的不确定性,实际闹钟时间将比先安排的要稍微晚一点
参数seconds设置为0将取消所有的已设置的闹钟请求
要是在接收SIGALRM之后再次调用alarm函数则闹钟重新开始计时
每个进程只能有一个闹钟时间,alarm函数的返回值是以前设置的闹钟时间的剩余的秒数
失败的话返回-1
闹钟实验的程序:
1 #include <signal.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 5 static int alarm_fired = 0; 6 7 void ding(int sig) 8 { 9 alarm_fired = 1;//设置一个整型为1 10 } 11 12 int main() 13 { 14 int pid; 15 printf("alarm application starting\n"); 16 if((pid = fork()) == 0) 17 { 18 sleep(5);//在子进程中等待5秒 19 kill(getppid(), SIGALRM);//向父进程发送一个闹钟信号 20 exit(0); 21 } 22 23 printf("waiting for alarm to go off\n"); 24 (void) signal(SIGALRM, ding);//安排信号处理,捕获闹钟信号到ding函数中 25 26 pause();//函数暂停运行,直到信号产生,程序继续运行 27 if (alarm_fired) 28 printf("Ding!\n"); 29 30 printf("done\n"); 31 exit(0); 32 }
程序的执行效果:
1 jason@t61:~/c_program/544977-blp3e/chapter11$ ./alarm 2 alarm application starting 3 waiting for alarm to go off 4 Ding! 5 done 6 jason@t61:~/c_program/544977-blp3e/chapter11$
健壮的信号接口
1 #include <signal.h> 2 int sigaction(int sig, const struct sigaction * act, struct sigaction * oact);
这个函数成功返回0失败的时候返回-1
第一个参数 sig要接收的信号
第二个参数是一个结构指针
sigaction这个结构是定义在接收到参数sig指定的信号后应该采取的行动
这个结构至少拥有下面的三个成员:
void (*) (int) sa_handler//是一个函数指针指向接收信号时将被调用的信号处理函数
sigset_t sa_mask//指定了一个信号集,在调用信号处理函数之前,该信号集将被加入到信号屏蔽字中
int sa_flags
第三个参数是结构指针
要是这个指针是空指针,sigaction将把原先对该信号的动作写到指向的位置
如果给出的信号或者试图对一个不允许捕获或者忽略的信号进行捕获或者忽略
错误变量errno将被设置为EINVAL
用这个函数代替信号处理函数signal的程序:
1 #include <signal.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 5 void ouch(int sig) 6 { 7 printf("OUCH! - I got signal %d\n", sig); 8 } 9 10 int main() 11 { 12 struct sigaction act; 13 14 act.sa_handler = ouch; 15 sigemptyset(&act.sa_mask); 16 act.sa_flags = 0; 17 18 sigaction(SIGINT, &act, 0);//信号处理函数,遇到这个信号去指定的函数中区处理 19 //这个函数可以连续处理SIGINT信号 20 while(1) { 21 printf("Hello World!\n"); 22 sleep(1);//没遇到之前一秒钟打一次招呼 23 } 24 }//终止程序需要提供SIGQUIT信号。由ctrl+\提供
程序执行的效果:
1 jason@t61:~/c_program/544977-blp3e/chapter11$ ./ctrlc2 2 Hello World! 3 Hello World! 4 ^COUCH! - I got signal 2 5 Hello World! 6 Hello World! 7 Hello World! 8 ^COUCH! - I got signal 2 9 Hello World! 10 Hello World! 11 Hello World! 12 ^\退出 (核心已转储) 13 jason@t61:~/c_program/544977-blp3e/chapter11$
信号集
头文件signal.h定义了类型sigset_t和用来处理信号集的函数
sigaction和其他函数将用这些信号集来修改进程在接收到信号时的行为
1 #include <signal.h> 2 int sigaddset(sigset_t * set, int signo);//从信号集中增加给定的信号 3 int sigemptyset(sigset_t *set);//将信号集初始化为空 4 int sigfillset(sigset_t * set);//将信号集初始化为包含所有已知的信号 5 int sigdelset(sigset_t * set, int signo);//从信号集中减少给定的信号 6 //成功时返回0失败时返回-1并设置errno(当给定的信号无效时,设置为EINVAL)
1 #include <signal.h> 2 int sigismember(sigset_t * set, int signo);//函数的作用是检测给定的信号时候是一个信号集的成员 3 //如果是就返回1如果不是就返回0 4 //给定的信号无效就返回-1并设置errno为EINVAL
#include <signal.h> int sigprocmask(int how, const sigset_t * set, signal_t * oset); /*信号屏蔽字的设置和检查是由这个函数完成的 信号屏蔽字是指当前被阻塞的一组信号,它们不能被当前进程接收到 这个函数按照how提供的方法,设置新的信号屏蔽字 新的信号屏蔽字由set指定 旧的信号屏蔽字保存到信号集oset中 how的取值和意义: SIG_BLOCK//把参数set中的信号添加到信号屏蔽字中 SIG_SETMASK//把信号屏蔽字设置为参数set中的信号 SIG_UNBLOCK//从信号屏蔽字中删除参数set中的信号 要是set是空指针的话how就没意义函数调用的目的就是将当前的信号屏蔽字的值保存在oset中 函数成功的话返回0要是how是无效的就返回-1并设置errno为EINVAL*/
1 #include <signal.h> 2 int sigpending(sigset_t * set);//函数的作用是把阻塞的信号中停留在待处理状态的一组信号写到参数set指向的信号集中 3 //成功的时候返回0失败的时候返回-1
1 #include <signal.h> 2 int sigsuspend(const sigset_t * sigmask);//函数将进程的信号屏蔽字替换为由参数sigmask给出的信号集 3 //然后挂起程序的执行,程序将在信号处理函数执行完毕之后继续执行, 4 //要是接收到的信号中止了程序那么这个函数不会返回 5 //要是接收到的信号没有中止程序那么这个函数就会返回-1并将errno设置为EINTR
参考文献:Linux程序设计 Neil Matthew
时间:2015年 06月 29日 星期一 17:09:28 CST
万事走心 精益求美