Подсчет семафора с помощью SysV

Я пытаюсь понять концепцию подсчета семафоров на примере. Но я хочу реализовать это с помощью SysV в Linux.
Я знаком с теоретической частью двоичного семафора и счетного семафора.
Я сослался на это ссылка.

Концептуально семафоры используются как сигнальный механизм от одного процесса к другому, поэтому я пытался написать простую программу.

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

Так что вывод должен быть примерно таким: Hello From thread 3 Hello from thread 2 Hello from thread 1

Я знаю, что этого можно добиться с помощью pthread_join(), но я хочу добиться этого с помощью семафоров.

Код:

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

int sem_id;
struct sembuf sops[3];
void thread_1(void)
{
    sops[0].sem_num = 0;
    sops[0].sem_op = 0;
    sops[0].sem_flg = 0;

    if(semop(sem_id, sops, 1) < 0)
        perror("Semop In thread 3");
    else
        printf("Hello From thread 1\n");
}

void thread_2(void)
{
    sops[0].sem_num = 0;
    sops[0].sem_op = -1;
    sops[0].sem_flg = 0;

    if(semop(sem_id, sops, 1) < 0)
        perror("Semop In thread 2");
    else
        printf("Hello from thread 2\n");
}

void thread_3(void)
{

    sops[0].sem_num = 0;
    sops[0].sem_op = -1;
    sops[0].sem_flg = 0;

    if(semop(sem_id, sops, 1) < 0)
        perror("Semop In thread 3");
    else
        printf("Hello from thread 3\n");
}

int main(void)
{
    void (*funct[]) = {thread_1, thread_2, thread_3};

    key_t semkey;
    char i;
    union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           }arg;
    pthread_t thread_id[3];

    semkey = ftok("/tmp", 'a');

    if(semkey < 0)
        perror("Cannot Create Semaphore Key");
    else
    {
        sem_id = semget(semkey, 1, (IPC_CREAT|IPC_EXCL|0666));
        if(sem_id < 0)
            perror("Cannot create semaphore\n");
        else
        {
            arg.val = 3;
            if (semctl(sem_id, 0, SETVAL, arg) == -1) {
                perror("semctl");
                exit(1);
             }   
        }
    }

    for(i = 0; i < 3; i++)
    {
        if(pthread_create(&thread_id[i], NULL, funct[i], NULL) < 0)
            perror("Cannot Create thread\n");
    }

   for(i = 0; i < 3; i++)
        pthread_join(thread_id[i], NULL);

    if(semctl(sem_id, 0, IPC_RMID, NULL) == -1)
        perror("semctl");

    return 0;
}

Должен ли я использовать более одного набора семафоров для достижения того, что я пытаюсь сделать?


person Gaurav Pathak    schedule 21.04.2017    source источник
comment
Там нет pthread_wait, кстати. Вероятно, вы имеете в виду pthread_join.   -  person PSkocik    schedule 21.04.2017
comment
Да, спасибо. Исправление сделано!   -  person Gaurav Pathak    schedule 21.04.2017


Ответы (2)


Вам нужно два мьютекса/семафора со счетчиком 1. Предположим, что ваши потоки называются t0,t1,t2, а ваши семафоры sem0 и sem1, тогда t0 работает свободно и увеличивает sem0, t1 ожидает sem0 и увеличивает sem1, а t2 ждет sem1.

Вот полный черновик без проверки ошибок:

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

static int sem0, sem1;
#define POST1(Sem) semop(Sem, &(struct sembuf){0,1,0}, 1)
#define WAIT1(Sem) semop(Sem, &(struct sembuf){0,-1,0}, 1)

static void* t0(void *unused) { puts("hello from t0"); POST1(sem0); return 0; }
static void* t1(void *unused) { WAIT1(sem0); puts("hello from t1"); POST1(sem1); return 0; }
static void* t2(void *unused) { WAIT1(sem1); puts("hello from t2"); return 0; }

