进程间通讯(3)——共享内存和信号量

【4】共享内存

(1)概述

  • 两个不一样进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间,进程A能够即时看到进程B对共享内存中数据的更新,反之,进程B也能够即时看到进程A对共享内存中数据的更新。c++

  • 共享内存是存在于内核级别的一种资源shell

  • 在系统内核为一个进程分配内存地址时,经过分页机制可让一个进程的物理地址不连续,同时也可让一段内存同时分配给不一样的进程。共享内存机制就是经过该原理来实现的,共享内存机制只是提供数据的传送,如何控制服务器端和客户端的读和写操做互斥,这就须要一些其余的辅助工具,例如信号量的概念。数组

  • 共享内存能够说是Linux下最快速、最有效的进程间通讯方式服务器

  • 由于进程能够直接读写内存,而不须要任何数据的拷贝
  • 管道和消息队列等通讯方式,须要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据:一次从输入文件到共享内存区,另外一次从共享内存区到输出文件
  • 进程之间在共享内存时,并不老是读写少许数据后就解除映射,有新的通讯时,再从新创建共享内存区域。而是保持共享区域,直到通讯完毕为止,这样,数据内容一直保存在共享内存中,并无写回文件。共享内存中的内容每每是在解除映射时才写回文件的
  • 共享内存的不足之处:因为多个进程对同一块内存区域具备访问的权限,各个进程间的同步问题尤其重要。必须控制同一时刻只有一个进程对共享内存区域写入数据,不然将形成数据的混乱。同步控制问题能够经过下一节介绍的信号量来解决。

(2)共享内存相关操做

用于Linux进程通讯共享内存。共享内存函数由shmget、shmat、shmdt、shmctl四个函数组成。ide

shmat函数原型函数

  • shmat(把共享内存区对象映射到调用进程的地址空间)工具

  • 所需头文件命令行

    #include <sys/types.h>
      #include <sys/shm.h>
  • 函数说明线程

    链接共享内存标识符为shmid的共享内存,链接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间同样访问
  • 函数原型code

    void *shmat(int shmid, const void *shmaddr, int shmflg)
  • 函数传入值

    shmid   共享内存标识符
      shmaddr  指定共享内存出如今进程内存地址的什么位置,直接指定为NULL让内核本身决定一个合适的地址位置
      shmflg   SHM_RDONLY:为只读模式,其余为读写模式
  • 函数返回值

    成功:附加好的共享内存地址
      出错:-1,错误缘由存于errno中

shmget()

  • 建立或打开共享内存的函数

    #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/shm.h>
      int shmget(key_t key,int size,int flag);

返回值:成功返回共享内存ID,出错返回-1

key:建立或打开的共享内存的键值

size:共享内存区域大小,只在建立一个新的共享内存时生效

flag:调用函数的操做类型,也可用于设置共享内存的访问权限,二者经过逻辑或表示

shmdt函数原型

  • shmdt(断开共享内存链接)

  • 所需头文件

    #include <sys/types.h>
      #include <sys/shm.h>
  • 函数说明

    与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
  • 函数原型

    int shmdt(const void *shmaddr)
  • 函数传入值

    shmaddr:链接的共享内存的起始地址
  • 函数返回值

    成功:0
      出错:-1,错误缘由存于error中

(3)使用shmget函数建立共享内存的例子,create_shm.c:

程序中在调用shmget函数时指定key参数值为IPC_PRIVATE,这个参数的意义是建立一个新的共享内存区,当建立成功后使用shell命令ipcs来显示目前系统下共享内存的状态。命令参数-m为只显示共享内存的状态。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFSZ 4096
int main(void)
{
        int shm_id;/*共享内存标识符*/
        shm_id = shmget(IPC_PRIVATE,BUFSZ,0666);
        /*建立共享内存*/
        if(shm_id < 0)
        {
                printf("shmget failed!\n");
                exit(1);/*shmget出错退出*/
        }
        printf("create a shared memory segment successfully:%d\n",shm_id);
        system("ipcs -m");/*调用ipcs命令查看IPC*/
        exit(0);
}

运行结果:

hyx@hyx-virtual-machine:~/test$ ./create_shm
create a shared memory segment successfully:2949133

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     链接数  状态      
0x00000000 65536      hyx        600        524288     2          目标       
0x00000000 1114113    hyx        600        524288     2          目标       
0x00000000 196610     hyx        600        524288     2          目标       
0x00000000 393219     hyx        600        524288     2          目标       
0x00000000 950276     hyx        600        524288     2          目标       
0x00000000 589829     hyx        600        524288     2          目标       
0x00000000 622598     hyx        600        16777216   2                       
0x00000000 655367     hyx        600        16777216   2          目标       
0x00000000 753672     hyx        600        524288     2          目标       
0x00000000 819209     hyx        600        2097152    2          目标       
0x00000000 1146890    hyx        600        1048576    2          目标       
0x00000000 1245195    hyx        600        524288     2          目标       
0x00000000 1933324    hyx        600        67108864   2          目标       
0x00000000 2949133    hyx        666        4096       0

执行ipcs,打印共享内存,信号量和消息队列的信息:

hyx@hyx-virtual-machine:~/test$ ipcs

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     链接数  状态      
0x00000000 65536      hyx        600        524288     2          目标       
0x00000000 1114113    hyx        600        524288     2          目标       
0x00000000 196610     hyx        600        524288     2          目标       
0x00000000 393219     hyx        600        524288     2          目标       
0x00000000 950276     hyx        600        524288     2          目标       
0x00000000 589829     hyx        600        524288     2          目标       
0x00000000 622598     hyx        600        16777216   2                       
0x00000000 655367     hyx        600        16777216   2          目标       
0x00000000 753672     hyx        600        524288     2          目标       
0x00000000 819209     hyx        600        2097152    2          目标       
0x00000000 1146890    hyx        600        1048576    2          目标       
0x00000000 1245195    hyx        600        524288     2          目标       
0x00000000 1933324    hyx        600        67108864   2          目标       
0x00000000 2949133    hyx        666        4096       0                       

