linux信号捕捉和sigaction函数和sigqueue函数

关于学习linux系统编程的一定想法

学习linux系统编程其实就是学习一对API函数,掌握一些进程线程网络的概念,为linux服务器编程和linux网络编程打基础。
本人也是linux小白,所以想记录一下学习过程中的想法:

  • 切忌好高骛远,一定要脚踏实地,一便便地敲代码,熟悉各种系统函数
  • 务必将每个知识点吃透,这也就是我为什么写博客的原因:在写博客途中,其实也就加深了对该知识点的理解
  • 推荐一位博主的linux链接,居然暨大的!!!
    好东西多分享

下面是关于sigaction函数和sigqueue函数的代码讲解

  • 相关知识点在代码下面的注释中

  • 若我写的难以理解,可以参考那位博主的链接:已附在本博文最下面

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \   
    }while(0)

void myHandler_forsigaction(int signum, siginfo_t *s_t, void *p)
{
    int myint = 0;
    printf("recv signum: %d \n", signum);
    myint = s_t->si_value.sival_int;
    printf("%d   %d\n", myint, s_t->si_int);
}

int main()
{
    pid_t pid;
    int ret = 0;

    struct sigaction act;
    act.sa_sigaction = myHandler_forsigaction;
    sigemptyset(&act.sa_mask);

    act.sa_flags = SA_SIGINFO;

    if (sigaction(SIGINT, &act, NULL) < 0)
    {
        ERR_EXIT("sigaction error");
    }

    pid = fork();

    if (pid == -1)
    {
        printf("fork err.....\n");
        return 0;
    }

    if (pid == 0)
    {
        int i = 0;
        union sigval mysigval;
        mysigval.sival_int = 222;

        for (i = 0; i < 10; i++)
        {
            ret = sigqueue(getppid(), SIGINT, mysigval);
            if (ret != 0)
            {
                printf("sigqueue err....\n");
                exit(0);
            }
            else
            {
                printf("sigqueue success....\n");
                sleep(2);
            }
        }
     } 
     else if (pid > 0)
     {
        for(;;)
            pause();
     }

     return 0;

 } 

 /**************************************************************************************** 
 1、sigaction函数
#include 
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回-1。

signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。
若oact指针非空,则通过oact传出该信号原来的处理动作。
act和oact指向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);
           };
void (*sa_handler)(int);
    回调函数(带一个int参数),使用方法:
                                    act.sa_handler = myHandler;  //myHandler是自定义信号处理函数
                                    igaction(SIGINT, &act, NULL);//相当于signal(SIGINT, myHandler); 其实signal函数内部就是由igaction实现的

void     (*sa_sigaction)(int, siginfo_t *, void *);
    回调函数,除了可以定义信号处理函数,还可以接受信号的额外信息,与sigqueue搭配着用
    注意:当用户需要用第二个参数接受信号额外信息时,需设置sa_flags: act.sa_flags = SA_SIGINFO;                                  
 参数说明:第一个参数与sa_handler一样表示当前信号的编号,
            第二个参数是一个siginfo_t 结构体,
             第三个参数一般不用。
              当使用sa_handler时sa_flags设置为0即可 
注意:sa_handler 与  sa_sigaction不要同时使用
使用方法:
     act.sa_sigaction = myHandler_forsigaction;  //myHandler是自定义信号处理函数
                                                //void myHandler_forsigaction(int signum, siginfo_t *s_t, void *p)
                                                // s_t为 siginfo_t结构体,里面包含信号的额外信息(要想信号包含额外信息,需要用sigqueue函数发送信号)

 siginfo_t 结构体
 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 
               long     si_band;      Band event (was int in
                                        glibc 2.3.2 and earlier) 
               int      si_fd;        File descriptor 
               short    si_addr_lsb;  Least significant bit of address
                                        (since kernel 2.6.32) 
            } 

sigset_t   sa_mask;
    当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,
    当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,
    如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,
    除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,
    则用sa_mask字段说明这些需要额外屏蔽的信号,
    当信号处理函数返回时自动恢复原来的信号屏蔽字。

 int    sa_flags;
    sa_flags有几个选项,比较重要的有两个:SA_NODEFER 和 SA_SIGINFO
     当SA_NODEFER设置时在信号处理函数执行期间不会屏蔽当前信号;
     当SA_SIGINFO设置时与sa_sigaction 搭配出现,说明该进程要接受信号的附带信息 
     当使用sa_handler时sa_flags设置为0即可 

void     (*sa_restorer)(void);
    已经被废弃不用 

2、 
sigqueue函数
int sigqueue(pid_t pid, int sig, const union sigval value);

新的发送信号系统调用(与kill类似),主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。

参数说明:
        第一个参数是指定接收信号的进程id,
            第二个参数确定即将发送的信号,
                第三个参数是一个联合数据结构union sigval,该结构体包含了改信号的额外信息

返回值:成功返回0,失败返回-1  

union sigval结构体:
        typedef union sigval
         { 
        int sival_int; 
        void *sival_ptr; 
        }sigval_t; 
int sival_int; 
    sival_int的值将被接受信号的进程 sigaction结构体中的 sa_sigaction回调函数的
    第二个参数:siginfo_t 结构体中的si_value.sival_int 成员和 si_int成员接受 

 ****************************************************************************************/ 

