linux进程间通信--共享内存

2021-03-06

共享内存

共享内存是System V版本的最后一个进程间通信方式。共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

函数介绍

共享内存相关函数有shmget(),shmat(),shmdt(),shmctl()

shmget – 用来获得共享内存区域的ID。

函数定义

1
2
3
4
#include <sys/ipc.h>
#include <sys/shm.h>

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

参数:

返回值:

错误代码:

shmat – 把共享内存对象映射到调用进程的地址空间

函数定义

1
2
3
4
#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

返回值

错误代码:

shmdt – 断开共享内存的连接

1
2
3
4
#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);

参数: shmaddr: 连接的共享内存的起始地址

返回值:

errno:

shmctl – 完成对共享内存的控制

1
2
3
4
#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 */
};

返回值:

errno:

示例

头文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// shm.h
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
#define TEXT_SZ 2048
struct shared_use_st
{  
    int written;//作为一个标志,非0:表示可读,0表示可写 
    char text[TEXT_SZ];//记录写入和读取的文本
};
#endif

write

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "shm.h"
#include <errno.h>

int main()
{
    key_t key = 1234;
    struct shared_use_st *shared;
    int size;
    int shmid;
    int running = 1;
    shmid = shmget(key, sizeof(struct shared_use_st), 0666|IPC_CREAT);

    if(shmid == -1) {
        perror("shmget");
        return -1;
    }

    printf("memory attached at 0x%X\n", shmid);

    shared = shmat(shmid, 0, 0);
    if(!shared) {
        perror("shmat");
        return -1;
    }

    shared->written = 0;

    while(running) {
        if(shared->written != 0) {

            printf("wait.....\n");
            sleep(1);
        }
        else
        {
            printf("enter buf:");
            fgets(shared->text, BUFSIZ, stdin);
            shared->written = 1;
            if(strncmp(shared->text, "exit", 4) == 0) {
                running = 0;
            }
        }
    }

    printf("shadt\n");

    if(shmdt(shared) == -1) {
        perror("shmdt");
        return -1;
    }

    // printf("shmctl\n");
    // if(shmctl(shmid, IPC_RMID, 0) == -1) {
    //     perror("shmctl");
    //     return -1;
    // }

    return 0;
}

read

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "shm.h"
#include <errno.h>

int main()
{
    key_t key = 1234;
    struct shared_use_st *shared;
    int size;
    int shmid;
    int running = 1;
    shmid = shmget(key, sizeof(struct shared_use_st), 0666|IPC_CREAT);

    if(shmid == -1) {
        peror("shmget");
        return -1;
    }

    printf("memory attached at 0x%X\n", shmid);

    shared = shmat(shmid, 0, 0);
    if(!shared) {
        peror("shmat");
        return -1;
    }

    shared->written = 0;

    while(running) {
        if(shared->written != 0) {

            shared->written = 0;
            printf("read text:%s\n", shared->text);
            if(strncmp(shared->text, "exit", 4) == 0) {
                running = 0;
            }
        }
        else
        {
            sleep(1);
        }
    }

    printf("shadt\n");

    if(shmdt(shared) == -1) {
        perror("shmdt");
        return -1;
    }

    printf("shmctl\n");
    if(shmctl(shmid, IPC_RMID, 0) == -1) {
        perror("shmctl");
        return -1;
    }

    return 0;
}

总结

  1. 共享内存中的数据,从来不写入到实际磁盘文件中去;而通过mmap()映射普通文件实现的共享内存通信可以指定何时将数据写入磁盘文件中。 注:系统V共享内存机制实际是通过shm文件系统中的文件实现的,shm文件系统的安装点在交换分区上,系统重新引导后,所有的内容都丢失。

  2. 共享内存是随内核持续的,即使所有访问共享内存的进程都已经正常终止,共享内存区仍然存在(除非显式删除共享内存),在内核重新引导之前,对该共享内存区域的任何改写操作都将一直保留。

  3. 通过调用mmap()映射普通文件进行进程间通信时,一定要注意考虑进程何时终止对通信的影响。而通过共享内存实现通信的进程则不然。

words: 2239 tags: c linux