Linux编程——生产者消费者问题

一系列的练习,通过过程控制知识,线程和进程间通信。现在我们进行综合练习。首先,我们要实践经典问题:生产者和消费者。



1概述。问题



多个生产/消费者在有界缓冲区。它使用共享内存的字节N界的循环缓冲区,使用一个字符来模拟产品,并使用一个字符来模拟一个产品的消费。当缓冲区是空的,消费者应该阻止睡眠,和生产者应该阻止睡眠时缓冲区已满。一旦有缓冲区中的一个空单元格,生产者进程写入字符的空单元和报告的编写内容和位置。当有未读的字符缓冲区中,消费者在消费过程中读取单元的特点和报告的阅读位置。生产者不能超过两次在同一单元写同样的性格,和消费者看不懂了同一字符超过同一单位两次以上。



2分析。问题



第一类、二类是生产者和消费者,它们之间的缓冲区是共享内存,首先想到一件事:一个缓冲系统的内存和V共享;由于缓冲区是一个关键资源,所以实现了互斥;生产者和消费者采用PV信号来实现同步操作的过程。



由于需要同步多个进程,所以指向进程访问缓冲区的指针也需要共享内存实现。



通用方框图:



生产商-> { } --------(共享内存)位置指针缓冲------- {位置指针} ->消费者



三.原语



过程:生产者-生产者过程,消费者-消费者过程。



通用数据结构:



缓冲区:数组{ 0。K-1 }整数;



,:0。K-1;



mdash;记录第一个空缓冲区,并记录第一次没有空缓冲区



prod_key(缓冲空间的数量),cons_key(缓冲区满数),互斥信号量(临界区);



mdash;prod_key控制缓冲区的不满,cons_key控制缓冲器不空,互斥临界区保护;



初始化prod_key = k,cons_key = 0,互斥= 1



生产者(生产者过程):



item_type项目;



{



虽然(真实)



{



生产(项目);



P(prod_key);



P(互斥);



缓冲区{ = }:=项;



在:=(在+ 1)模K;



v(互斥);



V(cons_key);



}



}



消费者(消费者过程):



item_type项目;



{



虽然(真实)



{



P(cons_key);



P(互斥);



项目:=缓冲区{ };



输出:=(out + 1)模K;



v(互斥);



V(prod_key);



消费(项目);



}



}









4.ipc操作功能



我们编写了一个函数来实现IPC信息队列、共享内存和信号量的包,以便更好地编写。



程序



报头文件:声明IPC操作函数和一些变量



文件名:IPC。



#包括



#包括



#包括



#包括



#包括



#包括



#包括



# bufsz 256定义



一组函数,用于建立或获取原型



国际get_ipc_id(char * proc_file,key_t键);



char * set_shm(key_t shm_key shm_num,int,int shm_flag);



国际set_msq(key_t msq_key,int msq_flag);



国际set_sem(key_t sem_key sem_val,int,int sem_flag);



整型(int sem_id)下;



国际上(int sem_id);



社区控制的信号灯。



semuns typedef联盟



{



Int Val;



} sem_uns;



消息结构



typedef struct msgbuf



{



长米型;



char mtext { 1 };



} msg_buf;



共享缓冲区是与消费者相关的变量的生成。



key_t buff_key;



国际buff_num;



char * buff_ptr;



产品生产商的位置。



key_t pput_key;



国际pput_num;



int * pput_ptr;



用户接受产品位置/共享指针



key_t cget_key;



国际cget_num;



int * cget_ptr;



信号生产者



key_t prod_key; / / IPC密钥标识符



国际prod_sem; / /制作人同步信号



信号/消费者相关



key_t cons_key; / / IPC密钥标识符



国际cons_sem; / /消费者同步信号



国际sem_val;



国际sem_flg;



国际shm_flg;



* *互斥体



key_t mtx_key; / / IPC密钥标识符



国际mtx_sem;



这里封装了许多IPC操作功能,包括信息队列、共享内存、创建/获取信号量和PV操作。



这里对实现过程逐一进行了分析:(在注释中)



#include ipc.h



*



* get_ipc_id()从/ proc / sysvipc /文件系统中获取IPC的ID号



* pfile:相当于 / / sysvipc /proc目录IPC文件,分别



*消息队列、SEM信号量、SHM共享内存



*键:对应于要获得的IPC ID号的键值。



* /



国际get_ipc_id(char * proc_file,key_t键)



{



文件*;



int,j;



焦线{ } { } bufsz bufsz,柱;



如果((PF = fopen(proc_file,R))= = null)



{



perror(proc文件打不开);



退出(exit_failure);



}



fgets(线、BUFSZ,PF);



而(!Feof(PF))



{



i = j = 0;



fgets(线、BUFSZ,PF);



当(行{ }=)



++;



当(=行{ }!)



柱{ J++ } = {我行+ };



柱{ J } = 000;



如果(atoi(柱)!=键)



继续;



j=0;



当(行{ }=)



++;



当(=行{ }!)



柱{ J++ } = {我行+ };



柱{ J } = 000;



我= atoi(柱);



Fclose(PF);



还我;



Fclose(PF)};



返回- 1;



}









