实验准备:
1 一个一直在运行的程序
2 一个空的posix风格消息队列
3一个可以向消息队列中发送消息的程序 和 一个可以发送kill命令的控制台
实验描述:
进程a先试探性的获取消息队列中的消息 然后一直在运行
发现消息队列有消息进入,会由mq_notify发送sigusr1信号给宿主进程a,
宿主进程a捕获信号后,接受消息队列中的消息
进程a还可以捕获由kill -2(Ctrl+C)发送的信号 这将导致进程a在运行清理函数后退出
实验步骤:
开启新终端,编译运行第一篇代码
开启新终端,编译运行第二篇代码
观察现象:第一篇代码的终端有响应
可以循环运行第二篇代码,观察现象
使用第二篇代码的终端发送 kill -2 pid
观察现象:第一篇代码的进程终止
注意事项:
以下两个程序 必须编译后由独立终端开启 不可以在IDE直接运行,因为涉及到信号
此程序为linux gnu_c的一部分 在其他操作系统运行可能需要改动
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <time.h>
#include <limits.h>
#include <sys/sem.h> //System V信号量
#include <semaphore.h> //POSIX信号量
#include <sys/shm.h> //System V共享内存
#include <sys/mman.h> //POSIX共享内存
#include <sys/msg.h> //System V消息队列
#include <mqueue.h> //POSIX消息队列
//慎重使用全局变量
mqd_t global_mqd;
void mq_open_init()
{
//打开消息队列 能存10个消息每条大小64字节
struct mq_attr attr = {0};
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 64;
//读写模式,读写都非阻塞不用等,为notify提供条件
mqd_t mqd = mq_open("/mq1", O_CREAT | O_RDWR | O_NONBLOCK, 0777, &attr);
if (mqd == -1)
{
perror("mq_open");
}
//将消息队列描述符赋值给全局变量
global_mqd = mqd;
}
void mq_receive_init(mqd_t mqd)
{
char buffer[1024];
unsigned int msg_prio;
//把getattr的结果存到attr中,并打印当前消息队列中消息的数量
struct mq_attr attr = {0};
if (mq_getattr(mqd, &attr) == -1)
{
perror("mq_getattr");
}
printf("mq_curmsgs:%ld\n",attr.mq_curmsgs);
//接收缓冲区必须比attr.mq_msgsize大,buffer装的是消息正文,msg_prio是已接收消息的优先级
//mq_receive自动接收消息优先级大的消息,无法干预
ssize_t r_bytes = mq_receive(mqd, buffer, 1024, &msg_prio);
if (r_bytes == -1)
{
if (errno == EAGAIN)
{
perror("EAGAIN");
}
else
{
perror("mq_receive");
}
}
//再看看消息队列里有几条消息
if (mq_getattr(mqd, &attr) == -1)
{
perror("mq_getattr");
}
printf("mq_receive_bytes:%zd\nmq_context:%s\nmsg_prio:%d\nmq_curmsgs:%ld\n"
, r_bytes, buffer, msg_prio, attr.mq_curmsgs);
}
void mq_notify_init(mqd_t mqd)
{
struct sigevent se;
//如果有消息进入消息队列,使用信号通知
se.sigev_notify = SIGEV_SIGNAL;
//发送的通知信号为 SIGUSR1
se.sigev_signo = SIGUSR1;
//还需要携带mdq的值一起发走
se.sigev_value.sival_int = mqd;
if (mq_notify(mqd, &se) == -1)
{
perror("mq_notify");
}
}
void sig_handler_receive_notify(int signo, siginfo_t *siginfo, void *a)
{
printf("%s\n", strsignal(signo));
mqd_t mqd = siginfo->si_value.sival_int;
//接收
mq_receive_init(mqd);
//注册通知,实现循环接收功能
mq_notify_init(mqd);
}
void sig_handler_exit_cleanup(int signo, siginfo_t *siginfo, void *a)
{
//获取mqd的值 sigaction的处理函数是一个函数指针类型 不能修改函数头造型
mqd_t mqd = siginfo->si_value.sival_int;
//关闭与队列的链接
if (mq_close(mqd) == -1)
{
perror("mq_close");
}
//删除队列
if (mq_unlink("/mq1") == -1)
{
perror("mq_unlink");
}
printf("exit(EXIT_SUCCESS)\n");
exit(EXIT_SUCCESS);
}
void sigaction_SIGUSR1()
{ //注册收到SIGUSR1的处理函数sig_handler_receive_notify
struct sigaction sa = {0};
sa.sa_sigaction = sig_handler_receive_notify;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1)
{
perror("sigaction_SIGUSR1");
}
}
void sigaction_SIGINT()
{
//注册收到SIGINT的处理函数sig_handler_exit_cleanup
struct sigaction sa = {0};
sa.sa_sigaction = sig_handler_exit_cleanup;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGINT, &sa, NULL) == -1)
{
perror("sigaction_SIGINT");
}
}
int main()
{ //打开消息队列
mq_open_init();
//尝试接收消息
mq_receive_init(global_mqd);
//初始化并注册异步通知
mq_notify_init(global_mqd);
//注册信号函数SIGUSR1
sigaction_SIGUSR1();
//注册信号函数SIGINT
sigaction_SIGINT();
// 模仿程序运行
while (1)
{
printf("%d doing...\n", getpid());
sleep(1);
}
return 0;
}
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/sem.h> //System V信号量
#include <semaphore.h> //POSIX信号量
#include <sys/shm.h> //System V共享内存
#include <sys/mman.h> //POSIX共享内存
#include <sys/msg.h> //System V消息队列
#include <mqueue.h> //POSIX消息队列
int main()
{
//此程序用于向消息队列中增加一条消息
printf("%d\n", getpid());
struct mq_attr attr = {0};
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 64;
mqd_t mq_d = mq_open("/mq1", O_CREAT | O_RDWR, 0777, &attr);
if (mq_d == (mqd_t)-1)
{
perror("mq_open");
}
char buffer[1024] = {0};
sprintf(buffer, "%d say hello", getpid());
if (mq_send(mq_d, buffer, strlen(buffer) + 1, 1) == -1)
{
perror("mq_send");
}
mq_close(mq_d);
return 0;
}