Linux系统编程之进程间通信之浅谈信号

我们接着谈Linux学习过程中一个重要的话题--信号。



一、信号的概念:
       信号是一种软件中断,它提供了一种处理异步事件的方法,也是进程间唯一的异步通信方式。
二、信号的来源:
       1、硬件方式:
             当用户按下终端上某些键时,将产生信号。
             硬件异常产生信号:除0操作、访问非法空间……
       2、软件方式
             用户在终端下调用kill命令向进程发送任意信号
        进程调用kill或者sigqueue函数发送信号。
        当检测到某种软件条件发生时,如alarm或者setimer
 三、信号处理
        1、信号的捕捉和处理
              1、signal函数
                    a、函数作用:
                        signal函数用来设置进程在接收到信号时的动作。
                    b、函数原型:

              #include <signal.h>
         typedef void (*sighandler_t)(int);
         sighandler_t signal(int signum, sighandler_t handler);

c、参数解析与函数说明:
                              signal函数会根据参数signal指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会

跳到参数hander指定的函数执行。如果hander不是函数指针,则必须时常数SIG_IGN忽略该信号)或者S

IG_DFL(对该信号执行默认操作)。hander是 一个函数指针,它所指向的函数的类型时sighandel_t,即

它 所指向的函数有一个int型参数,且返回值的类型为void。
                    d、样这里给出signal处理信号的例子:


/**********************************************************
*    > File Name: signal-1.c
*    > Author:xiao_k
*    > Mail:[email protected] 
*    > Created Time: Fri 23 Feb 2018 12:05:46 AM CST
**********************************************************/
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<signal.h>
//信号处理函数
void do_sig(int num)
{
   printf("I am do_sig\n");
}
int main()
{
      //安装处理信号
     signal(SIGINT,do_sig);
     while(1)
      {
        printf("********\n");
        sleep(1);
      }
       return 0;
 }

      2、sigaction函数
                 a、函数作用:
                       sigction函数可以用来检查和设置进程在接收信号时的动作。
                 b、函数原型
                       #include <signal.h>
                       int sigaction(int signum, const struct sigaction *act,
                                                                 struct sigaction *oldact);
                 c、参数解析与函数说明:
                      sigzction函数会根据参数signum指定的信号编号来设置该信号的处理函数。参数signum可以是除了

SIGKILLh和SIGSTOP以外的任何信号。
                如果参数act 不是空指针,则为signum设置新的信号处理函数;
                如果oldzct不是空指针,则旧的信号处理函数将被存储在oldact中,struct sigaction的定义如下:     
                                    struct sigaction {
                                           void     (*sa_handler)(int);
                                           void     (*sa_sigaction)(int, siginfo_t *, void *);
                                           sigset_t   sa_mask;
                                           int        sa_flags;
                                           void     (*sa_restorer)(void);
                                   };
               sa_handler可以是常数SIG_DFL或者SIG_IGN,或者是一个信号处理函数名。
               sa_sigaction也是用来指定信号的signum的处理函数。
               sa_mask成员声明了一个信号集,在调用信号捕捉函数之前,该信号会增加到进程的信号屏蔽码中,

新的信号屏蔽码会自动包括正在处理的信号(sa_flags未指定SA_NODEFER或者SA_NOMASK)。当从信号捕

捉函数返回时,进程的信号屏蔽码会恢复为原来的值。因此,当处理一个给定的  信号时,如果这种

信号再次发生,那么它会阻塞直到本次信号处理结束为止。若这种信号发生了多次,则对于不可靠信号,

即本次信号处理结束以后只会再次处理一次(相当于丢失了信号),对于可靠信号(实时信号),则会

被阻塞多次,即信号不会丢失,信号发生了多少次就会调用信号处理函数多少次。
               sa_flags成员用来说明信号处理的其他相关操作。         
               当使用三参数的sa_sigaction来指定信号处理函数时,它的第 二个参数可以用来传递数据,其定义

如下:
                      siginfo_t {
                          int      si_signo;    /* Signal number */
                          int      si_errno;    /* An errno value */
                          int      si_code;     /* Signal code */
                          int      si_trapno;   /* Trap number that caused
                                         hardware-generated signal (unused on most architectures) */
                          pid_t    si_pid;      /* Sending process ID */
                          uid_t    si_uid;      /* Real user ID of sending process */
                          int      si_status;   /* Exit value or signal */
                          clock_t  si_utime;    /* User time consumed */
                          clock_t  si_stime;    /* System time consumed */
                          sigval_t si_value;    /* Signal value */
                          int      si_int;      /* POSIX.1b signal */
                          void    *si_ptr;      /* POSIX.1b signal */
                          int      si_overrun;  /* Timer overrun count;POSIX.1b timers */
                          int      si_timerid;  /* Timer ID; POSIX.1b timers */
                          void    *si_addr;     /* Memory location which caused fault */
                          int      si_band;     /* Band event */
                          int      si_fd;       /* File descriptor */
                                               }
                d、同样给出 sigaction的测试用例

