Linux/UNIX之信号(1)
信号(1)
信号是软件中断。每一个信号都有一个名字,这些名字都以SIG开头(如SIGABRT 夭折信号)。
在头文件<signal.h>中。这些信号都被定义成正整数。不存在编号为0的信号,kill函数对信号编号为0有特殊的应用。
当某个信号出现时,能够要求内核依照下列三种方式之中的一个进行处理:
1. 忽略此信号
2. 捕捉信号
3. 运行系统默认动作
signal函数
UNIX信号机制最简单的接口是signal函数。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
此函数中signum是在头文件<signal.h>定义的信号名,handler的值是常量SIG_IGN、常量SIG_DFL或当地接到此信号后要调用的函数地址。假设指定SIG_IGN,则向内核表示忽略此信号。假设指定SIG_DFL,则表示接到此信号后的动作是系统默认动作。当指定函数地址时,则在信号发生时。调用该函数,我们称这样的处理为捕捉该信号。称此函数为信号处理程序或信号捕捉函数。返回值也是一个函数指针。它是指向之前的信号处理程序的指针。
例如以下程序显示了一个简单的信号处理程序。它捕捉两个用户定义的信号并打印信号编号。
#include<stdio.h>
#include<signal.h>
static voidsig_usr(int); /* one handler for bothsignals */
int main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
perror("can't catchSIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
perror("can't catchSIGUSR2");
for ( ; ; )
pause();
}
static void sig_usr(intsigno) /* argument is signal number*/
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
printf("received signal%d\n", signo);
}
运行及输出结果例如以下:
chen123@ubuntu:~/user/apue.2e$ ./a.out &
[1] 5712
chen123@ubuntu:~/user/apue.2e$kill -USR1 5712
received SIGUSR1
chen123@ubuntu:~/user/apue.2e$kill -USR2 5712
received SIGUSR2
chen123@ubuntu:~/user/apue.2e$ kill 5712
[1]+ Terminated ./a.out
我们在后台调用该程序,并且用kill(1)命令将信号传送给它。注意,在UNIX中,kill不代表杀死进程。kill(1)和kill(2)仅仅是将一个信号送给一个进程或进程组。信号是否终止进程取决于信号的类型,以及进程是否安排了捕捉该信号。
程序启动:当一个程序启动时,全部信号的状态都是系统默认或忽略。在上面的程序中的以下代码:
if(signal(SIGUSR1, sig_usr) == SIG_ERR)
perror("can't catch SIGUSR1");
if(signal(SIGUSR2, sig_usr) == SIG_ERR)
perror("can't catch SIGUSR2");
表示仅当信号当前未被忽略时,进程才会捕捉它们。从signal的这两个调用中能够看到这样的函数的限制:不改变信号的处理方式就不能确定信号的当前处理方式。sigaction函数能够确定一个信号的处理方式,而无需改变它。
进程创建:当一个进程调用fork,其子进程继承父进程的信号处理方式。
kill和raise函数
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
#include <signal.h>
int raise(int sig);
成功返回0,出错返回-1。调用raise(sig)等价于调用kill(getpid(), sig)
kill的pid參数有四种可能:
pid > 0: 将该信号发送给进程ID为pid的进程
pid == 0: 将该信号发送给与发送进程属于同一进程组的全部进程,并且发送进程具有向这些进程发送信号的权限。
pid<0: 将该信号发送给其进程组ID等于pid的绝对值,并且发送进程具有向其发送信号的权限。
pid == -1:将该信号发送给发送进程有权限向他们发送信号的系统上的全部进程。
alarm和pause函数
#include<unistd.h>
unsigned intalarm(unsigned int seconds);
使用alarm函数能够设置一个计时器,在将来某个指定的时间该计时器会超时。当计时器超时时。产生SIGALRM信号。假设不忽略或不捕捉此信号。则其默认动作是终止调用该alarm函数的进程。
当中。參数seconds的值是秒数。要了解的是,经过指定的秒数后,信号由内核产生。由于进程调度的延迟,所以进程得到控制从而能处理信号还须要一些时间。
每一个进程仅仅能有一个闹钟时钟。
#include<unistd.h>
int pause(void);
pause函数使调用进程挂起直到捕捉到一个信号。
仅仅有运行了一个信号处理程序并从其返回时,pause才返回。在这样的情况下,pause返回-1,并将errno设置为EINTR。
信号集
我们须要一个能表示多个信号的——信号集的数据类型。
在诸如siprocmask之类的函数中使用这样的数据类型。以便告诉内核不同意发生该信号集中的信号。
POSIX.1定义了数据类型sigset_t以包括一个信号集,并且定义了下列五个处理信号集的函数。
#include<signal.h>
intsigemptyset(sigset_t *set);
intsigfillset(sigset_t *set);
intsigaddset(sigset_t *set, int signum);
intsigdelset(sigset_t *set, int signum);
intsigismember(const sigset_t *set, int signum);
函数sigemptyset初始化由set指向的信号集,清除当中全部的信号。
函数sigfillset初始化由set指向的信号集,使其包括全部信号。全部应用程序在使用信号集前。要对该信号集调用sigempty或sigfillset一次。这是由于C编译器把未赋初值的外部和静态变量都初始化为0,而这是否与给定系统上信号集的实现相相应却并不清楚。
一旦已经初始化了一个信号集,以后就可在该信号集中增、删特定的信号。
函数sigaddset 将加入一个信号到现有集中。sigdelset 则从信号集中删除一个信号。对全部以信号集作为參数的函数,我们总是以信号集地址作为向其传送的參数。
sigprocmask函数
#include<signal.h>
intsigprocmask(int how, const sigset_t *set, sigset_t *oldset);
返回值:若成功则返回0,若出错则返回-1。
首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。
其次,若set是一个非空指针,则參数how指示怎样改动当前信号屏蔽字。
以下说明了how可选用的值。
SIG_BLOCK 该进程的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包括了我们希望堵塞的附加信号。
SIG_UNBLOC 该进程的信号屏蔽字是其当前信号屏蔽字和set所指信号集补集的交集。set包括了我们希望解除堵塞的信号。
SIG_SETMASK 该进程新的信号屏蔽字将被set指向的信号集的值替换。
假设set是空指针。则不改变进程的信号屏蔽字。how的值也无意义。
sigpending函数
#include<signal.h>
intsigpending(sigset_t *set);
sigpending函数返回信号集,当中的各个信号对于调用进程时堵塞的而不能递送。因而也一定是当前未决的。该信号集通过set參数返回。
以下的程序使用了非常多前面说过的信号功能。
#include <stdio.h>
#include <signal.h>
static void sig_quit(int);
int
main(void)
{
sigset_t newmask, oldmask,pendmask;
if (signal(SIGQUIT, sig_quit) == SIG_ERR)
perror("can't catch SIGQUIT");
/*
* Block SIGQUIT and save current signal mask.
*/
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
perror("SIG_BLOCK error");
sleep(5); /* SIGQUIT here will remain pending */、
exit(0);
}
static void
sig_quit(intsigno)
{
printf("caught SIGQUIT\n");
if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
perror("can't reset SIGQUIT");
}
运行及输出结果:
./a.out
^\ 产生信号一次(在5秒钟之内)
SIGQUIT pending 从sleep返回后
caught SIGQUIT 在信号处理程序中
SIGQUIT unblocked 从sigprocmask返回后
^\Quit 再次产生信号
./a.out
^\^\^\^\^\^\^\^\^\^\ 产生信号10次
SIGQUIT pending
caught SIGQUIT 仅仅产生信号一次
SIGQUIT unblocked
^\Quit 再次产生信号
进程堵塞SIGQUIT信号,保存了当前信号屏蔽字(以便以后恢复),然后休眠5秒钟。再次期间所产生的退出信号SIGQUIT都会被堵塞,而不递送至该进程,直到该信号不再被堵塞。在5秒钟休眠结束后,检查该信号是否是未决的。然后将SIGQUIT设置为不再堵塞。
在设置SIGQUIT为堵塞时。我们保存了旧屏蔽字。为了解除对该信号的堵塞,用旧屏蔽字又一次设置了进程信号屏蔽字(SIG_SETMASK)。
在休眠期间假设产生退出信号,那么此时该信号是未决的,可是不再受堵塞。所以在sigprocmask返回之前。它被递送到调用进程。从程序的输出能够看出:SIGQUIT处理程序中的printf语句先运行。然后再运行sigprocmask之后的printf语句。
Linux/UNIX之信号(1)的更多相关文章
- Linux/UNIX之信号(2)
信号(2) sigaction函数 sigaction函数的功能是检查或改动与制定信号相关联的处理动作.此函数代替了signal函数. #include <signal.h> int si ...
- Linux/Unix监控其他用户和信号
--Linux/Unix监控其他用户和信号 ------------------------------------------------------2013/10/27 查看有哪些用户登录 w ...
- 《Linux/UNIX系统编程手册》第63章 IO多路复用、信号驱动IO以及epoll
关键词:fasync_helper.kill_async.sigsuspend.sigaction.fcntl.F_SETOWN_EX.F_SETSIG.select().poll().poll_wa ...
- [转]Linux进程间通信——使用信号
转载于:http://blog.csdn.net/ljianhui/article/details/10128731 经典!!! Linux进程间通信——使用信号 一.什么是信号 用过 ...
- 《Linux/Unix系统编程手册》读书笔记7 (/proc文件的简介和运用)
<Linux/Unix系统编程手册>读书笔记 目录 第11章 这章主要讲了关于Linux和UNIX的系统资源的限制. 关于限制都存在一个最小值,这些最小值为<limits.h> ...
- 练习--LINUX进程间通信之信号SIGNAL
同样的,信号也不要太迷信可靠信号及不及靠信号,实时或非实时信号. 但必须要了解这些信号之间的差异,函数升级及参数,才能熟练运用. ~~~~~~~~~~~~~~~~ 信号本质 信号是在软件层次上对中断机 ...
- 编写Linux/Unix守护进程
原文: http://www.cnblogs.com/haimingwey/archive/2012/04/25/2470190.html 守护进程在Linux/Unix系统中有着广泛的应用.有时,开 ...
- 转:linux/unix命令行终端的光标及字符控制快捷键
from:http://linux.chinaunix.net/techdoc/system/2007/11/23/973027.shtml 在使用linux/unix的命令行终端时,有时候会碰到键盘 ...
- 《Linux/Unix系统编程手册》 时间子系统
Linux下操作系统编程有两本经典APUE即<Advanced Programming in the UNIX Environment>和TLPI<The Linux Program ...
随机推荐
- RabbitMQ 五种工作模式
官网介绍:https://www.rabbitmq.com/getstarted.html 五种工作模式的主要特点 简单模式:一个生产者,一个消费者 work模式:一个生产者,多个消费者,每个消费者获 ...
- IO流读取和写入文件
package com.xmlmysql.demo.config; import java.io.BufferedReader; import java.io.BufferedWriter; impo ...
- apache配置域名子目录,访问不同子项目
<VirtualHost *:443> DocumentRoot "E:/phpstudy/WWW/nextplus" ServerName local-main.co ...
- System.Web.Mvc.ContentResult.cs
ylbtech-System.Web.Mvc.ContentResult.cs 1.程序集 System.Web.Mvc, Version=5.2.3.0, Culture=neutral, Publ ...
- Genymotion 的那些事
一.什么是Genymotion? Genymotion是一套完整的工具,它提供了Android虚拟环境. 支持Windows.Linux和Mac OS等操作系统. 在Android开发中可以代替安卓模 ...
- idea配置tomcat中war和war exploded的区别
是选择war还是war exploded 这里首先看一下他们两个的区别: war模式:将WEB工程以包的形式上传到服务器 :war exploded模式:将WEB工程以当前文件夹的位置关系上传到服务器 ...
- Python ----键抠图
背景 这段时间,经常有人来找我,说我是学计算机的,能不能帮他p一下证件照,我只想说,MMP的,我是学计算机的不错,可我不会ps阿. 我想了一会,python 这么火,能不能来个自动抠图,说好就干吧 介 ...
- MySQL-Utilities:mysqldbcompare及跳过复制错误
mysqldbcompare也是MySQL-Utilities工具集的一个脚本.mysqldbcompare从两个数据库比较对象和数据的不同.数据库中的对象包括:表.视图.触发器.存储过程.函数和事件 ...
- Redis学习笔记01-分布式锁
1.分布式锁的定义与理解 在并发任务中,当对数据执行修改和删除时为了防止多个任务同时拿到数据而产生的混乱,这时就要用到分布式锁来限制程序的并发执行. Redis分布式锁本质上要实现的目标就是在Redi ...
- fastjson对Date类型的格式化
@JSONField(format="yyyy-MM-dd HH:mm:ss.SSS") private Date sendMqDate; //MQ发送时间