Linux通信:基于System V共享内存方式实现进程间通信

Linux C 进程间的通信——共享内存、消息队列、信号量 早期:有名管道、无名管道、信号,system V IPC:共享内存、消息队列、信号量。除了这六种以外呢其实还有另一种进程间的通信——域通信,不过域通信就要结合TCP或者UDP来操作,基本和管道差不多,这部分有缘再更。 阅读详情

一、基于共享内存通信方式原理

 在虚拟地址空间中,堆栈之间的区域被称为共享内存区。不同进程间进行通信的前提是看到同一份资源,而共享内存区就是其中之一。操作系统既然可以发生缺页中断申请内存,同样也可以直接外物理内存中申请共享区空间资源,然后通过一定手段让不同进程看到同一块共享内存区,即让不同进程看到同一份资源!!

 基于共享内存通信就是先让操作系统申请一块真实物理空间上的共享内存资源。然后让该物理空间挂接到不同进程各自的共享区上,并建立页表(虚拟地址共享区和真实物理空间的映射)! 这样不同进程就可以通过各自页表查找到同一块资源!

 删除时,只需要先将页表中的映射关系等删除,然后释放共享资源即可!!(共享内存通信方式是基于地址空间,不是基于文件。共享资源的生命周期随内核,所以需要用户手动释放)

在这里插入图片描述

如何保证通信间的进程看到的是同一块共享资源?
 共享区资源存在很多,为了确保不同进程看到的是同一份资源,不同的共享资源是不同唯一的,并且需要唯一的标识符(即key)

 共享资源存在很多,所以操作系统要管理共享内存资源(共享内存多大,还有多少空间可以,有多少个进程使用等)。先描述,在组织!操作系统会为每一个共享内存形成一个结构体对象shm,其中包含了共享内存的各自属性和数据。然后在通过链表或其他数据结构将所有的结构体对象组织起来。从而将对内存资源的管理转化为对链表的增删查改!

 除此之外,操作系统会在shm结构体中维护一个key,用于标记共享内存,具有唯一性。操作系统不仅会将该标识值保存到结构体对象shm中,也会通过一些手段让打算进行通信的不同进程间也获取到该值(比如不同进程通过相同的文件路径,和一个相同的整型值,操作系统通过内部的相关转换算法就可以让不同进程获取到key)

  • 查看共享内存挂载进程控制数目:ipcs -m + shmid。移除:ipcr, -m + shmid

二、共享内存优缺点

2.1 共享内存优缺点

  1. 共享内存的通信方式,不会提供同步机制。共享内存是直接裸露给所有使用者,一定要注意共享内存的使用安全问题!
  2. 共享内存可以提供较大空间。
  3. 共享内存通信方式是最快的。
  4. 共享内存通信方式是基于虚拟地址空间的,但在计算机网络中通信方式是基于文件的。共享内存方式很难和文件进行整合,导致共享内存的通信方式比较独立,导致在实际场景中用处相对不多!

2.2 基于文件通信 vs 共享内存通信

【基于文件的通信方式】:
 Linux下一切皆文件,并且所有的数据转移,本质上都是拷贝。

 所有基于文件的通信方式有如下过程:将键盘上的数据拷贝到用户缓冲区(暂定进程A缓存区)、进程A通过系统调用write将数据拷贝到操作系统内存缓冲区、另一个进程(暂定进程B)通过系统调用接口read将操作系统内核数据拷贝到自身缓冲区中、最后将缓存区数据拷贝到显示器文件中!(共5次拷贝)(当然printf()、scanf()等函数内部还存在语言级缓冲区,没算!)
在这里插入图片描述

【共享内存通信方式】:
&emsp基于共享内存的通信方式A进程可以直接从键盘数据直接写到共享内存中,进程B直接在从共享内存中读取数据打印到显示器上!在这里插入图片描述

  • 通过上述两种通信方式对比,共享内存通信方式是最快的通信方式,没有之一!!!

三、相关函数调用接口

2.1 创建共享内存(shmget)

//原型
 #include <sys/ipc.h>
 #include <sys/shm.h>
 
 int shmget(key_t key, size_t size, int shmflg);

