IPC(SystemV) 之 共享内存

很久以来我都是只闻其名,未见其形.终于在这次系统的学习linux编程中接触到了共享内存.果然很牛.

上一篇文章中我们讲的信号量,个人感觉,严格的说,信号量只是进城通信的辅助.而共享内存才真正实现了进程通信.

共享内存机制允许两个不想关的进程访问同一段物理内存,当然得是一台主机.

头文件<sys/shm.h>   和信号量的情况一样,也需要包含sys/types.h 和 sys/ipc.h .当然有可能已经包含在了sys/shm.h中了.

共享内存函数如下:

int shmget(key_t key, size_t size, int shm_flg);

void * shmat(int shm_id, void * addr, int shmflg);

int shmdt(const void * addr);

int shmctl(int shm_id, int command, struct shmid_ds *buf);

其实IPC系列的函数很多地方都类似.

shmget   通过指定一个唯一的key来创建一个唯一的共享内存的标记.size指定了共享内存的大小.shm_flg指定了权限和动作.低九位是权限,其它位可取IPC_CREAT IPC_EXCL.当然key同样可以是IPC_PRIVATE,也就是说可以指定为进程内部的共享.如果单纯指定IPC_CREAT,那么如果共享内存已经存在则获取,如果另外指定了IPC_EXCL,则当共享内存存在时会出错.

shmget的返回值,当成功的时候返回共享内存的标记,失败返回-1

shmat   为共享内存分配一段真实可用的物理内存,并且返回一个指向这段内存的指针.第一个参数shm_id 即为shmget的返回值,第二个参数addr, 指定物理内存的地址.如果为空,则表示由系统分配.第三个参数指定进程对这段物理内存的权限.常用的权限包括SHM_RND,这个参数一般在第二个参数为用户指定的时候会使用这个参数.SHM_RDONLY,当前进程对物理内存仅有读权限.如果想要对共享内存可写,只要指定这个参数为0即可.也就是说,不是只读即可写.

shmat的返回值,成功的时候返回指向的物理内存,如果失败返回NULL.

shmdt   前面的at我们可以理解成是attach,即绑定.这个函数执行与shmat相反的操作---解绑定,即dettach.如果进程调用了这个函数,则通过使用共享内存标记将无法再对物理内存进行访问.

shmdt的返回值,当成功是返回0, 失败返回-1.

shmctl   每个IPC都有这样一个函数,它用来了解实时的IPC状态,设置IPC值和清理IPC.首先第一个参数还是共享内存的标记.第二个参数指定对共享内存采取的动作.第三个参数根据第二个参数的动作来决定填哪些字段.一般而言,使用这个函数最多的就是删除共享内存.

shmctl返回值, 成功是返回0, 失败时返回-1.

介绍完这四个函数,我们来设计一个小场景.比如一个老人临死前留下了100块钱的家产,不允许三兄弟评分,但是当三兄弟需要用钱的时候,可以过来拿.当然三兄弟之间不能冲突.不然可能会把钱弄丢了.我们来试着实现一下这个场景.

好吧,同样是信号量时候类似的问题.我又出错了.总结一下自己写遇到的问题.

1. 我过早的调用了shmctl,以至于其它的进程获取到的内容已经不是我存放在共享内存里面的内容了.也就是说,shmctl一定要在所有进程都使用完共享内存才可以.我自己的乌龙.

2. 一般而言,不需要使用IPC_EXCL这个标记,因为如果存在就使用,如果不存在就创建.

3. 同样是对等的用户,如果root创建了共享内存,而用普通用户去试着修改,肯定会报权限问题.

代码如下:

father.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/shm.h>

#define MY_LEGACY 100
#define MAXLEN 10

int main(int argc, char ** argv)
{
    int ret;

    int shmid;
    int *legacy = NULL;

    shmid = shmget((key_t)123456, 4, 0666 | IPC_CREAT);
    if (shmid < 0)
    {
        perror("oh, i am not die\n");
        exit(EXIT_FAILURE);
    }

    legacy = (int *)shmat(shmid, NULL, 0);
    if (NULL == legacy)
    {
        perror("\n");
        exit(EXIT_FAILURE);
    }

    *legacy = MY_LEGACY;
    printf("my legacy %d is up to you\n", *legacy);

    shmdt(legacy);
//    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

老子进程做的事情就是把钱存起来.供儿子挥霍.

son.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/shm.h>
#include <sys/sem.h>

union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};