int main(void)
{
    key_t sem0_k, sem1_k;
    sem0_k = ftok("/tmp", '0');
    sem1_k = ftok("/tmp", '1');

    sem0 = semget(sem0_k, 1, (IPC_CREAT|IPC_EXCL|0666));
    sem1 = semget(sem1_k, 1, (IPC_CREAT|IPC_EXCL|0666));

    pthread_t tids[3];
    pthread_create(tids+2, NULL, t2, NULL);
    sleep(1);
    pthread_create(tids+1, NULL, t1, NULL);
    sleep(1);
    pthread_create(tids+0, NULL, t0, NULL);

    for(int i = 0; i < 3; i++)
        pthread_join(tids[i], NULL);

    semctl(sem0, 0, IPC_RMID, NULL);
    semctl(sem1, 0, IPC_RMID, NULL);

    return 0;
}

Я запускаю потоки в обратном порядке и жду 1 секунду между t0 и t1, а также t1 и t2, чтобы показать, что семафоры выполняют работу по упорядочению потоков от t0 до t2.

person PSkocik    schedule 21.04.2017
comment
Большое спасибо за объяснение. - person Gaurav Pathak; 21.04.2017

@PSkocik, основываясь на вашем ответе, я изменил свой код, чтобы использовать набор из двух семафоров. Вот код:

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

int sem_id;

struct sembuf sops;
void thread_1(void)
{
    /*Wait on Set1 of Semaphore*/
    sops.sem_num = 1;
    sops.sem_op = -1;
    sops.sem_flg = 0;

    if(semop(sem_id, &sops, 1) < 0)
        perror("Semop Wait In thread 3");
    else
        printf("Hello From thread 1\n");
}


void thread_2(void)
{
    /*Wait on Set0 of Semaphore*/
    sops.sem_num = 0;
    sops.sem_op = -1;
    sops.sem_flg = 0;

    if(semop(sem_id, &sops, 1) < 0)
        perror("Semop Wait In thread 2");
    else
        printf("Hello from thread 2\n");

    /*Post on Set1 of Semaphore*/        
    sops.sem_num = 1;
    sops.sem_op = 1;
    sops.sem_flg = 0;

    if(semop(sem_id, &sops, 1) < 0)
        perror("Semop Post In thread 2");
}

void thread_3(void)
{
    printf("Hello from thread 3\n");

    /*Post operation on Set0 of semaphore*/
    sops.sem_num = 0;
    sops.sem_op = 1;
    sops.sem_flg = 0;

    if(semop(sem_id, &sops, 1) < 0)
        perror("Semop In thread 3");
    else
    { ; }
}

int main(void)
{
    void (*funct[]) = {thread_1, thread_2, thread_3};

    key_t semkey;
    char i;
    union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           }arg;
    pthread_t thread_id[3];

    semkey = ftok("/tmp", 'a');

    if(semkey < 0)
        perror("Cannot Create Semaphore Key");
    else
    {
        sem_id = semget(semkey, 2, (IPC_CREAT|IPC_EXCL|0666));
        if(sem_id < 0)
            perror("Cannot create semaphore\n");
        else
        {
            /*arg.val = 3;
            if (semctl(sem_id, 0, SETVAL, arg) == -1) {
                perror("semctl");
                exit(1);
             }*/   
        }
    }

    for(i = 0; i < 3; i++)
    {
        if(pthread_create(&thread_id[i], NULL, funct[i], NULL) < 0)
            perror("Cannot Create thread\n");
    }

   for(i = 0; i < 3; i++)
        pthread_join(thread_id[i], NULL);

    if(semctl(sem_id, 0, IPC_RMID, NULL) == -1)
        perror("semctl");

    return 0;
}

Кстати, большое спасибо за ваше объяснение.

person Gaurav Pathak    schedule 21.04.2017
comment
Ага. С семафорами SysV это лучший подход, чем два автономных набора семафоров одного семафора (хотя, если ваша проблема не более сложна, вам следует использовать мьютексы pthread или семафоры posix, поскольку они намного легче по сравнению с семафором sysv). - person PSkocik; 21.04.2017
comment
Да, я где-то изучал, что семафоры SysV работают на уровне ядра, но не совсем уверен. - person Gaurav Pathak; 21.04.2017