简体   繁体   English

使用互斥量构建信号量

[英]Building semaphores using Mutexes

I'm reading Modern Operating Systems and there's a exercise that asks you to build a counting-semaphore with a binary-semaphore.我正在阅读现代操作系统,并且有一个练习要求您使用二进制信号量构建计数信号量。

I was wondering if I could do it with semaphores & shared memory (C and POSIX IPC).我想知道我是否可以使用信号量和共享 memory(C 和 POSIX IPC)来做到这一点。

I'll leave a simplification of my program here.我将在这里简化我的程序。

typedef struct sem {
    int semid; /* contains two semaphores with value set to 1, one for the critical region and the other for locking */
    int shmid; /* shared counter */
}Sem;

/* the parent process spawns a few child processes and then waits for them */
void child() {
    Sem *s;
    
    s = get_sem();
    down_sem(s);
    printf("working...");
    sleep(5);
    printf("finished!");
    up_sem(s);
}

void down_sem(Sem *s) {
    int n;

    enter_critical(s); // down to critical region sem
    n = get_counter(s);
    
    if (n == 0) {
        exit_critical(s); // up to critical region sem
        acquire_lock(s); // down to lock sem
    } else {
        n--;
        exit_critical(s); // up to critical region sem
    }
}

void up_sem(Sem *s) {
    int n;

    enter_critical(s);
    n = get_counter(s);
    n++;
    
    if (n == 1) {
        exit_critical(s);
        release_lock(s); // up to lock sem
    } else {
        exit_critical(s);
    }
}

But this doesn't work since some processes block in the lock semaphore forever.但这不起作用,因为某些进程永远阻塞在锁信号量中。 I'm just now learning these concepts so my design is probably totally off.我刚刚开始学习这些概念,所以我的设计可能完全不合时宜。

I can share the complete code if you wish.如果你愿意,我可以分享完整的代码。

Thanks in advance!提前致谢!

EDIT: requested mre编辑:要求mre

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/shm.h>

#define NPROC 5
#define SLOTS 2

#define PATH "."
#define SID 'S'
#define SEMN 2

#define SHMID 'M'
#define SHM_SIZE 32

#define CRIT 0
#define LOCK 1

#define UP 1
#define DOWN -1

typedef struct sem {
    int shmid;
    int semid;
}Sem;

typedef union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
}Args;

void err (char *msg) {
    char error[50];
    int n;

    n = snprintf(error, 50, "[%d] %s", getpid(), msg);
    error[n] = 0;
    perror(error);
    exit(1);
}

int* get_n(Sem *s) {
    int *data = NULL;

    data = shmat(s->shmid, (void *)0, 0);
    if (data == (int *)(-1))
        err("get_n shmat");

    return data;
}

void dettach_n(int *data) {
    if (shmdt(data) == -1)  
        err("dettach_n shmdt");
}

void enter_crit(Sem *s) {       
    struct sembuf sb;

    sb.sem_num = CRIT;
    sb.sem_op = DOWN;

    printf("[%d] enter crit\n", getpid());
    if (semop(s->semid, &sb, 1) == -1)
        err("enter crit");
}

void exit_crit(Sem *s) {
    struct sembuf sb;

    sb.sem_num = CRIT;
    sb.sem_op = UP;

    printf("[%d] exit crit\n", getpid());
    if (semop(s->semid, &sb, 1) == -1)
        err("exit crit");
}

void acquire_lock(Sem *s) {
    struct sembuf sb;

    sb.sem_num = LOCK;
    sb.sem_op = DOWN;

    printf("[%d] acquire lock\n", getpid());
    if (semop(s->semid, &sb, 1) == -1)
        err("acquire lock");
}

void release_lock(Sem *s) {
    struct sembuf sb;

    sb.sem_num = LOCK;
    sb.sem_op = UP;

    printf("[%d] release lock\n", getpid());
    if (semop(s->semid, &sb, 1) == -1)
        err("release lock");
}

