Linux进程间通信 共享内存+信号量+简单例子

每一个进程都有着自己独立的地址空间,比如程序之前申请了一块内存,当调用fork函数之后,父进程和子进程所使用的是不同的内存。因此进程间的通信,不像线程间通信那么简单。但是共享内存编程接口可以让一个进程使用一个公共的内存区段,这样我们便能轻易的实现进程间的通信了(当然对于此内存区段的访问还是要控制好的)。

共享内存实现进程通信的优点:

共享内存是进程通信方式中最快速的方式之一,它的快速体现在,为数据共享而进行的复制非常少。这里举例来说,使用消息队列时,一个进程向消息队列写入消息时,这里有一次数据的复制,从用户空间到内核空间。而另一个进程读取消息的时候,又有一次数据的复制,从内核空间到用户空间,这无疑是耗费时间与资源的。然而共享内存的存在则无需这两次复制,进程是从它们各自的地址空间直接访问共享的内存区段的。

缺点:

还是拿消息队列来对比,消息队列已经实现了对自身读写的保护,然而共享内存需要我们开发者自己来实现,这无疑增加了开发的难度

下面我们还是沿袭之前的风格,先简要介绍GNU/LINUX中提供哪些关于共享内存的编程接口,再以一个简单的小例子收尾。

#include<sys/shm,h>

int   shmget(key_t  key, size_t size,int flag)

该函数用于创建一个新的共享内存区段,或者是获取一个已经存在的共享内存区段

返回一个id(描述符),该id唯一表示系统中创建的这段内存

key可以是一个非0的值,也可以指定为IPC_PRIVATE,跟信号量一样,IPC_PRIVATE指明创建的是一个私有的内存区段,这样的话其它进程无法找到它,通常在仅需要一个进程组

的内部进行访问时,会使用这种方法。

size是创建的内存区段的大小,因为内存区段是创建在内存页面上的,它的容量上限通常为4MB,视实际环境决定。

flag,该参数通常是由两部分组成,一部分是访问权限,一部分是指令。指定具有三种情况,一种是创建一个共享内存区段,那么设置为IPC_CREAT即可,如果该内存区段以存在时我们需要返回一个错误的话,那么传入IPC_CREAT|IPC_EXCL,在已经存在的情况下,会返回一个错误,并且将errno置为EEXIST.第三种是获取已经存在的内存区段,那么传入0即可。关于权限,一般情况下我们设置0666或者0600就行了,它的具体值和意义如下所示:

0400   用户拥有读取权限

0200   用户拥有写入权限

0040   用户组拥有读取权限

0020   用户组拥有写入权限

0004   其他用户拥有读取权限

0002   其他用户拥有写入权限

int shmctl(int shmid,int cmd,struct shmid_ds* buf)

操作成功返回0,否则返回错误值

shmid,要操作的共享内存区段的描述符

cmd,要进行操作的指令

shmid_ds 用于获取共享内存数据结构体,该结构体里存储关于该段内存的所有信息,这里不细说了,大家可以百度了解一下。

该函数通常是用于完成三个功能,一是cmd为IPC_STAT,读取当前的共享内存数据结构体,二是cmd为IPC_SET,用于写入共享内存结构体。三是传入IPC_RMID,用于移除该段内存

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

返回值是共享内存地址映射在该进程内存地址的起始地址,为-1时,挂接失败。

shmid,共享内存的描述符

shmaddr,指定共享内存地址映射在进程内存地址的什么位置,置为NULL时,让内核自己决定。

flag,如果为SHM_READONLY,那么在调用进程中将以只读方式挂接内存区段,传入0时(不指定时)将以可读写方式挂接。

int shmdt(void* shmaddr)

该函数作用是脱离内存区段,取消从共享内存区段想进程的局部空间的映射,从而也释放了为了挂接区段而占用的局部空间地址。成功返回0,失败返回-1.

