Синхронизация с использованием семафора System V из 3 процессов - PullRequest
0 голосов
/ 28 июня 2018

У меня есть 3 процесса, я хочу синхронизировать, используя системный семафор. Процесс 1,2,3 запишет данные в один файл.

Процесс 1, запись от А до Я, Процесс 2 записать в я, Процесс 3 записать с 1 по 9.

Вывод, который я ожидаю Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9.

Процесс 1

#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
int main()
{
    int id,ret,fd;  
    char i;

    struct sembuf v;

    id=semget(8, 5, IPC_CREAT | 0644);
    if(id <0)
    {
        printf("wrong\n");
    }
    fd= open("sample1", O_RDWR | O_APPEND | O_CREAT,0644);

    v.sem_num = 1;
    v.sem_op = 0;
    v.sem_flg = 0;

    semctl(id, 1, SETVAL, 0);   
    semctl(id, 2, SETVAL, 0);   
    semctl(id, 3, SETVAL, 0);

    for(i='A';i<='I';i++)
    {
        semop(id,&v,1);
        semctl(id, 1, SETVAL, 1);
        semctl(id, 2, SETVAL, 1);
        semctl(id, 3, SETVAL, 1);       

        write(fd, &i, 1);

        semctl(id, 2, SETVAL, 0);
        semctl(id, 3, SETVAL, 1);
        semctl(id, 1, SETVAL, 1);
    }

    printf("Done...\n");    
}

Процесс 2

#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
int main()
{
    int id,ret,fd;  
    char i;

    struct sembuf v;

    id=semget(8,5, IPC_CREAT | 0644);
    if(id <0)
    {
        printf("wrong\n");
    }
    fd= open("sample1", O_RDWR | O_APPEND | O_CREAT, 0644);

    v.sem_num = 2;
    v.sem_op = 0;
    v.sem_flg = 0;

    for(i='a';i<='i';i++)
    {
        semop(id,&v,2); 
        semctl(id, 1, SETVAL, 1);
        semctl(id, 2, SETVAL, 1);   
        semctl(id, 3, SETVAL, 1);

        write(fd, &i, 1);

        semctl(id, 3, SETVAL, 0);
        semctl(id, 1, SETVAL, 1);
        semctl(id, 2, SETVAL, 1);
    }

    printf("Done...\n");    
}

Процесс 3

#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>

int main()
{
    int id,ret,fd;  
    char i;

    struct sembuf v;

    id=semget(8,5, IPC_CREAT | 0644);
    if(id <0)
    {
        printf("wrong\n");
    }
    fd= open("sample1", O_RDWR | O_APPEND | O_CREAT,0644);

    v.sem_num = 3;
    v.sem_op = 0;
    v.sem_flg = 0;

    for(i='1';i<='9';i++)
    {
        semop(id,&v,3); 
        semctl(id, 1, SETVAL, 1);
        semctl(id, 2, SETVAL, 1);   
        semctl(id, 3, SETVAL, 1);

        write(fd, &i, 1);

        semctl(id, 1, SETVAL, 0);
        semctl(id, 2, SETVAL, 1);
        semctl(id, 3, SETVAL, 1);
    }

    printf("Done...\n");    
}

Ожидаемый результат: Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9. но я не получаю правильный вывод. Пожалуйста, можно ли помочь выйти из этой проблемы?

Какой семпахор хорош? семафор system V или семафор POSIX? Я новичок в процессе синхронизации. Пожалуйста, помогите мне!

Заранее спасибо.

1 Ответ

0 голосов
/ 28 июня 2018

У вас, кажется, есть несколько проблем, как в стратегии, так и в реализации.

Во-первых , обычный базовый механизм для контроля хода выполнения программы / потока через семафор SysV предназначен для потока, для управления которым необходимо использовать semop(), чтобы попытаться уменьшить семафор.

struct sembuf sb = { .semnum = 1, .sem_op = -1 };
int rval = semop(semid, &sb, 1);
// handle any error ...

Это будет блокировать, пока значение семафора не станет достаточно большим для продолжения (учитывая, что оно никогда не может опуститься ниже нуля) Чтобы позволить этому потоку продолжить, другой поток, возможно, в другом процессе, будет использовать semop() до приращения того же семафора:

struct sembuf sb = { .semnum = 1, .sem_op = 1 };
int rval = semop(semid, &sb, 1);
// handle any error ...

Кроме того, этот подход означает, что поток, сумевший заблокировать семафор (уменьшив его значение), автоматически уменьшает возможности других потоков (или самого себя) также заблокировать его. Когда вы расположите его так, чтобы значения семафора никогда не превышали 1, это приводит к уменьшению функции семафора, подобно блокировке мьютекса, и увеличению одной функции, например, разблокировке мьютекса.

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

Второй , у вас проблема с инициализацией семафора. Это выполняется только процессом 1, но вы не предоставляете ничего, что гарантировало бы, что процесс 1 завершит свою инициализацию до того, как другие процессы начнут пытаться использовать набор семафоров. Это оказывается одним из самых проблемных аспектов семафоров SysV.

Один из способов обойти это - запустить средство запуска, которое устанавливает и инициализирует набор семафоров, и только затем запускает три процесса, которые фактически его используют (ни один из которых затем не должен инициализировать его).

Если вы можете полагаться на набор семафоров, изначально не существующий (и это проблематично для вас, потому что вы используете фиксированный ключ и никогда не удаляете набор семафоров), тогда ваши процессы могут использовать флаг O_EXCL в дополнение к O_CREAT когда они звонят semget(). Это будет успешным только в одном из процессов, который затем может взять на себя ответственность за инициализацию семафоров. Другие выполняют новый semget() без O_EXCL, а затем ждут завершения инициализации. Они могут сделать это путем опроса semctl() с IPC_STAT, отслеживая, чтобы otime семафора стал ненулевым, что произойдет, когда инициализирующий поток выполнит semop().

В-третьих , в вашем коде несколько мелких странностей, включая

  • Вы запрашиваете набор семафоров из 5 членов, но используете только 3.
  • Члены набора семафоров нумеруются от 0, но наименьшее число семафоров, которое вы используете, равно 1.
  • За исключением случаев, когда вы используете IPC_PRIVATE в качестве ключа семафора, обычно ключ получают с помощью ftok() вместо использования фиксированного идентификатора ключа. Это помогает избежать столкновений клавиш.
  • Когда дано, четвертый аргумент semctl должен быть union (который вы обязаны определить самостоятельно, в соответствии с документацией), но вы передаете int s. Если это работает так, как вы ожидали, то только потому, что вам повезло.
  • После того, как последний процесс завершен с семафором, вы должны удалить его. Это можно сделать, выполнив действие IPC_RMID с помощью semctl() или, если необходимо, запустив соответствующую команду ipcrm из оболочки.
  • Вам следует проверять код возврата при каждом вызове функции , который ее предоставляет, за исключением случаев, когда вам действительно все равно, удалось ли это сделать.
...