Linux进程间通信(下)之共享内存实践

简介: Linux进程间通信(下)之共享内存实践

上节和上上节我们分享了Linux进程间通信的管道、消息队列、信号以及信号量的基本原理和实践,文章如下:


Linux进程间通信(上)之管道、消息队列实践


Linux进程间通信(中)之信号、信号量实践


这节我们就来分享一下Linux的最后一种进程间通信的方式:共享内存。

1、什么是共享内存

共享内存就是两个不相关的进程之间可以直接访问同一段内存,共享内存在两个正在运行的进程之间共享和传递数据起到了非常有效的方式。在不同的进程之间共享的内存通常安排为同一段物理内存,进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以直接访问共享内存中的地址。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程;其实就是映射一段能够被其它内存所访问到的内存,这段内存由一个进程创建,但是多个进程都可以去访问。共享内存是最快的IPC方式,它是通过其它通信方式的效率不足而专门设计的。往往都是和其它通信机制配合使用,来实现进程间的同步和通信。


共享内存的使用和信号量其实也是差不多的,都是使用接口的形式,共享内存的接口比信号量的接口更加的简单,我们一起去了解下共享内存的使用。


共享内存函数由shmget、shmat、shmdt、shmctl四个函数组成。我们下面来分析每一个函数的用法。

1.1、创建共享内存

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

第一个参数是共享内存段的命名,shmget成功时返回一个关于key相关的标识符,用于后续的共享内存函数。当调用失败返回-1。其它进程也可以通过shmget函数返回值访问同一个共享内存。第二个参数是指定共享内存的容量;第三个shmflg是一个权限标志,它的作用和open和mode函数都是相同的,当共享内存不存在的时候则通过IPC_CREAT来创建。共享内存的权限标准和文件读写的权限一样。

1.2、启动对共享内存的访问

void *shmat(int shm_id, const void *shm_addr, int shmflg);

当我们第一次创建完共享内存时,它还不能被任何进程访问,shmat函数就是用来启动对共享内存的访问,并把共享内存连接到当前进程的地址空间。


shm_id是由shmget函数返回的共享内存标识;shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。最后一个参数是标志位通常都是0。调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1。

1.3、共享内存从当前内存中分离

int shmdt(const void *shmaddr);

这个函数只是从共享内存中分离而不是删除,这一点要分清楚,对于初学者而言这里很容易掉坑,使共享内存在当前进程中不可再用。


参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1。

1.4、控制共享内存

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

第一个参数是shaget函数返回的共享内存标识符;command参数是要采取的操作,它由 IPC_STAT、IPC_SET和IPC_RMID组成,分别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;
};

2、共享内存案例

shm_snd.c

#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main()
{
        int shmid;
        char *shmptr;   
        //创建共享内存
        shmid = shmget(0x66, SHM_SIZE, IPC_CREAT|0666);
        //创建失败
        if(shmid < 0)
        {               
            perror("shmget");
            return -1 ;
        }
        //对共享内存的访问
        shmptr = shmat(shmid, 0, 0);
         if (shmptr == (void *)-1)
        {
            perror("shmat");
            return -2 ;
        }
        // 往共享内存写数据
        strcpy(shmptr, "shmat write ok");
        shmdt(shmptr);
        return 0 ;
}

shm_rcv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main()
{
    int shmid;
    char *shmptr;
    shmid = shmget(0x66, SHM_SIZE, IPC_CREAT|0666);
    if(shmid < 0)
    {
        perror("shmget");
        return -1 ;
    }
    shmptr = shmat(shmid, 0, 0);
    if (shmptr == (void *)-1)
    {
        perror("shmat");
        return -2 ;
    }
    // 从共享内存读数据
    printf("read:%s\n", shmptr);
    shmdt(shmptr);
    return 0 ;
}

运行结果:


先分别编译shm_snd.c和shmrcv.c这两个程序,生成shmrcv和shmsnd这两个可执行程序。

640.png

接下来,首先执行shmsnd,会得到以下结果:

640.png

什么都没有?共享内存创建成功了吗?当然是成功了,可以通过ipcs –m命令查看:

640.jpg

如图上图所示,nattch项下的数字为0那个就是刚刚使用shmsnd这个可执行程序创建的一段共享内存。当然,我们还往共享内存发了shmat write ok这个字符串,下面运行shmrcv这个程序,看看是否能把写进共享内存的数据读出来。

640.png

成功读出。同样的,也可以删除共享内存,如何删除?也一样有两种方法。

(1)使用ipcrm –m shmid可以删除共享内存

如上图,我们已经知道0x66的shmid为1835021,所以只要执行ipcrm –m 1835021命令即可删除,如下图所示,成功删除。

640.png

