Linux下进程间通信-共享内存 原创 Linux平台 2022年2月17日 20:48 夏至未至 1620 当前内容 5070 字,在路上,马上到,马上到 ### 目录 [TOC] ### 共享内存介绍 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。 #### 何为共享内存 两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。 #### 共享内存弊端 由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。 #### 共享内存优点 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据: 1. 一次从输入文件到共享内存区, 2. 另一次从共享内存区到输出文件。 实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建 立共享内存区域,而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回 文件的。因此,采用共享内存的通信方式效率是非常高的。 ### 共享内存实现 #### 相关函数 与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h中。 ##### shmget 函数 该函数用来创建共享内存,它的原型为: int shmget(key_t key, size_t size, int shmflg); ###### 入参 1. 第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名。 2. 第二个参数,size以字节为单位指定需要共享的内存容量 3. 第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。 共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。 ###### 返回值 shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1。不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。 ##### shmat 函数 第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下: void *shmat(int shm_id, const void *shm_addr, int shmflg); ###### 入参 1. 第一个参数,shm_id是由shmget函数返回的共享内存标识。 2. 第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。 3. 第三个参数,shm_flg是一组标志位,通常为0。 ###### 返回值 调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1. ##### shmdt 函数 与信号量的semctl函数一样,用来控制共享内存,它的原型如下: int shmctl(int shm_id, int command, struct shmid_ds *buf); ###### 入参 参数shmaddr是shmat函数返回的地址指针 ###### 返回值 调用成功时返回0,失败时返回-1. ##### shmctl 函数 与信号量的semctl函数一样,用来控制共享内存,它的原型如下: int shmctl(int shm_id, int command, struct shmid_ds *buf); ###### 入参 1. 第一个参数,shm_id是shmget函数返回的共享内存标识符 2. 第二个参数,command是要采取的操作,它可以取下面的三个值 : IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。 IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值 IPC_RMID:删除共享内存段 3. 第三个参数,共享内存管理结构体,buf是一个结构指针,它指向共享内存模式和访问权限的结构。 shmid_ds结构至少包括以下成员: struct shmid_ds { struct ipc_perm shm_perm; /* Ownership and permissions */ size_t shm_segsz; /* Size of segment (bytes) */ time_t shm_atime; /* Last attach time */ time_t shm_dtime; /* Last detach time */ time_t shm_ctime; /* Last change time */ pid_t shm_cpid; /* PID of creator */ pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */ shmatt_t shm_nattch; /* No. of current attaches */ ...... }; ###### 返回值 函数返回值 成功:0 出错:-1,错误原因存于error中,错误代码: - EACCESS:参数cmd为IPC_STAT,确无权限读取该共享内存 - EFAULT:参数buf指向无效的内存地址 - EIDRM:标识符为shmid的共享内存已被删除 - EINVAL:无效的参数cmd或shmid - EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行 #### 代码实现 #include #include #include #include include #include static int fd[2]; int main(void) { printf("https://www.codecomeon.com/index \n"); int shmid; if ((shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | IPC_EXCL | 0777)) < 0) { perror("shmget error"); exit(1); } pid_t pid; //初始化管道 if (pipe(fd) < 0) { perror("pipe error"); } if ((pid = fork()) < 0) { perror("fork error"); exit(1); } else if (pid > 0) { //父进程 int *pi = (int *)shmat(shmid, 0, 0); if (pi == (int *)-1) { perror("shmat error"); exit(1); } //往共享内存中写入数据 *pi = 100; *(pi + 1) = 20; //操作完毕,解除共享内存的映射 shmdt(pi); //通知子进程读取数据 char c = 'c'; if (write(fd[1], &c, 1) != 1) { perror("notify pipe error"); } //销毁管道 close(fd[0]); close(fd[1]); //等待子进程结束 wait(0); } else { //子进程 //子进程阻塞,等待父进程先往内存中写入数据 char c; //管道读写默认是阻塞性的 if (read(fd[0], &c, 1) < 0) { perror("wait pipe error"); } //子进程从共享内存中读取数据 //子进程进行共享内存的映射 int *pi = (int *)shmat(shmid, 0, 0); if (pi == (int *)-1) { perror("shmat error"); exit(1); } printf("start: %d, end: %d\n", *pi, *(pi+1)); //读取完毕后解除映射 shmdt(pi); //删除共享内存 shmctl(shmid, IPC_RMID, NULL); //销毁管道 close(fd[0]); close(fd[1]); } return 0; } 内存共享一种效率最高的IPC机制。 本文标题: Linux下进程间通信-共享内存 本文作者: 夏至未至 发布时间: 2022年2月17日 20:48 最近更新: 2022年2月28日 09:39 原文链接: 许可协议: 署名-非商业性-禁止演绎 4.0 国际(CC BY-NC-ND 4.0) 请按协议转载并保留原文链接及作者 进程间通信(8) 共享内存(1) 上一个 Linux进程间通信-信号量 下一个 Linux进程间通信-消息队列 当前文章评论暂未开放,请移步至留言处留言。