一、sigqueue函數
功能:新的發送信號系統調用,主要是針對實時信號提出的支持信號帶有參數,與函數sigaction()配合使用。
原型:int sigqueue(pid_t pid, int sig, const union sigval value);
參數:
sigqueue的第一個參數是指定接收信號的進程id,第二個參數確定即將發送的信號,第三個參數是一個聯合數據結構union sigval,指定了信號傳遞的參數,即通常所說的4字節值。
返回值:成功返回0,失敗返回-1
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;
sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號,而不能發送信號給一個進程組。
寫兩個小程序測試一下:
首先是接收信號:
/*************************************************************************
> File Name: process_.c
> Author: Simba
> Mail: [email protected]
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#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只能取其一
//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);
}
在前面的《信號捕捉與sigaction函數》中說過,sa_sigaction與SA_SIGINFO要配合使用,如上所示,siginfo_t 結構體也可以參見這篇文章。
然後是信號發送:
/*************************************************************************
> File Name: process_.c
> Author: Simba
> Mail: [email protected]
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#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程序:
simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigqueue_recv
再ps出recv進程的pid,然後運行send程序:
simba@ubuntu:~/Documents/code/linux_programming/APUE/signal$ ./sigqueue_send 3323
則recv進程會輸出一條recv語句,當然我們也可以ctrl+c 給自己發送信號,如下所示,結果是一樣的。
recv a sig=2 data=100 data=100
^Crecv a sig=2 data=100 data=100
^Crecv a sig=2 data=100 data=100.
....................................................
需要提醒一下的是siginfo_t 結構體的兩個參數(int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */)的值也會與si_value 一致,取決於發送的是sival_int 還是 sival_ptr。
二、實時信號與不可靠信號的區別
下面通過程序來說明區別,主要就是實時信號支持排隊不會丟失。
先是recv程序:
#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,即再次產生此信號時還是會被阻塞,處於未決狀態。
接著是send程序:
/*************************************************************************
> File Name: sigrtime_send.c
> Author: Simba
> Mail: [email protected]
> Created Time: Sat 23 Feb 2013 02:34:02 PM CST
************************************************************************/
#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個信號都接收到了,而不可靠信號不支持排隊,只保留一個信號。