linux實時信號與sigqueue函數編程實例

一、sigqueue函數

功能:新的發送信號系統調用,主要是針對實時信號提出的支持信號帶有參數,與函數sigaction()配合使用。

原型:int sigqueue(pid_t pid, int sig, const union sigval value);

第一個參數是指定接收信號的進程id;
第二個參數確定即將發送的信號;
第三個參數是一個聯合數據結構union sigval,指定了信號傳遞的參數,即通常所說的4字節值。  
typedef union sigval  {  int sival_int;  void *sival_ptr;  }sigval_t; 

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

sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號,而不能發送信號給一個進程組。

接收程序:

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.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, siginfo_t *, void *);  

int main(int argc, char *argv[])
{
    struct sigaction act;
    act.sa_sigaction = handler; 
    //sa_sigaction和sa_handler都是結構體sigaction的成員,只能取其一,當設置了sa_sigaction就 
    //不用設置sa_handler了,sa_sigaction多用於實時信號,可以保存信息
   
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO; // 設置標誌位後可以接收其他進程
    // 發送的數據,保存在siginfo_t結構體中

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

    for (; ;)
        pause();

    return 0;

}

void handler(int sig, siginfo_t *info, void *ctx)
{
    printf("recv a sig=%d data=%d data=%d\n",
           sig, info->si_value.sival_int, info->si_int);

}

-----------------------------------------------------
struct sigaction
{
    void (*sa_handler)(int);
    sigset_t sa_mask;       
    int sa_flags;           
   
    void (*sa_sigaction)(int, siginfo_t *, void *);
};
如果不設置sa_flags的話,初始化爲void (*sa_handler)(int);這個就和signal差不多了哦,void handler(int signo);

如果設置了sa_flags=SA_SIGINFO則,初始化爲void (*sa_sigaction)(int, siginfo_t *, void *);
其中siginfo、context這個結構體的定義參見APUE(Advanced Programming in the UNIX Environment),即《UNIX環境高級編程》,上述例子中用
void handler(int signo, siginfo_t* info, void* context);作爲信號處理函數

注意兩者不可以同時使用...

 

發送程序:

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>

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


int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage %s pid\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    pid_t pid = atoi(argv[1]); //字符串轉換爲整數
    union sigval val;
    val.sival_int = 100;
    sigqueue(pid, SIGINT, val); //只可以發信號給某個進程,而不能是進程組

    return 0;
}

終端打印結果:

recv a sig=2 data=100 data=100

實時信號收發編程

接受程序:

#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);

int main(int argc, char *argv[])
{
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;

    sigset_t s;
    sigemptyset(&s);
    sigaddset(&s, SIGINT);
    sigaddset(&s, SIGRTMIN);
    sigprocmask(SIG_BLOCK, &s, NULL);
    if (sigaction(SIGINT, &act, NULL) < 0)
        ERR_EXIT("sigaction error");

    if (sigaction(SIGRTMIN, &act, NULL) < 0)
        ERR_EXIT("sigaction error");

    if (sigaction(SIGUSR1, &act, NULL) < 0)
        ERR_EXIT("sigaction error");
    for (;;)
        pause();
    return 0;
}

void handler(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);
    }
}

在主函數中將SIGINT和SIGRTMIN信號加入信號屏蔽字,只有當接收到SIGUSR1信號時纔對前面兩個信號unblock。需要注意的是如《信號的未決與阻塞》中說的一樣:如果在信號處理函數中對某個信號進行解除阻塞時,則只是將pending位清0,讓此信號遞達一次(同個實時信號產生多次進行排隊都會抵達),但不會將block位清0,即再次產生此信號時還是會被阻塞,處於未決狀態。

發送程序:

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>

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



int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage %s pid\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    pid_t pid = atoi(argv[1]); //字符串轉換爲整數
    union sigval val;
    val.sival_int = 100;
    sigqueue(pid, SIGINT, val); // 不可靠信號不會排隊,即會丟失
    sigqueue(pid, SIGINT, val);
    sigqueue(pid, SIGINT, val);
    sigqueue(pid, SIGRTMIN, val); //實時信號會排隊,即不會丟失
    sigqueue(pid, SIGRTMIN, val);
    sigqueue(pid, SIGRTMIN, val);
    sleep(3);
    kill(pid, SIGUSR1);

    return 0;
}

先是運行recv程序:

simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigrtime_recv2

接着ps出recv進程的pid,運行send程序:

simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigrtime_send 4076

在send程序中連續各發送了SIGINT和SIGRTMIN信號3次,接着睡眠3s後使用kill函數發送SIGUSR1信號給recv進程,此時recv進程會輸出如下:

recv a sig=34
recv a sig=34
recv a sig=34
recv a sig=2

即實時信號支持排隊,3個信號都接收到了,而不可靠信號不支持排隊,只保留一個信號。

 

參考:《APUE》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章