6 pid_t childpid;
7 struct stat stat;
8 if (argc != 2)
9 err_quit("usage: test3
10 shm_unlink(Px_ipc_name(argv[1]));
11 fd1 = Shm_open(Px_ipc_name(argv[1]), O_RDWR | O_CREAT | O_EXCL, FILE_MODE);
12 Ftruncate(fd1, sizeof(int));
13 fd2 = Open("/etc/motd", O_RDONLY);
14 Fstat(fd2, &stat);
15 if ((childpid = Fork()) == 0) {
16 /* дочерний процесс */
17 ptr2 = Mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd2, 0);
18 ptr1 = Mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
19 MAP_SHARED, fd1, 0);
20 printf("child: shm ptr = %p, motd ptr = %p\n", ptr1, ptr2);
21 sleep(5);
22 printf("shared memory integer = %d\n", *ptr1);
23 exit(0);
24 }
25 /* родительский процесс: вызовы map следуют в обратном порядке */
26 ptr1 = Mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);
27 ptr2 = Mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd2, 0);
28 printf("parent: shm ptr = %p, motd ptr = %p\n", ptr1, ptr2);
29 *ptr1 = 777;
30 Waitpid(childpid, NULL, 0);
31 exit(0);
32 }
10-14 Создаем сегмент разделяемой памяти с именем, принимаемым в качестве аргумента командной строки. Его размер устанавливается равным размеру целого. Затем открываем файл /etc/motd.
15-30 После вызова fork и родительский, и дочерний процессы вызывают mmap дважды, но в разном порядке. Каждый процесс выводит начальный адрес каждой из областей памяти. Затем дочерний процесс ждет 5 секунд, родительский процесс помещает значение 777 в область разделяемой памяти, после чего дочерний процесс считывает и выводит это значение.
Запустив эту программу, мы убедимся, что объект разделяемой памяти начинается с разных адресов в пространствах дочернего и родительского процессов:
solaris % test3 test3.data
parent: shm ptr = eee30000, motd ptr = eee20000
child: shm ptr = eee20000, motd ptr = eee30000
shared memory integer = 777
Несмотря на разницу в начальных адресах, родительский процесс успешно помещает значение 777 по адресу 0xeee30000, а дочерний процесс благополучно считывает его по адресу 0хеее20000. Указатели ptr1 в родительском и дочернем процессах указывают на одну и ту же область разделяемой памяти, хотя их значения в этих процессах различны.
13.5. Увеличение общего счетчика
Разработаем программу, аналогичную приведенной в разделе 12.3, — несколько процессов увеличивают счетчик, хранящийся в разделяемой памяти. Итак, мы помещаем счетчик в разделяемую память, а для синхронизации доступа к нему используем именованный семафор. Отличие программы из этого раздела от предыдущей состоит в том, что процессы более не являются родственными. Поскольку обращение к объектам разделяемой памяти Posix и именованным семафорам Posix осуществляется по именам, процессы, увеличивающие общий счетчик, могут не состоять в родстве. Достаточно лишь, чтобы каждый из них знал имя IPC счетчика и чтобы у каждого были соответствующие разрешения на доступ к объектам IPC (области разделяемой памяти и семафору).
В листинге 13.6 приведен текст программы-сервера, которая создает объект разделяемой памяти, затем создает и инициализирует семафор, после чего завершает работу.
//pxshm/server1.c
1 #include "unpipc.h"
2 struct shmstruct { /* структура, помещаемая в разделяемую память */
3 int count;
4 };
5 sem_t *mutex; /* указатель на именованный семафор */
6 int
7 main(int argc, char **argv)
8 {
9 int fd;
10 struct shmstruct *ptr;
11 if (argc != 3)
12 err_quit("usage: server1