int main(int argc, char ** argv)
{
    if (argc < 2)
    {
        printf("usage : which son of the poor old man you are\n");
        return 0;
    }

    int ret;
    int cost = 1;

    int shmid;
    int *legacy = NULL;

    int semid;
    struct sembuf sem_p;
    struct sembuf sem_v;
    union semun sem_un;

    semid  = semget((key_t)123456789, 1, 0666 | IPC_CREAT);
    if (semid < 0)
    {
        perror("\n");
        exit(EXIT_FAILURE);
    }

    sem_un.val = 1;
    ret = semctl(semid, 0, SETVAL, sem_un);

    sem_p.sem_num = 0;
    sem_p.sem_op = -1;
    sem_p.sem_flg = SEM_UNDO;

    sem_v.sem_num = 0;
    sem_v.sem_op = 1;
    sem_v.sem_flg = SEM_UNDO;

    shmid = shmget((key_t)123456, 4, 0666|IPC_CREAT);
    if (shmid < 0)
    {
        perror("\n");
        exit(EXIT_FAILURE);
    }

    srand(time(NULL));
    legacy = (int *)shmat(shmid, NULL, 0);
    while (cost)
    {
        semop(semid, &sem_p, 1);

        if (*legacy <= 0)
        {
            break;
        }
        else
        {
            int spend = rand() % 10;
            *legacy = *legacy - spend;
            printf("son %s have spend %d, left %d \n",
                    argv[1], spend, *legacy);
        }

        sleep(1);
        semop(semid, &sem_v, 1);
    }

    shmdt((void*)legacy);
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

儿子果然很不争气的争着抢着去花老子的钱.银行怕出问题,所以用信号量给他们维护秩序.

看下运行结果:

首先是老子存钱:

然后是儿子花钱:

唉,这个有点问题,这没出息的儿子透支了.主要是使用了随机数,所以如果判断当前还有五块钱,这是大于0的,但是如果随机数生成了10块,自然就会透支5块.我就不修改了.同学们在学习的过程中还是应该实地的去敲一下代码加深印象,如果能够自己去设想一个场景然后实现,相信效果会更好.本文都是一些入门的基础,希望对大家有所帮助.

时间: 2024-12-05 21:21:54

IPC(SystemV) 之 共享内存的相关文章

Linux IPC实践(8) --共享内存/内存映射

概述 共享内存区是最快的IPC形式.一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图). 共享内存 VS. 其他IPC形式 用管道/消息队列传递数据 用共享内存传递数据 共享内存生成之后,传递数据并不需要再走Linux内核,共享内存允许两个或多个进程共享一个给定的存储区域,数据并不需要在多个进程之间进行复制,因此,共享内存的传输速度更快! mmap内存映射 将文件/设备空间映射到共享内存区 #incl

【UNIX】什么是IPC对象以及共享内存

sys V 的IPC 对象:对于内核中创建的文件对象,就是文件标识符(它引用了文件对象的全部信息)在进程中文件描述符一般使用当前最小可用值. 对于IPC标识符返回的是索引的整数值,它是全局变量的流水号,在系统中唯一分配的,若果在创建的IPC对象没有关闭,他会全局存在,只有在系统关闭的时候才关闭,这样会造成内存空间的资源被占用. 为了进程之间交换信息,内核专门留了一块内存空间,由进程映射到各自进程私有空间. [共享内存的实现]: 1)创建/打开共享内存,这里的创建/打开和文件的创建/打开差不多,只

IPC通信_共享内存

共享内存允许两个或多个进程共享一个给定的存储区,就是多个进程将同一块物理内存映射到自己的虚拟地址上.因为不需要在客户进程和服务进程之间拷贝,所以是最快的一种IPC. 函数1 #include <sys/shm.h> int shmget(key_t key, size_t size, int flag); 该函数转换键值获取共享内存ID, key键值的获取还是通过ftok函数. 返回值:成功返回共享内存ID, 失败返回-1 参数key:键值(key) 参数size:请求的共享内存的长度,单位字

进程间通信(IPC)之————共享内存

一. 共享内存 在系统中,两个不同的进程都会维护自己的一块地址空间,这个地址空间一般是虚拟地址,会通过mmu和页表映射到对应的物理内存中,因为不同的进程会有不同的内存空间,因此两个进程之间是无法看见彼此的数据的,而共享内存就是使两个进程看到同一块地址空间,以此来实现不同进程间的数据交互. 值得提出的是,共享内存是进程间通信方式中最高效的一种,因为是直接通过访问内存来交换数据的,省去了消息队列中数据的复制和信号量中进行P.V操作所占用的时间. 二. 共享内存中的函数 共享内存的创建与销毁 创建:

Unix IPC之基于共享内存的计数器

目的 本文主要实现一个基于共享内存的计数器,通过父子进程对其访问. 本文程序需基于<<Unix网络编程-卷2>>的环境才能运行.程序中大写开头的函数为其小写同名函数的包裹函数,增加了错误处理信息. 1 函数介绍 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <sys/mman> /**  * Map addresses starting near ADDR an

System V IPC 之共享内存

IPC 是进程间通信(Interprocess Communication)的缩写,通常指允许用户态进程执行系列操作的一组机制: 通过信号量与其他进程进行同步 向其他进程发送消息或者从其他进程接收消息 和其他进程共享一段内存区 System V IPC 最初是在一个名为 "Columbus Unix" 的开发版 Unix 变种中引入的,之后在 AT&T 的 System III 中采用.现在在大部分 Unix 系统 (包括 Linux) 中都可以找到. IPC 资源包含信号量.

Java共享内存

1   共享内存对应应用开发的意义 对熟知UNIX系统应用开发的程序员来说,IPC(InterProcess   Communication)机制是非常熟悉的,IPC基本包括共享内存.信号灯操作.消息队列.信号处理等部分,是开发应用中非常重要的必不可少的工具.其中共享内存IPC机制的关键,对于数据共享.系统快速查询.动态配置.减少资源耗费等均有独到的优点. 对应UNIX系统来说,共享内存分为一般共享内存和映像文件共享内存两种,而对应   Windows,实际上只有映像文件共享内存一种.所以jav

Linux进程IPC浅析[进程间通信SystemV共享内存]

Linux进程IPC浅析[进程间通信SystemV共享内存] 共享内存概念,概述 共享内存的相关函数 共享内存概念,概述: 共享内存区域是被多个进程共享的一部分物理内存 多个进程都可把该共享内存映射到自己的虚拟内存空间,所有用户空间的进程若要操作共享内存,都要将其映射到自己的虚拟内存空间中,通过映射的虚拟内存空间地址去操作共享内存,从而达到进程间的数据通信 共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容 本身不提供同

Linux 进程间共享内存 SYSTEMV

#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, int size, int shmflag) key取值为IPC_PRIVATE时,shmflag应为IPC_CREAT,则新建共享内存key取值不为IPC_PRIVATE则应为已创建的key值,shmflag不应包含IPC_CREAT和IPC_EXCL,且大小小于等于原共享内存大小成功则返回共享内存id,失败返回-1 void *shmat(int sh