int sem_init(Sem *s, int n) {
    key_t key;
    Args arg;
    int *data;

    /* sem */
    if ((key = ftok(PATH, SID)) == -1) {
        return -1;
    }
    if ((s->semid = semget(key, SEMN, 0600 | IPC_CREAT)) == -1) {
        return -1;
    }

    arg.val = 1;
    if (semctl(s->semid, 0, SETVAL, arg) == -1) {
        return -1;
    }
    if (semctl(s->semid, 1, SETVAL, arg) == -1) {
        return -1;
    }

    /* mem */
    if ((key = ftok(PATH, SHMID)) == -1) {
        return -1;
    }
    if ((s->shmid = shmget(key, SHM_SIZE, 0664 | IPC_CREAT)) == -1) {
        return -1;
    }

    data = shmat(s->shmid, (void *)0, 0);
    if (data == (int *)(-1)) {
        return -1;
    }
    *data = n;
    if (shmdt(data) == -1) {
        return -1;
    }

    return 0;
}

int sem_get(Sem *s) {
    key_t key;
    
    if ((key = ftok(PATH, SID)) == -1) {
        return -1;
    }
    if ((s->semid = semget(key, SEMN, 0)) == -1) {
        return -1;
    }

    if ((key = ftok(PATH, SHMID)) == -1) {
        return -1;
    }
    if ((s->shmid = shmget(key, SHM_SIZE, 0)) == -1) {
        return -1;
    }

    return 0;
}

int sem_up(Sem *s) {
    int *data;

    enter_crit(s);
    data = get_n(s);
    printf("[%d] read %d\n", getpid(), *data);
    (*data)++;

    if (*data == 1) {
        printf("[%d] now is %d\n", getpid(), *data);
        dettach_n(data);
        exit_crit(s);
        release_lock(s);
    } else {
        exit_crit(s);
    }

    return 0;
}

int sem_down(Sem *s) {
    int *data;

    enter_crit(s);
    data = get_n(s);

    printf("[%d] checked %d\n", getpid(), *data);
    if (*data == 0) {
        dettach_n(data);
        exit_crit(s);
        acquire_lock(s);
    } else {
        (*data)--;
        dettach_n(data);
        exit_crit(s);
    }

    return 0;
}

int sem_rm(Sem *s) {
    if (semctl(s->semid, 0, IPC_RMID) == -1)
        return -1;

    if (shmctl(s->shmid, 0, IPC_RMID) == -1)
        return -1;

    return 0;
}

void child() {
    pid_t pid;
    Sem s;

    pid = getpid();
    printf("\x1b[31m[%d] hello!\033[0m\n", pid);

    if (sem_get(&s) == -1) {
        perror("sem_get");
        exit(1);
    }   
    sem_down(&s);
    printf("\x1b[32m[%d] working...\033[0m\n", pid);
    sleep(5);
    printf("\x1b[32m[%d] finishing...\033[0m\n", pid);
    sem_up(&s);
    printf("\x1b[34m[%d] **child leaving**\033[0m\n", pid);
    exit(0);
}

int main() {
    Sem s;
    int i;

    if (sem_init(&s, SLOTS) == -1) {
        perror("sem_init");
        exit(1);
    }

    printf("[%d] parent\n", getpid());

    for (i = 0; i < NPROC; i++) {
        switch(fork()) {
        case 0:
            child();
        case -1:
            perror("fork");
            exit(1);
        }
    }   

    printf("waiting for children...\n");
    for (i = 0; i < NPROC; i++)
        wait(NULL);

    if (sem_rm(&s) == -1) {
        perror("sem_rm");
        exit(1);
    }

    printf("good bye!\n");

    return 0;
}

The problem was in the up_sem function. It only does an up to the lock sem (release_lock func) if n was previously 0. This is incorrect since it might be several process waiting.问题出在 up_sem function。如果 n 之前为 0,它只会执行锁定 sem(release_lock func)。这是不正确的,因为它可能有多个进程在等待。

My solution was adding an extra shared memory counting the waiting processes and doing an up to the lock sem when that number is greater than 0.我的解决方案是添加一个额外的共享 memory 来计算等待进程并在该数字大于 0 时执行锁定 sem。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM