精华内容
下载资源
问答
  • 进程间通信:共享内存概念及代码

    千次阅读 2020-03-10 15:46:15
    前言 接下讨论的IPC机制,它们最初由System V版本的Unix引入。由于这些机制都出现在同一个版本中并且有着相似的编程接口,所以它们被称为System V IPC机制。接下来的内容包括: ...共享内存是在两个正在运行...

    前言

    接下讨论的IPC机制,它们最初由System V版本的Unix引入。由于这些机制都出现在同一个版本中并且有着相似的编程接口,所以它们被称为System V IPC机制。接下来的内容包括:

    信号量:用于管理对资源的访问。

    共享内存:用于在程序之间高效地共享数据。

    消息队列:在程序之间传递数据。

    操作系统中的同步和异步:https://blog.csdn.net/qq_38289815/article/details/81012826

    进程间通信:管道和命名管道(FIFO)  https://blog.csdn.net/qq_38289815/article/details/104742682

    进程间通信:信号量  https://blog.csdn.net/qq_38289815/article/details/104762940

    进程间通信:消息队列  https://blog.csdn.net/qq_38289815/article/details/104786412

     

    共享内存

    共享内存允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式。不同进程之间共享的内存安排为同一段物理内存。

    共享内存是由IPC为进程创建的一个特殊的地址范围,它将出现在该进程的地址空间中。其他进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

    特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。

     

    Linux数字权限含义解释

    在学习Linux编程时,常会用到755、777这些数字设置权限下面我来详细的介绍644、755、777这些数字所代表的含义Linux系统使用9个比特的权限标志来表示文件的权限。在终端中键入ll或ls -l查看目录下的文件:

    图片中所展示的数字,从左至右,1-3位数字代表文件所有者的权限,4-6位数字代表同组用户的权限,7-9数字代表其他用户的权限。具体的权限是由数字来表示的,读取(r)的权限等于4;写入(w)的权限等于2;执行(x)的权限等于1

    通过4、2、1的组合,得到以下几种权限:0 (没有权限);4 (读取权限);5 (4+1 | 读取+执行);6 (4+2 | 读取+写入);7 (4+2+1 | 读取+写入+执行)。以755为例:
    1-3位7等于4+2+1,rwx,所有者具有读取、写入、执行权限;
    4-6位5等于4+1+0,r-x,同组用户具有读取、执行权限但没有写入权限;
    7-9位5,同上,也是r-x,其他用户具有读取、执行权限但没有写入权限。

     

    共享内存的使

    与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。定义如下:

    #include <sys/shm.h>
    
    void *shmat(int shm_id, const void *shm_addr, int shmflg);
    int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
    int shmdt(const void *shm_addr);
    int shmget(key_t key, size_t size, int shmflg);

     

    shmget()

    shmget函数用来创建共享内存,它的原型为:

    int shmget(key_t key, size_t size, int shmflg);
    创建成功返回一个非负整数,即共享内存标识符;失败返回-1。

    第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1。

    第二个参数,size以字节为单位指定需要共享的内存容量。

    第三个参数,shmflg是权限标志,如果要想在key标识的共享内存不存在时创建它的话,可以与IPC_CREAT做按位或操作。共享内存的权限标志与文件的读写权限一样

    不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。

    权限标志对共享内存非常有用,因为它们允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,同时其他用户创建的进程只能读取该共享内存。我们可以利用这个功能来提供一种有效的对数据进行只读访问的方法,通过将数据放入共享内存并设置它的权限,就可以避免其他用户修改它。

     

    shmat()

    第一次创建完共享内存时,它还不能被任何进程访问,shmat()函数的作用就是启动对该共享内存的访问,并把共享内存连接到进程的地址空间。它的原型为:

    void *shmat(int shm_id, const void *shm_addr, int shmflg);
    shmat调用成功时返回一个指向共享内存第一个字节的指针;调用失败时返回-1。

    第一个参数,shm_id是由shmget()函数返回的共享内存标识。

    第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常是一个空指针,表示让系统来选择共享内存出现的地址。

    第三个参数,shm_flg是一组标志位,通常为0。一般很少需要控制共享内存连接的地址,通常都是让系统来选择一个地址,否则就会使应用程序对硬件的依赖性过高。

    共享内存的读写权限由它的属主(共享内存的创建者)、它的访问权限和当前进程的属主决定。共享内存的访问权限类似于文件的访问权限。这个规则有个例外,当shmflg & SHM_RDONLY(它使得连接的内存只读)为true时,此时即使该共享内存的访问权限允许写操作,它也不能被写入。

     

    shmdt()

    shmdt函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型为:

    int shmdt(const void *shmaddr);
    参数shmaddr是shmat()函数返回的地址指针,调用成功时返回0,失败时返回-1.

     

    shmctl()

    shmctl用来控制共享内存,它的原型为:

    int shmctl(int shm_id, int command, struct shmid_ds *buf);
    成功时返回0,失败时返回-1。

    第一个参数,shm_id是shmget()函数返回的共享内存标识符。

    第二个参数,command是要采取的操作,它可以取下面的三个值 :

            IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

            IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

            IPC_RMID:删除共享内存段

    第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。shmid_ds结构 至少包括以下成员:

    struct shmid_ds
    {
        uid_t shm_perm.uid;
        uid_t shm_perm.gid;
        mode_t shm_perm.mode;
    };

     

    使用共享内存完成进程间通信

    下面就以两个不相关的进程来说明进程间如何利用共享内存来进行通信。其中一个文件shm1.c(消费者)创建共享内存,并读取其中的信息,另一个文件shm2.c(生产者)将连接一个已有的共享内存段,向共享内存中写入数据。

    #ifndef _SHM_COM_H_HEADER   //shm_com.h
    #define _SHM_COM_H_HEADER
    
    #define TEXT_SZ 2048
     
    struct shared_use_st
    {
        int written_by_you;       // 作为一个标志,非0:表示可读,0:表示可写
        char some_text[TEXT_SZ];  // 记录写入 和 读取 的文本
    };
     
    #endif
    
    #include <stddef.h>  //shm1.c
    #include <sys/shm.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include "shm_com.h"
     
    int main(int argc, char **argv)
    {
    	int running = 1;
        void *shared_memory = NULL;
        struct shared_use_st *shared_stuff; // 指向shm
        int shmid; // 共享内存标识符
    	
    	srand((unsigned int)getpid());
    	
        // 创建共享内存
        shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
        if (shmid == -1)
        {
            fprintf(stderr, "shmget failed\n");
            exit(EXIT_FAILURE);
        }
     
        // 将共享内存连接到当前进程的地址空间
        shared_memory = shmat(shmid, (void *)0, 0);
        if (shared_memory == (void *)-1)
        {
            fprintf(stderr, "shmat failed\n");
            exit(EXIT_FAILURE);
        }
     
        printf("\nMemory attached at %p\n", shared_memory);
        printf("Memory attched at %d\n", *(int*)shared_memory);
     
        // 设置共享内存
        shared_stuff = (struct shared_use_st*)shared_memory; // 注意:shm有点类似通过 malloc() 获取到的内存,所以这里需要做个 类型强制转换
        shared_stuff->written_by_you = 0;
        while (running) // 读取共享内存中的数据
        {
            // 没有进程向内存写数据,有数据可读取
            if (shared_stuff->written_by_you == 1)
            {
                printf("You wrote: %s", shared_stuff->some_text);
                sleep(1);
     
                // 读取完数据,设置written使共享内存段可写
                shared_stuff->written_by_you = 0;
     
                // 输入了 end,退出循环(程序)
                if (strncmp(shared_stuff->some_text, "end", 3) == 0)
                {
                    running = 0;
                }
            }
        }
     
        // 把共享内存从当前进程中分离
        if (shmdt(shared_memory) == -1)
        {
            fprintf(stderr, "shmdt failed\n");
            exit(EXIT_FAILURE);
        }
     
        // 删除共享内存
        if (shmctl(shmid, IPC_RMID, 0) == -1)
        {
            fprintf(stderr, "shmctl(IPC_RMID) failed\n");
            exit(EXIT_FAILURE);
        }
     
        exit(EXIT_SUCCESS);
        reutrn 0;
    }
    #include <unistd.h>  //shm2.c
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/shm.h>
    #include "shm_com.h"
     
    int main(int argc, char **argv)
    {
        int running = 1;
        void *shared_memory = NULL;
        struct shared_use_st *shared_stuff; // 指向shm
        char buffer[BUFSIZ];
        int shmid; // 共享内存标识符
     
        // 创建共享内存
        shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
        if (shmid == -1)
        {
            fprintf(stderr, "shmget failed\n");
            exit(EXIT_FAILURE);
        }
     
        // 将共享内存连接到当前的进程地址空间
        shared_memory = shmat(shmid, (void *)0, 0);
        if (shared_memory == (void *)-1)
        {
            fprintf(stderr, "shmat failed\n");
            exit(EXIT_FAILURE);
        }
     
        printf("Memory attched at %p\n", shared_memory);
        printf("Memory attched at %d\n", *(int*)shared_memory);
     
        // 设置共享内存
        shared_stuff = (struct shared_use_st *)shared_memory;
        while (running) // 向共享内存中写数据
        {
            // 数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本
            while (shared_stuff->written_by_you == 1)
            {
                sleep(1);
                printf("Waiting...\n");
            }
     
            // 向共享内存中写入数据
            printf("Enter some text: ");
            fgets(buffer, BUFSIZ, stdin);
            strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
     
            // 写完数据,设置written使共享内存段可读
            shared_stuff->written_by_you = 1;
     
            // 输入了end,退出循环(程序)
            if (strncmp(buffer, "end", 3) == 0)
            {
                running = 0;
            }
        }
     
        // 把共享内存从当前进程中分离
        if (shmdt(shared_memory) == -1)
        {
            fprintf(stderr, "shmdt failed\n");
            exit(EXIT_FAILURE);
        }
     
        exit(EXIT_SUCCESS);
        return 0;
    }

    实验解析

    第一个程序shm1创建共享内存段,然后它连接到自己的地址空间中。我们在共享内存的开始处使用一个结构体shared_use_st。该结构体中有个标志written_by_you,当共享内存中有数据写入时,就设置这个标志。这个标志被设置时,程序就从共享内存中读取文本,将它打印出来,然后清除这个标志表示已经读完数据。我们用一个特殊字符串end来退出循环。接下来,程序分离共享内存段并删除它。

    第二个程序shm2使用相同的键1234来取得并连接同一个共享内存段。然后它提示用户输入一些文本。如果标志written_by_you被设置,shm2就知道客户进程还未读完上一次的数据,因此就继续等待。当其他进程清除了这个标志后,shm2写入数据并设置该标志。它还使用字符串end来终止并分离共享内存段。

    注意,这里提供了非常简陋的同步标志written_by_you,它包括一个非常缺乏效率的忙等待(不停地循环)。这可以使得我们的示例比较简单,但在实际编程中,应该使用信号量或通过传递消息、生成信号的方式来提供应用程序读、写部分之间的一种更有效的同步机制。

    展开全文
  • ShareMemLib将共享内存代码封装成lib,定义了发送者和监听者 两个进程在实例化ShareMemory时指定相同的map,并指定当前为发送者或监听者 发送者负责发送消息,监听者负责接受消息(监听者务必实现接受消息处理方法) ...
  • 共享内存定义在kernel函数里面还是外面? 我下面有一个类似图像直方图统计功能的内核函数,每个线程对应一个像素点。 如果不使用共享内存是这样的: ``` atomicAdd(&gpu_EO_0_stats_pix_count[catagory_row_id*...
  • 【Linux】Linux的共享内存

    万次阅读 多人点赞 2018-08-10 19:17:45
    实现进程间通信最简单也是最直接的方法就是共享内存——为参与通信的多个进程在内存中开辟一个共享区。由于进程可以直接对共享内存进行读写操作,因此这种通信方式效率特别高,但其弱点是,它没有互斥机制,需要信号...

    实现进程间通信最简单也是最直接的方法就是共享内存——为参与通信的多个进程在内存中开辟一个共享区。由于进程可以直接对共享内存进行读写操作,因此这种通信方式效率特别高,但其弱点是,它没有互斥机制,需要信号量之类的手段来配合。

     

    共享内存原理与shm系统

    共享内存,顾名思义,就是两个或多个进程都可以访问的同一块内存空间,一个进程对这块空间内容的修改可为其他参与通信的进程所看到的。

    显然,为了达到这个目的,就需要做两件事:一件是在内存划出一块区域来作为共享区;另一件是把这个区域映射到参与通信的各个进程空间。

    通常在内存划出一个区域的方法是,在内存中打开一个文件,若通过系统调用mmap()把这个文件所占用的内存空间映射到参与通信的各个进程地址空间,则这些进程就都可以看到这个共享区域,进而实现进程间的通信。

    为了方便,再把mmap()的原理简述如下:

    mmap()原型如下:

    void * mmap(void *start, size_t len, int prot, int flags, int fd, off_t offset);

    其中,参数fd用来指定被映射的文件;offset指定映射的起始位置偏移量(通常为0);len指定文件被映射部分的长度;start用来指定映射到虚地址空间的起始位置(通常为NULL,即由系统确定)。

    mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。

    mmap()映射过程示意图如下所示:

    那么mmap是怎么形成这个文件映射过程呢?

    mmap本身其实是一个很简单的操作,在进程页表中添加一个页表项,该页表项是物理内存的地址。调用mmap的时候,内核会在该进程的地址空间的映射区域查找一块满足需求的空间用于映射该文件,然后生成该虚拟地址的页表项,改页表项此时的有效位(标志是否已经在物理内存中)为0,页表项的内容是文件的磁盘地址,此时mmap的任务已经完成。 

    简而言之,就是在进程对应的虚存段添加一个段,也就是创建一个新的vm_area_struct结构,并将其与文件的物理磁盘地址相连。在创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,引发缺页异常,内核进行请页。

    IPC的共享内存通信方式与上面的mmap()方式极为相似,但因为建立一个文件的目的仅是为了通信,于是这种文件没有永久保存的意义,因此IPC并没有使用正规的文件系统,而是在系统初始化时在磁盘交换区建立了一个专门用来实现共享内存的特殊临时文件系统shm,当系统断电后,其中的文件会全部自行销毁。

    文章参考:mmap内存映射认真分析mmap:是什么 为什么 怎么用

     

    Linux共享内存结构

    Linux的一个共享内存区由多个共享段组成。用来描述共享内存段的内核数据结构shmid_kernel如下:

    struct shmid_kernel /* private to the kernel */
    {	
    	struct kern_ipc_perm	shm_perm;        //描述进程间通信许可的结构
    	struct file *		shm_file;            //指向共享内存文件的指针
    	unsigned long		shm_nattch;            //挂接到本段共享内存的进程数
    	unsigned long		shm_segsz;            //段大小
    	time_t			shm_atim;            //最后挂接时间
    	time_t			shm_dtim;            //最后解除挂接时间
    	time_t			shm_ctim;            //最后变化时间
    	pid_t			shm_cprid;            //创建进程的PID
    	pid_t			shm_lprid;            //最后使用进程的PID
    	struct user_struct	*mlock_user;
    };

    shmid_kernel中最重要的域是指针shm_file,它指向临时文件file对象。当进程需要使用这个文件进行通信时,由内核负责将其映射到用户地址空间。

    为了便于管理,内核把共享内存区的所有描述结构shmid_kernel都存放在结构ipc_id_ary中的一个数组中。结构ipc_id_ary的定义如下:

    struct ipc_id_ary
    {
            int size;
            struct kern_ipc_perm *p[0];            //存放段描述结构的数组
    };

    同样,为了描述一个共享内存区的概貌,内核使用了数据结构ipc_ids。该结构的定义如下:

    struct ipc_ids {
    	int in_use;
    	unsigned short seq;
    	unsigned short seq_max;
    	struct rw_semaphore rw_mutex;
    	struct idr ipcs_idr;
            struct ipc_id_ary *entries;        //指向struct ipc_id_ary的指针
    };

    由多个共享段组成的共享区的结构如下所示:

     

    共享内存的使用

    头文件:

    #include <sys/shm.h>

    共享内存的打开或创建

    进程可以通过调用函数shmget()来打开或创建一个共享内存区。函数shmget()内部由系统调用sys_shmget来实现。函数shmget()的原型如下:

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

    其中,参数key为用户给定的键值。

    所谓的键值,是在IPC的通信模式下每个IPC对象的名字。进程通过键值识别所有的对象。如果不使用键,进程将无法获取IPC对象,因此IPC对象并不存在于进程本身所使用的的内存中。

    因此任何进程都无法为一块共享内存定义一个键值。因此,在调用函数shmget()时,需要key设为IPC_PRIVATE,这样,操作系统将忽略键,建立一个新的共享内存,指定一个键值并返回这块共享内存的IPC标识符ID,然后再设法将这个新的共享内存的标识符ID告诉其他需要使用这个共享内存区的进程。

    函数中的参数size为所申请的共享存储段的长度(以页为单位)。

    函数中的参数flag为标志,常用的有效标志有IPC_CREAT和IPC_EXCL,它们的功能与文件打开函数open()的O_CREAT和O_EXCL相当。如果用户希望所创建的共享内存区可读,则需要使用标志S_IRUSR;若可读,则需要使用标志S_IWUSR。

    函数shmget()调用成功后,返回共享内存区的ID,否则返回-1。

    Linux用shmid_ds数据结构表示每个新建的共享内存。当shmget()创建一块新的共享内存后,返回一个可以引用该共享内存的shmid_ds数据结构的标识符。定义在include/linux/shm.h文件中的shmid_ds如下:

    struct shmid_ds {
    	struct ipc_perm		shm_perm;	/* operation perms */
    	int			shm_segsz;	/* size of segment (bytes) */
    	__kernel_time_t		shm_atime;	/* last attach time */
    	__kernel_time_t		shm_dtime;	/* last detach time */
    	__kernel_time_t		shm_ctime;	/* last change time */
    	__kernel_ipc_pid_t	shm_cpid;	/* pid of creator */
    	__kernel_ipc_pid_t	shm_lpid;	/* pid of last operator */
    	unsigned short		shm_nattch;	/* no. of current attaches */
    	unsigned short 		shm_unused;	/* compatibility */
    	void 			*shm_unused2;	/* ditto - used by DIPC */
    	void			*shm_unused3;	/* unused */
    };

    例如:调用函数shmget()为当前进程创建一个共享内存区。

    代码如下:

    int main(void)
    {
            int shmid;
            if((shmid = shmget(IPC_PRIVATE, 10, IPC_CREAT)) < 0)
            {
                    perror("shmget error!");
                    exit(1);
            }else
                    printf("shmget success!");
    
            return 0;
    }

    共享内存与进程的连接

    如果一个进程已创建或打开一个共享内存,则在需要使用它时,要调用函数shmat()把该共享内存连接到进程上,即要把待使用的共享内存映射到进程空间。函数shmat()通过系统调用sys_shmat()实现。函数shmat()的原型如下:

    void * shmat(int shmid, char __user * shmaddr, int shmflg);

    其中,参数shmid为共享内存的标识;参数shmaddr为映射地址,如果该值为0,则由内核决定;参数shmflg为共享内存的标志,如果shmflg的值为SHM_RDONLY,则进程以只读的方式访问共享内存,否则以读写方式访问共享内存。

    若函数调用成功,则返回共享存储段地址;若出错,则返回-1。

    断开共享内存与进程的连接

    调用函数shmdt()可以断开共享内存与进程的连接,其原型如下:

    int shmdt(coid * addr);

    其中,参数addr为共享存储段的地址,即调用shmat时的返回值。shmdt将使相关shmid_ds结构中的shm_nattch计数器值减1。

    共享内存的控制

    调用函数shmctl()可以对共享内存进行一些控制,其原型如下:

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

    其中,参数shmid为共享存储段的ID;参数cmd为控制命令,常用的值有IPC_STAT(赋值)、IPC_SET(赋值)、IPC_RMID(删除)、SHM_LOCK(上锁)、SHM_UNLOCK(解锁)等等;参数buf为struct shmid_ds类型指针,由buf返回的数值与命令参数cmd表示的操作相关。

    共享内存不会随着程序的结束而自动消除,要么调用shmctl()删除,要么手动使用命令ipcrm -m shmid去删除,否则一直保留在系统中,直至系统掉电。

    例子:调用函数shmget()为当前进程创建一个共享内存区并使用它。

    代码如下:

    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <stdio.h>
    #include <sys/stat.h>
    
    int main(void)
    {
    	int shm_id;							//定义共享内存键
    	char* shared_memory;				//定义共享内存指针
    	struct shmid_ds shmbuffer;			//定义共享内存缓冲
    	int shm_size;						//定义共享内存大小
    
    	shm_id = shmget(IPC_PRIVATE, 0x6400, IPC_CREAT | IPC_EXCL | S_IRUSE | S_IWUSE);		//创建一个共享内存区
    	shared_memory = (char*)shmat(shm_id, 0, 0);					//绑定到共享内存
    	printf("shared memory attached at address %p\n", shared_memory);
    
    	shmctl(shm_id, IPC_STAT, &shmbuffer);				//读共享内存结构struct shmid_ds
    	shm_size = shmbuffer.shm_segsz;						//自结构struct shmid_ds获取内存大小
    	printf("segment size:%d\n", shm_size);
    
    	sprintf(shared_memory, "Hello,world.");				//向共享内存中写入一个字符串
    	shmdt(shared_memory);							//脱离该共享内存
    
    	shared_memory = (char*)shmat(shm_id, (void *)0x500000, 0);			//重新绑定共享内存
    	printf("shared memory reattched at address %p\n", shared_memory);
    	printf("%s\n", shared_memory);
    	shmdt(shared_memory);						//脱离该共享内存
    	shmctl(shm_id, IPC_RMID, 0);				//释放共享内存
    
    	return 0;
    }

    共享内存的互斥

    从上面的叙述中可以看到,共享内存是一种低级的通信机制,它没有提供进程间同步和互斥的功能。所以,共享内存通常是要与信号量结合使用。

     

    展开全文
  • C++实现的共享内存缓冲区

    热门讨论 2012-09-20 14:41:06
    共享内存操作封装成C++类,通过信号灯semaphore进行进程同步。可以像操作普通缓冲区那样操作共享内存,实现进程间通信 编译时需要添加-lrt编译选项
  • 共享单车、共享充电宝、共享雨伞,世间的共享有千万种,而我独爱共享内存。早期的共享内存,着重于强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),以便于CPU...

    共享单车、共享充电宝、共享雨伞,世间的共享有千万种,而我独爱共享内存。

    早期的共享内存,着重于强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),以便于CPU可以在各个进程访问到这片内存。

    现阶段广泛应用于多媒体、Graphics领域的共享内存方式,某种意义上不再强调映射到进程虚拟地址空间的概念(那无非是为了让CPU访问),而更强调以某种“句柄”的形式,让大家知道某一片视频、图形图像数据的存在并可以借助此“句柄”来跨进程引用这片内存,让视频encoder、decoder、GPU等可以跨进程访问内存。所以不同进程用的加速硬件其实是不同的,他们更在乎的是可以通过一个handle拿到这片内存,而不再特别在乎CPU访问它的虚拟地址(当然仍然可以映射到进程的虚拟地址空间供CPU访问)。

    只要内存的拷贝(memcpy)仍然是一个占据内存带宽、CPU利用率的消耗大户存在,共享内存作为Linux进程间通信、计算机系统里各个不同硬件组件通信的最高效方法,都将持续繁荣。关于内存拷贝会大多程度地占据CPU利用率,这个可以最简单地尝试拷贝1080P,帧率每秒60的电影画面,我保证你的系统的CPU,蛋会疼地不行。

    我早就想系统地写一篇综述Linux里面各种共享内存方式的文章了,但是一直被带娃这个事业牵绊,今日我决定顶着娃娃们的山呼海啸,也要写一篇文章不吐不快。

    共享内存的方式有很多种,目前主流的方式仍然有:

    共享内存的方式

    1.基于传统SYS V的共享内存;

    2.基于POSIX mmap文件映射实现共享内存;

    3.通过memfd_create()和fd跨进程共享实现共享内存;

    4.多媒体、图形领域广泛使用的基于dma-buf的共享内存。

    共享内存

    SYS V共享内存

    历史悠久、年代久远、API怪异,对应内核代码linux/ipc/shm.c,当你编译内核的时候不选择CONFIG_SYSVIPC,则不再具备此能力。

    你在Linux敲ipcs命令看到的share memory就是这种共享内存:

    下面写一个最简单的程序来看共享内存的写端sw.c:

    以及共享内存的读端sr.c:

    编译和准备运行:

    在此之前我们看一下系统的free:

    下面运行sw和sr:

    我们发现sr打印出来的和sw写进去的是一致的。这个时候我们再看下free:

    可以看到used显著增大了(711632 -> 715908), shared显著地增大了(2264 -> 6360),而cached这一列也显著地增大326604->330716。

    我们都知道cached这一列统计的是file-backed的文件的page cache的大小。理论上,共享内存属于匿名页,但是由于这里面有个非常特殊的tmpfs(/dev/shm指向/run/shm,/run/shm则mount为tmpfs):

    所以可以看出tmpfs的东西其实真的是有点含混:我们可以理解它为file-backed的匿名页(anonymous page),有点类似女声中的周深。前面我们反复强调,匿名页是没有文件背景的,这样当进行内存交换的时候,是与swap分区交换。磁盘文件系统里面的东西在内存的副本是file-backed的页面,所以不存在与swap分区交换的问题。但是tmpfs里面的东西,真的是在统计意义上统计到page cache了,但是它并没有真实的磁盘背景,这又和你访问磁盘文件系统里面的文件产生的page cache有本质的区别。所以,它是真地有那么一点misc的感觉,凡事都没有绝对,唯有变化本身是不变的。

    也可以通过ipcs找到新创建的SYS V共享内存:

    POSIX共享内存

    我对POSIX shm_open()、mmap () API系列的共享内存的喜爱,远远超过SYS V 100倍。原谅我就是一个懒惰的人,我就是讨厌ftok、shmget、shmat、shmdt这样的API。

    上面的程序如果用POSIX的写法,可以简化成写端psw.c:

    读端:

    编译和执行:

    这样我们会在/dev/shm/、/run/shm下面看到一个文件:

    坦白讲,mmap、munmap这样的API让我找到了回家的感觉,刚入行做Linux的时候,写好framebuffer驱动后,就是把/dev/fb0 mmap到用户空间来操作,所以mmap这样的 API,真的是特别亲切,像亲人一样。

    当然,如果你不喜欢shm_open()这个API,你也可以用常规的open来打开文件,然后进行mmap。关键的是mmap,wikipedia如是说:

    mmap

    In computing, mmap(2) is a POSIX-compliant Unix system call that maps files or devices into memory. It is a method of memory-mapped file I/O. It implements demand paging, because file contents are not read from disk directly and initially do not use physical RAM at all. The actual reads from disk are performed in a "lazy" manner, after a specific location is accessed. After the memory is no longer needed, it is important to munmap(2) the pointers to it. Protection information can be managed using mprotect(2), and special treatment can be enforced using madvise(2).

    POSIX的共享内存,仍然符合我们前面说的tmpfs的特点,在运行了sw,sr后,再运行psw和psr,我们发现free命令再次戏剧性变化:

    请将这个free命令的结果与前2次的free结果的各个字段进行对照:

    第3次比第2次的cached大了这么多?是因为我编写这篇文章边在访问磁盘里面的文件,当然POSIX的这个共享内存本身也导致cached增大了。

    memfd_create

    如果说POSIX的mmap让我找到回家的感觉,那么memfd_create()则是万般惊艳。见过这种API,才知道什么叫天生尤物——而且是尤物中的尤物,它完全属于那种让码农第一眼看到就会两眼充血,恨不得眼珠子夺眶而出贴到它身上去的那种API;一般人见到它第一次,都会忽略了它的长相,因为它的身材实在太火辣太抢眼了。

    先不要浮想联翩,在所有的所有开始之前,我们要先提一下跨进程分享fd(文件描述符,对应我们很多时候说的“句柄”)这个重要的概念。

    众所周知,Linux的fd属于一个进程级别的东西。进入每个进程的/proc/pid/fd可以看到它的fd的列表:

    这个进程的0,1,2和那个进程的0,1,2不是一回事。

    某年某月的某一天,人们发现,一个进程其实想访问另外一个进程的fd。当然,这只是目的不是手段。比如进程A有2个fd指向2片内存,如果进程B可以拿到这2个fd,其实就可以透过这2个fd访问到这2片内存。这个fd某种意义上充当了一个中间媒介的作用。有人说,那还不简单吗,如果进程A:

    fd = open();

    open()如果返回100,把这个100告诉进程B不就可以了吗,进程B访问这个100就可以了。这说明你还是没搞明白fd是一个进程内部的东西,是不能跨进程的概念。你的100和我的100,不是一个东西。这些基本的东西你搞不明白,你搞别的都是白搭。

    Linux提供一个特殊的方法,可以把一个进程的fd甩锅、踢皮球给另外一个进程(其实“甩锅”这个词用在这里不合适,因为“甩锅”是一种推卸,而fd的传递是一种分享)。我特码一直想把我的bug甩(分)锅(享)出去,却发现总是被人把bug甩锅过来。

    那么如何甩(分)锅(享)fd呢?

    Linux里面的甩锅需要借助cmsg,用于在socket上传递控制消息(也称Ancillary data),使用SCM_RIGHTS,进程可以透过UNIX Socket把一个或者多个fd(file descriptor)传递给另外一个进程。

    比如下面的这个函数,可以透过socket把fds指向的n个fd发送给另外一个进程:

    而另外一个进程,则可以透过如下函数接受这个fd:

    那么问题来了,如果在进程A中有一个文件的fd是100,发送给进程B后,它还是100吗?不能这么简单地理解,fd本身是一个进程级别的概念,每个进程有自己的fd的列表,比如进程B收到进程A的fd的时候,进程B自身fd空间里面自己的前面200个fd都已经被占用了,那么进程B接受到的fd就可能是201。数字本身在Linux的fd里面真地是一点都不重要,除了几个特殊的0,1,2这样的数字外。同样的,如果你把 cat /proc/interrupts 显示出的中断号就看成是硬件里面的中断偏移号码(比如ARM GIC里某号硬件中断),你会发现,这个关系整个是一个瞎扯。

    知道了甩锅API,那么重要的是,当它与memfd_create()结合的时候,我们准备甩出去的fd是怎么来?它是memfd_create()的返回值。

    memfd_create()这个函数的玄妙之处在于它会返回一个“匿名”内存“文件”的fd,而它本身并没有实体的文件系统路径,其典型用法如下:

    我们透过memfd_create()创建了一个“文件”,但是它实际映射到一片内存,而且在/xxx/yyy/zzz这样的文件系统下没有路径!没有路径!没有路径!

    所以,当你在Linux里面编程的时候,碰到这样的场景:需要一个fd,当成文件一样操作,但是又不需要真实地位于文件系统,那么,就请立即使用memfd_create()吧,它的manual page是这样描述的:

    memfd_create

    memfd_create() creates an anonymous file and returns a file descriptor that refers to it.  The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on.
    However, unlike a regular file, it lives in RAM and has a volatile backing storage.

    重点理解其中的regular这个单词。它的行动像一个regular的文件,但是它的背景却不regular。

    那么,它和前面我们说的透过UNIX Socket甩锅fd又有什么关系呢?memfd_create()得到了fd,它在行为上类似规则的fd,所以也可以透过socket来进行甩锅,这样A进程相当于把一片与fd对应的内存,分享给了进程B。

    下面的代码进程A通过memfd_create()创建了2片4MB的内存,并且透过socket(路径/tmp/fd-pass.socket)发送给进程B这2片内存对应的fd:

    下面的代码进程B透过相同的socket接受这2片内存对应的fd,之后通过read()读取每个文件的前256个字节并打印:

    上述代码参考了: 

    https://openforums.wordpress.com/2016/08/07/open-file-descriptor-passing-over-unix-domain-sockets/

    上述的代码中,进程B是在进行read(fds[i], buffer, sizeof(buffer)),这体现了基于fd进行操作的regular特点。当然,如果是共享内存,现实的代码肯定还是多半会是mmap:

    mmap(NULL, SIZE, PROT_READ, MAP_SHARED, fd, 0);

    那么,透过socket发送memfd_create() fd来进行进程间共享内存这种方法,它究竟惊艳在哪里?

    我认为首要的惊艳之处在于编程模型的惊艳。API简单、灵活、通用。进程之间想共享几片内存共享几片内存,想怎么共享怎么共享,想共享给谁共享给谁,无非是多了几个fd和socket的传递过程。比如,我从互联网上面收到了jpeg的视频码流,一帧帧的画面,进程A可以创建多片buffer来缓存画面,然后就可以透过把每片buffer对应的fd,递交给另外的进程去解码等。Avenue to Jane(大道至简),简单的才是最好的!

    memfd_create()的另外一个惊艳之处在于支持“封印”(sealing,就是你玩游戏的时候的封印),sealing这个单词本身的意思是封条,在这个场景下,我更愿意把它翻译为“封印”。中国传说中的封印,多是采用如五行、太极、八卦等手段,并可有例如符咒、法器等物品的辅助。现指对某个单位施加一种力量,使其无法正常使用某些能力的本领(常出现于玄幻及神魔类作品,游戏中)。我这一生,最喜欢玩的游戏就是《仙剑奇侠传》和《轩辕剑——天之痕》,不知道是否暴露年龄了。

    采用memfd_create()的场景下,我们同样可以用某种法器,来控制共享内存的shrink、grow和write。最初的设想可以详见File Sealing & memfd_create()这篇文章:

    https://lwn.net/Articles/591108/

    我们如果在共享内存上施加了这样的封印,则可以限制对此片区域的ftruncate、write等动作,并建立某种意义上进程之间的相互信任,这是不是很拉风?

    还记得镇压孙悟空的五行山顶的封印吗?还记得孙悟空的紧箍咒吗?还记得悟空每次离开师傅的时候在师傅周围画的一个圈吗?

    封印

    * SEAL_SHRINK: If set, the inode size cannot be reduced
    * SEAL_GROW: If set, the inode size cannot be increased
    * SEAL_WRITE: If set, the file content cannot be modified

    File Sealing & memfd_create()文中举到的一个典型使用场景是,如果graphics client把它与graphics compoistor共享的内存交给compoistor去render,compoistor必须保证可以拿到这片内存。这里面的风险是client可能透过ftruncate()把这个memory shrink小,这样compositor就拿不到完整的buffer,会造成crash。所以compositor只愿意接受含有SEAL_SHRINK封印的fd,如果没有,对不起,我们不能一起去西天取经。

    在支持memfd_create()后,我们应尽可能地使用这种方式来替代传统的POSIX和SYS V,基本它也是一个趋势,比如我们在wayland相关项目中能看到这样的patch:

    dma_buf

    dma_buf定义

    The DMABUF framework provides a generic method for sharing buffers between multiple devices. Device drivers that support DMABUF can export a DMA buffer to userspace as a file descriptor (known as the exporter role), import a DMA buffer from userspace using a file descriptor previously exported for a different or the same device (known as the importer role), or both. 

    简单地来说,dma_buf可以实现buffer在多个设备的共享,应用可以把一片底层驱动A的buffer导出到用户空间成为一个fd,也可以把fd导入到底层驱动 B。当然,如果进行mmap()得到虚拟地址,CPU也是可以在用户空间访问到已经获得用户空间虚拟地址的底层buffer的。

    上图中,进程A访问设备A并获得其使用的buffer的fd,之后通过socket把fd发送给进程B,而后进程B导入fd到设备B,B获得对设备A中的buffer的共享访问。如果CPU也需要在用户态访问这片buffer,则进行了mmap()动作。

    为什么我们要共享DMA buffer?想象一个场景:你要把你的屏幕framebuffer的内容透过gstreamer多媒体组件的服务,变成h264的视频码流,广播到网络上面,变成流媒体播放。在这个场景中,我们就想尽一切可能的避免内存拷贝

    技术上,管理framebuffer的驱动可以把这片buffer在底层实现为dma_buf,然后graphics compositor给这片buffer映射出来一个fd,之后透过socket发送fd 把这篇内存交给gstreamer相关的进程,如果gstreamer相关的“color space硬件转换”组件、“H264编码硬件组件”可以透过收到的fd还原出这些dma_buf的地址,则可以进行直接的加速操作了。比如color space透过接收到的fd1还原出framebuffer的地址,然后把转化的结果放到另外一片dma_buf,之后fd2对应这片YUV buffer被共享给h264编码器,h264编码器又透过fd2还原出YUV buffer的地址。

    这里面的核心点就是fd只是充当了一个“句柄”,用户进程和设备驱动透过fd最终寻找到底层的dma_buf,实现buffer在进程和硬件加速组件之间的zero-copy,这里面唯一进行了exchange的就是fd。

    再比如,如果把方向反过来,gstreamer从网络上收到了视频流,把它透过一系列动作转换为一片RGB的buffer,那么这片RGB的buffer最终还要在graphics compositor里面渲染到屏幕上,我们也需要透过dma_buf实现内存在video的decoder相关组件与GPU组件的共享。

    Linux内核的V4L2驱动(encoder、decoder多采用此种驱动)、DRM(Direct Rendering Manager,framebuffer/GPU相关)等都支持dma_buf。比如在DRM之上,进程可以透过

    int drmPrimeHandleToFD(int fd,

    uint32_t handle,

    uint32_t flags,

    int * prime_fd 

    )

    获得底层framebuffer对应的fd。如果这个fd被分享给gstreamer相关进程的video的color space转换,而color space转换硬件组件又被实现为一个V4L2驱动,则我们可以透过V4L2提供的如下接口,将这片buffer提供给V4L2驱动供其导入:

    如果是multi plane的话,则需要导入多个fd:

    相关细节可以参考这个文档:

    https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dmabuf.html

    一切都是文件不是文件创造条件也要把它变成文件!这就是Linux的世界观。是不是文件不重要,关键是你得觉得它是个文件。在dma_buf的场景下,fd这个东西,纯粹就是个"句柄",方便大家通过这么一个fd能够对应到最终硬件需要访问的buffer。所以,透过fd的分享和传递,实际实现跨进程、跨设备(包括CPU)的内存共享。

    如果说前面的SYS V、POSIX、memfd_create()更加强调内存在进程间的共享,那么dma_buf则更加强调内存在设备间的共享,它未必需要跨进程。比如:

    有的童鞋说,为嘛在一个进程里面设备A和B共享内存还需要fd来倒腾一遍呢?我直接设备A驱动弄个全局变量存buffer的物理地址,设备B的驱动访问这个全局变量不就好了吗?我只能说,你对Linux内核的只提供机制不提供策略,以及软件工程每个模块各司其责,高内聚和低耦合的理解,还停留在裸奔的阶段。在没有dma_buf等类似机制的情况下,如果用户空间仍然负责构建策略并连接设备A和B,人们为了追求代码的干净,往往要进行这样的内存拷贝:

    dma_buf的支持依赖于驱动层是否实现了相关的callbacks。比如在v4l2驱动中,v4l2驱动支持把dma_buf导出(前面讲了v4l2也支持dma_buf的导入,关键看数据方向),它的代码体现在:

    drivers/media/common/videobuf2/videobuf2-dma-contig.c中的:

    其中的vb2_dc_dmabuf_ops是一个struct dma_buf_ops,它含有多个成员函数:

    当用户call VIDIOC_EXPBUF这个IOCTL的时候,可以把dma_buf转化为fd:

    int ioctl(int fd, VIDIOC_EXPBUF, struct v4l2_exportbuffer *argp);

    对应着驱动层的代码则会调用dma_buf_fd():

    应用程序可以通过如下方式拿到底层的dma_buf的fd:

    dma_buf的导入侧设备驱动,则会用到如下这些API:

    dma_buf_attach()

    dma_buf_map_attachment()

    dma_buf_unmap_attachment()

    dma_buf_detach()

    下面这张表,是笔者对这几种共享内存方式总的归纳:

    落花满天蔽月光,借一杯附荐凤台上。

    全剧终

    Linux阅码场原创精华文章汇总

    更多精彩,尽在"Linux阅码场",扫描下方二维码关注


    你的随手转发或点个在看是对我们最大的支持!

    展开全文
  • Qt 读写共享内存

    千次阅读 2020-10-01 14:05:43
    Qt 读写共享内存 一、简述 记--使用Qt进行简单的读写共享内存。 二、工程结构 三、测试代码 #include <QDebug> #include <QSharedMemory> //测试使用的数据结构 typedef struct{ int age; ...

                                                               Qt 读写共享内存

    一、简述

            记--使用Qt进行简单的读写共享内存。

    二、工程结构

    三、测试代码

    #include <QDebug>
    #include <QSharedMemory>
    
    //测试使用的数据结构
    typedef struct{
        int age;
        char name[12];
    }PeopleInfo_t;//注意考虑内存对齐,尽量凑为8的倍数,不够的添加保留位
    
    //定义共享内存的大小
    #define SHM_SIZE    (sizeof(PeopleInfo_t))
    
    int main(int argc, char *argv[])
    {
        Q_UNUSED(argc);
        Q_UNUSED(argv);
    
        bool isOK;
    
        /************************************ [1] 将数据写入共享内存 ****************************************/
        //1 创建共享内存操作对象
        QSharedMemory *shm_write = new QSharedMemory("QtShmTest");
    
        //2 创建共享内存
        isOK = shm_write->create(SHM_SIZE, QSharedMemory::ReadWrite);
        if (!isOK) {
            qDebug()<<"create shm failed.";
            return -1;
        }
    
        //准备数据
        PeopleInfo_t people = {71, "zhangsan"};
    
        //3 写入数据
        shm_write->lock();//上锁
        char *shm_addr = (char *)shm_write->data();//获取共享内存映射首地址
        memcpy(shm_addr, &people, qMin(SHM_SIZE, sizeof(people)));//写入数据, 这里注意写入的数据长度不能超过共享内存的大小
        shm_write->unlock();//解锁
    
        //4 释放共享内存
        //shm_write->detach(); //因为后面要测试读取,这里放在最后释放
    
        /************************************ [2] 从共享内存读物数据 ****************************************/
        //1 创建共享内存操作对象
        QSharedMemory *shm_read = new QSharedMemory("QtShmTest");
    
        //2 关联到指定的共享内存
        shm_read->attach(QSharedMemory::ReadOnly);//只读
    
        //3 读取数据
        PeopleInfo_t peopleInfo = {0, ""};
        shm_read->lock();//加锁
        memcpy(&peopleInfo, shm_read->constData(), qMin(SHM_SIZE, sizeof(PeopleInfo_t)));
        shm_read->unlock();//解锁
    
        //4 释放资源
        shm_read->detach();
    
        qDebug()<<"age:"<<peopleInfo.age<<" name:"<<peopleInfo.name;
    
        qDebug()<<"QtShm Test Finish. 2020.10.01 ";
    
        shm_write->detach();
        return 0;
    }
    

    四、效果

    五、总结

     5.1 注意事项

    读写共享内存注意不要超过共享内存大小,注意加锁和解锁防止出现死锁情况,共享内存数据结构需要注意内存对齐,不然数据会发生偏移。

    加锁解锁操作会降低操作共享内存的效率,但不加锁就容易导致几个程序同时修改共享内存从而发生数据丢失(相互覆盖)

    所以对共享内存写入数据有必要加锁,对于读取共享内存数据,看具体需求(实时性,数据容错性,数据完整性),一般都会加锁。

     

    5.2 重要步骤

    1. 创建共享内存操作对象
    
    QSharedMemory(const QString &key, QObject *parent = Q_NULLPTR);
    
    2. 创建共享内存
    
    bool create(int size, AccessMode mode = ReadWrite);
    
    3. 共享内存关联--非创建者就需要关联,创建共享内存的不用该步骤
    
    bool isAttached() const;//判断是否已经关联
    
    bool attach(AccessMode mode = ReadWrite);//关联并指定读写方式
       
    
    4. 共享内存数据操作,共享内存首地址
    
    void *data();
    
    const void* constData() const;
    
    const void *data() const;
    
    5 释放--取消关联
    
    bool detach();
    
    如果没有程序关联该共享内存,该共享内存才会真正被释放、被系统回收。
    
    

     

    展开全文
  • 本文是zynq 7000 AMP模式 双裸核CPU同时...本文定义OCM 为共享内存,地址为0xffff_0000。 在2个文件的头部都这样定义共享内存 #include "xil_mmu.h" #define COM_VAL (*(volatile unsigned int*)(0xffff0000)) #d...
  • 共享内存

    千次阅读 2018-09-19 15:38:10
     共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式。共享内存的具体实现是不同进程共享的内存安排为同一段物理地址。  如上图所示,进程A和进程B共享同一块物理内存,共享内存中的数据进程A和...
  • 共享内存实现原理

    万次阅读 2018-09-28 18:02:39
    共享内存的使用实现原理(必考必问,然后共享内存段被映射进进程空间之后,存在于进程空间的什么位置?共享内存段最大限制是多少?) nmap函数要求内核创建一个新额虚拟存储器区域,最好是从地质start开始的一个...
  • C# 简答操作共享内存方法

    千次阅读 2018-07-12 13:20:45
    C#操作共享内存的一个帮助类,读写和打开 class ShareMemoryHelper { [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] private static extern IntPtr OpenFileMapping(int dwDesiredAccess, ...
  • 共享单车、共享充电宝、共享雨伞,世间的共享有千万种,而我独爱共享内存。 早期的共享内存,着重于强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),以便于CPU可以在各个进程访问到...
  • C++共享内存实现

    万次阅读 2018-05-23 14:56:43
    服务端:(1)创建共享内存区域 (2)内存映射到当前进程 (3)写入数据#include "stdafx.h" #include &lt;windows.h&gt; #include &lt;iostream&gt; using namespace std; #define ...
  • Windows共享内存解析

    千次阅读 2017-07-29 23:47:42
    在Windows程序开发过程中,当多个进程之间需要使用同样的数据的时候我们最好的方式就是通过共享内存进行处理(比如:当A进程运行时,进行数据处理,那么此时我想知道数据是不是正确,用B监控,那么A与B之间就可以...
  • Java 共享内存

    千次阅读 2019-06-11 20:00:08
    对UNIX系统来说,共享内存分为一般共享内存和映像文件共享内存两种,对windows实际上只有映像文件共享内存一种。所以java应用中也是只能创建映像文件共享内存。使用共享内存,有如下几个特点: 1、可以被多个进程...
  • Linux之用信号量实现的共享内存机制

    千次阅读 2018-03-09 16:59:19
    IPC(Inter-Process Communication)机制即进程间通信机制,我们最为熟悉的IPC机制有三种,即信号量、共享内存和消息队列。今天要介绍的共享内存机制就是IPC三大机制之一。 一.共享内存先知 共享内存是在两个正在...
  • python多任务是个痛点,因为GIL的存在,multi_thread名存实亡非常渣,multiProcess虽然提供可用的方案,实际上也不怎么样,共享c_type变量还行,但是共享一个python自定义类型变量还需要第三方manage进程做中转,...
  • 一、共享内存简介 共享内存是进程间通信中高效方便的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针,两个进程可以对一块共享内存进行...
  • 开辟一块共享内存,使得相关进程均可访问同一块区域,再将互斥锁定义在该区域(即共享内存)上,使得相关进程可以使用该锁。 02 进程间的互斥锁和线程间互斥锁的区别 函数pthread_mutex_init(互斥锁地址, 属性...
  • linux下c语言共享内存调用

    千次阅读 2019-06-18 14:40:42
    共享内存作用: 让同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新 共享内存使用步骤:(这里还有要通过ftok()函数获得键值,所以还有打开文件的操作) 1....
  • Linux: 修改共享内存大小

    千次阅读 2018-06-15 15:17:48
    共享内存.  缺省时是只支持 4 MB 的共享内存。请记住共享内存是不能分页的;它是锁在 RAM 里面的。要增加你的系统支持的共享缓冲区数目,向你的内核配置文件里增加下面的行: options "SHMALL=8192" options ...
  • Linux共享内存与互斥锁

    千次阅读 2018-08-07 09:02:34
    Linux共享内存 共享内存是从系统的空闲内存池中分配,并希望访问它的每个进程都能连接它。连接的过程称为映射。映射后,每个进程都可通过访问自己的内存而访问共享内存区域,进而与其它进程进行通信。 共享内存...
  • Linux下共享内存+信号量实现

    千次阅读 2018-04-04 15:09:59
    Linux下共享内存+信号量实现 sem1.c按 Ctrl+C 复制代码按 Ctrl+C 复制代码sem2.c按 Ctrl+C 复制代码按 Ctrl+C 复制代码在gcc下需要注意Linux和sys两个头文件位置的选择,否则会出现错误。下面将讲解进程间通信的另...
  • linux共享内存实现

    千次阅读 2019-04-19 20:57:58
    答:共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之...
  • 怎么定义共享内存 大小 linux

    千次阅读 2014-11-26 15:11:06
    kernel.shmax 参数定义共享内存段的最大大小(以字节为单位)。kernel.shmall 参数设置了在系统上可以一次使用的共享内存的总数(以页为单位)。将这两个参数的值设为计算机上的物理内存量。请以十进制字节数指定...
  • 不同的进程拥有独立的进程空间,是不能共享各自的变量的,这就引申了一个问题,就是不同的进程不能操作同一个链表,那如何解决这个问题呢,我们用到了共享内存共享内存是物理地址,可以映射给多个进程使用。
  • linux共享内存原理剖析

    千次阅读 2017-05-08 16:41:14
    linux共享内存原理剖析共享内存区域是被多个进程共享的一部分物理内存。如果多个进程都把该内存区域映射到自己的虚拟地址空间,则这些进程就都可以直接访问该共享内存区域,从而可以通过该区域进行通信。共享内存是...
  • optee的共享内存的介绍

    千次阅读 2020-08-20 20:10:24
    optee是按照buffer来管理共享内存的,而不是按照pool来管理的. 每一个buf需要配置如下属性: buffer的起始地址和size 该buffer的cache属性 如果是被map到非连续的buf,则列出它所有的块(chunk) 配置成连续的share ...
  • 进程间通信_共享内存

    2015-04-13 09:19:52
    用于进程间通信,使用共享内存方法。内部的共享内存大小的结构体什么的需要自己定义

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 521,461
精华内容 208,584
关键字:

共享内存的定义