2.1.1 key

  key用于标识待申请的共享内存传递!理论上,该值可以随便填充,但为了减少偶然性,我们一般通过操作系统提供的一些算法接口tok生成。函数原型如下:

 #include <sys/types.h>
 #include <sys/ipc.h>

 key_t ftok(const char *pathname, int proj_id);

 我们只需要先让待进行通信的进程间约定特定的文件路径和proj_id,此处的proj_id用户可以自己随意填充。路径和proj_id都是数据,ftok会结合这两个数据,通过内部的转换算法生成一个整型值,该值可能会和已存在的数字冲突,但概率非常小。

2.1.2 size

 size为待申请内存的空间大小,建议设置成为n*4096。原因在于操作系统申请资源的最小单位为4MB。

2.1.3 shmflg

 shmget函数不仅可以创建共享内存,还可以获取已创建的共享内存的唯一标识key,具体有所传递的shmflg决定的。shmflg参数选项常见的有两种IPC_CREAT和IPC_EXCL

  1. IPC_CREAT:共享内存不存在就创建,否则就获取唯一标识key。
  2. IPC_EXCL单独使用没意义。通常配合IPC_CREAT使用(IPC_CREAT | IPC_EXCL)。此时共享内存资源不存在就创建,否则出错返回,确保创建出的共享内存是全新的!!

 除此之外,shmflg参数还可以传递待创建资源的权限!!如:(IPC_CREAT | IPC_EXCL | 0664)

2.1.4 返回值

 由于共享内存的通信方式不是基于文件的,导致返回值虽然可以标记共享内存,但无法和文件描述符一样,进行各种重定向等工作!而网络通信方式是基于文件的,导致该种通信方式实用性大大受限!

2.2 共享内存段挂接到进程地址空间(shmat)

//原型
 #include <sys/shm.h>

 void *shmat(int shmid, const void *shmaddr, int shmflg);
//返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
  • shmid: 共享内存标识。
  • shmaddr:指定连接的地址。我们通常设为nullptr。表示让操作系统自动选择一块为使用的区域,开辟进程地址空间,然后将共享内存挂接到给地址空间上!
  • shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY,和权限相关。权限我们可以直接在shmget函数中给定。所以我们这里可以直接设为0

2.3 将共享内存段与当前进程脱离(shmdt)

//原型
 #include <sys/types.h>
 #include <sys/shm.h>

 int shmdt(const void *shmaddr);
  • shmaddr: 由shmat所返回的指针。返回值:成功返回0;失败返回-1
  • 注意:将共享内存段与当前进程脱离不等于删除共享内存段!!

2.4 用于控制共享内存(删除)(shmctl)

//原型
 #include <sys/ipc.h>
 #include <sys/shm.h>

 int shmctl(int shmid, int cmd, struct shmid_ds *buf));
  • shmid:由shmget返回的共享内存标识码。
  • cmd:将要采取的动作(有三个可取值)
命令说明
IPC_RMID删除共享内存段
IPC_STAT(获取)将shmid_ds结构中的数据设置为共享内存当前的关联值
IPC_SET(设置)在进程权限足够的前提下,将共享内存的当前关联值设为shmid_ds数据结构中给出的数据
  • buf用于获取当前的共享内存的相关属性信息,具体如下:
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 */
    ...
};

struct ipc_perm {
    key_t          __key;    /* Key supplied to shmget(2) */
    uid_t          uid;      /* Effective UID of owner */
    gid_t          gid;      /* Effective GID of owner */
    uid_t          cuid;     /* Effective UID of creator */
    gid_t          cgid;     /* Effective GID of creator */
    unsigned short mode;     /* Permissions + SHM_DEST and
                                SHM_LOCKED flags */
    unsigned short __seq;    /* Sequence number */
};

四、基于System V共享内存实现进程间通信

 接下来我们会创建两个进程client和server,用于不同进程间通信测试!这里博主规定在server端创建共享内存、读取数据,client端仅向已创建好的共享内存中进行写入!
 由于共享内存方式的进程通信不具备同步机制。这也就意味着我们在写端向共享内存写数据还未写完前,读端就可以直接读数据。读端同理。这就会导致数据失效。所以我们在共享内存的基础上,创建命名管道。利用命名管道同步机制,来控制System V版本进程通信的同步机制!(向命名管道写入信号码,整型code;读端获取到code后遭读取共享内存中的数据)

3.1 GetKey()、CreaterShm、GetShm

 由于不同进程间通信都需要key,和唯一标识(暂时称为shmid)。所以我们可以先在xxx.chh文件中封装事先好相关接口。初次之外,我们还将封装了命名管道创建接口,具体如下:

const std::string pathname = "/home/wzy/Stu/lesson27/shm";//共享内存
const int proj_id = 0x11223344;
const int size = 4096; // 共享内存的大小,设置成为n*4096
const std::string filename = "fifo";//命名管道

key_t GetKey()//获取key值
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key < 0)
    {
        std::cout << "errno:" << errno << " strerror" << strerror(errno) << std::endl;
        exit(0);
    }
    return key;
}

//将直接获取接唯一标识符、创建共享内存后间接唯一标识符接口统一
int GetShmHelper(key_t key, int flag)
{
    int shm = shmget(key, size, flag);
    std::cout << shm << " "  << "get" << std::endl;
    if (shm < 0)
    {
        std::cout << "errno:" << errno << " strerror" << strerror(errno) << std::endl;
        exit(2);
    }
    return shm;
}

//创建共享内存,获取唯一标识符
int CreaterShm(key_t key)
{
    return GetShmHelper(key, IPC_CREAT | IPC_EXCL | 0644);
}

//直接获取唯一标识符(共享内存已经存在)
int GetShm(key_t key)
{
    return GetShmHelper(key, IPC_CREAT);
}

//创建命名管道
bool Makefifo()
{
    int n = mkfifo(filename.c_str(), 0666);
    if (n < 0)
    {
        std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
        return false;
    }
    std::cout << "creater fifo success!" << std::endl;
    return true;
}

3.2 server端创建共享内存、并建立联系

前置工作

 server端的主要工作有:创建共享内存,将共享内存挂接到地址空间,打开命名管道读端、读取共享内存数据(具体合适读取有命名管道控制)、移除共享内存和地址空间映射关系、删除共享内存,关闭命名管道读端!
 所以我们可以将:创建共享内存,将共享内存挂接到地址空间、打开命名管道读端;移除共享内存和地址空间映射关系、删除共享内存、关闭命名管道读端;这些工作封装为一个类,让代码更加优雅!!

class Init
{
public:
    Init()
    {
    	// 1. 创建管道
        bool r = Makefifo(); 
        if (!r)
            exit(1);
            
        // 2.共享内存
        key_t key = GetKey();
        shmid = CreaterShm(key);

        // 3. 虚拟地址空间挂接
        std::cout << "server: 开始将shm映射到虚拟地址空间!" << std::endl;
        s = (char *)shmat(shmid, nullptr, 0);

        // 4. 打开管道
        rfd = open(filename.c_str(), O_RDONLY);
        if (rfd < 0)
        {
            std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
            exit(1);
        }
    }

    ~Init()
    {
        // 1. 移除shm映射关系
        int shmrm = shmdt(s);
        if (shmrm == 0)
            std::cout << "server: 移除成功!" << std::endl;
        else
            std::cout << "server: 移除失败!" << std::endl;

        // 2. 删除共享内存空间
        int rm = shmctl(shmid, IPC_RMID, nullptr);
        if (rm < 0)
            std::cout << "从OS中移除失败" << std::endl;
        else
            std::cout << "从OS中成功移除" << std::endl;
            
          // 3. 关闭命名管道写端
        close(rfd);
    }

public:
    int shmid;
    char *s;
    int rfd;
};

整体代码

 下面我们直接定义Init类的变量,直接调用析构函数,自动完成:创建共享内存,将共享内存挂接到地址空间、打开命名管道读端3向工作!!

 接下来就是读取共享内存数据了。我们通过read读取命名管道中的数据;如果读取到命名管道数据,此时让server端开始读取命名管道中的数据。然后进入下一次循环!

 当上述工作做完后,自动调用Init类的变量的析构函数,回收资源!!

【代码如下】:

int main()
{
	//1. 创建Init类的变量
    Init init;
    
    //开始读取共享内存中的数据
    int code = 0;
    while (true)
    {
        ssize_t n = read(init.rfd, &code, sizeof(code));
        if (n > 0)
        {
            std::cout << "共享内存数据: " << init.s << std::endl;
            sleep(1);
        }
        else
        {
            break;
        }
    }
    return 0;
}

3.3 client端和共享内存建立联系

  既然server端已经创建好了共享内存和命名管道了, client端只需要直接获取该共享内存的唯一标识。然后将该共享内存挂接到自己的地址空间上即可。
 此时我们就可以直接向共享内存中写入数据。当 client端共享内存写完后,立即向命名管道发生任务码(暂定为code),告诉server端可以开始读取共享内存数据了。

 当上述工作做完后, client端的使命就已经完成。最后移除共享内存和地址空间映射关系、关闭命名管道写端即可。

