1

我正在尝试使用 IPC,并提出了一个涉及 Mutex 的基本示例。这是一个精简版本以突出显示该错误。没有互斥锁的代码按预期工作,子进程可以从父修改的 SHM 区域读取值。但是,在这个版本中,一旦父级释放互斥锁,子级就无法获取它并卡在块中。我正在使用“kbhit.h”来简化键盘界面。代码也附在下面。如果有人知道为什么会这样,请分享知识。

程序片段:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/mman.h>
#include "kbhit.h"

typedef struct {
    pthread_cond_t cond;
    pthread_condattr_t condattr;
    pthread_mutex_t mutex;
    pthread_mutexattr_t mutexattr;
    int status;
} IPCData;

int main(int argc, char const *argv[])
{
    IPCData* sharedMemory;
    pid_t childPID; //child process PID returned by fork
    char ch='x';

    sharedMemory = (IPCData*)mmap(NULL, sizeof(IPCData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    if ((void*)sharedMemory == -1){
        printf("Memory mapping failed\n");
        return -1;
    }

    sharedMemory->status = 1; //Set shared variable value

    if (pthread_condattr_init(&(sharedMemory->condattr)) != 0 && pthread_condattr_setpshared(&(sharedMemory->condattr), PTHREAD_PROCESS_SHARED) != 0){
        printf("Error initializing and setting condition variable attribute values\n");
        return -1;
    }

    if (pthread_cond_init(&(sharedMemory->cond), &(sharedMemory->condattr)) !=0){
        printf("Error initializing condition variable with attribute\n");
        return -1;
    }

    if (pthread_mutexattr_init(&(sharedMemory->mutexattr)) != 0 && pthread_mutexattr_setpshared(&(sharedMemory->mutexattr), PTHREAD_PROCESS_SHARED) != 0){
        printf("Error initializing mutex attribute and set to process shared\n");
        return -1;
    }

    if (pthread_mutex_init(&(sharedMemory->mutex), &(sharedMemory->mutexattr)) !=0){
        printf("Error initializing mutex with attributes\n");
        return -1;
    }

    //forking
    switch (childPID = fork()){
        case -1:
            printf("Failure to fork new child process\n");
            return -1;
        case 0:
            //child process
            printf("Child Started\n");
            while (__sync_fetch_and_add(&(sharedMemory->status), 0) !=0){ //atomically vertify variable value to continue execution as long as value !=0
                printf("Child - Looping, Status: %d\n", sharedMemory->status);
                //child fails to acquire lock after parent release it in SHM region
                pthread_mutex_lock(&(sharedMemory->mutex)); //acquire lock
                printf("Child Mutex Acquired\n");
                if (__sync_fetch_and_add(&(sharedMemory->status), 0) > 1) {printf("Increment Detected. Status: %d\n", sharedMemory->status);}
                printf("Child Mutex Released\n");
                pthread_mutex_unlock(&(sharedMemory->mutex)); //release lock
            }
            printf("Child Exiting\n");
            _Exit(3);
            break;
        default:
            //parent process

            init_keyboard();

            while(ch != 'q') {
                if (kbhit()) {
                    do{
                        ch=readch();
                    } while(kbhit());
                    if (ch == 's'){
                        pthread_mutex_lock(&(sharedMemory->mutex));
                        printf("Parent Mutex Acquired\n");
                        printf("Subbing\n");
                        printf("Status: %d\n", __sync_fetch_and_sub(&(sharedMemory->status), 1));
                        printf("Parent Mutex Released\n");
                        pthread_mutex_unlock(&(sharedMemory->mutex));
                    } else if (ch == 'a'){
                        pthread_mutex_lock(&(sharedMemory->mutex));
                        printf("Parent Mutex Acquired\n");
                        printf("Adding\n");
                        printf("Status: %d\n", __sync_fetch_and_add(&(sharedMemory->status), 1));
                        printf("Parent Mutex Released\n");
                        pthread_mutex_unlock(&(sharedMemory->mutex));
                    } else if (ch == 'z'){
                        printf("Returning value(Adding 0)\n");
                        printf("Status: %d\n", __sync_fetch_and_add(&(sharedMemory->status), 0));
                    }
                }
            }
            printf("Parent Exiting\n");
            close_keyboard();
            break;
    }

    pthread_condattr_destroy(&(sharedMemory->condattr));
    pthread_cond_destroy(&(sharedMemory->cond));
    pthread_mutexattr_destroy(&(sharedMemory->mutexattr));
    pthread_mutex_destroy(&(sharedMemory->mutex));

    if (munmap(sharedMemory, sizeof(IPCData)) == -1){
        printf("Unmap shared memory region failed\n");
        return -1;
    }

    return 0;
}

kbhit.h

#ifndef KBHITh
#define KBHITh

#include <termios.h>
#include <unistd.h>   // for read()

void   init_keyboard(void);
void   close_keyboard(void);
int      kbhit(void);
int     readch(void); 

static struct termios initial_settings, new_settings;
static int peek_character = -1;

void init_keyboard()
{
    tcgetattr(0,&initial_settings);
    new_settings = initial_settings;
    new_settings.c_lflag &= ~ICANON;
    new_settings.c_lflag &= ~ECHO;
    new_settings.c_lflag &= ~ISIG;
    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &new_settings);
}

void close_keyboard()
{
    tcsetattr(0, TCSANOW, &initial_settings);
}

int kbhit()
{
unsigned char ch;
int nread;

    if (peek_character != -1) return 1;
    new_settings.c_cc[VMIN]=0;
    tcsetattr(0, TCSANOW, &new_settings);
    nread = read(0,&ch,1);
    new_settings.c_cc[VMIN]=1;
    tcsetattr(0, TCSANOW, &new_settings);
    if(nread == 1) 
    {
        peek_character = ch;
        return 1;
    }
    return 0;
}

int readch()
{
char ch;

    if(peek_character != -1) 
    {
        ch = peek_character;
        peek_character = -1;
        return ch;
    }
    read(0,&ch,1);
    return ch;
}


#endif

在玩弄了代码之后,不确定到底发生了什么,它按预期运行。以下是跨进程使用条件变量和互斥锁的 IPC 示例;特别是在父母和孩子之间。

如何玩弄代码:

  1. 使用 -lpthread 编译并将上面的“kbhit.h”包含在同一目录中

  2. 按键盘上的“v”检查位于 SHM 中的 3 个公共变量的值

  3. 按键盘上的“c”更改变量之一的值(增量)

  4. 再次按“v”进行验证

退出:

1.按“k”修改变量值

2.再次按“c”,因为孩子将在退出循环之前被阻塞等待

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/mman.h>
#include "kbhit.h"

typedef struct {
    pthread_cond_t cond;
    pthread_condattr_t condattr;
    pthread_mutex_t mutex;
    pthread_mutexattr_t mutexattr;
    int status;
    int jobsignal;
    int count;
} IPCData;

int main(int argc, char const *argv[])
{
    IPCData* sharedMemory;
    pid_t childPID; //child process PID returned by fork
    char ch='x';

    sharedMemory = (IPCData*)mmap(NULL, sizeof(IPCData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    if ((void*)sharedMemory == -1){
        printf("Memory mapping failed\n");
        return -1;
    }

    sharedMemory->status = 1; //set shared variable value 1 = execute 0 = terminate
    sharedMemory->jobsignal = 0; //turn off Signal status 1 = job exist 0 = no job
    sharedMemory->count = 0; //common count variable

    if (pthread_condattr_init(&(sharedMemory->condattr)) != 0 || pthread_condattr_setpshared(&(sharedMemory->condattr), PTHREAD_PROCESS_SHARED) != 0){
        printf("Error initializing and setting condition variable attribute values\n");
        return -1;
    }

    if (pthread_cond_init(&(sharedMemory->cond), &(sharedMemory->condattr)) !=0){
        printf("Error initializing condition variable with attribute\n");
        return -1;
    }

    if (pthread_mutexattr_init(&(sharedMemory->mutexattr)) != 0 || pthread_mutexattr_setpshared(&(sharedMemory->mutexattr), PTHREAD_PROCESS_SHARED) != 0){
        printf("Error initializing mutex attribute and set to process shared\n");
        return -1;
    }

    if (pthread_mutex_init(&(sharedMemory->mutex), &(sharedMemory->mutexattr)) !=0){
        printf("Error initializing mutex with attributes\n");
        return -1;
    }

    //forking
    switch (childPID = fork()){
        case -1:
            printf("Failure to fork new child process\n");
            return -1;
        case 0:
            //child process
            printf("Child Process Started\n");
            while (__sync_fetch_and_add(&(sharedMemory->status), 0) !=0 ){ //atomically vertify variable value to continue execution as long as value !=0
                if (pthread_mutex_lock(&(sharedMemory->mutex)) != 0) { //acquire lock
                    printf("Child Process Mutex Acquisition Failed\n");
                } else {
                    printf("Child Process Mutex Acquisition Success\n");
                    if (pthread_cond_wait(&(sharedMemory->cond), &(sharedMemory->mutex)) != 0){
                        printf("Child Process Condition Wait Failed\n");
                    } else {
                        printf("Child Process Condition Wait Success\n");
                    }
                    __sync_fetch_and_add(&(sharedMemory->count), 1); //add 1
                    if (pthread_mutex_unlock(&(sharedMemory->mutex)) != 0) { //release lock
                        printf("Child Process Mutex Released Failed\n");
                    } else {
                        printf("Child Process Mutex Release Success\n");
                    }
                }           
            }
            printf("Child Process Exiting\n");
            _Exit(3);
            break;
        default:
            //parent process
            printf("Parent Process Started\n");
            init_keyboard();

            while(ch != 'q') {
                if (kbhit()) {
                    do{
                        ch=readch();
                    } while(kbhit());
                    if (ch == 'c'){
                        if (pthread_mutex_lock(&(sharedMemory->mutex)) != 0) {
                            printf("Parent Process Mutex Acquisition Failed\n");
                        } else {
                            printf("Parent Process Mutex Acquisition Success\n");
                            if (pthread_cond_signal(&(sharedMemory->cond)) != 0){
                                printf("Parent Process Signal Failed\n");
                            } else {
                                printf("Parent Process Signal Success\n");
                            }
                            if (pthread_mutex_unlock(&(sharedMemory->mutex)) != 0){
                                printf("Parent Process Mutex Release Failed\n");
                            } else {
                                printf("Parent Process Mutex Release Success\n");
                            }
                        }
                    } else if (ch == 'v'){
                        printf("Status: %d\n", __sync_fetch_and_add(&(sharedMemory->status), 0));
                        printf("JobSignal: %d\n", __sync_fetch_and_add(&(sharedMemory->jobsignal), 0));
                        printf("Count: %d\n", __sync_fetch_and_add(&(sharedMemory->count), 0));
                    } else if (ch == 'k'){
                        printf("Terminating Child Process\n");
                        __sync_fetch_and_sub(&(sharedMemory->status), 1);
                    }
                }
            }
            printf("Parent Process Exiting\n");
            close_keyboard();
            break;
    }

    pthread_condattr_destroy(&(sharedMemory->condattr));
    pthread_cond_destroy(&(sharedMemory->cond));
    pthread_mutexattr_destroy(&(sharedMemory->mutexattr));
    pthread_mutex_destroy(&(sharedMemory->mutex));

    if (munmap(sharedMemory, sizeof(IPCData)) == -1){
        printf("Unmap shared memory region failed\n");
        return -1;
    }

    return 0;
}
4

1 回答 1

1

您不能使用 pthread_mutex 来同步不同的进程,只能同步同一进程的线程。如果您可以摆脱它,请为 ipc 使用管道,那么您不需要同步对不同数据结构的访问。

编辑:

只是用谷歌搜索,似乎有时在设置正确的属性时可以,对不起

于 2013-08-27T17:22:18.687 回答