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
…
此时,您可以看到同步过程。