【代码如下】:

int main()
{
	// 1. 获取唯一标识符shmid
    key_t key = GetKey();
    int shmid = GetShm(key);

    // 2. 虚拟地址空间挂接
    char *s = (char *)shmat(shmid, nullptr, 0);
	
	// 3.打开命名管道写端
    int wfd = open(filename.c_str(), O_WRONLY);
    if (wfd < 0)
    {
        std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
        return 1;
    }

    //4. 开始写入数据
    char in = 'a';
    for (; in <= 'z'; in++)
    {
        s[in - 'a'] = in;
        sleep(1);
        
		//发生任务码,通知server端
        int code = 1;
        write(wfd, &code, sizeof(code));
    }

    // 5. 移除shm映射关系、关闭写端
    std::cout << "server:开始将shm从进程地址空间中移除" << std::endl;
    int shmrm = shmdt(s);
    if (shmrm == 0)
        std::cout << "client: 移除成功!" << std::endl;
    else
        std::cout << "client: 移除失败!" << std::endl;

    close(wfd);
    return 0;
}

五、所以代码

【commit.chh】:

#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>

const std::string pathname = "/home/wzy/Stu/lesson27/shm";//共享内存
const int proj_id = 0x11223344;
const int size = 4096; // 共享内存的大小,设置成为n*4096
const std::string filename = "fifo";//命名管道

key_t GetKey()//获取key值
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key < 0)
    {
        std::cout << "errno:" << errno << " strerror" << strerror(errno) << std::endl;
        exit(0);
    }
    return key;
}

//将直接获取接唯一标识符、创建共享内存后间接唯一标识符接口统一
int GetShmHelper(key_t key, int flag)
{
    int shm = shmget(key, size, flag);
    std::cout << shm << " "  << "get" << std::endl;
    if (shm < 0)
    {
        std::cout << "errno:" << errno << " strerror" << strerror(errno) << std::endl;
        exit(2);
    }
    return shm;
}

//创建共享内存,获取唯一标识符
int CreaterShm(key_t key)
{
    return GetShmHelper(key, IPC_CREAT | IPC_EXCL | 0644);
}

//直接获取唯一标识符(共享内存已经存在)
int GetShm(key_t key)
{
    return GetShmHelper(key, IPC_CREAT);
}

//创建命名管道
bool Makefifo()
{
    int n = mkfifo(filename.c_str(), 0666);
    if (n < 0)
    {
        std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
        return false;
    }
    std::cout << "creater fifo success!" << std::endl;
    return true;
}

【server.cc端】:

#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>

class Init
{
public:
    Init()
    {
    	// 1. 创建管道
        bool r = Makefifo(); 
        if (!r)
            exit(1);
            
        // 2.共享内存
        key_t key = GetKey();
        shmid = CreaterShm(key);

        // 3. 虚拟地址空间挂接
        std::cout << "server: 开始将shm映射到虚拟地址空间!" << std::endl;
        s = (char *)shmat(shmid, nullptr, 0);

        // 4. 打开管道
        rfd = open(filename.c_str(), O_RDONLY);
        if (rfd < 0)
        {
            std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
            exit(1);
        }
    }

    ~Init()
    {
        // 1. 移除shm映射关系
        int shmrm = shmdt(s);
        if (shmrm == 0)
            std::cout << "server: 移除成功!" << std::endl;
        else
            std::cout << "server: 移除失败!" << std::endl;

        // 2. 删除共享内存空间
        int rm = shmctl(shmid, IPC_RMID, nullptr);
        if (rm < 0)
            std::cout << "从OS中移除失败" << std::endl;
        else
            std::cout << "从OS中成功移除" << std::endl;
            
          // 3. 关闭命名管道写端
        close(rfd);
    }

public:
    int shmid;
    char *s;
    int rfd;
};


int main()
{
	//1. 创建Init类的变量
    Init init;
    
    //开始读取共享内存中的数据
    int code = 0;
    while (true)
    {
        ssize_t n = read(init.rfd, &code, sizeof(code));
        if (n > 0)
        {
            std::cout << "共享内存数据: " << init.s << std::endl;
            sleep(1);
        }
        else
        {
            break;
        }
    }
    return 0;
}

【client端】:

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "comm.chh"

