LINUX.ORG.RU

Проверка наличия такого блока в shmget

 


0

1

Мне нужно, чтобы первый, кому понадобится блок его проинициализировал, остальным нужен доступ «только на чтение». Я так понимаю, что действовать надо как-то так:

if ((id = shmget(..., ..., 0444 | IPC_CREATE | IPC_EXCL)) == -1)
{
   id = shmget(..., ..., 0666 | PC_CREATE);
   initdata(id) // shmat внутри
}

char * data = (char *)shmat(id, 0, 0);

Но так получается, что тот, кто инициализировал имеет доступ и на запись. Имеет смысл после инициализации переоткрыть id по 0444 или уже навсегда этому блоку будут выданы права 0666? Будет ли идеологически верным переоткрывать по 0444?

Дополнительный опциональный вопрос по ftok - оно должно брать имя существующего файла. `touch /tmp/somename' подойдет?

P.S. Бггг, у нас нет тега unix system v.

★★

Обычно используют либо CREAT|EXCL|perms, чтобы создать сегмент, либо 0, чтобы открыть существующий сегмент. Пермиссии используются только при создании, не используются при открытии. Если нужно, чтобы память сегмента была отмаплена п адресное пространство процесса только для чтения, надо сказать об этом в shmat - SHM_RDONLY.

iliyap ★★★★★
()
Ответ на: комментарий от iliyap

Многое стало понятней, спасибо.

Обычно используют либо CREAT|EXCL|perms, чтобы создать сегмент, либо 0, чтобы открыть существующий сегмент.

Но так как у меня нет информации о существовании\несуществовании сегмента, имеет ли на право на жизнь такой код:?

char * pointer;
if ((id = shmget(..., ..., 0666 | IPC_CREATE | IPC_EXCL)) == -1) // Чтобы задетектить наличие блока
{
   id = shmget(..., ..., 0666 | PC_CREATE);
   pointer = shmat(id, 0, 0);
   for (int i = 0; i < N; i++)
      pointer[i] = ...;
}

char * data = (char *)shmat(id, 0, SHM_RDONLY); // Так как права уже уже не нужны.

Как-то так? Или опять хню сморозил?

sambist ★★
() автор топика
Ответ на: комментарий от sambist

shmget может вернуть -1 не только из-за того, что сегмент уже существует.

int id;
for (;;) {
   /* Trying to create segment exclusively */
   id = shmget(key, size, 0666|IPC_CREAT|IPC_EXCL);
   if (id != -1) {
      init(id);
      break;
   }
   if (errno == EEXIST) {
      /* Segment already exists, trying to open it */
      id = shmget(key, size, 0);
      if (id != -1) {
         break;
      }
   }
   fprintf(stderr, "shmget(): %s, retrying", strerror(errno));
}

iliyap ★★★★★
()
Ответ на: комментарий от iliyap

Окей, спасибо. Попробовал механизм, возникла такая трабла - по умному надо помечать блок как

if (shmctl(unicodevalid_id, IPC_RMID, NULL) == -1)
      return SML_ERR_BADACCESS;
По идее, надо помечать так блок для того, чтобы когда разконнекнится последний процесс блок удалялся. Но сразу поставить флаг нельзя - потому что эта зараза ключ блока пометит как 0x000000:
0x010312dc 473989156  alex       666        65536      2
VVV
0x00000000 473956388  alex       666        65536      2
Начиная с этого момента никто к нему новый подконнектиться не сможет, ибо ключ 0. Поэтому нужно самостоятельно следить за подсчетом подконнектившихся процессов и в деструкторе последнего экземпляра ставить этот флаг.

Вопрос - мне смотреть в сторону shm_semaphore (POSIX || System V) с каунтером или они такое не умеют? Или есть более правильное решение на самих флагах блоков памяти?

sambist ★★
() автор топика
Ответ на: комментарий от sambist

У тебя сегмент остался в выводе ipcs из-за того, что не все процессы от него отцепились с помощью shmdt().

Можно попробовать shmctl(IPC_STAT) после shmdt(). Если shm_nattch == 0 (сегмент никто не отмапил в своё адресное пространство), удалять сегмент с помощью shmctl(IPC_RMID).

Но мне кажется, это гоночный подход. Вряд ли ненужность сегмента должна определяться тем, что в данный момент его никто не отмапил. Наверное сегмент должен удаляться, когда его кто-нибудь(все) обработал(и).

Можно хранить эту информацию внутри сегмента. Для этого нужен протокол, исключающий гонку при создании/открытии сегмента. Например, сегмент создаётся с правами 0777, мапится в адресное пространство, инициализируется (например, в нём создаётся POSIX семафор-счётчик или POSIX мьютекс и структура данных, доступ к которой сериализуется по этому мьютексу), затем права сегмента меняются на 0666. При открытии сегмента в цикле проверяется, что его права 0666, и только после того, как права стали 0666, он мапится в адресное пространство.

iliyap ★★★★★
()
Ответ на: комментарий от iliyap

У тебя сегмент остался в выводе ipcs из-за того, что не все процессы от него отцепились с помощью shmdt().

На «скрине» момент работы программы. По деструктору отмапливаю.

Вряд ли ненужность сегмента должна определяться тем, что в данный момент его никто не отмапил.

В данной ситуации все-таки определяется. Так сложились обстоятельства, что в любой момент может быть затребовано одно из значений.

Можно попробовать shmctl(IPC_STAT) после shmdt(). Если shm_nattch == 0 (сегмент никто не отмапил в своё адресное пространство), удалять сегмент с помощью shmctl(IPC_RMID).

Спасибо, попробую. Если не сработает, придется мутить семафоры.

Последний вопрос - скорость доступа к блоку сильно страдает по сравнению со стандартным malloc?

sambist ★★
() автор топика
Ответ на: комментарий от iliyap

Да, с проверкой количества процессов все получилось. Остался лишь маленький вопрос по скорости.

Код, для тех, кому может в будущем понадобится:

SmlErrors SmlInitStrings()
{
    key_t token = ftok(SML_TOKEN_TARGETFILE, SML_TOKEN_UNICODEVALID);

    if ((shared_unicodevalid_id = shmget(token, SML_SHARED_UNICODEVALIDSIZE,
                                  0666 | IPC_CREAT | IPC_EXCL)) != -1)
    {
        if ((int32_t)(shared_unicodevalid = shmat(shared_unicodevalid_id, NULL, 0)) == -1)
            return SML_ERR_BADALLOC;

        // Init it here by the shared_unicodevalid address.

        if (shmdt(shared_unicodevalid) == -1)
            return SML_ERR_BADACCESS;
    }
    else
    {
        if ((shared_unicodevalid_id = shmget(token, SML_SHARED_UNICODEVALIDSIZE, 0)) == -1)
            return SML_ERR_BADALLOC;
    }

    shared_unicodevalid = shmat(shared_unicodevalid_id, NULL, SHM_RDONLY);

    // Now there is full access to values by the shared_unicodevalid address.

    return SML_ERR_SUCCESS;
}

SmlErrors SmlDeinitStrings()
{
    if (shmdt(shared_unicodevalid) == -1)
        return SML_ERR_BADACCESS;

    struct shmid_ds info;
    if (shmctl(shared_unicodevalid_id, IPC_STAT, &info) == -1)
        return SML_ERR_BADACCESS;

    if (info.shm_nattch == 0)
    {
        if (shmctl(shared_unicodevalid_id, IPC_RMID, NULL) == -1)
            return SML_ERR_BADACCESS;
    }

    return SML_ERR_SUCCESS;
}

sambist ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.