设计题目四:读者写者
一、设计题目要求
(1)掌握信号量通信机制,实现进程之间通过信号进行通信;
(2)掌握匿名管道及有名管道通信机制,实现进程之间通过管道进行通信;
(3)理解System V IPC通信机制工作原理;
(4)掌握共享内存、消息、管道、信号量通信实现方法。
二、实验说明
(1)利用共享主存解决读者-写者问题
(2)要求由写者创建一个共享主存,并向其中写入数据,读者进程随后从该共享区 中访问数据
三、程序设计思路及流程图
程序功能简介:
读者功能描述:有一个数据块被多个用户共享,读者部分对数据块是只读的,而且允许多个读者同时读;
写者功能描述: 写者部分对数据块是只写的,当一个写者正在向数据块写信息的时候,不允许其他用户使用,无论是读还是写。
程序设计思路:
(1)为基于共享主存解决读者写者问题,需要由写进程首先创建一个共享主存,并将该共享主存区分别映射到读者进程和写者进程的虚拟空间
(2)随后,写进程开始向共享主存写数据,读进程从共享主存区获取数据
程序流程图:
见附录A
四、涉及的背景知识及所用函数简介
1、shmat函数
函数原型 :void *shmat(int shmid, const void *shmaddr, int shmflg)
头文件 :sys/types.h sys/shm.h
作用 :将参数shmmid所指的共享内存和目前进程连接(attach)
参数 :shmaddr不为0,参数shmflg也没有指定SHM_RND旗标,则参数 shmaddr为连接地址;
shmaddr不为0,参数shmflg设置了SHM_RND旗标,则参数shmaddr 会自动调整为SHMLAB的整数倍;
shmaddr为0,核心自动选择一个地址。
返回值 :成功,返回共享内存识别代码;出错,-1,错误原因存于error中
2、pthread_create函数
头文件 :pthread.h
函数原型 :int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_rtn)(void*),void *arg);
作用 :创建一个线程
参数 :thread 待创建线程的id指针
pthread_attr_t 创建线程时的线程属性
*start_rtn(void * ) 返回值是void*类型的指针函数
arg 函数start_rtn的参数
返回值 :成功返回0;失败返回错误编号
3、pthread_join函数
函数原型 :int pthread_join( pthread_t thread, void ** rval_ptr)
头文件 :pthread.h
作用 :1)调用者将挂起并等待指定线程终止
2)当新线程调用pthread_exit()退出或者return时,进程中的其 他线可通过pthread_join()获得进程的退出状态
参数 :thread 线程的ID号
rval_ptr 线程的返回状态
返回值 :成功返回0;失败返回错误编码
4、shmctl 函数
函数原型 :int shmctl(int shmid, int cmd, struct shmid_ds *buf)
头文件 :sys/types.h sys/shm.h
作用 :完成对共享内存的控制
参数 :shmid 共享内存标识符;
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构 复制到buf中;
IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的 uid、gid、mode复制到共享内存的shmid_ds结构内;
IPC_RMID:删除这片共享内存;
buf 共享内存管理结构体,具体说明参见共享内存内核结构定义部 分。
返回值 :成功返回0,失败返回-1,错误原因存于error中。
5、pthread_mutex_init 函数
函数原型 :pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
头文件 :pthread.h
作用 :该函数用于C函数的多线程编程中,互斥锁的初始化
返回值 :函数成功完成之后会返回零,其他任何返回值都表示出现了错误。函数成功执行后,互斥锁被初始化为未锁住态。
6、pthread_mutex_lock函数
函数原型 :int pthread_mutex_lock(pthread_mutex_t *mutex);
头文件 :pthread.h
作用 :当pthread_mutex_lock()返回时,该互斥锁已被锁定。线程调用 该函数让互斥锁上锁,如果该互斥锁已被另一个线程锁定和拥有,则 调用该线程将阻塞,直到该互斥锁变为可用为止。
返回值 :在成功完成之后会返回零。其他任何返回值都表示出现了错误。如 果出现以下任一情况,该函数将失败并返回对应的值。
7、pthread_mutex_unlock函数
函数原型 :int pthread_mutex_unlock(pthread_mutex_t *mutex);
头文件 :pthread.h
作用 :释放互斥锁,与pthread_mutex_lock成对存在。
参数 :需要解锁的锁变量对象
8、pthread_mutex_destroy函数
函数原型 :int pthread_mutex_destroy(pthread_mutex_t *mutex);
头文件 :pthread.h
作用 :销毁互斥锁
参数 :mutex 指向要销毁的互斥锁的指针
返回值 :互斥锁销毁函数在执行成功后返回 0,否则返回错误码。
五、程序所用数据结构简介
pthread_mutex_t mutex, Rmutex;
互斥信号量,对缓冲区的互斥使用,读者和写者之间互斥。int counter;
记录读者的数量来与写者互斥。int num_reader, num_writer;
分别记录读者写者的数量。pthread_t threads_r[100], threads_w[100];
读者写者线程数组线程。
int shmid;
共享内存块号。
char *shmaddr;
共享内存块地址。char *shmaddread;
读者共享内存块地址。struct shmid_ds buf;
共享内存的数据结构定义。void Read_operation();
读者读数据。void Writer_the_data();
写者写数。void *reader_thread(void *arg);
读者线程执行函数。void *writer_thread(void *arg);
写者线程执行函数void Quit();
删除共享内存和数据结构,退出程序。
六、程序源代码
见附录B
七、调试方案及调试过程记录与分析
测试结果、调试过程:
第一次测试结果:使用一块缓冲区,用while(1)循环来创建线程,只能执行一次写 操作,读操作可以多次执行,读操作不符合预期结果,还要注意对信号量的初始化操作。
第一次调试:向老师请教后,得知原因应该是系统已经自己定义好了共享内存的读写规则,循环时,只能写入一次,之后写者被阻塞。所以改为由用户选择输入创建读者线程还是写着线程。
第二次测试结果:修改之后可以正常执行,结果正确。
八、程序运行结果分析
一块缓冲区可以被多个读者同时读,读到的是相同的内容;一块缓冲区只能被一个写者写。程序刚开始时,没有内容,必须要写者先写入内容,读者才可以读。通过编程实现,对于读者写者之间的互斥关系理解更深刻。
程序源代码: /* ************************************************************************************** *Project/File :main.c *By *Mail : *Status :finished *version :1.0 *Created Time :2014年10月14日星期二20时24分30秒 ************************************************************************************** *Note: *有一个数据块被多个用户共享,其中一部分用户是读者,另一部分是写者。 ************************************************************************************** */ #include"reader_writer.h" int main(void) { int choose; int i; counter = 0; num_reader = 1; num_writer = 1; while((shmid = shmget(KEY, SIZE, IPC_CREAT | 0600))== -1);//创建共享内存; printf("Welcome to the ****** Reader And Writer ******\n"); while(1) { printf("\n**********1.Create a reader, and read the memory;\n"); printf("**********2.Create a writer, and write to the memory;\n"); printf("**********3.Exit the Reader And Writer!\n"); printf("Please input your choose:\n"); scanf("%d", &choose); pthread_mutex_init(&mutex, NULL); pthread_mutex_init(&Rmutex, NULL); //初始化信号量; switch(choose) { case 1: pthread_create(&threads_r[num_reader++],NULL,reader_thread, NULL); break; case 2: pthread_create(&threads_w[num_writer++],NULL,writer_thread,NULL); break; case 3: Quit(); break; default: printf("Not find your choose, please input again!\n"); } } return 0; } /* ************************************************************************************** *Project/File :reader.c *By : *Mail : *Status :finished *version :1.0 *Created Time :2014年10月14日星期二20时24分30秒 ************************************************************************************** *Note: *读者对数据块是只读的,而且允许多个读者同时读; *当读者使用数据块时,不允许任何一个写者使用。 ************************************************************************************** */ #include"reader_writer.h" void *reader_thread(void *arg) { pthread_mutex_lock(&Rmutex);//P(Rmutex); if(counter == 0) //If counter = 0 then P(mutex); pthread_mutex_lock(&mutex); counter = counter + 1; pthread_mutex_unlock(&Rmutex); //V(Rmutex); Read_operation(); pthread_mutex_lock(&Rmutex); //P(Rmutex); if(counter == 1) //If counter = 1 then V(mutex); pthread_mutex_unlock(&mutex); counter = counter + 1; pthread_mutex_unlock(&Rmutex); //V(Rmutex); } /* ************************************************************************************** *Project/File :writer.c *By : *Mail : *Status :finished *version :1.0 *Created Time :2014年10月14日星期二20时24分30秒 ************************************************************************************** *Note: *写者对数据块是只写的,当一个写者正在向数据块写信息的时候,不允许其他用户使用,无论读者写者。 ************************************************************************************** */ #include"reader_writer.h" void *writer_thread(void *arg) { pthread_mutex_lock(&mutex); //P(mutex); Writer_the_data(); pthread_mutex_unlock(&mutex); //V(mutex); } /* ************************************************************************************** *Project/File :reader_writer.c *By : *Mail : *Status :finished *version :1.0 *Created Time :2014年10月14日星期二20时24分30秒 ************************************************************************************** *Note: *这里实现了所有.c文件所用到的函数; ************************************************************************************** */ #include"reader_writer.h" void Read_operation() { shmaddread = shmat(shmid, NULL, 0); printf("This is Reader %d, reading the share memory:%s\n", num_reader - 1, shmaddread); shmdt(shmaddread); } void Writer_the_data() { char string[100]; shmaddr = (char*)shmat(shmid, NULL, 0); strcpy(string, "Message was writing by Writer"); string[strlen(string) + 1] = '\0'; string[strlen(string)] = num_writer - 1 + '0'; strcpy(shmaddr, string); printf("Writer %d has writen to the memory!\n", num_writer - 1); shmdt(shmaddr); } void Quit() { int i; shmctl(shmid, IPC_RMID, NULL);;//销毁共享内存; pthread_mutex_destroy(&mutex); //销毁互斥信号量; pthread_mutex_destroy(&Rmutex); for(i=0;i<num_reader;i++) //销毁线程; pthread_join(threads_r[i],NULL); for(i=0;i<num_writer;i++) pthread_join(threads_w[i],NULL); exit(0); } /* ************************************************************************************** *Project/File :reader_writer.h *By : *Mail : *Status :finished *version :1.0 *Created Time :2014年10月14日星期二20时24分30秒 ************************************************************************************** *Note: *将所有.c文件所用到的头文件包含进去; *包含所有的宏定义; *全局变量的定义; *函数的声明; ************************************************************************************** */ #ifndef reader_writer_h #define reader_writer_h #include "stdio.h" #include "stdlib.h" #include "string.h" #include "pthread.h" #include "signal.h" #include "unistd.h" #include "sys/shm.h" #include "sys/ipc.h" #include "sys/types.h" #define SIZE 1024 #define KEY 1234 pthread_mutex_t mutex, Rmutex; int counter; int num_reader, num_writer; pthread_t threads_r[100], threads_w[100]; int pid; int shmid; char *shmaddr; char *shmaddread; struct shmid_ds buf; void Read_operation(); void Writer_the_data(); void *reader_thread(void *arg); void *writer_thread(void *arg); void Quit(); #endif Makefile vpath %.h ./ main:main.o reader.o writer.o reader_writer.o reader_writer.h gcc -o main -pthread main.o reader.o writer.o reader_writer.o main.o:main.c reader.c writer.c reader_writer.c reader_writer.h gcc -c main.c reader.c writer.c reader_writer.c reader.o:reader.c reader_writer.c reader_writer.h gcc -c reader.c writer.c reader_writer.c writer.o:writer.c reader_writer.c reader_writer.h gcc -c writer.c reader_writer.c reader_writer.o:reader_writer.c reader_writer.h gcc -c reader_writer.c