(2)使用shmctl 函数写入IPC_RMID指令删除共享内存

shmrm.c

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(void)
{
        int shmid ;
        //同样,首先先打开共享内存
        shmid = shmget(0x66 , 0 , 0);
        if(-1 == shmid)
        {
                perror("open  shmkey 0x66 fail");
                return -1 ;
        }
        //成功的话,向shmctl写入参数,IPC_RMID表示立刻删除,后面的参数被忽略,为0
        int ret ;
        //写入的是参数
        ret = shmctl(shmid , IPC_RMID , NULL);
        if(ret < 0)
        {
                perror("remove shm fail");
                return -2 ;
        }
        printf("remove key:%d success ... \n" , 0x66);
        return 0 ;
}

运行结果:

640.png

往期精彩

Linux进程间通信(中)之信号、信号量实践


Linux进程间通信(上)之管道、消息队列实践


【Linux系统编程】IO标准缓冲区


替代传统串口屏的Yoxios了解一下!

目录
相关文章
|
1月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
149 67
|
2月前
|
存储 Linux 调度
【Linux】进程概念和进程状态
本文详细介绍了Linux系统中进程的核心概念与管理机制。从进程的定义出发,阐述了其作为操作系统资源管理的基本单位的重要性,并深入解析了task_struct结构体的内容及其在进程管理中的作用。同时,文章讲解了进程的基本操作(如获取PID、查看进程信息等)、父进程与子进程的关系(重点分析fork函数)、以及进程的三种主要状态(运行、阻塞、挂起)。此外,还探讨了Linux特有的进程状态表示和孤儿进程的处理方式。通过学习这些内容,读者可以更好地理解Linux进程的运行原理并优化系统性能。
77 4
|
2月前
|
Linux
Linux:守护进程(进程组、会话和守护进程)
守护进程在 Linux 系统中扮演着重要角色,通过后台执行关键任务和服务,确保系统的稳定运行。理解进程组和会话的概念,是正确创建和管理守护进程的基础。使用现代的 `systemd` 或传统的 `init.d` 方法,可以有效地管理守护进程,提升系统的可靠性和可维护性。希望本文能帮助读者深入理解并掌握 Linux 守护进程的相关知识。
88 7
|
2月前
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
121 5
|
2月前
|
Linux 应用服务中间件 nginx
Linux 进程管理基础
Linux 进程是操作系统中运行程序的实例,彼此隔离以确保安全性和稳定性。常用命令查看和管理进程:`ps` 显示当前终端会话相关进程;`ps aux` 和 `ps -ef` 显示所有进程信息;`ps -u username` 查看特定用户进程;`ps -e | grep &lt;进程名&gt;` 查找特定进程;`ps -p &lt;PID&gt;` 查看指定 PID 的进程详情。终止进程可用 `kill &lt;PID&gt;` 或 `pkill &lt;进程名&gt;`,强制终止加 `-9` 选项。
50 3
|
2月前
|
Linux
linux命令详细说明以及案例
本文介绍了常用的 Linux 命令及其详细说明和示例,包括:`ls`(列出目录内容)、`cd`(更改目录)、`rm` 和 `mv`(删除与移动文件)、`grep`(搜索文本)、`cat`(显示文件内容)以及 `chmod`(更改文件权限)。每个命令均配有功能描述、选项说明及实际案例,帮助用户更好地掌握 Linux 命令的使用方法。
173 56
|
1月前
|
Linux 定位技术
Linux系统中的cd命令:目录切换技巧
踏过千山,越过万水,人生就是一场不断前行的旅程,总充满了未知与挑战。然而,“cd”命令如同你的旅伴,会带你穿梭在如棋盘一般的文件系统中,探索每一处未知。希望你能从“cd”命令中找到乐趣,像是掌控了一种络新妙的魔法,去向未知进发,开始你的探索之旅。
119 24
|
1月前
|
安全 Shell Linux
Linux系统之su命令的基本使用
Linux系统之su命令的基本使用
102 3
Linux系统之su命令的基本使用
|
26天前
|
Linux
Linux命令的基本格式解析
总的来说,Linux命令的基本格式就像一个食谱,它可以指导你如何使用你的计算机。通过学习和实践,你可以成为一个真正的“计算机厨师”,创造出各种“美味”的命令。
64 15
|
1月前
|
监控 数据可视化 Java
调试技巧 - 用Linux命令排查Java问题
总的来说,使用Linux命令来排查Java问题,需要一定的实践经验和理论知识。然而,只要我们愿意花时间深入了解这些工具,我们就能够熟练地使用它们来分析和解决问题。此外,这些工具只是帮助我们定位问题,真正解决问题需要我们对Java和JVM有深入的理解,并能够读懂和分析代码。
71 13

热门文章

最新文章