这是带恶人的课内上机作业,一共两次,分别于4.28与5.26布置。此处感谢杨大佬帮忙一起debug!
实验:linux 通过有名信号量和共享内存通信机制实现两个进程间的通信
程序设计流程图
receiver.c
#include <unistd.h>
#include <stdio.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#define TEXT_SZ 2048
struct shared_use_st
{
int written;//作为一个标志,非0:表示可读,0表示可写
char text[TEXT_SZ];//记录写入和读取的文本
};
int main()
{
sem_t *semr = NULL;
sem_t *semw = NULL;
semr = sem_open("mysem_r", O_CREAT | O_RDWR , 0666, 0);
semw = sem_open("mysem_w", O_CREAT | O_RDWR , 0666, 1);
int running = 1;//程序是否继续运行的标志
void *shm = NULL;//分配的共享内存的原始首地址
struct shared_use_st *shared;//指向shm
int shmid;//共享内存标识符
//创建共享内存
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("\nMemory attached at %X\n", (int)shm);
//设置共享内存
shared = (struct shared_use_st*)shm;
shared->written = 0;
while(running)//读取共享内存中的数据
{
sem_wait(semr);
printf("You wrote: %s", shared->text);
//读取完数据,设置written使共享内存段可写
shared->written = 0;
//输入了over,退出循环(程序)
if(strncmp(shared->text, "over", 4) == 0)
running = 0;
sem_post(semw);
}
sleep(1);
exit(EXIT_SUCCESS);
}
sender.c
#include <unistd.h>
#include <stdio.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#define TEXT_SZ 2048
struct shared_use_st
{
int written;//作为一个标志,非0:表示可读,0表示可写
char text[TEXT_SZ];//记录写入和读取的文本
};
int main()
{
sem_t *semr = NULL;
sem_t *semw = NULL;
semr = sem_open("mysem_r", O_CREAT | O_RDWR , 0666, 0);
semw = sem_open("mysem_w", O_CREAT | O_RDWR , 0666, 1);
int running = 1;
void *shm = NULL;
struct shared_use_st *shared = NULL;
char buffer[BUFSIZ + 1];//用于保存输入的文本
int shmid;
//创建共享内存
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
//将共享内存连接到当前进程的地址空间
shm = shmat(shmid, (void*)0, 0);
if(shm == (void*)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %X\n", (int)shm);
//设置共享内存
shared = (struct shared_use_st*)shm;
while(running)//向共享内存中写数据
{
//数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本
sem_wait(semw);
//向共享内存中写入数据
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared->text, buffer, TEXT_SZ);
//写完数据,设置written使共享内存段可读
shared->written = 1;
//输入了over,退出循环(程序)
if(strncmp(buffer, "over", 4) == 0)
running = 0;
sem_post(semr);
}
//把共享内存从当前进程中分离
if(shmdt(shm) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
//删除共享内存
if(shmctl(shmid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
主要函数与说明:
①信号量初始化
semr = sem_open(“mysem_r”, O_CREAT | O_RDWR , 0666, 0);
semw = sem_open(“mysem_w”, O_CREAT | O_RDWR , 0666, 1);
设置读、写信号量的初值为0和1
②创建共享内存
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
③将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
④设置共享内存
shared = (struct shared_use_st*)shm;
⑤信号量控制访问
sem_wait(semr);
sem_post(semw);
sem_wait(semr);
sem_post(semw);
遇到的问题:
对‘sem_open’未定义的引用
对‘sem_open’未定义的引用
对‘sem_wait’未定义的引用
对‘sem_post’未定义的引用
解决方案:
程序运行结果:
参考资料:
①博客园:Linux进程间通信—使用共享内存
https://www.cnblogs.com/nima/p/11751466.html?tdsourcetag=s_pctim_aiomsg
②博客园:信号量学习 & 共享内存同步
https://www.cnblogs.com/charlesblc/p/6142868.html
③CSDN:Linux编译错误:对‘sem_wait’未定义的引用
https://blog.csdn.net/merlone/article/details/38677553
实验:linux通过有名信号量和消息队列机制实现三个线程间通信
程序设计流程图
MsgQ.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<sys/msg.h>
#include<semaphore.h>
pthread_t s,r1,r2; //创建线程sender1,receiver1,receiver2
sem_t Write, Read1, Read2; //创建信号量变量
int flag1=0,flag2=0; //两个receiver结束的标志
struct msgbuf{ //消息缓冲区
long mtype ; //消息类型
char mtext[100]; //消息内容
};
struct MyMsqids{
int id1;
int id2;
};
void *receiver1(void *arg) //arg即传入的msgid,下同
{
printf("receiver1 is running\n");
struct msgbuf buf;
int ret;
while(1)
{
sem_wait(&Read1);
//消息队列初始化,将buf的sizeof(buf)字节置为0
memset(&buf,0,sizeof(buf));
sem_wait(&Write); //申请写的权限
ret=msgrcv(*(int *)arg,&buf,sizeof(buf.mtext),2,0);
if (ret==-1)
{//返回值为-1表示接收失败
perror("msgrcv error");
sem_post(&Write); //释放写的权限
exit(1); //异常退出程序
}
else if(!strncmp(buf.mtext,"exit",4))
{
flag1++;
printf("---receiver 1 exit !!\n");
sleep(1);
sem_post(&Write); //释放写的权限
pthread_cancel(r1);
sleep(1);
}
else //正常传输
{
printf("receiver 1 :%s\n",buf.mtext);
sem_post(&Write); //释放写的权限
sleep(1);
}
}
}
void *receiver2(void *arg)
{
printf("receiver2 is running\n");
struct msgbuf buf;
int ret;
while(1)
{
sem_wait(&Read2);
//消息队列初始化,将buf的sizeof(buf)字节置为0
memset(&buf,0,sizeof(buf));
sem_wait(&Write); //申请写的权限
ret=msgrcv(*(int *)arg,&buf,sizeof(buf.mtext),2,0);
if (ret==-1)
{
printf("msgrcv error");
sem_post(&Write); //释放写的权限
exit(1); //异常退出程序
}
else if(!strncmp(buf.mtext,"exit",4))
{
flag2++;
printf("---receiver 2 exit !!\n");
sleep(1);
sem_post(&Write); //释放写的权限
pthread_cancel(r2);
sleep(1);
}
else //正常传输
{
printf("receiver 2 :%s\n",buf.mtext);
sem_post(&Write); //释放写的权限
sleep(1);
}
}
}
void *sender(void *arg)
{
printf("sender is running\n");
char t[100];
struct msgbuf buf;
int choose;
int ret, msqid1, msqid2;
int x=1;
struct MyMsqids *msgids;
msgids = (struct MyMsqids *)arg;
msqid1=(*msgids).id1;
msqid2=(*msgids).id2;
printf("sender-msqid1 : %d\n",msqid1);
printf("sender-msqid2 : %d\n",msqid2);
while(x==1)
{
if(flag1+flag2==2)
{
printf("---both receiver exit, sender exit !!\n");
sem_post(&Write); //释放写的权限
pthread_cancel(s);
x=0;
}
else if(flag1+flag2!=2)
{
memset(&buf,0,sizeof(buf)); //初始化buf
sem_wait(&Write); //申请写的权限
printf("---please enter some words to send !!\n");
scanf("%s",t); //从键盘读入字符串
buf.mtype=2; //定义消息类型
strcat(buf.mtext,t);
choose=rand()%2;
if(flag2==1)choose=0; //如果r2结束,发送给r1
else if(flag1==1)choose=1; //如果r1结束,发送给r2
if(choose==0)
{
ret=msgsnd(msqid1,&buf,sizeof(buf.mtext),0);
printf("---send to receiver 1 !!\n");
sem_post(&Read1);
}
else if(choose==1)
{
ret=msgsnd(msqid2,&buf,sizeof(buf.mtext),0);
printf("---send to receiver 2 !!\n");
sem_post(&Read2);
}
if(ret==-1) //如果两个接收者线程都结束了,则循环结束
{
perror("msgsnd error");
}
sem_post(&Write); //释放写的权限
sleep(1);
}
}
}
int main()
{
printf("main is running\n");
int ret,msqid1,msqid2;
struct MyMsqids msqids;
int x;
sem_init(&Write,0,1);
sem_getvalue(&Write,&x);
if(x==0) sem_post(&Write);
printf("---semaphore_Write: %d\n",x);
sem_init(&Read1,0,0);
sem_getvalue(&Read1,&x);
if(x==1) sem_wait(&Read1);
printf("---semaphore_Read1: %d\n",x);
sem_init(&Read2,0,0);
sem_getvalue(&Read2,&x);
if(x==1) sem_wait(&Read2);
printf("---semaphore_Read2: %d\n",x);
//key必须不一样,消息队列与key相对应,所以我给两个消息队列取了不同的key
key_t key;
key=100;
msqid1=msgget(key,IPC_CREAT|0666);
key=101;
msqid2=msgget(key,IPC_CREAT|0666);
msqids.id1=msqid1;
msqids.id2=msqid2;
printf("main-msqid1 : %d\n",msqid1);
printf("main-msqid2 : %d\n",msqid2);
if(msqid1==-1)
{
perror("msgid 1 error");
printf("---msgid 1 error\n");
exit(1);
}
if(msqid2==-1)
{
perror("msgid 2 error");
printf("---msgid 2 error\n");
exit(1);
}
//创建线程时以结构体形式传值message queue id 1和2,成功传入。
ret=pthread_create(&s,NULL,sender,(void *)&msqids);
if (ret!=0)
{
perror("sender create error");
printf("---sender create error\n");
exit(1);
}
//创建线程时传值message queue id 1
ret=pthread_create(&r1,NULL,receiver1,(void *)&msqid1);
if (ret!=0)
{
perror("receiver 1 create error");
printf("---receiver 1 create error\n");
exit(1);
}
//创建线程时传值message queue id 2
ret=pthread_create(&r2,NULL,receiver2,(void *)&msqid2);
if (ret!=0)
{
perror("receiver 2 create error");
printf("---receiver 2 create error\n");
exit(1);
}
pthread_join(s,NULL);
pthread_join(r1,NULL);
pthread_join(r2,NULL);
printf("MsgQ Done !!\n");
msgctl(msqid1,IPC_RMID,NULL); //ctl为control
msgctl(msqid2,IPC_RMID,NULL);
return 0;
}
主要函数与说明:
sem_init;sem_getvalue;sem_post;sem_wait;对信号量的操作
pthread_create;pthread_join;pthread_cancel;对线程的操作
msgrcv;msgsnd;msgget;msgctl;对消息队列的操作
strcat;strncmp;对字符串的操作
遇到的问题:
①如何pthread_create传两个msqid:建立结构体,有两个int,传结构体
②地址越界:msgsnd第一个参数msqid不需要强制类型转换,本身就是int
程序运行结果:
参考资料:
①CSDN:杭电操作系统实验三之利用Linux的消息队列通信机制实现两个线程间的通信
https://blog.csdn.net/qq_42276781/article/details/90672038
②CSDN:pthread_create如何传递多个参数
https://blog.csdn.net/nine_locks/article/details/48134957