int main()
{
	// 1. 获取唯一标识符shmid
    key_t key = GetKey();
    int shmid = GetShm(key);

    // 2. 虚拟地址空间挂接
    char *s = (char *)shmat(shmid, nullptr, 0);
	
	// 3.打开命名管道写端
    int wfd = open(filename.c_str(), O_WRONLY);
    if (wfd < 0)
    {
        std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
        return 1;
    }

    //4. 开始写入数据
    char in = 'a';
    for (; in <= 'z'; in++)
    {
        s[in - 'a'] = in;
        sleep(1);
        
		//发生任务码,通知server端
        int code = 1;
        write(wfd, &code, sizeof(code));
    }

    // 5. 移除shm映射关系、关闭写端
    std::cout << "server:开始将shm从进程地址空间中移除" << std::endl;
    int shmrm = shmdt(s);
    if (shmrm == 0)
        std::cout << "client: 移除成功!" << std::endl;
    else
        std::cout << "client: 移除失败!" << std::endl;

    close(wfd);
    return 0;
}

六、所有代码(debug版本)

 下面博主在上述代码中,增加调试信息。

【commit.chh】:

#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>

const std::string pathname = "/home/wzy/Stu/lesson27/shm";
const int proj_id = 0x11223344;
const int size = 4096; // 共享内存的大小,设置成为n*4096
const std::string filename = "fifo";

key_t GetKey()
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key < 0)
    {
        std::cout << "errno:" << errno << " strerror" << strerror(errno) << std::endl;
        exit(0);
    }
    return key;
}

int GetShmHelper(key_t key, int flag)
{
    int shm = shmget(key, size, flag);
    std::cout << shm << " "  << "get" << std::endl;
    if (shm < 0)
    {
        std::cout << "errno:" << errno << " strerror" << strerror(errno) << std::endl;
        exit(2);
    }
    return shm;
}

int CreaterShm(key_t key)
{
    return GetShmHelper(key, IPC_CREAT | IPC_EXCL | 0644);
}

int GetShm(key_t key)
{
    return GetShmHelper(key, IPC_CREAT);
}

std::string toHex(int id)
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "0x%x", id);
    return buffer;
}

bool Makefifo()
{
    int n = mkfifo(filename.c_str(), 0666);
    if (n < 0)
    {
        std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
        return false;
    }
    std::cout << "creater fifo success!" << std::endl;
    return true;
}

【server.cc端】:

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "comm.chh"

class Init
{
public:
    Init()
    {
        bool r = Makefifo(); // 创建管道
        if (!r)
            exit(1);
        // 创建共享内存
        key_t key = GetKey();
        shmid = CreaterShm(key);
        std::cout << "server: key:" << key << ", shmid:" << shmid << std::endl;

        // 虚拟地址空间挂接
        std::cout << "server: 开始将shm映射到虚拟地址空间!" << std::endl;
        s = (char *)shmat(shmid, nullptr, 0);
        std::cout << s << std::endl;
        // sleep(10);

        // 打开管道
        rfd = open(filename.c_str(), O_RDONLY);
        std::cout << filename.c_str() << ":" << rfd << std::endl;
        if (rfd < 0)
        {
            std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
            exit(1);
        }
    }

    ~Init()
    {
        // 移除shm映射关系
        std::cout << "server: 开始将shm从进程地址空间中移除" << std::endl;
        int shmrm = shmdt(s);
        if (shmrm == 0)
            std::cout << "server: 移除成功!" << std::endl;
        else
            std::cout << "server: 移除失败!" << std::endl;
        // sleep(2);

        // 删除共享内存空间
        std::cout << "开始将shm从OS中删除" << std::endl;
        int rm = shmctl(shmid, IPC_RMID, nullptr);
        if (rm < 0)
            std::cout << "从OS中移除失败" << std::endl;
        else
            std::cout << "从OS中成功移除" << std::endl;
        close(rfd);
    }

public:
    int shmid;
    char *s;
    int rfd;
};

int main()
{

    Init init;

    // TODO
    int code = 0;
    while (true)
    {
        ssize_t n = read(init.rfd, &code, sizeof(code));
        if (n > 0)
        {
            std::cout << "共享内存数据: " << init.s << std::endl;
            sleep(1);
        }
        else
        {
            break;
        }
    }

    return 0;
}

【client端】:

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "comm.chh"

