【问题标题】:C-->How to coordinate processes behaviour using semaphore?C-->如何使用信号量协调进程行为?
【发布时间】:2019-05-28 01:45:41
【问题描述】:

我想请你们帮忙理解C Semaphores。 基本上我想创建两个进程ChildParent。 当然他们的行为是不同的:

1) 子进程做一些sleep() 并在先前创建的消息队列上发送消息

2) 父进程也做了一些sleep(),但随后会读取队列中发送的消息。

当然,我需要子进程成为第一个运行的进程。 我设法使用waitwaitpid 系统调用来做到这一点,现在我想使用Semaphore 来做到这一点。 下面是我设法编写的一些代码:

A) 我使用的函数来自 semaphore.h

//In order to create a Semaphore

int createSemaphore(key_t semaphoreKey){
    int semaphoreId = semget(semaphoreKey, 1, 0666 | IPC_CREAT);
    if(semaphoreId == -1){
        printf(RED "Semaphore Creation failed\n"RESET);
        return -1;
    }
    return semaphoreId;
}

void semaphoreWait(int semaphoreId, int semaphoreNumber){
    struct sembuf buffer;
    buffer.sem_num = semaphoreNumber;
    buffer.sem_op = -1;
    buffer.sem_flg = 0;
    int done = semop(semaphoreId, &buffer, 1);
    if(done == -1){
        printf(RED "Wait on Semaphore %d failed\n" RESET, semaphoreNumber);
        return;
    }
}

void semaphoreSignal(int semaphoreId, int semaphoreNumber){
    struct sembuf buffer;
    buffer.sem_num = semaphoreNumber;
    buffer.sem_op = 1;
    buffer.sem_flg = 0;
    int done = semop(semaphoreId, &buffer, 1);
    if(done == -1){
        printf(RED "semaphoreSignal Failed on Semaphore %d\n" RESET, semaphoreNumber);
        return;
    }
}

B) 我使用的函数来自messageQueue.h

typedef struct message{
    long type;
    char text[255];
}message;

//Creates a Message Queue and returns its ID
int createMessageQueue(key_t semaphoreKey){
    int queueId;
    queueId = msgget(semaphoreKey, 0666 | IPC_CREAT);
    return queueId;
}

//Sends a Message on Message Queue queueId
void sendMessage(int queueId, message* messaggio){
    int sent = msgsnd(queueId, messaggio, strlen(messaggio->text) + 1, 0);
    if(sent == -1){
        printf(RED "Sending the message %s failed on Message Queue %d\n"RESET, messaggio->text, queueId);
        return;
    }
}

//Receives a message from Message Queue queueId
void receiveMessage(int queueId, message* messaggio, int type){
    int received = msgrcv(queueId, messaggio, 255, type, 0);
    if(received == -1){
        printf(RED "Receiving message on Message Queue %d failed\n" RESET, queueId);
        return;
    }
}
void setText(message *mex, char text[], int size){
    for(int i = 0; i < size; i++){
        mex->text[i] = text[i];
    }
}

C) 最后是我的main.c 文件:

#include "semaphore.h"
#include "messageQueue.h"

int main(int argc, char *argv[]){
    pid_t piddo;
    message mex, mex2;
    mex2.type = 1;
    key_t semKey = ftok("/temp", 0);
    key_t mexKey = ftok("/temp", 1);
    char text[] = "This is a message sent from Child to Parent";
    int semId = createSemaphore(semKey);
    int mexId = createMessageQueue(mexKey);
    switch(piddo = fork()){
        case -1:
            printf(RED "Fork ERROR\n" RESET);
            exit(EXIT_FAILURE);
        case 0:
            printf(CYAN "Child Process %d has started\n" RESET, getpid());
            printf(CYAN "Doing some useless sleep\n" RESET);
            for(int i = 0; i < 4; i++){
                printf(CYAN "Sleep %d\n" RESET, i);
                sleep(1);
            }
            printf(CYAN "Useless Sleep Finished\n" RESET);
            printf(CYAN "Now I setup Message to send\n" RESET);
            mex.type = 1;
            setText(&mex, text, strlen(text));
            printf(CYAN "Settings saved, Now I send Message\n" RESET);
            sendMessage(mexId, &mex);
            printf(CYAN "Message succesfully sent, Child Process %d has finished\n" RESET, getpid());
            //Now I want Parent Process to know Child has finished
            semaphoreSignal(semId, 0);
            exit(EXIT_SUCCESS);
        default:
            printf(YELLOW "Parent Process %d started\n" RESET, getpid());
            printf(YELLOW "I have to wait for Child Process\n" RESET);
            //With this wait Parent Process should wait for Child to have finished
            semaphoreWait(semId, 0);
            //Now Parent knows Child has finished
            printf(YELLOW "Looks like Child Process has finished\n" RESET);
            printf(YELLOW "Doing some useless sleep\n" RESET);
            for(int i = 0; i < 4; i++){
                printf(YELLOW "Sleep %d\n" RESET, i);
                sleep(1);
            }
            printf(YELLOW "Useless sleep finished\n" RESET);
            printf(YELLOW "Receiving Message sent by Child...\n" RESET);
            receiveMessage(mexId, &mex2, 1);
            printf(YELLOW "Message Received: \n" RESET);
            printf(YELLOW "%s\n" RESET, mex2.text);
            printf(YELLOW "All Done, Parent Process %d has finished\n" RESET, getpid());
            semaphoreSignal(semId, 0);
            exit(EXIT_SUCCESS);
    }
}

你们可能注意到了,ChildParent 进程在这个 main.c 文件中并没有像我希望的那样同步。 我的问题是: 我如何确保 Child 在 Parent 开始之前完成他的代码要求的所有事情? 谢谢:)

【问题讨论】:

  • RED、YELLOW 和 CYAN 是在另一个头文件中定义的变量。它们只是在输出中为文本着色,因此在这里无关紧要。
  • 我认为 POSIX 信号量更容易使用:man7.org/linux/man-pages/man7/sem_overview.7.html。无论如何,看起来您没有正确使用 System V 信号量,因为它没有通过semctl 进行初始化。它可能更容易创建信号量,将其初始化为 1,并让父级等待为零,直到子级递减它。

标签: c message-queue message semaphore


【解决方案1】:

您的问题不清楚 - 最好清楚地说明您观察到的问题。由于程序正在尝试同步父项和子项,而您询问了这一点,我假设您看到父项试图在子项通过信号量发出完成信号之前读取消息。

正如 Fred 所说,您没有初始化 sysV 信号量;完成后也不要删除它 (this may help)。我假设信号量(如果尚不存在)从值 0 开始。由于它是 IPC 工具并且不属于任何单个进程,因此它在运行之间保持不变。

您的程序(我们可以看到它)调用了两次semaphoreSignal 和一次semaphoreWait,因此每次程序运行时,它都会将信号量的值加1,并保持不变;因此,在后续运行中,由于先前运行的剩余值,信号量值从 >0 开始,因此父级立即运行。

您还应该检查 ftok(3) 的返回值; /temp 不是正常存在的路径。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-13
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多