共享内存与mmap那点东西
2016-03-30 19:56
253 查看
共享内存是一种IPC形式,与其它IPC机制如管道、消息队列等相比,数据不必在进程与内核间多次交换,进程间通信的速度更快。当共享内存区映射到共享它的进程的地址空间时,再加以一些同步控制,这些进程就可以进行数据传送了。mmap函数提供了内存映射功能,可以把一个文件或一个Posix共享内存区对象映射到调用进程的地址空间,下面首先介绍mmap的用法。
mmap可使用普通文件以提供内存映射IO,或者是特殊文件以提供匿名内存映射,还可以使用shm_open以提供无亲缘关系进程间的Posix共享内存区,成功时返回指向映射区域的指针,失败时返回MAP_FAILED,即((void*)-1),并设置相应的errno。成功返回后,文件描述字fd可关闭,不影响后续共享内存的使用。
参数addr指定了文件描述字fd映射到进程地址空间的起始地址,不过这个参数一般为NULL,为空时地址由内核来选择。
参数length指定了fd映射到进程地址空间的字节数,字节计数从fd的offset处开始算起,offset为从fd文件开头的字节偏移量。
参数prot指定了内存映射区的读写权限,有几个常值:PROT_EXEC、PROT_READ、PROT_WRITE、PROT_NONE,分别表示可执行、可读、可写、不可访问,前三个可使用按位或符号拼起来使用。
参数flags指定了内存映射区的共享模式,必须从MAP_SHARED与MAP_PRIVATE两者之间选择一个,前者表示当前进程修改的内存映射区对其它进程是可见的,也确实修改了文件fd并影响到其它进程,后者则恰好相反,表示当前进程修改的内存映射区对其它进程是不可见的,并没有修改文件fd也不会影响其它进程。flags除了这两个值之外,还有一些其它的可选值,用按位或符号连起来即可,不过考虑到移植安全,并不使用MAP_FIXED,参数addr也指定为NULL。
mmap用以内存映射,解除一个进程的的内存映射关系使用munmap。
参数addr为mmap返回的地址,length为映射区的大小。成功时返回0,失败时返回-1,并设置相应的errno。
对于mmap函数的flags参数,为MAP_PRIVATE时,调用进程对共享内存作的修改都会被丢弃掉;为MAP_SHARED时,内核维护共享内存与文件fd的内容一致,但有时候我们要手动同步共享内存与文件fd的内容,这个工作由msync函数来完成。
msync成功时返回0,失败时返回-1,并设置相应的errno。参数addr为mmap返回的地址,length即映射区的大小,flags是MS_ASYNC、MS_SYNC、MS_INVALIDATE中的集合,但前两个不可同时指定。MS_ASYNC表示异步写,函数立即返回;MS_SYNC表示同步写,同步完成后函数才返回;MS_INVALIDATE表示与同一个文件fd映射的其它内存区域的原有数据将无效,以使它们立即更新。
下面以例子说明mmap的用法。
例一:fork
例一是一个计数程序,全局计数变量g_count初始值为0,计数次数nloop从命令行指定,我们都知道fork的子进程不会继承父进程的地址空间,如这里的g_count,而是一份独立的拷贝,父、子进程分别对g_count计数nloop次,并输出计数结果,程序运行结果如下,可以看出父、子进程对g_count的操作并没有互相影响。
例二:mmap sem
例二在例一的基础上增加了mmap内存映射以在进程间共享数据,因为涉及进程间的数据同步,所以还增加了Posix信号灯sem。程序中,信号灯是个有名信号灯,名字为“/semtest”,mmap所需的共享文件fd从命令行指定,打开这个文件后,初始化为0以提供计数功能,而不是例一中的一个全局变量用来计数,接着使用mmap完成内存映射,mmap的参数指定了文件读写权限和进程间共享模式,随后关闭文件,内存映射完成后关闭文件是没有影响的。关于sem信号灯,打开时初始化为1,接着调用sem_unlink,这个对后续信号灯的使用并没有影响,因为内核维护着信号灯的引用计数,只有当最后一个sem_close调用或者程序结束后信号灯才会从系统中移除。最后同样是父、子进程对计数值的操作,操作对象是mmap返回的地址,因为mmap指定了MAP_SHARED,所以进程间互相影响也确实修改了共享文件中的内容,所以计数期间加了信号灯sem_wait/sem_post以完成同步,避免冲突,否则计数结果就会出错。程序运行结果如下,可以看出父、子进程实现了计数共享。
使用file命令查看共享文件count的类型为data数据文件。
例三:sem_init
例二中的信号灯是个有名信号灯,信号灯还可以是基于内存映射的匿名信号灯,为此例三添加了shard结构体,成员变量为一个信号灯和一个计数器,mmap映射长度为这个结构体的长度,调用sem_init初始化信号灯的值为1,其第二个参数为1,目的是保证信号灯同步在进程间使用而非同一进程的不同线程间,其它用法不变,程序运行结果一样。
例四:MAP_ANONYMOUS
例二、例三中的mmap是对一个共享文件的内存映射,为此首先要打开一个文件,不过这并不是必需的,mmap同样也提供了匿名内存映射,不用基于一个确实存在的文件来实现映射,这时mmap的flags为MAP_SHARED | MAP_ANONYMOUS,fd和offset被忽略,不过fd一般为-1,offset为0。例四在例二的基础上作了修改,通过mmap的参数变更,使用匿名内存映射,程序运行结果相同。
除了匿名内存映射之外,有时候我们还可以打开一个特殊的文件“/dev/zero”,将这个文件描述字fd作为mmap的参数也是可以的,任何从这个fd读取的数据都为0,写往这个fd的数据也都被丢弃,也可以完成进程间的数据共享。
mmap映射长度——
使用mmap映射一个普通文件时,还有一点值得注意,那就是mmap的第二个参数,即mmap的映射长度,上面的例子中mmap的映射长度等于共享文件的大小,然而它们可以不相等。
shm_open创建或打开一个共享内存区对象,创建成功后共享内存区对象的位置在/dev/shm目录下,shm_unlink移除一个共享内存区对象,链接时使用“-lrt”选项。
ftruncate设置一个文件的长度,参数fd为shm_open的返回值,fstat获取一个文件fd的状态。
下面以一个例子说明shm_open等函数的用法。
例子中,仍然是一个计数程序,不过共享内存区的同步是在没有亲缘关系的进程间完成的。计数变量count放到了shmstruct结构中,同步机制使用Posix信号灯sem。server.c的作用是创建一个共享内存区供其它进程使用,其大小为shmstruct结构体的大小,接着创建一个有名信号灯并初始化为1。client.c的作用是计数,使用server.c已经创建好的共享内存区和信号灯,输出当前进程号和计数值,运行结果如下。
可以看出,open与shm_open的用法类似,只不过shm_open创建的共享内存区对象位于虚拟文件系统中,在Linux上的位置是“/dev/shm”,通过file命令查看一下例子中创建的shm的类型,data类型,查看其内容可使用od命令。
1、mmap
include <sys/mman.h> void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
mmap可使用普通文件以提供内存映射IO,或者是特殊文件以提供匿名内存映射,还可以使用shm_open以提供无亲缘关系进程间的Posix共享内存区,成功时返回指向映射区域的指针,失败时返回MAP_FAILED,即((void*)-1),并设置相应的errno。成功返回后,文件描述字fd可关闭,不影响后续共享内存的使用。
参数addr指定了文件描述字fd映射到进程地址空间的起始地址,不过这个参数一般为NULL,为空时地址由内核来选择。
参数length指定了fd映射到进程地址空间的字节数,字节计数从fd的offset处开始算起,offset为从fd文件开头的字节偏移量。
参数prot指定了内存映射区的读写权限,有几个常值:PROT_EXEC、PROT_READ、PROT_WRITE、PROT_NONE,分别表示可执行、可读、可写、不可访问,前三个可使用按位或符号拼起来使用。
参数flags指定了内存映射区的共享模式,必须从MAP_SHARED与MAP_PRIVATE两者之间选择一个,前者表示当前进程修改的内存映射区对其它进程是可见的,也确实修改了文件fd并影响到其它进程,后者则恰好相反,表示当前进程修改的内存映射区对其它进程是不可见的,并没有修改文件fd也不会影响其它进程。flags除了这两个值之外,还有一些其它的可选值,用按位或符号连起来即可,不过考虑到移植安全,并不使用MAP_FIXED,参数addr也指定为NULL。
mmap用以内存映射,解除一个进程的的内存映射关系使用munmap。
int munmap(void *addr, size_t length);
参数addr为mmap返回的地址,length为映射区的大小。成功时返回0,失败时返回-1,并设置相应的errno。
对于mmap函数的flags参数,为MAP_PRIVATE时,调用进程对共享内存作的修改都会被丢弃掉;为MAP_SHARED时,内核维护共享内存与文件fd的内容一致,但有时候我们要手动同步共享内存与文件fd的内容,这个工作由msync函数来完成。
int msync(void *addr, size_t length, int flags);
msync成功时返回0,失败时返回-1,并设置相应的errno。参数addr为mmap返回的地址,length即映射区的大小,flags是MS_ASYNC、MS_SYNC、MS_INVALIDATE中的集合,但前两个不可同时指定。MS_ASYNC表示异步写,函数立即返回;MS_SYNC表示同步写,同步完成后函数才返回;MS_INVALIDATE表示与同一个文件fd映射的其它内存区域的原有数据将无效,以使它们立即更新。
下面以例子说明mmap的用法。
例一:fork
// increment.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> int g_count = 0; int main(int argc, char **argv) { int i, nloop; if (2 != argc) { printf("usage: %s <#loops>\n", argv[0]); exit(EXIT_FAILURE); } nloop = atoi(argv[1]); setbuf(stdout, NULL); if (fork() == 0) { // child for (i = 0; i < nloop; ++i) { printf("child: %d\n", g_count++); } exit(EXIT_SUCCESS); } for (i = 0; i < nloop; ++i) { // parent printf("parent: %d\n", g_count++); } exit(EXIT_SUCCESS); }
例一是一个计数程序,全局计数变量g_count初始值为0,计数次数nloop从命令行指定,我们都知道fork的子进程不会继承父进程的地址空间,如这里的g_count,而是一份独立的拷贝,父、子进程分别对g_count计数nloop次,并输出计数结果,程序运行结果如下,可以看出父、子进程对g_count的操作并没有互相影响。
$ gcc -o test increment.c $ ./test 10 parent: 0 parent: 1 parent: 2 parent: 3 parent: 4 parent: 5 parent: 6 parent: 7 parent: 8 parent: 9 child: 0 child: 1 child: 2 child: 3 child: 4 child: 5 child: 6 child: 7 child: 8 child: 9
例二:mmap sem
// increment2.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <semaphore.h> #include <sys/mman.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) #define SEM_NAME "/semtest" int main(int argc, char **argv) { int fd, i, nloop, zero = 0; int *ptr; sem_t *sem; if (3 != argc) { printf("usage: %s <pathname> <#loops>\n", argv[0]); exit(EXIT_FAILURE); } nloop = atoi(argv[2]); // open file if ((fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE)) == -1) { printf("open error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // initialize fd to 0 if (write(fd, &zero, sizeof(int)) == -1) { printf("write error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // map into memory if ((ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { printf("mmap error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // close file if (close(fd) == -1) { printf("close error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // create semaphore and initialize to 1 if ((sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1)) == SEM_FAILED) { printf("sem_open error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // unlink semaphore if (sem_unlink(SEM_NAME) == -1) { printf("sem_unlink error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // stdout is unbuffered setbuf(stdout, NULL); if (fork() == 0) { // child for (i = 0; i < nloop; ++i) { if (sem_wait(sem) == -1) { printf("sem_wait child: error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("child: %d\n", (*ptr)++); if (sem_post(sem) == -1) { printf("sem_post child error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } for (i = 0; i < nloop; ++i) { // parent if (sem_wait(sem) == -1) { printf("sem_wait parent error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("parent: %d\n", (*ptr)++); if (sem_post(sem) == -1) { printf("sem_post parent error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); }
例二在例一的基础上增加了mmap内存映射以在进程间共享数据,因为涉及进程间的数据同步,所以还增加了Posix信号灯sem。程序中,信号灯是个有名信号灯,名字为“/semtest”,mmap所需的共享文件fd从命令行指定,打开这个文件后,初始化为0以提供计数功能,而不是例一中的一个全局变量用来计数,接着使用mmap完成内存映射,mmap的参数指定了文件读写权限和进程间共享模式,随后关闭文件,内存映射完成后关闭文件是没有影响的。关于sem信号灯,打开时初始化为1,接着调用sem_unlink,这个对后续信号灯的使用并没有影响,因为内核维护着信号灯的引用计数,只有当最后一个sem_close调用或者程序结束后信号灯才会从系统中移除。最后同样是父、子进程对计数值的操作,操作对象是mmap返回的地址,因为mmap指定了MAP_SHARED,所以进程间互相影响也确实修改了共享文件中的内容,所以计数期间加了信号灯sem_wait/sem_post以完成同步,避免冲突,否则计数结果就会出错。程序运行结果如下,可以看出父、子进程实现了计数共享。
$ gcc -o test -pthread increment2.c $ ./test count 10 parent: 0 parent: 1 child: 2 child: 3 child: 4 child: 5 child: 6 child: 7 child: 8 child: 9 child: 10 child: 11 parent: 12 parent: 13 parent: 14 parent: 15 parent: 16 parent: 17 parent: 18 parent: 19
使用file命令查看共享文件count的类型为data数据文件。
$ file count $ count: data
例三:sem_init
// increment3.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <semaphore.h> #include <sys/mman.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) struct shared { sem_t sem; int count; } shared; int main(int argc, char **argv) { int fd, i, nloop; struct shared *ptr; if (3 != argc) { printf("usage: %s <pathname> <#loops>\n", argv[0]); exit(EXIT_FAILURE); } nloop = atoi(argv[2]); // open file if ((fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE)) == -1) { printf("open error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // initialize fd to 0 if (write(fd, &shared, sizeof(struct shared)) == -1) { printf("write error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // map into memory if ((ptr = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { printf("mmap error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // close file if (close(fd) == -1) { printf("close error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // initialize semaphore that is shared between processes if (sem_init(&ptr->sem, 1, 1) == -1) { printf("sem_init error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // stdout is unbuffered setbuf(stdout, NULL); if (fork() == 0) { // child for (i = 0; i < nloop; ++i) { if (sem_wait(&ptr->sem) == -1) { printf("sem_wait child: error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("child: %d\n", ptr->count++); if (sem_post(&ptr->sem) == -1) { printf("sem_post child error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } for (i = 0; i < nloop; ++i) { // parent if (sem_wait(&ptr->sem) == -1) { printf("sem_wait parent error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("parent: %d\n", ptr->count++); if (sem_post(&ptr->sem) == -1) { printf("sem_post parent error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); }
例二中的信号灯是个有名信号灯,信号灯还可以是基于内存映射的匿名信号灯,为此例三添加了shard结构体,成员变量为一个信号灯和一个计数器,mmap映射长度为这个结构体的长度,调用sem_init初始化信号灯的值为1,其第二个参数为1,目的是保证信号灯同步在进程间使用而非同一进程的不同线程间,其它用法不变,程序运行结果一样。
例四:MAP_ANONYMOUS
// increment_map_anon.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <semaphore.h> #include <sys/mman.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) #define SEM_NAME "/semtest" int main(int argc, char **argv) { int i, nloop; int *ptr; sem_t *sem; if (2 != argc) { printf("usage: %s <#loops>\n", argv[0]); exit(EXIT_FAILURE); } nloop = atoi(argv[1]); // anonymous mmap if ((ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) { printf("mmap error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // create, initialize, and unlink semaphore if ((sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1)) == SEM_FAILED) { printf("sem_open error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } if (sem_unlink(SEM_NAME) == -1) { printf("sem_unlink error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // stdout is unbuffered setbuf(stdout, NULL); if (fork() == 0) { // child for (i = 0; i < nloop; ++i) { if (sem_wait(sem) == -1) { printf("sem_wait child: error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("child: %d\n", (*ptr)++); if (sem_post(sem) == -1) { printf("sem_post child error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } for (i = 0; i < nloop; ++i) { //parent if (sem_wait(sem) == -1) { printf("sem_wait parent error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("parent: %d\n", (*ptr)++); if (sem_post(sem) == -1) { printf("sem_post parent error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); }
例二、例三中的mmap是对一个共享文件的内存映射,为此首先要打开一个文件,不过这并不是必需的,mmap同样也提供了匿名内存映射,不用基于一个确实存在的文件来实现映射,这时mmap的flags为MAP_SHARED | MAP_ANONYMOUS,fd和offset被忽略,不过fd一般为-1,offset为0。例四在例二的基础上作了修改,通过mmap的参数变更,使用匿名内存映射,程序运行结果相同。
除了匿名内存映射之外,有时候我们还可以打开一个特殊的文件“/dev/zero”,将这个文件描述字fd作为mmap的参数也是可以的,任何从这个fd读取的数据都为0,写往这个fd的数据也都被丢弃,也可以完成进程间的数据共享。
mmap映射长度——
使用mmap映射一个普通文件时,还有一点值得注意,那就是mmap的第二个参数,即mmap的映射长度,上面的例子中mmap的映射长度等于共享文件的大小,然而它们可以不相等。
2、Posix共享内存区
mmap函数中的参数fd,可以是open函数打开的一个内存映射文件,还可以是shm_open函数打开的一个共享内存区对象,相关函数如下。#include <sys/mman.h> int shm_open(const char *name, int oflag, mode_t mode); int shm_unlink(const char *name);
shm_open创建或打开一个共享内存区对象,创建成功后共享内存区对象的位置在/dev/shm目录下,shm_unlink移除一个共享内存区对象,链接时使用“-lrt”选项。
#include <unistd.h> #include <sys/stat.h> int ftruncate(int fd, off_t length); int fstat(int fd, struct stat *buf);
ftruncate设置一个文件的长度,参数fd为shm_open的返回值,fstat获取一个文件fd的状态。
下面以一个例子说明shm_open等函数的用法。
// server.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <semaphore.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) struct shmstruct // struct stored in shared memory { int count; }; sem_t *sem; // pointer to named semaphore int main(int argc, char **argv) { int fd; struct shmstruct *ptr; if (3 != argc) { printf("usage: %s <shm_name> <sem_name>\n", argv[0]); exit(EXIT_FAILURE); } shm_unlink(argv[1]); // unlink if used, OK if failed // open shm if ((fd = shm_open(argv[1], O_RDWR | O_CREAT | O_EXCL, FILE_MODE)) == -1) { printf("shm_open error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // set size if (ftruncate(fd, sizeof(struct shmstruct)) == -1) { printf("ftruncate error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // mmap shm if ((ptr = mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { printf("mmap error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // close shm if (close(fd) == -1) { printf("close error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } sem_unlink(argv[2]); // unlink if used, OK if failed // open sem if ((sem = sem_open(argv[2], O_CREAT | O_EXCL, FILE_MODE, 1)) == SEM_FAILED) { printf("sem_open error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // close sem if (sem_close(sem) == -1) { printf("sem_close error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
// client.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <semaphore.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) struct shmstruct // struct stored in shared memory { int count; }; sem_t *sem; // pointer to named semaphore int main(int argc, char **argv) { int fd, i, nloop; pid_t pid; struct shmstruct *ptr; if (4 != argc) { printf("usage: %s <shm_name> <sem_name> <#loops>\n", argv[0]); exit(EXIT_FAILURE); } nloop = atoi(argv[3]); // open shm if ((fd = shm_open(argv[1], O_RDWR, FILE_MODE)) == -1) { printf("shm_open error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // mmap shm if ((ptr = mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { printf("mmap error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // close shm if (close(fd) == -1) { printf("close error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } // open sem if ((sem = sem_open(argv[2], 0)) == SEM_FAILED) { printf("sem_open error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } pid = getpid(); for (i = 0; i < nloop; ++i) { if (sem_wait(sem) == -1) { printf("sem_wait error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("pid %ld: %d\n", (long)pid, ptr->count++); if (sem_post(sem) == -1) { printf("sem_post error: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); }
例子中,仍然是一个计数程序,不过共享内存区的同步是在没有亲缘关系的进程间完成的。计数变量count放到了shmstruct结构中,同步机制使用Posix信号灯sem。server.c的作用是创建一个共享内存区供其它进程使用,其大小为shmstruct结构体的大小,接着创建一个有名信号灯并初始化为1。client.c的作用是计数,使用server.c已经创建好的共享内存区和信号灯,输出当前进程号和计数值,运行结果如下。
$ gcc -o server server.c -pthread -lrd $ gcc -o client client.c -pthread -lrd $ ./server shm sem $ ./client shm sem 10 & ./client shm sem 10 [1] 8423 pid 8423: 0 pid 8423: 1 pid 8423: 2 pid 8423: 3 pid 8423: 4 pid 8423: 5 pid 8423: 6 pid 8423: 7 pid 8423: 8 pid 8423: 9 pid 8424: 10 pid 8424: 11 pid 8424: 12 pid 8424: 13 pid 8424: 14 pid 8424: 15 pid 8424: 16 pid 8424: 17 pid 8424: 18 pid 8424: 19 [1]+ 已完成 ./c shm sem 10
可以看出,open与shm_open的用法类似,只不过shm_open创建的共享内存区对象位于虚拟文件系统中,在Linux上的位置是“/dev/shm”,通过file命令查看一下例子中创建的shm的类型,data类型,查看其内容可使用od命令。
$ file /dev/shm/shm /dev/shm/shm: data
相关文章推荐
- MyBatis/Ibatis中#和$的区别
- LATEX保护视力背景色设置
- 【游戏服务端开服 一 】 单线程可以处理一对一的请求
- swift基础
- java-入门:模拟网络购物支付系统
- IOS学习之——彩票应用开发
- java学习第三天
- 有线时不需要hosts直接上谷歌
- 25 API-GUI(事件监听机制,适配器模式),Netbeans的概述和使用(模拟登陆注册GUI版)
- Mybatis 配置实体类的别名
- 合并有序链表
- 初学用HashMap输出
- mvc模式 学生管理系统
- 英伟达硬件加速解码器在 FFMPEG 中的使用
- 第4周项目5-用递归方法求解2
- 安装Bcache中遇到的问题记录
- 存储过程和函数的区别
- android 图像渲染(Shader)
- 南阳题目206-矩形的个数
- css3 字体旋转