int main()
{
    key_t key = GetKey();
    int shmid = GetShm(key);
    std::cout << "client: key:" << key << ", shmid:" << shmid << std::endl;

    // 虚拟地址空间挂接
    std::cout << "client: 开始将shm映射到虚拟地址空间!" << std::endl;
    char *s = (char *)shmat(shmid, nullptr, 0);
    std::cout << s << std::endl;
    // sleep(3);

    int wfd = open(filename.c_str(), O_WRONLY);
    if (wfd < 0)
    {
        std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
        return 1;
    }

    // TODO
    char in = 'a';
    for (; in <= 'z'; in++)
    {
        s[in - 'a'] = in;
        sleep(1);
        int code = 1;
        write(wfd, &code, sizeof(code));
    }

    sleep(2);
    // 移除shm映射关系
    std::cout << "server:开始将shm从进程地址空间中移除" << std::endl;
    int shmrm = shmdt(s);
    if (shmrm == 0)
        std::cout << "client: 移除成功!" << std::endl;
    else
        std::cout << "client: 移除失败!" << std::endl;

    close(wfd);
    return 0;
}

5.1 最终结果

请添加图片描述

进程间的通信 目录 一.进程间通信的介绍 1.1通信的目的 1.2进程间通信的分类 二.匿名管道 2.1概念 2.1.2使用步骤 2.2管道读写规则 2.3特点 2.4多进程派发任务的实现 2.5管道大小 三.命名管道 3.1概念 3.2命名管道的打开规则 3.3命名管道演示 3.3.1用命名管道实现serve&client通信 3.3.2用命名管道实现进程控制 3.4命名管道和匿名管道的区别 3.5命令行中的管道 四.system V进程间通信 4.1system V 阅读详情

相关推荐

OASIS协议标准文档的解读_第一部分

OASIS标准协议的文档的解读。完整原协议的中文翻译和解读。

洋葱的专栏 4903

进程间通信之共享内存

共享内存实现进程间通信

m0_70749276的博客 2949

部署资源管理系统chemx

资源管理系统的部署教程

weixin_40784215的博客 823

【共享内存】共享内存(Shared Memory)与进程通信

共享内存作为一种高效的进程间通信机制,以其独特的优势在多进程环境中发挥着重要的角色。它允许多个进程直接访问同一块内存区域,从而实现了数据的快速共享和交换,显著提高了系统性能。然而,这也带来了数据同步和并发控制的挑战。我们需要结合锁、信号量等同步技术来解决这些问题。 总的来说,共享内存是一种强大而灵活的工具,但需要谨慎、有效地使用,以确保程序的正确性和稳定性。

crr411422的博客 7789

按照autoware.universe官方从零搭建自己的自动导航小车(三)(续)

本文介绍了在Autoware框架下完善自定义传感器套件描述包的详细过程。主要内容包括:1)创建传感器描述包tinyplc_sensor_kit_description,包含YAML标定文件和URDF模型;2)配置传感器与车辆基座的相对位姿,实现sensor_kit.xacro和sensors.xacro宏文件;3)更新sensor_kit.launch.xml文件以集成激光雷达和GNSS/IMU驱动。通过xacro验证URDF模型正确性后,下一步计划接入Helios16激光雷达数据并在Rviz2中验证点云显

qq_45335326的博客 1273

OTN传送网实战指南:从光层到电层的完整架构解析(附配置示例)

本文深入解析OTN传送网从光层到电层的完整架构,涵盖关键组件、技术实现及配置示例。通过高速公路与光传送网的类比,详细介绍了光层超宽传输管道构建、电层业务调度核心引擎以及控制平面智能运维实战。附有典型组网方案与故障排查指南,助力网络工程师高效部署与维护OTN传送网。

tomato的博客 684

共享内存(进程间的通信方式)

共享内存,函数接口定义及代码演示

m0_68210771的博客 6096

ATGM332D模块串口配置保姆级教程:从9600波特率到NMEA数据过滤,一次搞定

本文提供ATGM332D模块串口配置的详细教程,涵盖从基础硬件连接到高级NMEA数据过滤的全过程。内容包括波特率优化、数据输出频率调整、NMEA语句过滤等实用技巧,帮助开发者快速掌握GNSS模块配置,提升定位系统性能。

weixin_30664051的博客 513

