HDU-操作系统之进程通信-内存共享/消息队列与有名信号量

这是带恶人的课内上机作业,一共两次,分别于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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值