Linux-代码实现通过system v共享内存实现的进程间的通信

一.makefile编写

.PHONY:all
all:processa processb

processa : processa.cc
	g++ -o $@ $^ -g -std=c++11
processb : processb.cc
	g++ -o $@ $^ -g -std=c++11

.PHONY:clean
clean:
	rm -rf processa processb

二.创建system v共享内存

        1.系统调用接口          

                key:a.key是一个数值,在内核中具有唯一性,可以让不同的进程标识唯一的共享内存。

                        b.第一个进程可以通过key创建共享内存,从此第二个进程在拿着相同的key就可以和第一个进程看到同一块共享内存了。

                        c.这个key保存在共享内存的描述结构体中。

                        d.需要强调的是key是由用户给出相应的信息然后,系统根据算法来返回一个计算到的key。

                        ftok函数调用成功返回key值,失败返回-1。

                        用户在程序中调用这个函数就可以得到一个key。然后将这个key传给shmget就可以得到一个又这个key在内核中唯一标识的共享内存。

                size:所需要的共享内存的大小

            shmflg:IPC_CREAT(可以单独使用)其作用是如果这个key在内核中没有就创建一个新的共享内存,如果有就直接获取它。

                           IPC_EXCL(不可以单独使用要配合IPC_CREAT一起使用)作用是如果申请的共享空间存在了就会出错返回,不存在就创建,确保我们每次的调用创建的都是新的共享内存。

                返回值:成功返回共享内存的标识符shmid,识别返回-1。 

                shmid和key的对比:

                                shmid是在进程内用来表示共享内存唯一性的。

                                 key是在内核中表示共享内存唯一性的。一般这个key只在创建共享内存的时候使用,其他的时候就不用了。

        

        2.代码实现
#include <string>
#include <cerrno>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

#include "log.hpp"

using namespace std;

//使用日志
Log log;

const string pathname = "/home/cky";
const int proj_id     = 0x6666;

key_t GetKey()
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if(key < 0)
    {
        log(Fatal, "ftok error : %s", strerror(errno));
        exit(1);
    }
    log(Info, "ftok success, key is %d", key);

    return key;

}

#define SIZE 4096
int GetshareMemHelper(int flag)
{
    key_t key = GetKey();

    int shmid = shmget(key, SIZE, flag);
    if(shmid < 0)
    {
        log(Fatal, " create share memory error : %s", strerror(errno));
        exit(1);
    }
    log(Info, " create share memory success, shmid is : %d", shmid);

    return shmid;
}

int Greateshm()
{
    return GetshareMemHelper(IPC_CREAT | IPC_EXCL);
}

int Getshm()
{
    return GetshareMemHelper(IPC_CREAT);
}

                查看我们创建的共享内存:

                创建的共享内存如果我们不手动释放掉的话,即使是使用的进程退出了,系统也不会主动的释放掉它。 

                perm:是共享内存的使用权限,此时我们的程序中并没有指定权限所以是0。 

int Greateshm()
{
    return GetshareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}

int Getshm()
{
    return GetshareMemHelper(IPC_CREAT);
}

 

                nattch:是同这个共享内存关联的进程数,此时我们并没有为这个新创建的共享内存关联进程,所以是0。 

                在bash上删除我们创建的共享内存:

        

三.将创建好的共享内存同进程关联在一起 ,去除关联,释放共享内存资源

        1.系统调用接口                

                shmaddr:将指定的共享内存关联到进程地址空间中的什么位置,如果是NULL是由操作系统决定,此时具体在什么位置通过返回值来确定。

                shmflg:默认是0;

         

                        去关联函数。

                        cmd:IPC_RMID 是设置为删除。

                        buf:是类似于内核中管理共享内存的结构体,删除时设置为NULL就可以。

        2.代码实现
    //同共享内存关联
    char* shmaddr = nullptr;
    shmaddr = (char*)shmat(shmid, NULL, 0);


    //去除关联
    shmdt(shmaddr);

    //释放共享内存资源
    shmctl(shmid, IPC_RMID, NULL);

          一但创建好的共享内存和进程所关联了,那么就可以直接使用了,不需要系统调用。

四.进行通信

        processa.cc

#include "comm.hpp"

int main(void)
{
    //创建共享内存
    int shmid = 0;
    shmid = Greateshm();

    //同共享内存关联
    char* shmaddr = nullptr;
    shmaddr = (char*)shmat(shmid, NULL, 0);

    //进行通信
    while(true)
    {
        cout << "client say@ " << shmaddr << endl; //直接访问共享内存
    }

    //去除关联
    shmdt(shmaddr);

    //释放共享内存资源
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

        processb.cc

#include "comm.hpp"

int main()
{
    int shmid = Getshm();
    char *shmaddr = (char*)shmat(shmid, nullptr, 0);

    
    // 一旦有了共享内存,挂接到自己的地址空间中,你直接把他当成你的内存空间来用即可!
    // 不需要调用系统调用
    // ipc code
    while(true)
    {
        cout << "Please Enter@ ";
        fgets(shmaddr, 4096, stdin);

    }

    shmdt(shmaddr);

    return 0;
}

        单纯的共享内存是没有同步互斥的,但是可以通过加管道的方式来实现。

五.通过管道实现共享内存的同步互斥

        命名管道:

enum
{
    FIFO_CREATE_ERR = 1,
    FIFO_DELETE_ERR,
    FIFO_OPEN_ERR
};

#define MODE 0664
#define FIFO_FILE "./myfifo"

class Init
{
public:
    Init()
    {
        // 创建管道
        int n = mkfifo(FIFO_FILE, MODE);
        if (n == -1)
        {
            perror("mkfifo");
            exit(FIFO_CREATE_ERR);
        }
    }
    ~Init()
    {

        int m = unlink(FIFO_FILE);
        if (m == -1)
        {
            perror("unlink");
            exit(FIFO_DELETE_ERR);
        }
    }
};

        processa.cc

#include "comm.hpp"

int main(void)
{
    //创建共享内存
    int shmid = 0;
    shmid = Greateshm();

    //同共享内存关联
    char* shmaddr = nullptr;
    shmaddr = (char*)shmat(shmid, NULL, 0);

    //创建管道
    Init init;

    //打开管道
    int fd = open(FIFO_FILE, O_RDONLY);
    if(fd < 0)
    {
        log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);
        exit(FIFO_OPEN_ERR);
    }

    //进行通信
    while(true)
    {
        char c;
        ssize_t s = read(fd, &c, 1);
        if(s == 0) break;
        else if(s < 0) break;
        
        cout << "client say@ " << shmaddr << endl; //直接访问共享内存
    }

    //去除关联
    shmdt(shmaddr);

    //释放共享内存资源
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

        processb.cc

#include "comm.hpp"

int main()
{
    int shmid = Getshm();
    char *shmaddr = (char*)shmat(shmid, nullptr, 0);

    int fd = open(FIFO_FILE, O_WRONLY);
    if(fd < 0)
    {
        log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);
        exit(FIFO_OPEN_ERR);
    }
    
    // 一旦有了共享内存,挂接到自己的地址空间中,你直接把他当成你的内存空间来用即可!
    // 不需要调用系统调用
    // ipc code
    while(true)
    {
        cout << "Please Enter@ ";
        fgets(shmaddr, 4096, stdin);

        write(fd, "c", 1); // 通知对方
    }

    shmdt(shmaddr);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_52159554/article/details/134860333