0
点赞
收藏
分享

微信扫一扫

mysql自动备份多个数据库

Go_Viola 2023-07-19 阅读 23

共享内存


1.进程间通信的分类:
(1)管道:1、匿名管道pipe;2、命名管道mkfifo
(2)System V IPC:1、System V 消息队列;2、System V 共享内存;3、System V 信号量。
(3)POSIX IPC:1、消息队列;2、共享内存;3、信号量;4、互斥量;5、条件变量;6、读写锁。
前面已经了解了进程间管道通信,那么共享内存又是什么原理?

1.共享内存的概念

什么是共享内存?
共享内存通信是一种进程间通信的方式,它允许两个或更多进程访问同一块内存,就如同 malloc () 函数向不同进程返回了指向同一个物理内存区域的指针。共享内存是 Unix/Linux下的多进程之间的通信方法,这种方法通常用于一个程序的多进程间通信,实际上多个程序间也可以通过共享内存来传递信息。而且共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

共享内存和管道的区别:

共享内存示意图:
在这里插入图片描述

共享内存数据结构:
用man shmctl指令可以查看共享内存的数据结构。

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 */
    ...
};

而其中ipc_perm是一个内核为每个IPC对象所维护的一个数据结构,如下:

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 */
};

2.共享内存函数

2.1 shmget函数

shmget函数功能:用来创建共享内存。

NAME
       shmget - allocates a System V shared memory segment
	   //分配System V共享内存段
SYNOPSIS
       #include <sys/ipc.h>
       #include <sys/shm.h>

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

DESCRIPTION
       shmget() returns the identifier of the System V shared memory segment associated with the value of the argument key.  A new shared memory seg?
       ment, with size equal to the value of size rounded up to a multiple of PAGE_SIZE, is created if key has the value  IPC_PRIVATE  or  key  isn't
       IPC_PRIVATE, no shared memory segment corresponding to key exists, and IPC_CREAT is specified in shmflg.

       If shmflg specifies both IPC_CREAT and IPC_EXCL and a shared memory segment already exists for key, then shmget() fails with errno set to EEX?
       IST.  (This is analogous to the effect of the combination O_CREAT | O_EXCL for open(2).)

       The value shmflg is composed of:

       IPC_CREAT   to create a new segment.  If this flag is not used, then shmget() will find the segment associated with key and check  to  see  if
                   the user has permission to access the segment.

       IPC_EXCL    used with IPC_CREAT to ensure failure if the segment already exists.

RETURN VALUE
       On success, a valid shared memory identifier is returned.  On errir, -1 is returned, and errno is set to indicate the error.
       //成功返回有效的共享内存标识符,失败返回-1,并且错误码被设置。

ftok函数:

NAME
       ftok - convert a pathname and a project identifier to a System V IPC key

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

       key_t ftok(const char *pathname, int proj_id);
       
RETURN VALUE
       On success, the generated key_t value is returned.  On failure -1 is returned, with errno indicating the error as for the stat(2) system call.

ftok会将这个路径pathname和proj_id(可以随便写)经过算法生成出一个冲突概率低的值。

2.2 shmat函数

shmat函数功能:将共享内存段连接到进程地址空间。

返回值为一个指针,并且指针指向共享内存,所以使用这个指针进行数据的写入或读出。

2.3 shmdt函数

shmdt函数功能:将共享内存段与当前进程脱离。

2.4 shmctl函数

shmctl函数功能:用于控制共享内存。

命令说明
IPC_STAT将shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,将共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

3. 共享内存的使用

使用之前,先认识下面的IPC指令,共享内存,消息队列,信号量等指令基本相似,所以在使用共享内存,消息队列,信号量进行通信时,其都有一批函数,总的说是大同小异,但是原理是不同的。

查看命令删除命令
ipcs -m : 查看共享内存ipcrm -m shmid : 删除共享内存
ipcs -q : 查看共享内存ipcrm -q msqid : 删除消息队列
ipcs -s : 查看共享内存ipcrm -s semid : 删除信号量

comm.hpp代码如下:

#pragma once
#include <iostream>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <cassert>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
using namespace std;

//  IPC_CREAT and IPC_EXCL
// 单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回
// IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
// IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!

#define PATHNAME "."
#define PROJID 0x6666

const int gsize = 4096; // 共享内存的大小

key_t getKey()
{
    key_t k = ftok(PATHNAME, PROJID); //key_t ftok(const char *pathname, int proj_id);
    if(k == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

server.cc代码如下:

#include "comm.hpp"

int main()
{
    //1.创建共享内存先要创建一个key_t k(ftok)
    key_t k = getKey();

    //2.创建一个共享内存(shmget)
    umask(0); //默认权限
    int shmid = shmget(k, gsize, IPC_CREAT | IPC_EXCL | 0666); //int shmget(key_t key, size_t size, int shmflg)
    // 因为server创建共享内存,所以第三个参数为IPC_CREAT | IPC_EXCL,这个共享内存一定是最新的
    if(shmid == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(2);
    }

    //3.将共享内存段连接到进程地址空间(shmat)
    char* start = (char*)shmat(shmid, nullptr, 0);
    if(*start == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(3);
    }

    //3.写入信息"i am process server"
    char buffer[64] = "i am process server";
    int i = 0;
    while (buffer[i])
    {
        start[i] = buffer[i];
        ++i;
    }
    //start = buffer; 错误写法,因为这样写直接就将start指针修改,start就不是指向共享内存的地址
    sleep(10);

    //将共享内存段与当前进程脱离(shmdt)
    int n = shmdt(start);
    assert(n != -1);
    (void)n;

    //4.删除共享内存(shmctl)
    int m = shmctl(shmid, IPC_RMID, nullptr); // IPC_RMID | 删除共享内存段
    assert(m != -1);
    (void)m;

    return 0;
}

client.cc代码如下:

#include "comm.hpp"

int main()
{
    //1.获取已经存在的共享内存
    key_t k = getKey();

    int shmid = shmget(k, gsize, IPC_CREAT); //int shmget(key_t key, size_t size, int shmflg)
    // 单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回;
    // 这里获取的是已经存在的


    //2.将共享内存段连接到进程地址空间(shmat)
    char* start = (char*)shmat(shmid, nullptr, 0);
    if(*start == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(3);
    }

    //3.从共享内存中读取数据
    int m = 3;
    while (m--)
    {
        cout << "i am client,i read: " << start << endl;
        sleep(3);
    }

    //将共享内存段与当前进程脱离(shmdt)
    int n = shmdt(start);
    assert(n != -1);
    (void)n;

    return 0;
}

makefile代码如下:

.PHONY:all
all:server client

server:server.cc
	g++ -o $@ $^ -std=c++11
client:client.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f server client

运行结果如下:
在这里插入图片描述

如果在自写代码中有如下错误,File exists,这是因为执行server.cc程序,程序并不是完整退出,而是程序进行一半时退出,例如:程序进行一半时按ctrl+c强制退出,程序没有执行到最后,也就是共享内存没有被删除,这时,就可以用ipcs -m查看共享内存;
在这里插入图片描述
然后ipcrm -m shmid(4)如下shmid为4,进行删除,重新运行程序即可。
在这里插入图片描述
如果是别的问题,那一定是代码错着,仔细检查代码吧。

举报

相关推荐

0 条评论