Unix IPC之基于共享内存的计数器
目的
本文主要实现一个基于共享内存的计数器,通过父子进程对其访问。
本文程序需基于<<Unix网络编程-卷2>>的环境才能运行。程序中大写开头的函数为其小写同名函数的包裹函数,增加了错误处理信息。
1 函数介绍
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
|
#include <sys/mman> /** * Map addresses starting near ADDR and extending for LEN bytes. * from OFFSET into the file FD describes according to PROT and FLAGS. * If ADDR is nonzero, it is the desired mapping address. * If the MAP_FIXED bit is set in FLAGS, the mapping will be at ADDR exactly (which must be * page-aligned); otherwise the system chooses a convenient nearby address. * The return value is the actual mapping address chosen or MAP_FAILED * for errors (in which case `errno' is set). A successful `mmap' call * deallocates any previous mapping for the affected region. */ void *mmap ( void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset); /* Deallocate any mapping for the region starting at ADDR and extending LEN bytes. Returns 0 if successful, -1 for errors (and sets errno). */ extern int munmap ( void *__addr, size_t __len) __THROW; /* Synchronize the region starting at ADDR and extending LEN bytes with the file it maps. Filesystem operations on a file being mapped are unpredictable before this is done. Flags are from the MS_* set. This function is a cancellation point and therefore not marked with __THROW. */ extern int msync ( void *__addr, size_t __len, int __flags); |
2 计数器——非共享内存
默认情况下,通过fork派生的子进程并不与其父进程共享内存区。
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
|
#include "unpipc.h" #define SEM_NAME "mysem" int count = 0; // 计数器 int main( int argc, char **argv) { int i, nloop; sem_t *mutex; if (argc != 2) err_quit( "usage: incr1 <#loops>" ); nloop = atoi (argv[1]); /* 4create, initialize, and unlink semaphore */ mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1); /** * sem_unlink() * removes the named semaphore referred to by name. * The semaphore name is removed immediately. * The semaphore is destroyed once all other processes that have the semaphore open close it. */ Sem_unlink(Px_ipc_name(SEM_NAME)); // 非缓冲模式,防止两个线程的输出交叉 // 父子线程共同访问一个信号量 // 由于mutex是2值信号量,相当于同步父子线程 // 所以实际上这里并不会发生交叉 setbuf (stdout, NULL); /* stdout is unbuffered */ if (Fork() == 0) /* child */ { for (i = 0; i < nloop; i++) { Sem_wait(mutex); printf ( "child: %d\n" , count++); Sem_post(mutex); } exit (0); } /* 4parent */ for (i = 0; i < nloop; i++) { Sem_wait(mutex); printf ( "parent: %d\n" , count++); Sem_post(mutex); } exit (0); } |
由于子进程为父进程的拷贝,所以子进程自己有一个count的副本,所以父子进程操作自己的count。这里使用一个有名信号量来同步父子进程。
3 计数器——共享内存
程序说明:
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
|
#include "unpipc.h" #define SEM_NAME "mysem" // 有名信号量用于同步父子进程(加锁) int main( int argc, char **argv) { int fd, i, nloop, zero = 0; int *ptr; // 访问共享内存的指针 sem_t *mutex; if (argc != 3) err_quit( "usage: incr2 <pathname> <#loops>" ); nloop = atoi (argv[2]); /* 4open file, initialize to 0, map into memory */ fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE); Write(fd, &zero, sizeof ( int )); // 向文件中写入一个int型的0 // ptr返回共享内存起始位置 ptr = Mmap(NULL, sizeof ( int ), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // 映射一个int型大小的共享内存 Close(fd); /* 4create, initialize, and unlink semaphore */ mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1); // 信号量 Sem_unlink(Px_ipc_name(SEM_NAME)); setbuf (stdout, NULL); /* stdout is unbuffered */ if (Fork() == 0) /* child */ { for (i = 0; i < nloop; i++) { Sem_wait(mutex); printf ( "child: %d\n" , (*ptr)++); Sem_post(mutex); } exit (0); } /* 4parent */ for (i = 0; i < nloop; i++) { Sem_wait(mutex); printf ( "parent: %d\n" , (*ptr)++); Sem_post(mutex); } exit (0); } |
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[dell@localhost shm]$ . /incr2 . /count .data 100 parent: 0 parent: 1 parent: 2 parent: 3 .... child: 197 child: 198 child: 199 [dell@localhost shm]$ ll count.data -rw-r--r--. 1 dell dell 4 8月 18 14:28 count.data [dell@localhost shm]$ file count.data count.data: data [dell@localhost shm]$ hexdump -d count.data 0000000 00200 00000 0000004 [dell@localhost shm]$ |
注意:这里的count.data文件类型data类型,需用od、xxd、hexdump等命令才能查看。
4 计数器——共享内存(2)
上面3中的信号量为有名信号量,其具体实现由Posix决定,但是至少是内核持续性的。这里将其改为基于内存的信号量,并将其放置在共享内存中。
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
|
#include "unpipc.h" struct shared { sem_t mutex; /* the mutex: a Posix memory-based semaphore */ // 匿名信号量 int count; /* and the counter */ } shared; int main( int argc, char **argv) { int fd, i, nloop; struct shared *ptr; if (argc != 3) err_quit( "usage: incr3 <pathname> <#loops>" ); nloop = atoi (argv[2]); /* 4open file, initialize to 0, map into memory */ fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE); Write(fd, &shared, sizeof ( struct shared)); ptr = Mmap(NULL, sizeof ( struct shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); Close(fd); /* 4initialize semaphore that is shared between processes */ Sem_init(&ptr->mutex, 1, 1); // 初始化匿名信号量,设置为进程共享,初始值为1 setbuf (stdout, NULL); /* stdout is unbuffered */ if (Fork() == 0) /* child */ { for (i = 0; i < nloop; i++) { Sem_wait(&ptr->mutex); printf ( "child: %d\n" , ptr->count++); Sem_post(&ptr->mutex); } exit (0); } /* 4parent */ for (i = 0; i < nloop; i++) { Sem_wait(&ptr->mutex); printf ( "parent: %d\n" , ptr->count++); Sem_post(&ptr->mutex); } exit (0); } |
5 计数器——共享内存(无亲缘关系进程)
服务器端:创建有名信号量及共享内存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "unpipc.h" struct shmstruct /* struct stored in shared memory */ { int count; }; sem_t *mutex; /* pointer to named semaphore */ // 有名信号量 int main(int argc, char **argv) { int fd; struct shmstruct *ptr; if (argc != 3) err_quit("usage: server1 <shmname> <semname>"); shm_unlink(Px_ipc_name(argv[1])); /* OK if this fails */ /* 4create shm, set its size, map it, close descriptor */ fd = Shm_open(Px_ipc_name(argv[1]), O_RDWR | O_CREAT | O_EXCL, FILE_MODE); Ftruncate(fd, sizeof(struct shmstruct)); // 设置共享内存大小 ptr = Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); Close(fd); sem_unlink(Px_ipc_name(argv[2])); /* OK if this fails */ mutex = Sem_open(Px_ipc_name(argv[2]), O_CREAT | O_EXCL, FILE_MODE, 1); Sem_close(mutex); exit(0); }
客户端:使用有名信号量;映射共享内存空间,并给共享内存空间内的计数器+1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "unpipc.h" struct shmstruct /* struct stored in shared memory */ { int count; }; sem_t *mutex; /* pointer to named semaphore */ int main(int argc, char **argv) { int fd, i, nloop; pid_t pid; struct shmstruct *ptr; // 注意这里的指针类型 if (argc != 4) err_quit("usage: client1 <shmname> <semname> <#loops>"); nloop = atoi(argv[3]); fd = Shm_open(Px_ipc_name(argv[1]), O_RDWR, FILE_MODE); ptr = Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); Close(fd); mutex = Sem_open(Px_ipc_name(argv[2]), 0); pid = getpid(); for (i = 0; i < nloop; i++) { Sem_wait(mutex); printf("pid %ld: %d\n", (long) pid, ptr->count++); Sem_post(mutex); } exit(0); }
说明:服务器运行一个实例;客户端运行多个实例,实例之间互斥对计数器进行+1操作;