轉載於:http://blog.csdn.net/zx714311728/article/details/53197196
1.消息隊列
消息隊列可以認為是一個消息鏈表,消息隊列是隨內核持續的。隊列中每個消息的屬性有:一個無符號整數優先級(Poxis)或一個長整數類型(System V);消息的數據部分長度(可以為0);數據本身。鏈表頭含有當前隊列的兩個屬性:隊列中運行的最大消息數、每個消息的最大大小。消息隊列的可能布局如下:

Posix消息隊列與System V消息隊列主要區別:
1.對Posix消息隊列的讀總是返回最高優先級的消息,對System V消息隊列的讀則可以返回任一指定優先級消息。
2.往空隊列放置消息時,Posix消息隊列允許產生一個信號或啟動一個線程,System V則不提供類似機制
Posix消息隊列與管道或FIFO的主要區別:
1.在某個進程往一個隊列寫入消息之前,並不需要另外某個進程在該隊列上等待消息的到達。對管道和FIFO來說,除非讀出者已存在,否則先有寫入者是沒有意義的。
2.管道和FIFO是字節流模型,沒有消息邊界;消息隊列則指定了數據長度,有邊界。
2.mq_open 、 mq_close、 mq_unlink
//創建新消息隊列或打開已存在的消息隊列
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, .../*mode_t mode, struct mq_attr *attr*/);
返回值:成功,消息隊列描述符;失敗,-1
消息隊列描述符用作其余消息隊列函數(mq_unlink除外)的第一個參數。
name規則:必須以一個斜杠符打頭,並且不能再包含任何其他斜杠符
oflag:O_RDONLY、O_WRONLY、O_RDWR三者之一,按位或上O_CREAT、O_EXCL
mode:S_ISRUSR、S_ISWUSR、S_ISRGRP、S_ISWGRP、S_ISROTH、S_ISWOTH
attr:
struct mq_attr
{
long mq_flags;//阻塞標志, 0或O_NONBLOCK
long mq_maxmsg;//最大消息數
long mq_msgsize;//每個消息最大大小
long mq_curmsgs;//當前消息數
};
//關閉已打開的消息隊列
int mq_close(mqd_t mqd);
返回值:成功,0;出錯,-1
一個進程終止時,它的所有打開的消息隊列都關閉,如同調用了mq_close。
//刪除消息隊列的name
int mq_unlink(const char *name);
返回值:成功,0;出錯,-1
每個消息隊列有一個保存其當前打開的描述符數的引用計數,只有當引用計數為0時,才刪除該消息隊列。mq_unlink和mq_close都會讓引用數減一
例1:創建一個消息隊列,並可使用排他性檢驗
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <unistd.h>
- #include <sys/stat.h>
- #include <errno.h>
- #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
- int main(int argc, char *argv[])
- {
- int c;
- int flag = O_RDWR | O_CREAT;
- while ((c = getopt(argc, argv, "e")) != -1)
- {
- switch(c)
- {
- case 'e':
- flag |= O_EXCL;
- break;
- }
- }
- if (optind != argc -1)
- {
- printf("usage: mqcreate [- e] <name>\n");
- exit(0);
- }
- mqd_t mqd = mq_open(argv[optind], flag, FILE_MODE, NULL);
- if (mqd == -1)
- {
- printf("mq_open() error %d : %s\n", errno, strerror(errno));
- exit(-1);
- }
- mq_close(mqd);
- exit(0);
- }
#include <stdio.h>
#include <mqueue.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main(int argc, char *argv[])
{
int c;
int flag = O_RDWR | O_CREAT;
while ((c = getopt(argc, argv, "e")) != -1)
{
switch(c)
{
case 'e':
flag |= O_EXCL;
break;
}
}
if (optind != argc -1)
{
printf("usage: mqcreate [- e] <name>\n");
exit(0);
}
mqd_t mqd = mq_open(argv[optind], flag, FILE_MODE, NULL);
if (mqd == -1)
{
printf("mq_open() error %d : %s\n", errno, strerror(errno));
exit(-1);
}
mq_close(mqd);
exit(0);
}
分析:
1.mq_xxx()函數不是標准庫函數,所以鏈接時需指定庫,通過在最后加上-lrt選項來指定。
2.程序運行之后可能會看不到消息隊列,要看到創建的Posix消息隊列,需執行以下操作:
mkdir /dev/mqueue
mount -t mqueue none /dev/mqueue
3.程序通過getopt()函數獲取帶選項的命令行參數,optind指向下一個要讀取的參數在argv[]中的位置。若遇到沒包含在getopt第三個參數的選項字母,或者遇到一個沒有所需參數的選項字母(通過在第三個參數后跟一個冒號指示)則出錯。
4.若命令行參數帶有-e選項,則進行排他性檢測。
5.若mq_open()出錯,則通過strerror(errno)函數得到錯誤說明。
結果:

