System V共享内存区

System V共享内存区在概念上类似Posix共享内存区。对于每个共享内存区,内核维护如下信息结构,它定义在<sys/shm.h>头文件中。

struct shmid_ds
{
	struct ipc_perm shm_perm;	/* Operation permission value */
	size_t		shm_segsz;	    /* Size of segment in bytes */
	pid_t		shm_lpid;	    /* PID of last shared memory op */
	pid_t		shm_cpid;	    /* PID of creator */
	shmatt_t	shm_nattch;	    /* Number of current attaches */
	time_t		shm_atime;	    /* Time of last shmat() */
	time_t		shm_dtime;	    /* Time of last shmdt() */
	time_t		shm_ctime;	    /* Time of last shmctl() change */
	void		*shm_internal;	/* reserved for kernel use */
};
1
2
3
4
5
6
7
8
9
10
11
12

shmget 函数

#include <sys/shm.h>

int shmget(key_t key, size_t size, int oflag); /* 返回: 若成功则共享内存区对象,若出错则为 -1 */
1
2
3

shmget函数创建一个新的共享内存区,或者访问一个已经存在的共享内存区。

key的值既可以是ftok的返回值,也可以是IPC_PRIVATE

size以字节为单位指定内存区的大小。新建共享内存区时必须指定不为0的值,访问一个已存在的则应为0。

oflag是指读写权限值的组合,它还可以与IPC_CREATEIPC_CREAT|IPC_EXCL按位或。(IPC_CREATE:Create entry if key does not exist;IPC_EXCL:Fail if key exists)。 返回值是一个称为共享内存区标识符(shared memory identifier)的整数。

shmat 函数

#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int flag); /* 返回:成功返回映射区的起始地址,若出错返回 -1 */
1
2
3

由shmget创建或者打开的共享内存区后,通过调用shmat把它附接到调用进程的地址空间。shmid是标识符,shmat返回值是所指定的共享内存区在调用进程内的起始地址。

  • 如果shmaddr是空指针,那么系统替调用者选择地址,推荐使用;
  • 如果shmaddr非空,返回地址取决于调用者是否给flag参数指定了SHM_RND值:
    • 如果没有指定SHM_RND值,那么相应的内存附接到由shmaddr参数指定的地址
    • 如果有,那么相应的共享内存区附接到由shmaddr参数指定的地址向下舍入一个SHMLAB常值。LAB代表低端边界地址(Lower boundary address)。

默认情况下,只要调用进程具有某个共享内存区的读写权限,它附接到该内存后就能够同时读写该内存区。flag参数也可以指定 SHM_RDONLY值以限定只读。

shmdt 函数

#include <sys/shm.h>

int shmdt(const void *shmaddr); /* 返回:成功返回0,若出错返回 -1 */
1
2
3

当一个进程完成某个共享内存区的使用时,调用shmdt断接这个内存区。但不会删除所指定的共享内存区。

shmctl 函数

#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buff); /* 返回:成功返回0,若出错返回 -1 */
1
2
3

shmctl函数提供了对一个共享内存区的多种操作:

  • IPC_RMID

从系统中删除由shmid表示的共享内存区

  • IPC_SET

给所指定的共享内存区设置其shmid_ds结构的以下三个成员:shm_perm.uid, shm_perm.gid, shm_perm.mode,它们的值来自buff参数所指向的结构体中的相应成员。shm_ctime的值也用当前时间替换。

  • IPC_STAT

通过buff参数向调用者返回所指定共享内存区当前的shmid_ds结构。

示例

头文件unpipc.h

#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/ipc.h>

#define MAX_BUFF_SIZE 1024
#define SVSHM_MODE (SHM_R | SHM_W | SHM_R >> 3 | SHM_R >> 6)

void err_msg(const char *fmt, va_list ap)
{
    char buff[MAX_BUFF_SIZE];
    vsnprintf(buff, MAX_BUFF_SIZE, fmt, ap);
    perror(buff);
}

void err_printf(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_msg(fmt, ap);
    va_end(ap);
}

void err_quit(const char *fmt, ...)
{
    va_list ap;

    char buff[MAX_BUFF_SIZE];

    va_start(ap, fmt);
    err_msg(fmt, ap); /* we can pass the args only to functions that 
                        take va_args as argument. These have a v in 
                        their name: vprintf, vfprintf, vsnprintf */
    va_end(ap);

    exit(0);
}