--------- 信号量数组 -----------
键        semid      拥有者  权限     nsems     

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息

执行ipcs -m,只打印共享内存段信息:

hyx@hyx-virtual-machine:~/test$ ipcs -m

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     链接数  状态      
0x00000000 65536      hyx        600        524288     2          目标       
0x00000000 1114113    hyx        600        524288     2          目标       
0x00000000 196610     hyx        600        524288     2          目标       
0x00000000 393219     hyx        600        524288     2          目标       
0x00000000 950276     hyx        600        524288     2          目标       
0x00000000 589829     hyx        600        524288     2          目标       
0x00000000 622598     hyx        600        16777216   2                       
0x00000000 655367     hyx        600        16777216   2          目标       
0x00000000 753672     hyx        600        524288     2          目标       
0x00000000 819209     hyx        600        2097152    2          目标       
0x00000000 1146890    hyx        600        1048576    2          目标       
0x00000000 1245195    hyx        600        524288     2          目标       
0x00000000 1933324    hyx        600        67108864   2          目标       
0x00000000 2949133    hyx        666        4096       0                       

hyx@hyx-virtual-machine:~/test$

(4)使用共享内存进行通讯的实例

共享内存ID以命令行参数的形式传递给进程。

write_shm.c是写共享内存,即分10次向共享内存中写入people结构体的成员数据,(姓名和年龄)数据的值由for循环自动生成。程序的最后使用shmdt将共享内存段从当前的进程空间中脱离掉。

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct
{
        char name[4];
        int age;
}people;
int main(int argc,char** argv)
{
        int shm_id,i;
        char temp;
        people *p_map;
        if(argc != 2)/*命令行参数错误*/
        {
                printf("USAGE:atshm < d=identifier>");/*打印帮助消息*/
                exit(1);
        }
        shm_id = atoi(argv[1]);/*获得要引入的共享内存段,atoi将字符转换成整型*/
        p_map = (people *)shmat(shm_id,NULL,0);/*shmat:把共享内存区对象映射到调用进程的地址空间*/
        temp = 'a';
        for(i = 0;i < 10;i++)
        {
                temp+=1;
                memcpy((*(p_map+i)).name,&temp,1);
                (*(p_map+i)).age = 20+i;
                /*memcpy:c和c++使用的内存拷贝函数,memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标des所指的内存地址的起始位置中,void *memcpy(void *dest, const void *src, size_t n);*/
        }
        if(shmdt(p_map)==-1)
        {
                perror("detach error!\n");
        }
        return 0;
}

read_shm.c是读共享内存,即分10次从共享内存中读出people结构体的成员数据。程序的最后一样使用shmdt将共享内存段从当前的进程空间中脱离掉。

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct
{
        char name[4];
        int age;
}people;
int main(int argc,char** argv)
{
        int shm_id,i;
        people *p_map;
        if(argc != 2)
        {
                printf("USAGE:atshm <idenfifier>");
                exit(1);
        }
        shm_id = atoi(argv[1]);/*获得要引入的共享内存段,atoi将字符转换成整型*/
        p_map = (people*)shmat(shm_id,NULL,0);/*shmat:把共享内存区对象映射到调用进程的地址空间*/
        for(i=0;i<10;i++)
        {
                printf("name:%s  ",(*(p_map+i)).name);
                printf("age %d\n",(*(p_map+i)).age);
        }
        if(shmdt(p_map)==-1)
        {
                perror("detach error!\n");

        }
       return 0;
}

运行结果:

hyx@hyx-virtual-machine:~/test$ ./write_shm 2949133
hyx@hyx-virtual-machine:~/test$ ./read_shm 2949133
name:b  age 20
name:c  age 21
name:d  age 22
name:e  age 23
name:f  age 24
name:g  age 25
name:h  age 26
name:i  age 27
name:j  age 28
name:k  age 29
hyx@hyx-virtual-machine:~/test$

【5】信号量

  • 信号量的原理是一种数据操做锁的概念,它自己不具有数据交换的功能,而是经过控制其余的通讯资源(如文件、外部设备等)来实现进程间通讯。信号量自己不具有数据传输的功能,其只是一种外部资源的标识。

  • 信号量,有时也被称为信号灯,是在多进程环境下使用的一种设施,它负责协调各个进程,以保证它们可以正确、合理的使用公共资源。信号量分为单值和多值两种,前者只能被一个进程得到,后者能够被若干个进程得到。

  • 以停车场为例:假设停车场只有三个车位,一开始三个车位都为空。这时若是同时来了第五辆车,看门人容许其中三辆直接进入,而后放下车栏,剩下的车则必须在入口等待,在此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车栏,放入外面的一辆进去,若是又离开两辆,则又能够放入两辆,如此往复。

  • 车位:公共资源
  • 每辆车:一个进程
  • 看门人:信号量的做用
  • 抽象来说,信号量的特性以下:信号量是一个非负整数(车位数),全部经过它的进程/线程(车辆)都会将该整数减1(经过它是为了使用公共资源),当该整数值为时,全部试图经过它的进程都处于等待状态。在信号量上咱们定义两种操做:wait(等待)和releas(释放)。当一个进程调用wait操做时,它要么获得资源探后将信号量减1,要么一直等下去(指放入阻塞队列),直到信号量大于等于1时。release其实是在信号量上执行加1操做,对应于车辆离开停车场,该操做之因此叫作“释放”是由于释放了信号量守护的资源。
相关文章
相关标签/搜索