例2:使用mq_unlink刪除一個消息隊列
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- int main(int argc, char *argv[])
- {
- if (argc != 2)
- {
- printf("argument error\n");
- exit(-1);
- }
- int val = mq_unlink(argv[1]);
- if (val != 0)
- {
- printf("mq_unlink() error %d : %s\n", errno, strerror(errno));
- exit(-2);
- }
- exit(0);
- }
#include <stdio.h>
#include <mqueue.h>
#include <errno.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("argument error\n");
exit(-1);
}
int val = mq_unlink(argv[1]);
if (val != 0)
{
printf("mq_unlink() error %d : %s\n", errno, strerror(errno));
exit(-2);
}
exit(0);
}
分析:通過strerror(errno)得到錯誤信息
結果:

3.mq_getattr、 mq_setattr
//獲取、設置消息隊列屬性
int mq_getattr(mqd_t mqd, struct mq_attr *attr);
int mq_setattr(mqd_t mqd, const struct mq_attr *attr, struct mq_attr *oattr);
返回值:成功,0;出錯,-1
消息隊列先前屬性返回到oattr中
消息隊列有4個屬性,如下:
struct mq_attr
{
long mq_flags;//阻塞標志, 0或O_NONBLOCK
long mq_maxmsg;//最大消息數
long mq_msgsize;//每個消息最大大小
long mq_curmsgs;//當前消息數
};
其中,mq_setattr只能設置mq_flags屬性;mq_open只能設置mq_maxmsg和mq_msgsize屬性,並且兩個必須要同時設置;mq_getattr返回全部4個屬性。
例3:打開一個消息隊列並輸出其屬性
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- int main(int argc, char *argv[])
- {
- if (argc != 2)
- {
- printf("must have 2 arguments !\n");
- exit(-1);
- }
- mqd_t mqd = mq_open(argv[1], O_RDONLY);
- if (mqd == -1)
- {
- printf("mq_open() error %d: %s\n", errno, strerror(errno));
- exit(-2);
- }
- struct mq_attr attr;
- if (mq_getattr(mqd, &attr) != 0)
- {
- printf("mq_open() error %d: %s\n", errno, strerror(errno));
- exit(-3);
- }
- if (attr.mq_flags == 0)
- printf("mq_flags = 0, ");
- else
- printf("mq_flags = O_NONBLOCK, ");
- printf("mq_maxmsg = %d, ", attr.mq_maxmsg);
- printf("mq_msgsize = %d, ", attr.mq_msgsize);
- printf("mq_curmsgs = %d\n", attr.mq_curmsgs);
- exit(0);
- }
#include <stdio.h>
#include <mqueue.h>
#include <errno.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("must have 2 arguments !\n");
exit(-1);
}
mqd_t mqd = mq_open(argv[1], O_RDONLY);
if (mqd == -1)
{
printf("mq_open() error %d: %s\n", errno, strerror(errno));
exit(-2);
}
struct mq_attr attr;
if (mq_getattr(mqd, &attr) != 0)
{
printf("mq_open() error %d: %s\n", errno, strerror(errno));
exit(-3);
}
if (attr.mq_flags == 0)
printf("mq_flags = 0, ");
else
printf("mq_flags = O_NONBLOCK, ");
printf("mq_maxmsg = %d, ", attr.mq_maxmsg);
printf("mq_msgsize = %d, ", attr.mq_msgsize);
printf("mq_curmsgs = %d\n", attr.mq_curmsgs);
exit(0);
}
分析:因為要先通過mq_open打開消息隊列,才能獲取其屬性,所以消息隊列的命令行參數必須要符合name的規則。
結果:

例4:重新實現例1,這次創建消息隊列時,指定其屬性。
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- #include <unistd.h>
- #include <fcntl.h>
- #define MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
- struct mq_attr attr;
- int main(int argc, char *argv[])
- {
- int c;
- int flag = O_RDWR | O_CREAT;
- while ((c = getopt(argc, argv, "em:z:")) != -1)
- {
- switch (c)
- {
- case 'e':
- flag |= O_EXCL;
- break;
- case 'm':
- attr.mq_maxmsg = atol(optarg);
- break;
- case 'z':
- attr.mq_msgsize = atol(optarg);
- break;
- }
- }
- if (optind != argc -1)
- {
- printf("usage: mqcreate -e [-m maxsg] [-z msgsize] <name>\n");
- exit(-1);
- }
- if ((attr.mq_maxmsg != 0 && attr.mq_msgsize == 0)
- || (attr.mq_maxmsg == 0 && attr.mq_msgsize != 0))
- {
- printf("must specify both -m maxmsg and -z msgsize\n");
- exit(-2);
- }
- mqd_t mqd = mq_open(argv[optind], flag, MODE, (attr.mq_maxmsg != 0) ? &attr : NULL);
- if (mqd == -1)
- {
- printf("mq_open() error %d: %s\n", errno, strerror(errno));
- exit(-3);
- }
- int rtn = mq_close(mqd);
- if (rtn == -1)
- {
- printf("mq_close() error\n");
- exit(-4);
- }
- exit(0);
- }
#include <stdio.h>
#include <mqueue.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#define MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
struct mq_attr attr;
int main(int argc, char *argv[])
{
int c;
int flag = O_RDWR | O_CREAT;
while ((c = getopt(argc, argv, "em:z:")) != -1)
{
switch (c)
{
case 'e':
flag |= O_EXCL;
break;
case 'm':
attr.mq_maxmsg = atol(optarg);
break;
case 'z':
attr.mq_msgsize = atol(optarg);
break;
}
}
if (optind != argc -1)
{
printf("usage: mqcreate -e [-m maxsg] [-z msgsize] <name>\n");
exit(-1);
}
if ((attr.mq_maxmsg != 0 && attr.mq_msgsize == 0)
|| (attr.mq_maxmsg == 0 && attr.mq_msgsize != 0))
{
printf("must specify both -m maxmsg and -z msgsize\n");
exit(-2);
}
mqd_t mqd = mq_open(argv[optind], flag, MODE, (attr.mq_maxmsg != 0) ? &attr : NULL);
if (mqd == -1)
{
printf("mq_open() error %d: %s\n", errno, strerror(errno));
exit(-3);
}
int rtn = mq_close(mqd);
if (rtn == -1)
{
printf("mq_close() error\n");
exit(-4);
}
exit(0);
}
分析:
1.通過getopt參數獲取命令行選項,可見例1的分析。其中“em:z:”,選項字母后帶冒號的意思是,-m選項后要跟參數,-z選項后也要跟參數,否則出錯。
2.optarg代表選項后的參數。
3.如果設置,則必須同時設置mq_maxmsg和mq_msgsize兩個屬性。
結果:

4.mq_send、 mq_receive
//往消息隊列中放置一個消息
int mq_send(mqd_t mqd, const char *ptr, size_t len, unsigned int prio);
返回值:成功,0;出錯,-1
說明:優先級prio要小於MQ_PRIO_MAX(此值最少為32)。
//從消息隊列中取走一個消息
ssize_t mq_receive(mqd_t mqd, char *ptr, size_t len, unsigned int *prio);
返回值:成功,消息中字節數;出錯,-1
說明:消息內存的長度len,最小要等於mq_msgsize。mq_receive總是返回消息隊列中最高優先級的最早消息。
#include <mqueue.h>
int mq_timedsend(mqd_t mqdes, const char *msg_ptr,size_t
msg_len, unsigned msg_prio,const struct timespec *abs_timeout);
需要 -lrt 來鏈接
mq_timedsend():
_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L
mq_timedsend() 的行為與 mq_send() 很類似,只是消息隊列滿且
O_NONBLOCK 標志沒有設置時,abs_timeout 指向的結構指定了一個阻塞的
軟上限時間。這個上限通過從 Epoch 1970-01-01 00:00:00 +0000 (UTC) 開始的
絕對的秒和納秒數指定超時,它的結構定義如下:
struct timespec { time_t tv_sec; /* 秒 */ long tv_nsec; /* 納秒 */ };
如果消息隊列滿,並且調用時超時設置已經達到,mq_timedsend() 立刻返回
#include <time.h>
#include <mqueue.h>
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,size_t
msg_len, unsigned *msg_prio,const struct timespec *abs_timeout);
需要 -lrt 來鏈接。
mq_timedreceive():
_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L 描述
mq_timedreceive() 的行為與 mq_receive() 很相似,只不過當隊列是空且
O_NONBLOCK 標志沒有針對相應消息隊列描述符啟用時,調用會阻塞到
abs_timeout 時間點時返回。這個軟上限是一個絕對的時刻值,它計算從
Epoch 1970-01-01 00:00:00 +0000 (UTC) 開始的秒數和納秒數,這個結構的定
義如下:
struct timespec { time_t tv_sec; /* 秒 */ long tv_nsec; /* 納秒 */ }; 如果沒有消
息有效,並且超時的時刻已經達到,mq_timedreceive() 立即返回。
例5:往消息隊列中添加一個消息
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- int main(int argc, char *argv[])
- {
- char *ptr;
- size_t size;
- unsigned long prio;
- mqd_t mqd;
- if (argc != 4)
- {
- printf("usage: mqsend <name> <#bytes> <priority>\n");
- exit(-1);
- }
- if ((mqd = mq_open(argv[1], O_WRONLY)) == -1)
- {
- printf("mq_open() error %d: %s\n", errno, strerror(errno));
- exit(-1);
- }
- size = atoi(argv[2]);
- prio = atoi(argv[3]);
- ptr = calloc(size, sizeof(char));
- if (mq_send(mqd, ptr, size, prio) == -1)
- {
- printf("mq_send() error %d: %s\n", errno, strerror(errno));
- exit(-1);
- }
- exit(0);
- }
#include <stdio.h>
#include <mqueue.h>
#include <errno.h>
int main(int argc, char *argv[])
{
char *ptr;
size_t size;
unsigned long prio;
mqd_t mqd;
if (argc != 4)
{
printf("usage: mqsend <name> <#bytes> <priority>\n");
exit(-1);
}
if ((mqd = mq_open(argv[1], O_WRONLY)) == -1)
{
printf("mq_open() error %d: %s\n", errno, strerror(errno));
exit(-1);
}
size = atoi(argv[2]);
prio = atoi(argv[3]);
ptr = calloc(size, sizeof(char));
if (mq_send(mqd, ptr, size, prio) == -1)
{
printf("mq_send() error %d: %s\n", errno, strerror(errno));
exit(-1);
}
exit(0);
}
分析:
1.通過atoi()函數將命令行參數轉換成int變量
2.通過calloc()函數分配內存,並初始化為0
3.要發送消息,要先為消息分配內存。 結果:和例6一起
例6:從消息隊列中取出一個消息
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #define ERR_EXIT(m)\
- {\
- printf("%s() error %d: %s\n", m, errno, strerror(errno));\
- exit(EXIT_FAILURE);\
- }
- int main(int argc, char *argv[])
- {
- int c;
- int flag = O_RDONLY;
- //設置非阻塞方式
- while ((c = getopt(argc, argv, "n")) != -1)
- {
- switch(c)
- {
- case 'n':
- flag |= O_NONBLOCK;
- break;
- }
- }
- if (optind != argc -1)
- {
- printf("usage: mqreceive [-n] <name>\n");
- exit(-1);
- }
- //打開消息隊列
- mqd_t mqd;
- if ((mqd = mq_open(argv[optind], flag)) == -1)
- ERR_EXIT("mq_open");
- //獲取消息隊列中每個消息最大大小
- struct mq_attr attr;
- if ((mq_getattr(mqd, &attr)) == -1)
- ERR_EXIT("mq_getattr");
- long len = attr.mq_msgsize;
- //從消息隊列中取出消息
- unsigned int prio;//無符號整型
- ssize_t n;
- char *buff = (char *)malloc(attr.mq_msgsize * sizeof(char));
- if ((n = mq_receive(mqd, buff, len, &prio)) == -1)
- ERR_EXIT("mq_getattr");
- printf("read %d bytes, priority = %d\n", n, prio);
- exit(0);
- }
#include <stdio.h>
#include <mqueue.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define ERR_EXIT(m)\
{\
printf("%s() error %d: %s\n", m, errno, strerror(errno));\
exit(EXIT_FAILURE);\
}
int main(int argc, char *argv[])
{
int c;
int flag = O_RDONLY;
//設置非阻塞方式
while ((c = getopt(argc, argv, "n")) != -1)
{
switch(c)
{
case 'n':
flag |= O_NONBLOCK;
break;
}
}
if (optind != argc -1)
{
printf("usage: mqreceive [-n] <name>\n");
exit(-1);
}
//打開消息隊列
mqd_t mqd;
if ((mqd = mq_open(argv[optind], flag)) == -1)
ERR_EXIT("mq_open");
//獲取消息隊列中每個消息最大大小
struct mq_attr attr;
if ((mq_getattr(mqd, &attr)) == -1)
ERR_EXIT("mq_getattr");
long len = attr.mq_msgsize;
//從消息隊列中取出消息
unsigned int prio;//無符號整型
ssize_t n;
char *buff = (char *)malloc(attr.mq_msgsize * sizeof(char));
if ((n = mq_receive(mqd, buff, len, &prio)) == -1)
ERR_EXIT("mq_getattr");
printf("read %d bytes, priority = %d\n", n, prio);
exit(0);
}
分析:
1.要取出消息,則先要為消息分配內存。
2.open時,若未設置O_NONBLOCK選項,則一直阻塞,直到消息隊列中有消息為止;設置了O_NONBLOCK選項,若消息隊列中沒有消息,則立刻出錯返回。
結果:

5.mq_notify
Posix消息隊列允許異步事件通知,以告知何時有一個消息放置到了某個空消息隊列中。這種通知有兩種方式可供選擇:
(1)產生一個信號
(2)創建一個線程來執行指定的函數
//這種通知通過調用mq_notify建立
int mq_notify(mqd_t mqd, const struct sigevent *notification);
返回值:成功,0;出錯-1
1.notification非空,該進程被注冊為接收該消息隊列的通知
2.notification空,且當前隊列已被注冊,則已存在的注冊被撤銷
3.對一個消息隊列來說,任一時刻只有一個進程可以被注冊
4.在mq_receive調用中的阻塞比任何通知的注冊都優先收到消息
5.當通知被發送給注冊進程時,注冊即被撤銷,該進程若要重新注冊,則必須重新調用mq_notify()
#include <signal.h>
struct sigevent
{
int sigev_notify;
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
};
sigev_notify取值:
SIGEV_NONE:事件發生時,什么也不做;SIGEV_SIGNAL:事件發生時,將sigev_signo指定的信號發送給指定的進程;SIGEV_THREAD:事件發生時,內核會(在此進程內)以sigev_notify_attributes為線程屬性創建一個線程,並讓其執行sigev_notify_function,並以sigev_value為其參數
sigev_signo:在sigev_notify=SIGEV_SIGNAL時使用,指定信號類別
sigev_value:sigev_notify=SIGEV_SIGEV_THREAD時使用,作為sigev_notify_function的參數
union sigval
{
int sival_int;
void *sival_ptr;
};
sigev_notify_function:在sigev_notify=SIGEV_THREAD時使用,其他情況下置NULL
sigev_notify_attributes:在sigev_notify=SIGEV_THREAD時使用,指定創建線程的屬性,其他情況下置NULL
例7:注冊一個進程,當消息隊列中有消息到來時,該進程被通知,並在其信號處理程序中取出消息。
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <signal.h>
- #define ERR_EXIT(m)\
- {\
- printf("%s() error %d: %s\n", m, errno, strerror(errno));\
- exit(EXIT_FAILURE);\
- }
- mqd_t mqd;
- void *buff;
- struct mq_attr attr;
- struct sigevent event;
- static void sig_usr1(int signo);
- int main(int argc, char *argv[])
- {
- if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
- ERR_EXIT("signal");
- if ((mqd = mq_open(argv[1], O_RDONLY)) == -1)
- ERR_EXIT("mq_open");
- if (mq_getattr(mqd, &attr) == -1)
- ERR_EXIT("mq_getattr");
- buff = malloc(attr.mq_msgsize);
- //注冊進程接收隊列的通知
- event.sigev_notify = SIGEV_SIGNAL;
- event.sigev_signo = SIGUSR1;
- if (mq_notify(mqd, &event) == -1)//注冊進程,消息隊列中有消息來時,觸發事件。
- ERR_EXIT("mq_notify");
- //一直執行,等待信號到來
- for (;;)
- pause();
- exit(0);
- }
- static void sig_usr1(int signo)
- {
- //重新注冊
- if (mq_notify(mqd, &event) == -1)
- ERR_EXIT("mq_notify");
- //取出消息
- ssize_t n;
- unsigned int prio;
- if ((n = mq_receive(mqd, buff, attr.mq_msgsize, &prio)) == -1)
- ERR_EXIT("mq_receive");
- printf("SIGUSR1 received, read %d bytes, priority = %d\n", n, prio);
- }
#include <stdio.h>
#include <mqueue.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#define ERR_EXIT(m)\
{\
printf("%s() error %d: %s\n", m, errno, strerror(errno));\
exit(EXIT_FAILURE);\
}
mqd_t mqd;
void *buff;
struct mq_attr attr;
struct sigevent event;
static void sig_usr1(int signo);
int main(int argc, char *argv[])
{
if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
ERR_EXIT("signal");
if ((mqd = mq_open(argv[1], O_RDONLY)) == -1)
ERR_EXIT("mq_open");
if (mq_getattr(mqd, &attr) == -1)
ERR_EXIT("mq_getattr");
buff = malloc(attr.mq_msgsize);
//注冊進程接收隊列的通知
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGUSR1;
if (mq_notify(mqd, &event) == -1)//注冊進程,消息隊列中有消息來時,觸發事件。
ERR_EXIT("mq_notify");
//一直執行,等待信號到來
for (;;)
pause();
exit(0);
}
static void sig_usr1(int signo)
{
//重新注冊
if (mq_notify(mqd, &event) == -1)
ERR_EXIT("mq_notify");
//取出消息
ssize_t n;
unsigned int prio;
if ((n = mq_receive(mqd, buff, attr.mq_msgsize, &prio)) == -1)
ERR_EXIT("mq_receive");
printf("SIGUSR1 received, read %d bytes, priority = %d\n", n, prio);
}
分析:
因為通知被發送給注冊進程時,其注冊即被撤銷,所以在信號處理程序中重新注冊。 結果:

例7中的程序有個問題:
在信號處理程序中調用的函數都不是異步信號安全函數,即不可重入的,具體參考apue 10.6節。http://blog.csdn.net/zx714311728/article/details/53056927#t4
前面說過,通知有兩種方式,一種是產生一個信號,另一種是創建一個線程執行指定函數。例7是產生一個信號,下面例8是創建一個線程。
例8:空消息隊列有消息到來時,通知注冊進程,注冊進程創建一個線程執行指定函數操作。
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #define ERR_EXIT(m)\
- {\
- printf("%s() error %d: %s\n", m, errno, strerror(errno));\
- exit(EXIT_FAILURE);\
- }
- mqd_t mqd;
- struct mq_attr attr;
- struct sigevent event;
- static void thread_func(union sigval);
- int main(int argc, char *argv[])
- {
- if (argc != 2)
- {
- printf("argument error\n");
- exit(-1);
- }
- //打開消息隊列
- if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) == -1)
- ERR_EXIT("mq_open");
- if (mq_getattr(mqd, &attr) == -1)
- ERR_EXIT("mq_getattr");
- //注冊進程
- event.sigev_notify = SIGEV_THREAD;
- event.sigev_value.sival_ptr = NULL;
- event.sigev_notify_attributes = NULL;
- event.sigev_notify_function = thread_func;
- if (mq_notify(mqd, &event) == -1)
- ERR_EXIT("mq_notify");
- for (;;)
- pause();
- exit(0);
- }
- void thread_func(union sigval value)
- {
- ssize_t n;
- void *buff;
- long len;
- unsigned int prio;
- buff = malloc(attr.mq_msgsize);
- len = attr.mq_msgsize;
- if (mq_notify(mqd, &event) == -1)//重新注冊進程
- ERR_EXIT("mq_notify");
- if ((n = mq_receive(mqd, buff, len, &prio)))
- printf("read %d bytes, priority = %d\n", n, prio);
- free(buff);
- pthread_exit((void *)0);
- }
#include <stdio.h>
#include <mqueue.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define ERR_EXIT(m)\
{\
printf("%s() error %d: %s\n", m, errno, strerror(errno));\
exit(EXIT_FAILURE);\
}
mqd_t mqd;
struct mq_attr attr;
struct sigevent event;
static void thread_func(union sigval);
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("argument error\n");
exit(-1);
}
//打開消息隊列
if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) == -1)
ERR_EXIT("mq_open");
if (mq_getattr(mqd, &attr) == -1)
ERR_EXIT("mq_getattr");
//注冊進程
event.sigev_notify = SIGEV_THREAD;
event.sigev_value.sival_ptr = NULL;
event.sigev_notify_attributes = NULL;
event.sigev_notify_function = thread_func;
if (mq_notify(mqd, &event) == -1)
ERR_EXIT("mq_notify");
for (;;)
pause();
exit(0);
}
void thread_func(union sigval value)
{
ssize_t n;
void *buff;
long len;
unsigned int prio;
buff = malloc(attr.mq_msgsize);
len = attr.mq_msgsize;
if (mq_notify(mqd, &event) == -1)//重新注冊進程
ERR_EXIT("mq_notify");
if ((n = mq_receive(mqd, buff, len, &prio)))
printf("read %d bytes, priority = %d\n", n, prio);
free(buff);
pthread_exit((void *)0);
}
結果:已測試通過,和例7一樣,懶得放圖了。
6.sigwait
在多線程的環境中應使用sigwait、sigwaitinfo和sigtimedwait來處理所有信號,而絕不要用異步信號處理程序。多線程環境中也不應使用sigprocmask,而應使用pthread_sigmask。具體見:http://blog.csdn.net/yusiguyuan/article/details/14230719
#include <signal.h>
int sigwait(const sigset_t *set, int *sig);
返回值:成功,0;出錯,正的Exxx值
調用sigwait前阻塞set信號集,sigwait然后一直阻塞到這些信號中有一個或多個待處理,這時它返回其中一個信號存放到sig。此過程為“同步的等待一個異步事件”:我們使用了信號,但沒有涉及異步信號處理程序。
例9:修改例7的問題,不使用異步信號處理程序,而是使用sigwait。
程序:
- #include <stdio.h>
- #include <mqueue.h>
- #include <signal.h>
- #include <errno.h>
- #include <stdlib.h>
- #define ERR_EXIT(m)\
- {\
- printf("%s() error %d: %s\n", m, errno, strerror(errno));\
- exit(EXIT_FAILURE);\
- }
- int main(int argc, char *argv[])
- {
- if (argc != 2)
- {
- printf("argument error\n");
- exit(-1);
- }
- mqd_t mqd;
- struct sigevent event;
- sigset_t newmask, oldmask, zeromask;
- struct mq_attr attr;
- void *buff;
- long len;
- int signo;
- ssize_t n;
- unsigned int prio;
- //打開消息隊列,獲取消息隊列屬性
- if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) == -1)
- ERR_EXIT("mq_open");
- if (mq_getattr(mqd, &attr) == -1)
- ERR_EXIT("mq_getattr");
- buff = malloc(attr.mq_msgsize);
- len = attr.mq_msgsize;
- //設置阻塞信號集,阻塞信號
- sigemptyset(&newmask);
- sigaddset(&newmask, SIGUSR1);
- sigprocmask(SIG_BLOCK, &newmask, NULL);
- //注冊進程
- event.sigev_notify = SIGEV_SIGNAL;
- event.sigev_signo = SIGUSR1;
- if (mq_notify(mqd, &event) == -1)
- ERR_EXIT("mq_notify");
- //使注冊進程永久執行,當空消息隊列中有消息到來時,進行處理
- for (;;)
- {
- sigwait(&newmask, &signo);//阻塞,直到有個信號到來
- if (signo == SIGUSR1)
- {
- if (mq_notify(mqd, &event) == -1)//重新注冊
- ERR_EXIT("mq_notify");
- if ((n = mq_receive(mqd, buff, len, &prio)) == -1)//獲取消息隊列中消息
- {
- ERR_EXIT("mq_receive");
- }
- else
- printf("SIGUSR1 received, read %dbytes, priority = %d\n", n, prio);
- }
- }
- exit(0);
- }
#include <stdio.h>
#include <mqueue.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#define ERR_EXIT(m)\
{\
printf("%s() error %d: %s\n", m, errno, strerror(errno));\
exit(EXIT_FAILURE);\
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("argument error\n");
exit(-1);
}
mqd_t mqd;
struct sigevent event;
sigset_t newmask, oldmask, zeromask;
struct mq_attr attr;
void *buff;
long len;
int signo;
ssize_t n;
unsigned int prio;
//打開消息隊列,獲取消息隊列屬性
if ((mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK)) == -1)
ERR_EXIT("mq_open");
if (mq_getattr(mqd, &attr) == -1)
ERR_EXIT("mq_getattr");
buff = malloc(attr.mq_msgsize);
len = attr.mq_msgsize;
//設置阻塞信號集,阻塞信號
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigprocmask(SIG_BLOCK, &newmask, NULL);
//注冊進程
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGUSR1;
if (mq_notify(mqd, &event) == -1)
ERR_EXIT("mq_notify");
//使注冊進程永久執行,當空消息隊列中有消息到來時,進行處理
for (;;)
{
sigwait(&newmask, &signo);//阻塞,直到有個信號到來
if (signo == SIGUSR1)
{
if (mq_notify(mqd, &event) == -1)//重新注冊
ERR_EXIT("mq_notify");
if ((n = mq_receive(mqd, buff, len, &prio)) == -1)//獲取消息隊列中消息
{
ERR_EXIT("mq_receive");
}
else
printf("SIGUSR1 received, read %dbytes, priority = %d\n", n, prio);
}
}
exit(0);
}
分析:
此程序沒有使用異步信號處理程序,而是使用了sigwait以接收信號 結果:

7.Posix實時信號
信號可划分為兩個大組:
(1)值在SIGRTMIN和SIGRTMAX之間(包括兩者在內)的實時信號
(2)所有其他信號:SIGALRM、SIGINT、SIGKILL等
實時信號中“實時”的意思是:
(1)信號是排隊的,是按先進先出(FIFO)順序排隊的。同一信號產生3次,則遞交3次
(2)當有多個SIGRTMIN到SIGRTMAX范圍內的解阻塞信號排隊時,值較小的信號先與較大的信號遞交
(3)實時信號能比非實時信號攜帶更多信息
此部分因涉及到其他部分內容,只粗略看了下。

