一,信号安装

如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。

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讲解的更多相关文章

  1. linux系统编程之管道(三)

    今天继续研究管道的内容,这次主要是研究一下命名管道,以及与之前学过的匿名管道的区别,话不多说,进入正题: 所以说,我们要知道命名管道的作用,可以进行毫无关系的两个进程间进行通讯,这是匿名管道所无法实现 ...

  2. Linux系统编程(24)——信号的生命周期

    信号生命周期为从信号发送到信号处理函数的执行完毕. 对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号诞生:信号在进程中 ...

  3. Linux系统编程(20)——信号基本概念

    信号及信号来源 信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的.信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知 ...

  4. Linux系统编程(23)——信号的阻塞

    实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending).进程可以选择阻塞(Block)某个信号.被阻塞的信号产生时将保持在未决状态,直到进程 ...

  5. Linux系统编程(21)——信号的产生

    1.通过终端按键产生信号 通过上一篇我们知道了SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,现在我们来验证一下. 首先解释什么是Core Dump ...

  6. linux系统编程之进程(三)

    今天继续学习进程相关的东东,继上节最后简单介绍了用exec函数替换进程映像的用法,今天将来深入学习exec及它关联的函数,话不多说,正式进入正题: exec替换进程映象:   对于fork()函数,它 ...

  7. linux系统编程之信号(七)

    今天继续学习信号,主要是学习关于时间和定时器相关的函数的使用,关于这个实际上有很多内容,这里先简要进行说明,等之后再慢慢进行相关深入,也主要是为接下来要做的一个综合linux系统编程的例子做准备,好了 ...

  8. Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号

    Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...

  9. linux系统编程(一)概述

    glibc库封装了linux系统调用,并提供c语言接口 所以学习linux系统编程,主要参考glibc库系统调用相关api 一.进程控制: fork 创建一个新进程 clone 按指定条件创建子进程 ...

  10. Linux 系统编程 学习:11-线程:线程同步

    Linux 系统编程 学习:11-线程:线程同步 背景 上一讲 我们介绍了线程的属性 有关设置.这一讲我们来看线程之间是如何同步的. 额外安装有关的man手册: sudo apt-get instal ...

随机推荐

  1. RN项目中关于父子组件的通信

    子组件向父组件传递数据 子控件中在相应的函数中.通过props.coallback的回调通知父组件. 父组件调用callback属性时行 绑定,并在方法中去解析使用获取到的值 . //子控件: < ...

  2. 第五章 二叉树(d)二叉树实现

  3. 文件的概念以及VC里的一些文件操作API简介

    文件的基本概念 所谓“文件”是指一组相关数据的有序集合. 这个数据集有一个名称,叫做文件名. 实际上在前面的各章中我们已经多次使用了文件,例如源程序文件.目标文件.可执行文件.库文件 (头文件)等.文 ...

  4. focal

    focal 美 ['foʊk(ə)l]   英 ['fəʊk(ə)l]   adj.中心的:很重要的:焦点的:有焦点的 网络劲浪:在焦点上的:局部

  5. Go开发之VScode安装

    1.找到官网 https://code.visualstudio.com/ 2根据自己机器环境下载 3.下载vscode地址,macos版本 https://vscode.cdn.azure.cn/s ...

  6. 子类覆写的变量被private隐藏,强制转换方式通过子类访问父类的被覆写变量:

    import static java.lang.System.*; public class SuperParent{ public static void main(String[] args){ ...

  7. 02 请求库之 selenium模块

      selenium模块   一 介绍 selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题 selenium本质是通过驱动 ...

  8. 白盒静态自动化测试工具:FindBugs使用指南

    目 录     1     FINDBUGS介绍     2     在ECLIPSE中安装FINDBUGS插件     3     在ECLIPSE中使用FINDBUGS操作步骤     3.1   ...

  9. 彻底测试全部拷贝list相关操作的区别python

    1.用浅拷贝后修改数字,可以起到与原数据分离的效果 import copy origin = [, , [, ]] #origin 里边有三个元素:, ,[, ] cop1=origin.copy() ...

  10. 2018.10.15 bzoj3564: [SHOI2014]信号增幅仪(坐标处理+最小圆覆盖)

    传送门 省选考最小圆覆盖? 亦可赛艇(你们什么都没看见) 在大佬的引领下成功做了出来. 就是旋转坐标使椭圆的横轴跟xxx轴平行. 然后压缩横坐标使得其变成一个圆. 然后跑最小覆盖圆就可以了. 注意题目 ...