实时信号和不可靠信号的区别


#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)


void  myhandle(int sig)
{
    if (sig == SIGINT || sig == SIGRTMIN)
        printf("recv a sig=%d\n", sig);
    else if (sig == SIGUSR1)
    {
        sigset_t s;
        sigemptyset(&s);
        sigaddset(&s, SIGINT);
        sigaddset(&s, SIGRTMIN);
        sigprocmask(SIG_UNBLOCK, &s, NULL);
    }
}

void main()
{
    pid_t   pid;

    struct sigaction act;
    act.sa_handler = myhandle;
    act.sa_flags = 0;

    //注册信号处理函数 
    if ( sigaction(SIGINT, &act, NULL) <0 )
    {
        ERR_EXIT("sigaction SIGINT");
    }

    if ( sigaction(SIGRTMIN, &act, NULL) <0 )
    {
        ERR_EXIT("sigaction SIGINT");
    }

    if ( sigaction(SIGUSR1, &act, NULL) <0 )
    {
        ERR_EXIT("sigaction SIGINT");
    }

    sigset_t bset;
    sigemptyset(&bset);
    sigaddset(&bset, SIGINT);
    sigaddset(&bset, SIGRTMIN);

    sigprocmask(SIG_BLOCK, &bset, NULL);

    pid = fork();
    if (pid == -1)
    {
        ERR_EXIT("fork err");
    }

    //子进程向父进程发送信号 
    if (pid == 0)
    {
        int i = 0;
        int ret = 0;
         union sigval v;
         v.sival_int = 201;

        //发送非实时信号 SIGINT
        for (i=0; i<3; i++)
        {
            ret = sigqueue(getppid(), SIGINT, v);
            if (ret == -1)
            {
                printf("sigqueue SIGINT err,  ret: %d, errno:%d \n", ret, errno);
                exit(0);
            }   
            else
            {
                printf("sigqueue SIGINT success\n");
            }
        }

        //发送实时信号 SIGRTMIN
        v.sival_int = 301;
        for (i=0; i<3; i++)
        {
            ret = sigqueue(getppid(), SIGRTMIN, v);
            if (ret == -1)
            {
                printf("sigqueue  SIGRTMIN err, ret: %d, errno:%d \n", ret, errno);
                exit(0);
            }   
            printf("sigqueue SIGRTMIN success\n");
        }

        //向父进程发送 SIGUSR1信号,接触阻塞 
        kill(getppid(), SIGUSR1); 
    }

    while(1)
    {
        sleep(1);
    }

    printf("main....\n");
}

/*******************************************************************************************
输出结果:
 sigqueue SIGINT success
 sigqueue SIGINT success
 sigqueue SIGINT success
 sigqueue SIGRTMIN success
 sigqueue SIGRTMIN success
 sigqueue SIGRTMIN success
 recv a sig=2 
 recv a sig=34
 recv a sig=34
 recv a sig=34


 分析:
1、实时信号(可靠信号)和非实时信号(不可靠信号)说明区别:
        实时信号支持排队不会丢失。(实时信号还有一个特点,即到达的顺序是可以保证的) 

2、在主函数中将SIGINT和SIGRTMIN信号加入信号屏蔽字,只有当接收到SIGUSR1信号时才对前面两个信号unblock。

3、需要注意的是:
        如果在信号处理函数中对某个信号进行解除阻塞时,则只是将pending位清0,
            让此信号递达一次(同个实时信号产生多次进行排队都会抵达),
                但不会将block位清0,即再次产生此信号时还是会被阻塞,处于未决状态。

4、
在子进程中连续向父进程各发送了SIGINT和SIGRTMIN信号3次,接着睡使用kill函数发送SIGUSR1信号给父进程 
由输出结果可知:实时信号支持排队,3个信号都接收到了,而不可靠信号不支持排队,只保留一个信号。 
*******************************************************************************************/ 
  • 注意:信号队列中的信号个数是有限的(一般是8k),若超过这个数目,会将新的信号丢弃,而不是将旧的信号覆盖。

很赞的链接

linux信号捕捉和sigaction函数和sigqueue函数的详细讲解

  • 对sigaction函数分析的很不错,虽未得到博主同意,但是好东西还是想分分享的

你可能感兴趣的:(linux)