我正在尝试使用 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 示例;特别是在父母和孩子之间。
如何玩弄代码:
使用 -lpthread 编译并将上面的“kbhit.h”包含在同一目录中
按键盘上的“v”检查位于 SHM 中的 3 个公共变量的值
按键盘上的“c”更改变量之一的值(增量)
再次按“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;
}