十二、进程间通信——共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。共享内存是一种用于实现进程间通信(IPC)的方法,不同进程通过访问同一块内存区域实现数据共享和交互。每个进程可以将自身的虚拟地址映射到物理内存中的特定区域,当不同进程将相同的物理内存区域与各自的虚拟地址空间关联时,这些进程就能实现通过共享内存来完成IPC。若某进程更改了共享内存区的内容,其它进程都会觉察到该区域的更改。

weixin_54447296的博客 3818

ONFI5.0命令字实战指南:从基础操作到高级应用

本文深入解析ONFI5.0协议的核心——命令字,提供从基础操作到高级应用的实战指南。详细拆解了NAND闪存的复位、读状态、数据读取(00-30)、页编程(80-10)和块擦除(60-D0)等关键命令周期,并探讨了多平面操作与缓存操作等高级性能优化技巧,帮助开发者高效、稳定地与NAND芯片进行底层交互。

vscode5coder的博客 463

Java使用POI生成柱状图导出到word文档(柱状图)

本篇文章主要介绍,如何使用Apache POI组件生成柱状图导出到word文档中,具体步骤看下文。 一、实现效果 Java使用POI技术生成柱状图导出到word文档中,最终生成的柱状图如下所示: 二、环境准备 编程语言:Java 第三方依赖:Apache POI Apache POI依赖代码如下所示: <dependencies> <dependency> <groupId>org.apache.poi</groupId.

✅ Java 开发工程师 7376

进程间通信【共享内存】

进程间通信 共享内存

weixin_53230235的博客 2143

Android 13 Settings蓝牙列表卡顿问题排查及优化过程

此时我们的卡顿优化基本完成了,我们应该要考虑内存是否会泄漏,如果大家不知道哪些需要回收可以用leakcanary和android profiler进行内存检测,看代码有没有内存泄漏。

龚礼鹏的博客 2548

Linux《进程间通信(下)》

本文介绍了Linux进程间通信(IPC)的SystemV机制,包括共享内存、消息队列和信号量三种方式。共享内存是最快的IPC形式,通过将物理内存映射到进程地址空间实现;消息队列使用内核缓冲区维护消息链表,通过类型标识区分不同进程;信号量本质是计数器,用于进程间的同步互斥。文章详细讲解了每种机制的系统调用接口,并提供了C++实现示例代码。重点分析了共享内存无内置同步机制的特性,以及如何结合命名管道实现同步;消息队列的责任链模式应用;信号量的建造者模式实现。通过具体的代码演示,展示了这些IPC机制的实际应用场景

mljy.博客 1366

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Linux之进程间通信之消息队列,共享内存,信号量

消息队列 消息队列的结构有很多,但在这里讲的队列的结构都是基于链表实现的. 进程间通信的实质是:让不同的进程可以看到相同的资源. 消息队列的特点: 消息队列的通信是通过发送有类型数据块的方法. 每个数据块都有一个类型,接收方接受的数据块可以有不同的类型值. 所以:消息队列提供了一个进程向另一个进程发送了有类型数据块的方法. 实现消息队列的相关函数: #include &amp;amp;amp;lt;sys...

Yinghuhu333333的博客 963

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Linux System V IPC机制深度解析:共享内存、消息队列与信号量

在多进程编程中,进程间通信(IPC)是一个核心话题。System V IPC是Unix/Linux系统中经典的进程间通信机制,它包括共享内存、消息队列和信号量。本文将深入探讨这三种IPC机制的原理、使用方法和内核实现。

2401_86779365的博客 787

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Linux进程间通信(IPC)——system V

本文详细介绍了Linux系统下的三种进程间通信(IPC)机制:共享内存、消息队列和信号量。重点讲解了共享内存的实现原理,包括创建(shmget)、关联(shmat)、去关联(shmdt)和删除(shmctl)等系统调用,并通过示例代码展示了如何使用共享内存进行进程间通信。文章还分析了共享内存的优缺点,指出其通信速度快但缺乏同步机制的特点,并提供了通过管道实现同步的解决方案。最后简要介绍了消息队列和信号量的基本概念及使用方法,阐述了IPC资源在内核中的组织方式。这些IPC机制为Linux系统下进程间高效通信提

KEEP ON KEEPING ON 1180

【Linux系统编程】进程间通信:管道+共享内存

1,进程间通信的目的数据传输:一个 进程需要将它的数据发送给另一个进程资源共享:多个进程之间共享同样的资源。通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。进程控制:有些进程希望完全控制另⼀个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷⼊和异常,并能够及时知道它的状态改变。2,进程间通信的方式管道:匿名管道pipe,命名管道消息队列,System V 共享内存,System V 信号量。

