本文采取两种方法来实现:
<1>System V IPC 的消息队列(message queue)
<2>有名信号量/灯的同步操作(semaphore)
一、System V IPC 的消息队列
消息队列
概念:
消息队列是System V IPC对象的一种
消息队列的使用:
发送端:
1 申请Key
2打开/创建消息队列 msgget
3向消息队列发送消息 msgsnd
接收端:
1打开/创建消息队列 msgget
2从消息队列接收消息 msgrcv
3 控制(删除)消息队列 msgctl
实验现象
$ ./message_snd xxx(字符串) //发送一条消息
循环接收
源代码:
message_snd.c
发送端:发送一个消息
#include "message.h"
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Don't forget to input your message.\n");
return -1;
}
key_t key;
int msgid, ret;
msgbuf M;
if((key = ftok(".", 100)) < 0)
{
perror("ftok");
return -1;
}
if((msgid = msgget(key, IPC_CREAT | 0666)) < 0)
{
perror("msgget");
return -1;
}
M.mtype = 1;
strcpy(M.mtext, argv[1]);
if((ret = msgsnd(msgid, &M, MSGLEN, 0)) < 0)
{
perror("msgnd");
return -1;
}
return 0;
}
message_snd.c
接收端:循环接收消息
#include "message.h"
int main(void)
{
key_t key;
int msgid, ret;
msgbuf M;
if((key = ftok(".", 100)) < 0)
{
perror("ftok");
return -1;
}
if((msgid = msgget(key, IPC_CREAT | 0666)) < 0)
{
perror("msgget");
return -1;
}
while(1)
{
if((ret = msgrcv(msgid, &M, MSGLEN, 0, 0)) < 0)
{
perror("msgrcv");
return -1;
}
printf("Got a %d message:%s\n", (int)M.mtype, M.mtext);
}
return 0;
}
message.h
头文件
#ifndef __MESSAGE_H__
#define __MESSAGE_H__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
typedef struct {
long mtype; /* message type, must be > 0 */
char mtext[512]; /* message data */
}msgbuf;
#define MSGLEN (sizeof(msgbuf) - sizeof(long))
#endif
二、有名信号量/灯(semaphore)的同步操作
信号量/灯(semaphore)
定义:在同步两个 进程/线程 中 来使用有限的资源的时候来做的一种机制,主要用在生产者和消费者这种模型。
信号量代表某一类资源,其值表示系统中该资源的数量
信号量是一个受保护的变量,只能通过三种操作来访问
初始化
P操作(申请资源) ->消费者
V操作(释放资源) ->生产者
P(S) 含义如下:
if (信号量的值大于0) {
申请资源的任务继续运行;
信号量的值减一;
}
else { 申请资源的任务阻塞,直到系统把资源分配给该任务;}
V(S) 含义如下:
信号量的值加一;
if (有任务在等待资源) {
唤醒等待的任务,让其继续运行
}
sem_w.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <signal.h>
/*
编译时一定要记得 -lpthread
*/
//捕获ctrl+c信号,删除/dev/shm里面创建的sem文件,以便程序能重复使用
void del_semaphore(int sig_no)
{
if(sig_no == SIGINT)
{
sem_unlink("my_semaphore_write");
exit(0);
}
}
int main()
{
key_t key;
int shmid;
char *shmaddr;
sem_t *sem_r, *sem_w;
struct sigaction act;
act.sa_handler = del_semaphore;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
if((key = ftok(".", 100)) < 0)
{
perror("ftok");
return -1;
}
if((shmid = shmget(key, 512, IPC_CREAT |0666)) < 0)
{
perror("shmget");
return -1;
}
if((shmaddr = shmat(shmid, NULL, 0)) < 0)
{
perror("shmat");
return -1;
}
sem_r = sem_open("my_semaphore_read", O_CREAT | O_RDWR, 0666, 0);
sem_w = sem_open("my_semaphore_write", O_CREAT | O_RDWR, 0666, 1);
while(1)
{
/* P操作 检查资源能不能用,占用资源,准备执行操作,信号量-1*/
sem_wait(sem_w); //检查能不能写
printf(">");
fgets(shmaddr, 512, stdin); //这个函数要常记
/* V操作 执行完毕,释放资源(读),信号量+1 */
sem_post(sem_r);
}
return 0;
}
sem_r.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <signal.h>
/*
编译时一定要记得 -lpthread
*/
//捕获ctrl+c信号,删除/dev/shm里面创建的sem文件,以便程序能重复使用
void del_semaphore(int sig_no)
{
if(sig_no == SIGINT)
{
sem_unlink("my_semaphore_read");
exit(0);
}
}
int main()
{
key_t key;
int shmid;
char *shmaddr;
sem_t *sem_r, *sem_w;
struct sigaction act;
act.sa_handler = del_semaphore;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
if((key = ftok(".", 100)) < 0)
{
perror("ftok");
return -1;
}
if((shmid = shmget(key, 512, IPC_CREAT |0666)) < 0)
{
perror("shmget");
return -1;
}
if((shmaddr = shmat(shmid, NULL, 0)) < 0)
{
perror("shmat");
return -1;
}
sem_r = sem_open("my_semaphore_read", O_CREAT | O_RDWR, 0666, 0);
sem_w = sem_open("my_semaphore_write", O_CREAT | O_RDWR, 0666, 1);
while(1)
{
/* P操作 占用资源,准备执行操作,信号量-1*/
sem_wait(sem_r); //检查能不能读
printf("%s\n", shmaddr);
/* V操作 执行完毕,释放资源(写),信号量+1 */
sem_post(sem_w);
}
return 0;
}
实验现象
1.x86端
在这里捕捉SIGINT信号的原因:sem_open()会在/dev/shm下创建两个文件,我们需要在信号捕捉函数中执行sem_unlink()函数来在程序执行完毕后删除他们,否则这个程序就只能执行一次,不能反复执行。
2.ARM端
这种方法的好处就是可以打空格了哈哈哈哈哈哈哈哈哈哈
以上