linux系统编程之信号(三):信号安装、signal、kill,arise讲解
一,信号安装
如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。
linux主要有两个函数实现信号的安装:signal()、sigaction()。其中signal()只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;而sigaction()是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue() 系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。
二,signal()用法
#include <signal.h>
typedef void (*__sighandler_t) (int);
#define SIG_ERR ((__sighandler_t) -1)
#define SIG_DFL ((__sighandler_t) 0)
#define SIG_IGN ((__sighandler_t) 1)
void (*signal(int signum, void (*handler))(int)))(int);
如果该函数原型不容易理解的话,可以参考下面的分解方式来理解:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。
如果signal()调用成功,返回最后一次也就是上一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。
传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。
man帮助说明:
DESCRIPTION
The behavior of signal() varies across Unix versions, and has also var-
ied historically across different versions of Linux. Avoid its use:
use sigaction(2) instead. See Portability below.
signal() sets the disposition of the signal signum to handler, which is
either SIG_IGN, SIG_DFL, or the address of a programmer-defined func-
tion (a "signal handler").
If the signal signum is delivered to the process, then one of the fol-
lowing happens:
* If the disposition is set to SIG_IGN, then the signal is ignored.
* If the disposition is set to SIG_DFL, then the default action asso-
ciated with the signal (see signal(7)) occurs.
* If the disposition is set to a function, then first either the dis-
position is reset to SIG_DFL, or the signal is blocked (see Porta-
bility below), and then handler is called with argument signum. If
invocation of the handler caused the signal to be blocked, then the
signal is unblocked upon return from the handler.
The signals SIGKILL and SIGSTOP cannot be caught or ignored.
RETURN VALUE
signal() returns the previous value of the signal handler, or SIG_ERR
on error.
示例程序:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h> void sig_handler(int signo);
int main(void)
{
printf("mian is waiting for a signal\n");
if(signal(SIGINT,sig_handler) == SIG_ERR){
perror("signal errror");
exit(EXIT_FAILURE);
}
for(; ;);//有时间让我们发送信号 return 0;
} void sig_handler(int signo)
{
printf("catch the signal SIGINT %d\n",signo);
}
结果:
可知我们捕获了SIGINT信号,每当我们按下ctrl+c或利用kill发送SIGINT信号时,执行我们安装的信号处理函数,当我们按下:ctrl+\或kill –SIGQUIT pid发送SIGQUIT信号时,程序退出,那是因为进程对SIGQUIT信号的默认处理动作是退出程序。
现在我们来获得进程的最后一次为安装信号时所指定的处理函数:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h> void sig_handler(int signo);
int main(void)
{
printf("main is waiting for a signal\n");
__sighandler_t prehandler;
prehandler = signal(SIGINT,sig_handler);
if(prehandler == SIG_ERR){
perror("signal errror");
exit(EXIT_FAILURE);
} printf("the previous value of the signal handler is %d\n",(int)prehandler);
//for(; ;);//有时间让我们发送信号 return 0;
} void sig_handler(int signo)
{
printf("catch the signal SIGINT %d\n",signo);
}
结果:
为0,由前面的宏定义:#define SIG_DFL ((__sighandler_t) 0),可知处理动作为SIG_DFL,而SIGINT默认的处理动作就是终止进程
示例:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h> void sig_handler(int signo);
int main(void)
{
printf("main is waiting for a signal\n");
__sighandler_t prehandler;
prehandler = signal(SIGINT,SIG_DFL);
if(prehandler == SIG_ERR){
perror("signal errror");
exit(EXIT_FAILURE);
} for(; ;);//有时间让我们发送信号 return 0;
}
结果:
当按下ctrl+c时发送SIGINT信号给进程,然后进程终止
三,kill()发送信号
发送信号的主要函数有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。
这里我们先将kill函数使用:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int signo)
该系统调用可以用来向任何进程或进程组发送任何信号。参数pid的值为信号的接收进程
- pid>0 进程ID为pid的进程
- pid=0 同一个进程组的进程
- pid<0 pid!=-1 进程组ID为 -pid的所有进程
- pid=-1 除发送给每一个调用进程有权限发送的进程除自身及1(init)进程外
Sinno是信号值,当为0时(即空信号),实际不发送任何信号,但照常进行错误检查,因此,可用于检查目标进程是否存在,以及当前进程是否具有向目标发送信号的权限(root权限的进程可以向任何进程发送信号,非root权限的进程只能向属于同一个session或者同一个用户的进程发送信号)。
Kill()最常用于pid>0时的信号发送。该调用执行成功时,返回值为0;错误时,返回-1,并设置相应的错误代码errno。下面是一些可能返回的错误代码:
EINVAL:指定的信号sig无效。
ESRCH:参数pid指定的进程或进程组不存在。注意,在进程表项中存在的进程,可能是一个还没有被wait收回,但已经终止执行的僵死进程。
EPERM: 进程没有权力将这个信号发送到指定接收信号的进程。因为,一个进程被允许将信号发送到进程pid时,必须拥有root权力,或者是发出调用的进程的UID 或EUID与指定接收的进程的UID或保存用户ID(savedset-user-ID)相同。如果参数pid小于-1,即该信号发送给一个组,则该错误表示组中有成员进程不能接收该信号。
man帮助说明:
DESCRIPTION
The kill() system call can be used to send any signal to any process
group or process.
If pid is positive, then signal sig is sent to the process with the ID
specified by pid.
If pid equals 0, then sig is sent to every process in the process group
of the calling process.
If pid equals -1, then sig is sent to every process for which the call-
ing process has permission to send signals, except for process 1
(init), but see below.
If pid is less than -1, then sig is sent to every process in the pro-
cess group whose ID is -pid.
If sig is 0, then no signal is sent, but error checking is still per-
formed; this can be used to check for the existence of a process ID or
process group ID.
For a process to have permission to send a signal it must either be
privileged (under Linux: have the CAP_KILL capability), or the real or
effective user ID of the sending process must equal the real or saved
set-user-ID of the target process. In the case of SIGCONT it suffices
when the sending and receiving processes belong to the same session.
RETURN VALUE
On success (at least one signal was sent), zero is returned. On error,
-1 is returned, and errno is set appropriately.
ERRORS
EINVAL An invalid signal was specified.
EPERM The process does not have permission to send the signal to any
of the target processes.
ESRCH The pid or process group does not exist. Note that an existing
process might be a zombie, a process which already committed
termination, but has not yet been wait(2)ed for.
示例程序:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) void handler(int sig);
int main(int argc, char *argv[])
{
if (signal(SIGUSR1, handler) == SIG_ERR)
ERR_EXIT("signal error");
pid_t pid = fork();
if (pid == -1)
ERR_EXIT("fork error"); if (pid == 0)
{
sleep(1);
kill(getppid(), SIGUSR1);
exit(EXIT_SUCCESS);
} int n = 5;
do
{
printf("the number of seconds left to sleep is %d s\n",n);
n = sleep(n);
} while (n > 0);
return 0;
} void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}
结果:
以上程序里有子进程给父进程发送SIGUSR1信号,父进程收到信号后,睡眠被中断,然后去执行信号处理函数,返回后继续睡眠剩余的时间后退出程序。
现在利用kill给与给定pid同组所有进程发送信号:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) void handler(int sig);
int main(int argc, char *argv[])
{
if (signal(SIGUSR1, handler) == SIG_ERR)
ERR_EXIT("signal error");
pid_t pid = fork();
if (pid == -1)
ERR_EXIT("fork error"); if (pid == 0)
{
pid = getpgrp();
kill(-pid, SIGUSR1);
//kilpg(getpgrp(), SIGUSR1);
exit(EXIT_SUCCESS);
} int n = 5;
do
{
n = sleep(n);
} while (n > 0);
return 0;
} void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}
结果:
可知收到进行了两次信号处理函数的执行:因为当前所属组中只有父子两个进程,从上可知有两种方式给组进程发送信号:kill和killpg
四,arise函数
#include <signal.h>
int raise(int signo)
向进程本身发送信号,参数为即将发送的信号值。调用成功返回 0;否则,返回 -1。
man帮助说明:
DESCRIPTION
The raise() function sends a signal to the calling process or thread.
In a single-threaded program it is equivalent to
kill(getpid(), sig);
In a multithreaded program it is equivalent to
pthread_kill(pthread_self(), sig);
If the signal causes a handler to be called, raise() will only return
after the signal handler has returned.
RETURN VALUE
raise() returns 0 on success, and non-zero for failure.
示例程序:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h> void sig_handler(int signo);
int main(void)
{
printf("mian is waiting for a signal\n");
if(signal(SIGINT,sig_handler) == SIG_ERR){
perror("signal errror");
exit(EXIT_FAILURE);
}
printf("useing raise to send a signal to himself\n");
raise(SIGINT);
sleep(1);
printf("useing kill to send a signal to himself\n");
kill(getpid(),SIGINT); return 0;
} void sig_handler(int signo)
{
printf("catch the signal SIGINT %d\n",signo);
}
结果:
可知两种方式都可以给自身发送信号。
linux系统编程之信号(三):信号安装、signal、kill,arise讲解的更多相关文章
- linux系统编程之管道(三)
今天继续研究管道的内容,这次主要是研究一下命名管道,以及与之前学过的匿名管道的区别,话不多说,进入正题: 所以说,我们要知道命名管道的作用,可以进行毫无关系的两个进程间进行通讯,这是匿名管道所无法实现 ...
- Linux系统编程(24)——信号的生命周期
信号生命周期为从信号发送到信号处理函数的执行完毕. 对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号诞生:信号在进程中 ...
- Linux系统编程(20)——信号基本概念
信号及信号来源 信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的.信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知 ...
- Linux系统编程(23)——信号的阻塞
实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending).进程可以选择阻塞(Block)某个信号.被阻塞的信号产生时将保持在未决状态,直到进程 ...
- Linux系统编程(21)——信号的产生
1.通过终端按键产生信号 通过上一篇我们知道了SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,现在我们来验证一下. 首先解释什么是Core Dump ...
- linux系统编程之进程(三)
今天继续学习进程相关的东东,继上节最后简单介绍了用exec函数替换进程映像的用法,今天将来深入学习exec及它关联的函数,话不多说,正式进入正题: exec替换进程映象: 对于fork()函数,它 ...
- linux系统编程之信号(七)
今天继续学习信号,主要是学习关于时间和定时器相关的函数的使用,关于这个实际上有很多内容,这里先简要进行说明,等之后再慢慢进行相关深入,也主要是为接下来要做的一个综合linux系统编程的例子做准备,好了 ...
- Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号
Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...
- linux系统编程(一)概述
glibc库封装了linux系统调用,并提供c语言接口 所以学习linux系统编程,主要参考glibc库系统调用相关api 一.进程控制: fork 创建一个新进程 clone 按指定条件创建子进程 ...
- Linux 系统编程 学习:11-线程:线程同步
Linux 系统编程 学习:11-线程:线程同步 背景 上一讲 我们介绍了线程的属性 有关设置.这一讲我们来看线程之间是如何同步的. 额外安装有关的man手册: sudo apt-get instal ...
随机推荐
- cloudrea manager 调整datanode数据存储目录
由于datanode所需磁盘空间较大,所以工作中可能会涉及到给datanode增加磁盘目录或者更改数据目录 CM停止该datanode节点 CM页面增加目录或者修改目录 如果是修改目录的话 需要将服务 ...
- Ajax的get方式传值 避免& 与= 号
js代码 例如: var name = $("#name”).value;//为a&b=7 name=encodeURLComponent(name); 可以将a&b=7转化 ...
- 240. Search a 2D Matrix II&&74. Search a 2D Matrix 都用不严格递增的方法
一句话思路:从左下角开始找.复杂度是o(m+n). 一刷报错: 应该是一个while循环中有几个条件判断语句,而不是每个条件判断语句里去加while,很麻烦 数组越界了.从0开始,y的最大条件应该是& ...
- Excel日期格式调整
3-Aug-2008 自定义格式: [$-809]d-mmm-yyyy;@ Aug-2008 自定义格式: [$-809]mmm-yyyy;@
- 将php数据下载csv文件
<?php $sales = array( array( 'Northeast', '2005-01-01', '2005-02-01', 12.54 ), array( 'Northwest' ...
- 仿微信客户端 帧布局中加入fragment
学习内容来自“慕课网” 这里用Fragment来实现APP主界面 思路: 底部横向排列4个LinearLayout,每个LinearLayout包含一个图片按钮和一个文字 1.默认显示第一个功能(微信 ...
- 【深度好文】多线程之WaitHandle-->派生-》Semaphore信号量构造
Semaphore 继承自WaitHandle. 信号量说简单点就是为了线程同步,或者说是为了限制线程能运行的数量. //创建一个限制资源类 //资源数为5,开放资源数为2 //主线程自动占有3个资源 ...
- Halcon的二维码解码步骤和解码技巧
一.二维码简介 1 . 类型多样,常见的有QR Code二维码. Data Matrix二维码等. 2.高密度编码,信息容量大. 3.容错能力强,具有纠错功能:二维码因穿孔.污损等引起局部损坏时,照样 ...
- JavaScript跨域总结与解决办法(转)
什么是跨域 1.document.domain+iframe的设置 2.动态创建script 3.利用iframe和location.hash 4.window.name实现的跨域数据传输 5.使用H ...
- 教你如何制作饼干icon教程
Hello,不露又和大家见面了,今天给大家带来的是一个可爱Q弹的icon~ 看起来像块饼干是吧~ 做起来非常简单哦,快打开PS一起躁起来吧. 先来看看效果图: 步骤1:打开PS,新建一个800*600 ...