每个进程都有着自己独立的地址空间,比方程序之前申请了一块内存。当调用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个任务到任务队列中。子进程不断从任务队列中获取任务打印它的值。注意父进程要记得回收子进程资源,否则这里子进程可能成为僵尸进程。最后,释放掉共享内存和信号量,它们是不会随着进程退出就自己主动释放掉的,而是作为内核资源一直存在,须要我们手动释放。

若有错误欢迎指出。

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

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

    五.共享内存(shared memory) 共享内存映射为一段可以被其他进程访问的内存.该共享内存由一个进程所创建,然后其他进程可以挂载到该共享内存中.共享内存是最快的IPC机制,但由于linux本身 ...

  2. linux进程间通信-共享内存

    转载:http://www.cnblogs.com/fangshenghui/p/4039720.html 一 共享内存介绍 共享内存可以从字面上去理解,就把一片逻辑内存共享出来,让不同的进程去访问它 ...

  3. linux 进程间通信 共享内存 mmap

    共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进程A可以即时看到进程B对共享内存中数据的更新,反 ...

  4. Linux 进程间通信 共享内存

    1.特点: 1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝.如管道当在内核空间创建以后,用户空间需要内存  拷贝,需要拷贝数据,所以效率低. 2)为了在多个进 ...

  5. linux 进程间通信 共享内存 shmat

    系统调用mmap()通过映射一个普通文件实现共享内存.系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信.也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件(这是通过shm ...

  6. Linux环境进程间通信: 共享内存

    Linux环境进程间通信: 共享内存 第一部分 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进 ...

  7. Linux IPC 共享内存

    共享内存 共享内存(shared memory)是最简单的Linux进程间通信方式之一. 使用共享内存,不同进程可以对同一块内存进行读写. 由于所有进程对共享内存的访问就和访问自己的内存空间一样,而不 ...

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

    原文:C# 进程间通信(共享内存) 进程间通信的方式有很多,常用的方式有: 1.共享内存(内存映射文件,共享内存DLL). 2.命名管道和匿名管道. 3.发送消息 本文是记录共享内存的方式进行进程间通 ...

  9. extern外部方法使用C#简单样例

    外部方法使用C#简单样例 1.添加引用using System.Runtime.InteropServices; 2.声明和实现的连接[DllImport("kernel32", ...

随机推荐

  1. 获取minist数据并转换成lmdb

    caffe本身是没有数据集的,但在data目录下有获取数据的一些脚本.MNIST,一个经典的手写数字库,包含60000个训练样本和10000个测试样本,每个样本为28*28大小的黑白图片,手写数字为0 ...

  2. Python应该怎样实现快速入门?

    作为一名Python爱好者,我也想跟大家分享分享我自学Python的一些小经验.搬来你的小板凳,听听看吧.也许,你会很有收获,也许你也走上了自学Python的不归路.开讲啦~ 首先,你要有自信心,要明 ...

  3. 从C#程序中调用非受管DLLs

    从C#程序中调用非受管DLLs 文章概要: 众所周知,.NET已经渐渐成为一种技术时尚,那么C#很自然也成为一种编程时尚.如何利用浩如烟海的Win32 API以及以前所编写的 Win32 代码已经成为 ...

  4. Spring Data Redis入门示例:基于RedisTemplate (三)

    使用底层API:RedisConnection操作Redis,需要对数据进行手动转换(String <---->byte),需要进行多数重复性工作,效率低下:org.springframe ...

  5. 20181228 模拟赛 T3 字符串游戏 strGame 博弈论 字符串

    3  字符串游戏(strGame.c/cpp/pas) 3.1  题目描述 pure 和 dirty 决定玩 T 局游戏.对于每一局游戏,有n个字符串,并且每一局游戏由K轮组成.具体规则如下:在每一轮 ...

  6. 笔试算法题(04):实现 string & memcpy & strcpy & strlen

    出题:请实现给定String的类定义: 分析:注意检查标准类构造注意事项: 解题: #include <stdio.h> #include <string.h> /** * 检 ...

  7. Python之数字

    Python之数字 int(数字)===>在Python3中,int没有范围,在Python2中,int超出范围就叫长整型(Long). 浮点运算:单精度 float 双精度 double a: ...

  8. PAT 1059. C语言竞赛

    PAT 1059. C语言竞赛 C语言竞赛是浙江大学计算机学院主持的一个欢乐的竞赛.既然竞赛主旨是为了好玩,颁奖规则也就制定得很滑稽: 冠军将赢得一份"神秘大奖"(比如很巨大的一本 ...

  9. Mybatis传递多个参数的4种方式(干货)

    Mybatis传递多个参数的4种方式(干货)-----https://blog.csdn.net/youanyyou/article/details/79406486

  10. node.js 写流 createWriteStream----由浅入深

    createWriteStream 写流 基于fs模块 let fs=require("fs"); createWriteStream创建一个写流 我们先创建一个2.txt要写入的 ...