/**********************************************************
*    > File Name: sigaction-1.c
*    > Author:xiao_k
*    > Mail:[email protected] 
*    > Created Time: Fri 23 Feb 2018 01:47:43 AM CST
**********************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
int temp = 0;
//信号处理函数
void do_sigact(int num)
{
    printf("recv SIGINT\n");
    sleep(5);
    temp +=1;
    printf("the value of temp is :%d\n",temp);
    printf("in do_sigact,after sleep\n");
}
int main()
  {
     struct sigaction act;
      //结构体赋值
      act.sa_handler = do_sigact;
      act.sa_flags = SA_NOMASK;
      //安装信号处理函数
      sigaction(SIGINT,&act,NULL);
     while(1)
      {
        printf("********************\n");
        sleep(1);
      }
     return 0;
   }

编写信号处理函数时,要注意不要使用不可重入的函数,使用了不重入的函数将将产生不可预料的结果。

上边就是sigaction函数的基本用法,复杂的用法后边使用。
                补充:满足下列条件的函数是不可重入的:
                           a、使用了静态的数据结构,如getgrpid(),全局变量等。  
                           b、函数实现时,调用了malloc()或者free()函数。
                           c、函数实现时,使用了标准I/O函数。



    3、pause()函数
                 a、函数作用:
                      pause函数使调用进程挂起直到捕捉到一个信号。pause函数会令目前的进程暂停(进入睡眠状态),

直到被信号(signal)所中断,该函数只返回-1,并将errno设置为EINTR。
                 b、函数原型:
                       #include <unistd.h>
                       int pause(void);
四、信号的发送:
         信号的发送主要由函数kill、raise、sigqueue、alarm、setitimer以及 abort来完成。
         1、kill函数
               a、函数作用:
                   kill函数用来发送信号给指定的进程。
               b、函数原型

       #include <sys/types.h>
       #include <signal.h>
       int kill(pid_t pid, int sig);

c、参数解析与函数说明:
                     该函数的行为与第二个参数pid的取值有关,第二个参数sig表示信号编号:
                如果pid时正数,则发送信号sig给进程号为pid的进程;
                果pid为0,则发送信号sig给当前进程所属进程组里的所有进程;
                如果pid为-1,则把信号sig广播给系统内的除1号进程和自身以外的所有进程;
                如果pid为比-1还小的负数,则发送信号sig给属于进程组-pid的所有进程;
                如果参数sig是0,则kill(),仍然执行正常的错误检查,但不发送信号。可以利用这一点来确定某

进程是否有权向另外一个进程发送信号。如果向一个并不存在的进程发送空信号,则kill()返回-1,

errno则被设置为ESRCH。
            注意:只有具有root权限的进程才能向其他任意一个进程发送信号,非root权限的进程只能向属于同一

个组成或者同一个用户创建的进程发送信号。
        2、raise函数
              a、函数作用:
                   raise函数是ANSI C 而非POSIX标准定义的。用来给调用它的进程发送信号。
              b、函数原型

       #include <signal.h>
        int raise(int sig);

         3、sigqueue函数
                     a、函数作用
                          sigqueue函数是一个比较新的发送信号的函数,它支持信号带有参数,从而可以与函数sigaction配

合使用。
                     b、函数原型
                          #include <signal.h>
                          int sigqueue(pid_t pid, int sig, const union sigval value);
                     c、参数解析与函数说明:
                            sigqueue用来发送给信号sig给进程pid。与kill系统调用不同的是,sigqueue在发送信号的同时

还支持信号携带参数;另外一个不同点是sigqueue 不能给一组进程发送信号,参数value是一个共用
             体,其定义如下:
                            union sigval {
                               int   sival_int;
                               void *sival_ptr;
                                      };
     4、alarm函数
           a、函数作用
              alarm函数可以用来设置定时器,定时器超时将产生SIGALRM信号给调用进程。
           b、函数原型

         #include <unistd.h>
      unsigned int alarm(unsigned int seconds);

c、参数解析与函数说明:
                参数seconds表示设定的秒数,经过seconds后,内核将给调用该函数的进程发送SIGALRM信号。如果seconds为0,

则不再发送SIGALRM信号,最新一次调用alarm函数将取消之前一次的设定。  
     5、abrot函数
            a、函数作用:
                abrot函数用来向进程发送SIGABRT信号。
            b、函数原型

      #include <stdlib.h>
     void abort(void)


上边我只对信号的基本用法做了总结,后边将对信号的高级用法做单独总结。

原文地址:http://blog.51cto.com/967243153/2072443

时间: 2024-11-09 10:30:59

Linux系统编程之进程间通信之浅谈信号的相关文章

Linux系统编程之进程间通信

今天我们接着谈Linux系统编程中的进程间的通信,上一节我们讨论了进程的基本操作.这一节我们来讨论一下进程间的通信.        常见的进程间的通信方式有:无名管道.命名管道.信号.共享内存.消息队列.信号量.套接字. 接下来我们先来谈:                一.无名管道:                      1.管道是UNIX系统的IPC的最古老方式,并且多数unix系统都提供此种通信方式..                      2.管道是一种半双工的通信方式,数据只能

linux系统编程:进程间通信-mmap

进程间通信-mmap #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length); mmap本质是把内存与硬盘上的文件同步.某块内存中的内容会同步到硬盘文件上,即把文件映射到内存.故通过对同一文件的读写达到进程间的通信. 参数解释: addr:指定对哪儿块内存实行映射.NU

linux系统编程:进程间通信-fifo

进程间通信-fifo 进程间通信的还有一种方式是fifo. fifo是还有一种管道:有名管道.从名字能够看出.它也是队列. 使用fifo通信前,得先创建fifo $ mkfifo myfifo 随后仅仅需对myfifo像文件一样使用即可. fifo_w.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/sta

Linux系统编程@进程通信(一)

进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统的一个分支) POSIX进程间通信(POSIX:可移植操作系统接口,为了提高UNIX环境下应用程序的可移植性.很多其他系统也支持POSIX标准(如:DEC OpenVMS和Windows).) 现在Linux使用的进程间通信方式包括: 管道(pipe).有名管道(FIFO) 信号(signal) 消

Linux系统编程札记:进程通信(一) &nbsp; &nbsp;

进程简单来讲就是一个程序的一次执行,这里说的进程一般都指的是运行在用户态的进程,而处于用户态的不同进程之间是彼此相互隔离的,它们必须通过某种方式来进行通信,具体理由如下: (1)数据传输:有时候一个进程需要将它的数据发送给另一个进程. (2)资源共享:有时候多个进程之间需要共享同样的资源. (3)通知事件:有时候一个进程需要向另一个或一组进程发送消息,通知它们发生了某个事件. (4)进程控制:有些进程希望能够完全控制另一个进程的执行,此时控制进程希望能够拦截另一进程的所有操作,并能够及时知道它的

LINUX系统编程 由REDIS的持久化机制联想到的子进程退出的相关问题

19:22:01 2014-08-27 引言: 以前对wait waitpid 以及exit这几个函数只是大致上了解,但是看REDIS的AOF和RDB 2种持久化时 均要处理子进程运行完成退出和父进程需要做的什么事情,所以特定看了UNIX环境编程和LINUX系统编程这2本书 重新梳理下整个要点. 内容: 一般而言: 如果程序类似于下面的情况: if((pid=fork())==0) { dochildtthing(); exit(0); } else if(pid>0) { dofathertt

Linux系统编程-setitimer函数

功能:linux系统编程中,setitimer是一个经常被使用的函数,可用来实现延时和定时的功能. 头文件:sys/time.h 函数原型: int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); 参数含义: 1.which参数用来设置定时器类型,可选的值为 (1)ITIMER_REAL : 设置定时器以系统真实所花费的时间来计时,运行指定时间后发送SIGALRM信号. (

Linux系统编程笔记

写在开篇:出于对未来职业规划的考虑(其实还是一团糟),制定了一个基本的学习方向,那就是从系统编程学习API慢慢的深入内核,这是一个比较成熟的学习路线.所以从本篇开始,在这段时间会陆续记录Linux系统编程的学习笔记,除了供学习之余复习只用,同时也期望能记录初入职场摸爬滚打的第一个3年. 第一章 文件I/O 文件访问的基本调用一般是 read()和write(),但是在访问文件之前,要做的是一项很重要的工作就是:打开,没错!通过调用 open()或create()实现 #include <sys/

linux系统编程之管道(一):匿名管道(pipe)

原文地址:http://www.cnblogs.com/mickole/p/3192210.html 一,什么是管道 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数据只能向一个方向流动:需要双方通信时,需要建立起两个管道: 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程): 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中. 数据的读