目的
本文主要实现一个基于共享内存的计数器,通过父子进程对其访问。
本文程序需基于<<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 计数器——共享内存(无亲缘关系进程)
服务器端:创建有名信号量及共享内存
#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 != )
err_quit("usage: server1 <shmname> <semname>"); shm_unlink(Px_ipc_name(argv[])); /* OK if this fails */
/* 4create shm, set its size, map it, close descriptor */
fd = Shm_open(Px_ipc_name(argv[]), 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, );
Close(fd); sem_unlink(Px_ipc_name(argv[])); /* OK if this fails */
mutex = Sem_open(Px_ipc_name(argv[]), O_CREAT | O_EXCL, FILE_MODE, );
Sem_close(mutex); exit();
}
客户端:使用有名信号量;映射共享内存空间,并给共享内存空间内的计数器+1
#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 != )
err_quit("usage: client1 <shmname> <semname> <#loops>");
nloop = atoi(argv[]); fd = Shm_open(Px_ipc_name(argv[]), O_RDWR, FILE_MODE);
ptr = Mmap(NULL, sizeof(struct shmstruct), PROT_READ | PROT_WRITE,
MAP_SHARED, fd, );
Close(fd); mutex = Sem_open(Px_ipc_name(argv[]), ); pid = getpid();
for (i = ; i < nloop; i++)
{
Sem_wait(mutex);
printf("pid %ld: %d\n", (long) pid, ptr->count++);
Sem_post(mutex);
}
exit();
}
说明:服务器运行一个实例;客户端运行多个实例,实例之间互斥对计数器进行+1操作;