int Getopt(int argc, char * const *argv, const char *str)
{
    int opt;
    if ((opt = getopt(argc, argv, str)) == '?')
        exit(1);

    return opt;
}

key_t Ftok(const char *pathname, int id)
{
    key_t key;
    if((key = ftok(pathname, id)) == -1)
        err_quit("ftok error for pathname '%s' and id '%d'", pathname, id);
    
    return key;
}

int Shmget(key_t key, size_t size, int flags)
{
    int rc;
    if((rc = shmget(key, size, flags)) == -1)
        err_quit("shmget error");
    return rc;
}

void *Shmat(int id, const void *shmaddr, int flags)
{
    void *ptr;
    if ((ptr = shmat(id, shmaddr, flags)) == (void*) -1)
        err_quit("shmat error");
    return ptr;
}

void Shmdt(const void *shmaddr)
{
    if(shmdt(shmaddr) == -1)
        err_quit("shmdt error");
}


void Shmctl(int id, int cmd, struct shmid_ds *buff)
{
    if(shmctl(id, cmd, buff) == -1)
        err_quit("shmctl error");
}

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

shmget.c

#include "unpipc.h"

int main(int argc, char **argv)
{
    int c, id, oflag;
    size_t shm_size;
    char *ptr;

    oflag = IPC_CREAT | SVSHM_MODE;
    while ((c = Getopt(argc, argv, "e")) != -1)
    {
        switch (c)
        {
            case 'c':
                oflag |= IPC_EXCL;
                break;
        }
    }

    if(optind != argc - 2)
        err_quit("usage:shmget [ -e ] <pathname> <size>");

    shm_size = atoi(argv[optind + 1]);

    id = Shmget(Ftok(argv[optind], 0), shm_size, oflag);
    ptr = Shmat(id, NULL, 0);

    return 0;
}
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

shrmid.c

#include "unpipc.h"

int main(int argc, char const *argv[])
{
    int id;
    int oflag = SVSHM_MODE;
    if(argc != 2)
        err_quit("usage: shmrimid <pathname>");

    id = Shmget(Ftok(argv[1], 0), 0, oflag);
    Shmctl(id, IPC_RMID, NULL);
    
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

shmwrite.c

#include "unpipc.h"

int main(int argc, char const *argv[])
{
    int id, n;
    char *ptr;
    struct shmid_ds buff;

    if(argc != 2)
        err_quit("usage: shmwrite <pathname>");
    
    id = Shmget(Ftok(argv[1], 0), 0, SVSHM_MODE);
    ptr = Shmat(id, NULL, 0);
    Shmctl(id, IPC_STAT, &buff);

    printf("Share memory size is %zu bytes\n", buff.shm_segsz);

    fgets(ptr, buff.shm_segsz, stdin);
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

shmread.c

#include "unpipc.h"

int main(int argc, char const *argv[])
{
    int id, n;
    char *ptr, c;
    struct shmid_ds buff;

    if (argc != 2)
        err_quit("usage: shmread <pathname>");

    id = Shmget(Ftok(argv[1], 0), 0, SVSHM_MODE);
    ptr = Shmat(id, NULL, 0);
    Shmctl(id, IPC_STAT, &buff);

    for(n = 0; n < buff.shm_segsz; n++)
    {
        c = *ptr++;
        if(c != '\n')
            printf("%c", c);
        else
            printf("^");
    }
    return 0;
}
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

Makefile

shmget.o: shmget.c unpipc.h
	$(CC) -c $^
shmget:	shmget.o
	$(CC) $^ -o $@

shmrmid.o: shmrmid.c unpipc.h
	$(CC) -c $^
shmrmid: shmrmid.o
	$(CC) $^ -o $@

shmwrite.o: shmwrite.c unpipc.h
	$(CC) -c $^
shmwrite: shmwrite.o
	$(CC) $^ -o $@

shmread.o: shmread.c unpipc.h
	$(CC) -c $^
shmread: shmread.o
	$(CC) $^ -o $@

.PHONY: all clean
all: shmget shmrmid shmwrite shmread
clean:
	rm *.o shmget *gch shmrmid shmwrite shmread

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
最近更新: 1/26/2019, 9:26:10 PM