*



信号灯上的v/v操作



* sem_id:信号数组标识符



* sem_num:信号灯数组下标



* buf结构:运行信号灯



* /



p请求



国际p_operation(int sem_id)



{



结构sembuf buf;



buf.sem_op = - 1;



buf.sem_num = 0;



buf.sem_flg = sem_undo;



如果(((SEMOP(sem_id,buf,1))<0))



{



perror(下误差);



退出(exit_failure);



返回exit_success };



}



v的释放



国际v_operation(int sem_id)



{



结构sembuf buf;



buf.sem_op = 1;



buf.sem_num = 0;



buf.sem_flg = sem_undo;



如果(((SEMOP(sem_id,buf,1))<0))



{



perror(错误);



退出(exit_failure);



返回exit_success };



}



*



* set_sem函数建立一个N信号灯信号灯



*如果成功,返回一个标识符sem_id信号的阵列



*输入参数:



*的sem_key信号阵列的核心价值



*在sem_val信号阵列的信号灯数量



*的sem_flag信号阵列的访问权限



* /



实现过程:*建立信号灯-设置初始值>返回id * /



国际set_sem(key_t sem_key sem_val,int,int sem_flg)



{



国际sem_id;



sem_uns sem_arg;



/ /测试用信号灯阵sem_key鉴定了



如果((sem_id = get_ipc_id( / / / sysvipc触发扫描电镜



{



/ / semget创建一个新的信号灯,回到sem_id



如果((sem_id = semget(sem_key,1,sem_flg))<<0)



{



perror(信号量创建错误);



退出(exit_failure);



}



初始值设置信号



sem_arg.val = sem_val;



如果(semctl(sem_id,0,setval,sem_arg)<0)



{



perror(信号量集错误);



退出(exit_failure);



}



}



返回sem_id;



}



*



* set_shm函数建立一个共享内存区和N个字节



*如果成功,返回一个指针指向的内存区域的首地址,shm_buf



*输入参数:



* shm_key共享内存的关键值



* shm_val共享内存的字节长度



* shm_flag共享内存的访问权限



* /



指针实现过程:创建共享内存映射到进程并返回。



char * set_shm(key_t shm_key shm_num,int,int shm_flg)



{



int i,shm_id;



char * shm_buf;



/ /测试的共享内存区shm_key鉴定了



如果((shm_id = get_ipc_id( / / / proc sysvipc SHM



{



/ / shmget创建一个新的共享内存的长度与shm_num字节,它的标签返回shm_id



如果((shm_id = shmget(shm_key,shm_num,shm_flg))<0)



{



perror(sharememory设置错误);



退出(exit_failure);



}



/ / shmat高度共享内存的shm_id确定指针shm_buf



如果((shm_buf =(char *)shmat(shm_id,0,0))<(char *)0)



{



perror(得到sharememory错误);



退出(exit_failure);



}



初始化共享内存区域



为(i = 0;i)



我shm_buf { } = 0; / /初始0



}









共享内存区 / / shm_key身份已经确定,和共享内存的shm_id确定附加的指针shm_buf



如果((shm_buf =(char *)shmat(shm_id,0,0))<(char *)0)



{



perror(得到sharememory错误);



退出(exit_failure);



}



返回shm_buf;



}



*



* set_msq函数建立一个消息队列



*如果成功,返回一个消息队列的msq_id标识符



*输入参数:



*的msq_key消息队列键值



* msq_flag消息队列的访问权限



* /



*实现过程:创建消息队列,返回id



国际set_msq(key_t msq_key,int msq_flg)



{



国际msq_id;



/ /测试的msq_key确定建立消息队列



如果((msq_id = get_ipc_id( / / / proc sysvipc味精



{



/ / msgget创建一个新的消息队列,返回msq_id



如果((msq_id = msgget(msq_key,msq_flg))<0)



{



perror(消息队列设置错误);



退出(exit_failure);



}



}



返回msq_id;



}



5。生产者程序的实施:



首先设置一系列的信号和共享内存,然后是操作原语,代码如下所示:



文件名:生产者。



#包括IPC的。



int main(int argc、argv char * { })



{



int率;



在命令行中,第一个参数指定进程的秒数睡眠,执行和解过程的速度。



如果(argv { 1 }!= null)



率= atoi(argv { 1 });



其他的



速率= 3;未指定为3秒



使用变量共享内存,键为任意键,但要注意键。



仅使用相同的共享内存是在另一个文件中使用相同的键。



buff_key = 101; / /缓冲任何关键



buff_num = 8; / /缓冲任何长度



pput_key = 102; / /指针关键产品生产商



pput_num = 1; / /指针数



shm_flg = ipc_creat | 0644; / /共享内存的读写权限



得到/共享内存缓冲区,缓冲区的第一buff_ptr。



buff_ptr =(char *)set_shm(buff_key,buff_num,shm_flg);



得到/把产品生产pput_ptr位置指针



pput_ptr =(int *)set_shm(pput_key,pput_num,shm_flg);



使用变量信号



prod_key = 201; / /制作人同步信号灯的钥匙



mtx_key = 202; / /互斥信号的关键



cons_key = 301; / /消费者同步信号的关键



sem_flg = ipc_creat | 0644;



同步信号发生器初始值设置为缓冲区的最大数量



sem_val = buff_num;



得到/制作人同步信号灯,引号prod_sem



prod_sem = set_sem(prod_key,sem_val,sem_flg);



消费者初始产品理想,同步信号灯初始值设置为0。



sem_val = 0;



/ /让消费者同步信号,引号cons_sem



cons_sem = set_sem(cons_key,sem_val,sem_flg);



专用生产者1的信号的初始值。



sem_val = 1;



得到/互斥信号量,引号mtx_sem



mtx_sem = set_sem(mtx_key,sem_val,sem_flg);



循环执行模拟生产者继续生产产品



(1)



{



如果缓冲区充满了生产者/阻塞



p_operation(prod_sem);



如果有一个进程要进入生产者,阻塞



p_operation(mtx_sem);



以模拟生产者产品的形式编写一个字符,将过程和报告写入字符和位置。



buff_ptr { * pput_ptr } = a + * pput_ptr;



挂起/秒/秒



睡眠(率);



printf(%d制片人:%缓冲{ %d } n



位置 /下循环



* pput_ptr =(* pput_ptr + 1)% buff_num;



唤醒/阻塞进程



v_operation(mtx_sem);



唤醒/阻塞消费者



v_operation(cons_sem);



}



返回exit_success;



}









6。消费计划的实施



作为上面的生产者,代码如下所示:



消费者*



#包括IPC的。



int main(int argc、argv char * { })



{



int率;



在命令行中,第一个参数指定进程的秒数睡眠,执行和解过程的速度。



如果(argv { 1 }!= null)



率= atoi(argv { 1 });



其他的



速率= 3;未指定为3秒



使用共享内存变量



buff_key = 101; / /缓冲任何关键



buff_num = 8; / /缓冲任何长度



cget_key = 103; / /指针的消费者选择产品的关键



cget_num = 1; / /指针数



Shm_flg = IPC_CREAT | 0644; / / shared memory read and write permissions



得到/共享内存缓冲区,缓冲区的第一buff_ptr。



buff_ptr =(char *)set_shm(buff_key,buff_num,shm_flg);



以 / /让消费者产品地址指针,cget_ptr指数



cget_ptr =(int *)set_shm(cget_key,cget_num,shm_flg);



使用变量信号



prod_key = 201; / /制作人同步信号灯的钥匙



mtx_key = 202; / /互斥信号的关键



cons_key = 301; / /消费者同步信号的关键



sem_flg = ipc_creat | 0644; / /信号操作权限



同步信号发生器初始值设置为缓冲区的最大数量



sem_val = buff_num;



得到/制作人同步信号灯,引号prod_sem



prod_sem = set_sem(prod_key,sem_val,sem_flg);



消费者初始产品理想,同步信号灯初始值设置为0。



sem_val = 0;



/ /让消费者同步信号,引号cons_sem



cons_sem = set_sem(cons_key,sem_val,sem_flg);



专用消费者1的信号的初始值。



sem_val = 1;



得到/互斥信号量,引号mtx_sem



mtx_sem = set_sem(mtx_key,sem_val,sem_flg);



消费者继续采用循环模拟产品。



当(1){



如果没有消费者阻塞



p_operation(cons_sem);



如果存在输入此用户的进程,则阻塞



p_operation(mtx_sem);



通过读取一个字符/消费者表单获取产品,报告进程号和访问字符和读取位置。



睡眠(率);



printf(%d消费者得到:% frombuffer { %d } n



读/读位置下循环



* cget_ptr =(* cget_ptr + 1)% buff_num;



唤醒/阻塞进程



v_operation(mtx_sem);



唤醒/阻塞生产者



v_operation(prod_sem);



}



返回exit_success;



}



7。写makefile



因为在这个项目中有多个文件,makefile需要方便的编制



HDRS = IPC。H



选择= - G - C



c_src =消费者。C IPC C。



c_obj =消费者。O O IPC。



p_src =生产者。C IPC C。



p_obj =生产者。O O IPC。



所有:生产者消费者



消费者:$(c_obj)



海湾合作委员会(c_obj)-消费者美元



消费者。O(c_src美元)美元(HDRS)



GCC $(选择)$(c_src)



制作人:$(p_obj)



海湾合作委员会(p_obj)-生产者美元



制片人:$(p_src)$(HDRS)



GCC $(选择)$(p_src)



清洁:



消费者生产者*



8。编译:



为使



在运行时打开多个终端窗口,输入



$ 1



另一个窗口输入:



$ 1







此时,您可以看到同步过程。