Я обнаружил проблему, я использовал тот же идентификатор для генерации ключа ftok()
для двух дочерних процессов.
A key_t
имеет 32 бита и может иметь любое значение , которое мы хотим.
ftok
- это просто «удобная» функция для генерации уникального значения key_t
, которое будет использоваться при вызове shmget
(см. Ниже).
Если мы используем IPC_PRIVATE
для этого значения key_t
, мы получим закрытый дескриптор, который может использовать любой дочерний элемент одного родительского процесса. Он отображается в ipcs
как key_t
из 0 с уникальным shmid
[который похож на дескриптор файла].
Итак, если у нас один родитель, а мы просто делаем fork
, мы можем использовать это, потому что дети наследуют это от родителя. Это предпочтительный метод. Таким образом, в этом случае ftok
не требуется.
При использовании закрытых ключей, когда все присоединенные процессы завершаются, общие области автоматически удаляются ядром.
Если мы используем ненулевое значение key_t
, мы создаем перманент область. Это останется (с данными все еще там).
Чтобы удалить это, конечный процесс (то есть родительский процесс) должен сделать shmctl(shmid,IPC_RMID,NULL)
для всех shmid
, полученных из shmget
вызовов.
Если родительский процесс умрет до того, как сделает это, область останется. Такие области все еще будут отображаться в ipcs
Вот пример кода, который иллюстрирует использование IPC_PRIVATE
. Он может быть адаптирован для использования ненулевого значения key_t
, но для вашего приложения это не может быть гарантировано:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#define NCHILD 200
#define SIZE 4096
// child process control
typedef struct {
int tsk_cldidx; // child index
int tsk_shmid; // shmid for process
pid_t tsk_pid; // pid of process
void *tsk_shmadr; // address of attached area
} tsk_t;
tsk_t cldlist[NCHILD];
void
dofork(int cldidx)
{
tsk_t *tskself = &cldlist[cldidx];
do {
tskself->tsk_pid = fork();
// parent
if (tskself->tsk_pid != 0)
break;
// detach from all areas that are not ours
for (cldidx = 0; cldidx < NCHILD; ++cldidx) {
tsk_t *tsk = &cldlist[cldidx];
if (tsk->tsk_cldidx != tskself->tsk_cldidx)
shmdt(tsk->tsk_shmadr);
}
// do something useful ...
exit(0);
} while (0);
}
int
main(void)
{
int cldidx;
tsk_t *tsk;
// create private shared memory ids for each child
for (cldidx = 0; cldidx < NCHILD; ++cldidx) {
tsk = &cldlist[cldidx];
tsk->tsk_cldidx = cldidx;
tsk->tsk_shmid = shmget(IPC_PRIVATE,SIZE,0);
tsk->tsk_shmadr = shmat(tsk->tsk_shmid,NULL,0);
}
// start up all children
for (cldidx = 0; cldidx < NCHILD; ++cldidx)
dofork(cldidx);
// do something useful with children ...
// wait for all children
while (wait(NULL) >= 0);
// remove all segments
// NOTE: with IPC_PRIVATE, this may not be necessary -- it may happen
// automatically when we exit
for (cldidx = 0; cldidx < NCHILD; ++cldidx) {
tsk = &cldlist[cldidx];
shmctl(tsk->tsk_shmid,IPC_RMID,NULL);
}
return 0;
}
Если у нас есть отдельные программы, которые имеют нет отношения родитель / ребенок, нам нужен ненулевой key_t
. Может быть трудно создать уникальный key_t
, который не сталкивается / не конфликтует с другим, возможно, из совершенно не связанной группы программ (например, другого пользователя)
Не могли бы вы объяснить, сколько максимальных уникальных ключей можно сгенерировать с помощью ftok ().
AFAIK значат только последние младшие 8 бит. Можем ли мы использовать, можем ли мы использовать 2-байтовое целое число, например «300», для генерации ключа. Какова вероятность дублирования ключей здесь?
Для данного [существующего] файла и восемь бит proj_id
, как вы заметили, существует только 256 уникальных ключей, которые могут быть сгенерированы. Нам понадобится другой аргумент файла, чтобы получить следующие 256.
Возможно, было бы лучше вообще отказаться от ftok
[Я никогда не использовал его при использовании shmget
]. Я сделал 0xAA000000
в качестве базового key_t
значения. Я заменил нули на любое уникальное значение подключа, в котором я нуждаюсь (есть ~ 24 миллиона возможных комбинаций).
Если мы контролируем все программы, которые обращаются к областям общей памяти, необязательно иметь несколько областей.
Это может быть достаточно, и больше желательно иметь одну область общей памяти. В этом случае мы делаем только один shmget
и один shmat
. Тогда ftok(myfile,0)
может выдать хороший ключ.
Если размер, необходимый для общения с ребенком, составляет (например, страницу) (PER_CHILD = 4096
), и у нас есть NCHILD
дочерних элементов для создания, мы можем просто создать одну область размером TOTAL_SIZE = PER_CHILD * NCHILD
. Тогда для данного дочернего элемента X указатель его области равен shmaddr + (X * PER_CHILD)
UPDATE:
Могу ли я использовать флаг IPC_CREAT и выполнять exec () вызов для ребенка?
Я думаю, что вы имеете в виду использование ненулевого ключа с shmget
в сочетании с этим.
Вызов exec закроет все сопоставления : разделяемая память после exec ()
Он также закроет дескриптор файла, возвращаемый shmget
[или shm_open
].
Таким образом, использование ненулевого ключа - единственный [практический] способ убедиться, что он работает в execvp
и др.
Это вызовет какие-либо проблемы. AFAIK, если мы используем exec (), тогда процесс будет иметь другое адресное пространство. Это вызовет какие-либо проблемы?
Дочерняя программа должна будет (заново) установить свое собственное отображение с помощью shmget
и shmat
.
Но, если мы используем shm_open
[вместо shmget
], мы можем оставить дескриптор файла открытым , если , мы используем fcntl
, чтобы очистить FD_CLOEXEC
флаг в дескрипторе перед вызовом execvp
.
Однако это может быть бесполезно, поскольку дочерняя программа (цель execvp
) [вероятно] не будет знать номер дескриптора файла, который родительский элемент открыл с shm_open
, так что это немного спорный вопрос.