sillyoung的博客 20

进程间通信(IPC)原理与实战:命名管道与共享内存解析

本文深入探讨命名管道与System V共享内存两种IPC机制。命名管道通过文件系统路径让任意进程通信,详解其创建、打开及阻塞/非阻塞规则;共享内存通过虚拟内存映射实现最快数据交换,剖析了`ftok`、`shmget`、`shmat`等核心接口。对比两者在速度、同步机制、生命周期上的差异。最后指出,无论何种方式,IPC的本质都是让不同进程看到同一份资源。

2402_90030627的博客 493

mmap映射区和shm共享内存的区别总结

linux中的两种共享内存。一种是我们的IPC通信System V版本的共享内存,另外的一种就是我们今天提到的存储映射I/O(mmap函数)在说mmap之前我们先说一下普通的读写文件的原理,进程调用read或是write后会陷入内核,因为这两个函数都是系统调用,进入系统调用后,内核开始读写文件,假设内核在读取文件,内核首先把文件读入自己的内核空间,读完之后进程在内核回归用户态,内核把读入内核内存的...

a2796749的专栏 496

进程间通信IPC

进程通信IPC

m0_62591054的博客 187

32.进程间通信

本文摘要: 共享内存管理接口shmctl可通过IPC_RMID命令删除共享内存,shmid指定内存块标识。shmat实现进程地址空间与共享内存的映射,返回虚拟起始地址。共享内存提供连续虚拟地址空间,通过起始地址和大小即可访问。shmget创建、shmctl删除、shmat挂接,权限需设为0666。共享内存类似文件,重复创建需先删除。ipcs -m可查看使用情况(nattch计数)。mmap方式可实现文件与内存映射,用于动态库加载。共享内存无数据保护机制,需结合管道实现进程同步,如通过命名管道控制读写时序。操

qq_55640460的博客 67

linux当中的六大进程间通信方式

Unix域套接字 (Unix Domain Socket) 最初由BSD开发,是最通用的IPC机制,支持不同计算机间的网络通信。POSIX 共享内存通过内存映射文件(memfd)实现,使用文件描述符操作,比 System V 共享内存的 shmget/shmat 接口更统一。POSIX 消息队列相比 System V 消息队列,提供了基于优先级的消息传递、异步通知机制(信号或线程)、以及更简洁的文件描述符式 API。它提供了更现代化的API,并且新增了线程同步原语(如互斥量、条件变量)。

wxr2268715051的博客 33

Linux IPC 1 之 概述

Linux IPC 1 之 概述Linux IPC 1 之 概述背景 进程的含义 IPC的含义 三个名词 IPC的6种方式背景: 以前学习研究的Linux进程间通信,经常不用都要忘了,而且发现写的博客也有些不够完整,所以干脆推倒重来,温故知新而已:) 进程的含义进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放。可以认为进程是一个程序

cloud 的学习时代 639

进程间通信

特定: 只针对父子进程的通信解释:其他进程无法通过文件名获取对应的文件描述符,文件描述符是作用于进程的不同进程同一个文件描述符打开的文件不同,子进程通过写时拷贝获取父进程打开文件的文件描述符进行交流。,是内核(级)中的数据缓冲区,不是持久化存储它的"数据"是流式的、临时的,读走就消失了没有"文件大小"的概念,只有缓冲区中当前的数据量。匿名管道特点只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。

abssg的博客 377

Linux系统编程:线程死锁与进程通信-->核心问题解析

本文摘要: 主要探讨进程间通信机制中的死锁问题和常用通信方式。死锁部分分析其定义、发生条件(互斥、不可剥夺、请求保持、循环等待)及处理策略。重点介绍了三种进程间通信方式:1)管道(无名/有名),包括创建流程和父子进程通信实现;2)信号通信,讲解signal函数注册及处理机制;3)共享内存等System V IPC方式。通过代码示例演示了无名管道在父子进程间的双向通信实现,以及有名管道在任意进程间的通信应用,同时分析了管道的半双工特性和阻塞行为。

2301_77695365的博客 803
上一篇: Linux进程间通信:匿名管道 &命名管道
下一篇: Linux: Mysql环境安装
小白debug~
小白debug~ 新星创作者: C/C++技术领域 新星创作者: C/C++技术领域
博客等级 码龄4年 3510粉丝 128原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白debug~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值