shmaddr,是调用shmat时返回的进程空间地址(也就是共享内存地址映射在进程空间的地址)

共享内存的基本操作介绍完了,在文章开头提到过,共享内存需要我们进行读写控制,这里我采用信号量,(如果对于信号量不清楚的,可以去看我之前写的一篇文章用信号量和Posix线程操作来实现双线程高速下载)下面提供一个小例子:

1  父进程负责将数据写入到共享内存中,如果共享内存数据量满了则等待

2  子进程负责从共享内存中将数据读出来,如果没有数据,则等待

有点任务队列驱动的进程池的意思,下面是源代码:

#include <sys/shm.h>

#include <sys/sem.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <wait.h>

#define MAX_SIZE 100

#define SHM_KEY_T 9494

#define SEM_KEY_T 8989

struct SHM_BLOCK{

int semid;

int dataCount;

int beginPos;

int curPos;

char data[MAX_SIZE];

};

void childProcess();

void parentProcess();

SHM_BLOCK* block; //下面要用到的指向共享内存的指针

struct sembuf buf; //操作信号量的结构体

int  pid;//用于存储子进程的pid

int shmid;//共享内存的id

int main()

{

shmid=shmget(SHM_KEY_T,sizeof(SHM_BLOCK),0600|IPC_CREAT);

block=(SHM_BLOCK*)shmat(shmid,(const void*)0,0);

block->semid=semget(SEM_KEY_T,1,0600|IPC_CREAT);

block->dataCount=0;

block->beginPos=0;

block->curPos=0;

semctl(block->semid,0,SETVAL,1); //初始化信号量,赋值为1

pid=fork();

if(pid==0)

childProcess();

else

parentProcess();

return 0;

}

void childProcess(){

printf("Im child  Process pid is %d lets begin do work\n",getpid());

getchar();

int taskCount=0;

char task;

block=(SHM_BLOCK*)shmat(shmid,(const void*)0,0);

buf.sem_flg=0;

buf.sem_num=0;

while(taskCount<200){

buf.sem_op=-1;

semop(block->semid,&buf,1);

buf.sem_op=1;

if(block->dataCount==0){

semop(block->semid,&buf,1);

continue;

}

block->dataCount--;

task=block->data[block->beginPos++];

block->beginPos%=MAX_SIZE;

semop(block->semid,&buf,1);

taskCount++;

printf("Cur Task is %d   the data is %c\n",taskCount,task);

}

shmdt((const void*)block);

return ;

}

void parentProcess(){

printf("Im parent Process pid is %d lets begin add work\n",getpid());

getchar();

buf.sem_flg=0;

buf.sem_num=0;

int taskCount=0;

while(taskCount<200){

buf.sem_op=-1;

semop(block->semid,&buf,1);

buf.sem_op=1;

if(block->dataCount>=MAX_SIZE)

{

semop(block->semid,&buf,1);

continue;

}

block->dataCount++;

block->data[block->curPos++]=‘a‘+taskCount%26;

block->curPos%=MAX_SIZE;

semop(block->semid,&buf,1);

taskCount++;

}

//回收进程资源

waitpid(pid,NULL,0);

//释放调信号量和共享内存

semctl(block->semid,0,IPC_RMID);

shmdt((const void*)block);

shmctl(shmid,IPC_RMID,0);

return ;

}

运行截图就不贴了,父进程添加200个任务到任务队列中,子进程不断从任务队列中获取任务打印它的值。注意父进程要记得回收子进程资源,否则这里子进程可能成为僵尸进程。最后,释放掉共享内存和信号量,它们是不会随着进程退出就自动释放掉的,而是作为内核资源一直存在,需要我们手动释放。

若有错误欢迎指出。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 05:42:24

Linux进程间通信 共享内存+信号量+简单例子的相关文章

Linux进程间通信 共享内存+信号量+简单样例

