linux进程内存共享---实现生产者消费者问题

本文探讨了使用进程信号量实现生产者消费者模型的方法,并指出错误代码中存在的问题,如未正确处理子进程导致僵尸进程的出现及进程间数据共享的误区。通过引入共享内存技术解决了进程间的数据共享问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

操作系统实验三

要求:使用进程信号量实现生产者消费者

错误代码:

#include<semaphore.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<errno.h>
#include<stdlib.h>
struct Product{
int num;
struct Product *next;
};
struct Product *head;


int main(){
sem_t sem;
if(sem_init(&sem,1,1)<0){
perror("sem init error");
exit(0);  
}


struct Product *p;
int i=fork(),loop=10,k;
printf("i=%d\n",i);
//child Customer
if(i==0){
for(k=0;k<loop;k++){
sem_wait(&sem);

if(head==NULL)
sleep(3);  

p=head;
head=p->next;
printf("sell one NO.%d\n",p->num);
sem_post(&sem);
}
exit(0);  
}
//parent producer
for(k=0;k<loop;k++){
sem_wait(&sem);
p=malloc(sizeof(struct Product));
p->num=rand()%1000+1;
p->next=head;
head=p;
printf("produce one NO.%d\n",p->num);
sem_post(&sem);
sleep(1);
}
exit(0);  
}

错误:

1.父进程没有等待处理子进程

如果子进程在父进程前结束

(虽然exit,但为了方便父进程查看子进程结束时状态信息即怎么结束的等,仍会保留一些状态==zombie)

变成僵尸进程,

否则子进程则由init 进程领养;

2.注意与多线程下实现生产者消费者不同,进程间不会共享Product这个结构体

所以子进程中head指针一直为NULL  .................................

原因:

使用fork时 

子进程复制了父进程的堆栈段,数据段但不包括正文段,共用相同的正文段(代码段)(所以会执行同样的代码)

当父子进程有一个想要修改数据或者堆栈时,两个进程真正分裂。(复制方式 :搜索 写时拷贝技术(copy-on-write)

所以这里变量实际是各自独立保存在物理空间的,信号量不是(创建时pshared==1 ,设置为共享);


//----------------------------------------------修改--------------------------------------------------

方法一:

使用IPC(Inter-Process Communication,进程间通信)对象和有名POSIX信号量

函数介绍:

1:shmget shm (share memory)

<span style="font-size:14px;">#include<sys/ipc.h>
#include<sys/shm.h>
int shmget(key_t key,size_t size,int shmflg)</span>


功能:分配一块共享内存 

返回值:

调用成功返回一个shmid(类似打开一个或创建一个文件获得的文件描述符一样);
调用失败返回-1。

参数说明:

<1>key标识共享内存的键值(就像文件的标识是文件名):0  / IPC_PRIVATE。

当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;

如果key的取值为0,而参数shmflg中设置了IPC_CREATE这个标志,则同样创建一块新的共享内存。

通过这种方式分配的共享内存,一般用来亲缘关系的进程间通信。

<2>size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一个进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE = 4096字节)。

<3>shmflg有效的标志包括IPC_CREAT 和IPC_EXCL,他们的功能与open()的O_CREAT和O_EXCL相当。

IPC_CREAT      如果共享内存不存在,则创建一个共享内存,否则直接打开已存在的
IPC_EXCL        只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误
2:shmat   shmat(activate)

void * shmat(int shmid,const void * shmaddr,int shmflg)

函数shmat将标识号为shmid共享内存映射到调用进程的地址空间中。

参数说明:

shmid  :  要映射的共享内存区标识符

shmaddr  :  将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)

shmflg  :  SHM_RDONLY  共享内存只读
                默认0:共享内存可读写。

返回值 :调用成功放回映射后的地址 ,出错放回(void *)-1;
3.shmdt (deactive)

int shmdt(const void * shmaddr)

注意:当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段, 而是把相关shmid_ds结构的shm_nattch域的值减1 当这个值为0时,内核才从物理上删除这个共享段


4.shmctl(control)

int shmctl(int shmid,int cmd,struct shmid_ds *buf)
参数说明:


shmid  共享内存标识ID


cmd      IPC_STAT得到共享内存的状态
              IPC_SET改变共享内存的状态
              IPC_RMID删除共享内存


buf  是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定;

注意:


1.IPC_RMID命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生最后一个进程离开这个共享段时。


2.当cmd为IPC_RMID时,第三个参数应为NULL。呵呵,大部分我们都是这样做,用这个函数删除共享内存。

过程:获得共享内存->建立共享->取消共享->删除内存


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值