每个进程都有着自己独立的地址空间,比方程序之前申请了一块内存.当调用fork函数之后.父进程和子进程所使用的是不同的内存. 因此进程间的通信,不像线程间通信那么简单.可是共享内存编程接口能够让一个进程使用一个公共的内存区段,这样我们便能轻易的实现进程间的通信了(当然对于此内存区段的訪问还是要控制好的). 共享内存实现进程通信的长处: 共享内存是进程通信方式中最高速的方式之中的一个,它的高速体如今,为数据共享而进行的复制很少.这里举例来说.使用消息队列时.一个进程向消息队列写入消息时.这里有一次数

Linux进程间通信——共享内存

下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样.而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程. 特别提醒:共

Linux进程间通信—共享内存

五.共享内存(shared memory) 共享内存映射为一段可以被其他进程访问的内存.该共享内存由一个进程所创建,然后其他进程可以挂载到该共享内存中.共享内存是最快的IPC机制,但由于linux本身不能实现对其同步控制,需要用户程序进行并发访问控制,因此它一般结合了其他通信机制实现了进程间的通信,例如信号量. 共享内存与多线程共享global data和heap类似.一个进程可以将自己内存空间中的一部分拿出来,允许其它进程读写.当使用共享内存的时候,我们要注意同步的问题.我们可以使用 sema

Linux进程间通信 -- 共享内存 shmget()、shmat()、shmdt()、shmctl()

下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式.不同进程之间共享的内存通常安排为同一段物理内存.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样.而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程. 特别提醒

Linux进程间通信--共享内存

一.共享内存定义 (百度百科)共享内存指在多处理器的计算机系统中,可以被不同中央处理器访问的大量内存.由于多个CPU需要快速访问存储器,这样就要对存储器进程缓存.任何一个缓存的数据被更新后,由于其他处理器也可能要存取,共享内存就需要立即更新,否则,不同的处理器可能用到不同的数据. 在Linux系统中,共享内存允许一个进程或多个进程共享一个给定的存储区(共享内存).不同进程之间共享的内存通常安排为同一段物理地址.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址

Linux 进程间通信 - 共享内存shmget方式

共享内存区域是被多个进程共享的一部分物理内存.如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信.共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容.这块共享虚拟内存的页面,出现在每一个共享该页面的进程的页表中.但是它不需要在所有进程的虚拟内存中都有相同的虚拟地址 以下是使用共享内存机制进行进程间通信的基本操作: 需要包含的头文件: #include

Linux --进程间通信--共享内存

一.共享内存 共享内存是最高效的通信方式,因为不需要一个进程先拷贝到内核,另一个进程在存内核中读取. 二. ipcs -m 查看共享内存 ipcrm -m 删除共享内存 三.主要函数 shmget 创建 shmctl 删除 shmat 挂接 shmdt 取消挂接 ********* man 函数名 查看***** 四.代码实现 comm.h   1 #pragma once   2 #include<stdio.h>   3 #include<stdlib.h>   4 #incl

Linux下共享内存+信号量实现

sem1.c 1 #include"unistd.h" 2 #include"string.h" 3 #include"stdio.h" 4 #include"stdlib.h" 5 #include"linux/types.h" 6 #include"linux/sem.h" 7 #include"linux/ipc.h" 8 #include"linux

C# 进程间通信(共享内存)

原文:C# 进程间通信(共享内存) 进程间通信的方式有很多,常用的方式有: 1.共享内存(内存映射文件,共享内存DLL). 2.命名管道和匿名管道. 3.发送消息 本文是记录共享内存的方式进行进程间通信,首先要建立一个进程间共享的内存地址,创建好共享内存地址后,一个进程向地址中写入数据,另外的进程从地址中读取数据. 在数据的读写的过程中要进行进程间的同步. 进程间数据同步可以有以下的方式 1. 互斥量Mutex 2. 信号量Semaphore 3. 事件Event 本文中进程间的同步采用 信号量