C 生产者/消费者任务。正确写入共享缓冲区时出现问题

C 生产者/消费者任务。正确写入共享缓冲区时出现问题,c,linux,process,parent-child,semaphore,C,Linux,Process,Parent Child,Semaphore,我正在做一个项目,它解决了生产者/消费者日程安排的经典问题。 Linux开放Suse 42.3 Leep,API系统V,C语言 该项目由三个程序组成:生产者、消费者和调度员。 调度器的目的是创建3个信号量,即共享内存,其中有一个缓冲区(数组),其中包含写入(生产者)和读取(消费者),并运行n个生产者和m个消费者进程 每个生产者必须对缓冲区执行k个写入周期,消费者必须执行k个读取周期 使用了3个信号量:互斥、空和满。完整信号量的值在程序中用作数组中的索引 问题在于:例如,当缓冲区大小为3时,生产者

我正在做一个项目,它解决了生产者/消费者日程安排的经典问题。 Linux开放Suse 42.3 Leep,API系统V,C语言

该项目由三个程序组成:生产者、消费者和调度员。 调度器的目的是创建3个信号量,即共享内存,其中有一个缓冲区(数组),其中包含写入(生产者)和读取(消费者),并运行n个生产者和m个消费者进程

每个生产者必须对缓冲区执行k个写入周期,消费者必须执行k个读取周期

使用了3个信号量:互斥、空和满。完整信号量的值在程序中用作数组中的索引

问题在于:例如,当缓冲区大小为3时,生产者写入4部分数据,当缓冲区大小为4-5部分数据时(尽管应该有4部分数据)

消费者阅读正常

此外,调用get_semVal Function时,程序的行为不可预测

请帮忙,我会非常非常感谢你的回答

制作人

#define BUFFER_SIZE 3
#define MY_RAND_MAX 99      // Highest integer for random number generator
#define LOOP 3              //the number of write / read cycles for each process
#define DATA_DIMENSION 4    // size of portion of data for 1 iteration 
struct Data {
int buf[DATA_DIMENSION];
};
typedef struct Data buffer_item;

buffer_item buffer[BUFFER_SIZE];

void P(int semid)
{
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1;
op.sem_flg = 0;
semop(semid,&op,1);
}

void V(int semid)
{
struct sembuf op;
op.sem_num = 0;
op.sem_op = +1;
op.sem_flg = 0;
semop(semid,&op,1);
}

void Init(int semid,int index,int value)
{
    semctl(semid,index,SETVAL,value);
}

int get_semVal(int sem_id)
{
    int value = semctl(sem_id,0,GETVAL,0);
    return value;
}

int main()
{
    sem_mutex = semget(KEY_MUTEX,1,0);
    sem_empty = semget(KEY_EMPTY,1,0);
    sem_full = semget(KEY_FULL,1,0);

    srand(time(NULL));

const int SIZE = sizeof(buffer[BUFFER_SIZE]);

shm_id = shmget(KEY_SHARED_MEMORY,SIZE, 0);

int i=0;
buffer_item *adr;
do {
    buffer_item nextProduced;
   
    P(sem_empty);

    P(sem_mutex);

    //prepare portion of data
    for(int j=0;j<DATA_DIMENSION;j++)
    {
        nextProduced.buf[j]=rand()%5;
    }

    adr = (buffer_item*)shmat(shm_id,NULL,0);

    int full_value = get_semVal(sem_full);//get index of array
    printf("-----%d------\n",full_value-1);//it’s for test the index of array in buffer

   // write the generated portion of data by index full_value-1
    adr[full_value-1].buf[0] = nextProduced.buf[0];
    adr[full_value-1].buf[1] = nextProduced.buf[1];
    adr[full_value-1].buf[2] = nextProduced.buf[2];
    adr[full_value-1].buf[3] = nextProduced.buf[3];

    shmdt(adr);


    printf("producer %d produced %d %d %d %d\n", getpid(), nextProduced.buf[0],nextProduced.buf[1],nextProduced.buf[2],nextProduced.buf[3]);

    V(sem_mutex);
    V(sem_full);

    i++;
    } while (i<LOOP);

V(sem_empty); 
sleep(1); 
 }
#定义缓冲区大小3
#为随机数生成器定义MY_RAND_MAX 99//最高整数
#定义循环3//每个进程的写入/读取周期数
#定义数据维度4//1次迭代数据部分的大小
结构数据{
int buf[数据尺寸];
};
类型定义结构数据缓冲区\u项;
缓冲区\项目缓冲区[缓冲区\大小];
void P(int-semid)
{
结构sembuf op;
op.sem_num=0;
op.sem_op=-1;
op.sem_flg=0;
semop(semid和op,1);
}
void V(int-semid)
{
结构sembuf op;
op.sem_num=0;
op.sem_op=+1;
op.sem_flg=0;
semop(semid和op,1);
}
void Init(int-semid、int-index、int-value)
{
semctl(semid,index,SETVAL,value);
}
int get_semVal(int sem_id)
{
int value=semctl(sem_id,0,GETVAL,0);
返回值;
}
int main()
{
sem_mutex=semget(KEY_mutex,1,0);
sem_empty=semget(KEY_empty,1,0);
sem_full=semget(键_full,1,0);
srand(时间(空));
常量int SIZE=sizeof(缓冲区[buffer_SIZE]);
shm_id=shmget(键共享内存,大小为0);
int i=0;
缓冲项目*adr;
做{
下一个生产的缓冲区项目;
P(sem_-empty);
P(sem_互斥体);
//准备部分数据

对于(int j=0;j来说,试图解开其他人编写的未注释代码并不有趣,因此,我将解释一个经过验证的工作方案

(请注意,注释应始终解释程序员的意图或想法,而不是代码的作用;我们可以阅读代码来了解它的作用。问题是,我们需要首先理解程序员的想法/意图,然后才能将其与实现进行比较。如果没有注释,我需要首先阅读代码来尝试猜测I然后将其与代码本身进行比较;这就像是工作量的两倍。)

(我怀疑OP的根本问题是试图使用信号量值作为缓冲区索引,但并没有仔细阅读所有代码以100%确定。)

假设共享内存结构如下所示:

struct共享{
sem_t lock;/*初始化为值1*/
sem\u t more;/*已初始化为0*/
扫描室;/*初始化为最大项*/
size\u t num\u items;/*已初始化为0*/
大小\u t下一个\u项;/*初始化为0*/
项目类型项目[最大项目];
};
我们有
struct shared*mem
指向共享内存区域

请注意,在运行时,您应该包括
,并验证
MAX\u ITEMS lock
信号量是否像互斥锁一样使用。也就是说,要锁定结构以进行独占访问,进程将等待它。当它完成时,它将在其上发布

请注意,虽然
sem_post(&(mem->lock))
将始终成功(忽略像
mem
为NULL或指向未初始化内存或已被垃圾覆盖之类的错误),但从技术上讲,
sem_wait()
可能会被发送到未安装
SA_RESTART
标志的用户空间处理程序的信号中断。这就是为什么我建议使用静态内联帮助程序函数而不是
sem_wait()

静态内联int信号量等待(sem\u t*const s)
{
int结果;
做{
结果=sem_等待(s);
}而(结果==-1&&errno==EINTR);
返回结果;
}
静态内联int信号量\u post(sem\u t*const s)
{
返回sem_职位;
}
如果信号传递不应中断对信号量的等待,则使用
semaphore\u wait()
。如果确实希望信号传递中断对信号量的等待,则使用
sem\u wait()
;如果它返回
-1
并带有
errno==EINTR
,则操作因信号传递而中断,信号量实际上没有减少。(许多其他低级函数,如
read()
write()
send()
recv())
,可以以完全相同的方式中断;它们也可以只返回一个短计数,以防中途中断。)

semaphore\u post()
只是一个包装器,因此您可以使用“matching`post and wait”操作。您可以看到,使用那种“无用”的包装器确实有助于理解代码

item[]
数组用作循环队列。
num\u items
表示其中的项目数。如果
num\u items>0
,下一个要消费的项目是
item[next\u item]
。如果
num\u items
,下一个要生产的项目是
item[(next\u item+num\u items)%MAX\u items]

%
是模运算符。在这里,因为
下一项
数量项
总是正的,
(下一项+数量项)%MAX\u项
总是介于
0
最大项-1
之间。这就是缓冲区循环的原因<
 …
int main()
{

sem_mutex = semget(KEY_MUTEX,1,0);
sem_empty = semget(KEY_EMPTY,1,0);
sem_full = semget(KEY_FULL,1,0);

srand(time(NULL));

const int SIZE = sizeof(buffer[BUFFER_SIZE]);

shm_id = shmget(KEY_SHARED_MEMORY,SIZE,0);

int i=0;
buffer_item *adr;
do
{
    buffer_item nextConsumed;

    P(sem_full);
    P(sem_mutex);

    int full_value = get_semVal(sem_full);

    adr = (buffer_item*)shmat(shm_id,NULL,0);

    for(int i=0;i<BUFFER_SIZE;i++)
    {
        printf("--%d %d %d %d\n",adr[i].buf[0],adr[i].buf[1],adr[i].buf[2],adr[i].buf[3]);
    }

    for(int i=0;i<BUFFER_SIZE;i++)
    {
        buffer[i].buf[0] = adr[i].buf[0];
        buffer[i].buf[1] = adr[i].buf[1];
        buffer[i].buf[2] = adr[i].buf[2];
        buffer[i].buf[3] = adr[i].buf[3];
    }

    tab(nextConsumed);

        nextConsumed.buf[0]=buffer[full_value-1].buf[0];
        nextConsumed.buf[1]=buffer[full_value-1].buf[1];
        nextConsumed.buf[2]=buffer[full_value-1].buf[2];
        nextConsumed.buf[3]=buffer[full_value-1].buf[3];



    // Set buffer to 0 since we consumed that item
    for(int j=0;j<DATA_DIMENSION;j++)
    {
        buffer[full_value-1].buf[j]=0;
    }

    for(int i=0;i<BUFFER_SIZE;i++)
    {
        adr[i].buf[0]=buffer[i].buf[0];
        adr[i].buf[1]=buffer[i].buf[1];
        adr[i].buf[2]=buffer[i].buf[2];
        adr[i].buf[3]=buffer[i].buf[3];

    }

    shmdt(adr);

    printf("consumer %d consumed %d %d %d %d\n", getpid() ,nextConsumed.buf[0],nextConsumed.buf[1],nextConsumed.buf[2],nextConsumed.buf[3]);

    V(sem_mutex);
    // increase empty
    V(sem_empty);

    i++;
    } while (i<LOOP);

V(sem_full);
sleep(1);
}
…
struct Data {
   int buf[DATA_DIMENSION];
 };
typedef struct Data buffer_item;

buffer_item buffer[BUFFER_SIZE];

struct TProcList
{
  pid_t processPid;
};
typedef struct TProcList ProcList;
 …

ProcList createProcess(char *name)
{
   pid_t pid;
   ProcList a;

   pid = fork();
   if (!pid){
      kill(getpid(),SIGSTOP);
      execl(name,name,NULL);
      exit(0);
   }
   else if(pid){
       a.processPid=pid;
   }
   else
       cout<<"error forking"<<endl;
   return a;
   }

   int main()
   {
      sem_mutex = semget(KEY_MUTEX,1,IPC_CREAT|0600);
      sem_empty = semget(KEY_EMPTY,1,IPC_CREAT|0600);
      sem_full = semget(KEY_FULL,1,IPC_CREAT|0600);

Init(sem_mutex,0,1);//unlock mutex
Init(sem_empty,0,BUFFER_SIZE); 
Init(sem_full,0,0);//unlock empty

const int SIZE = sizeof(buffer[BUFFER_SIZE]);

shm_id = shmget(KEY_SHARED_MEMORY,SIZE,IPC_CREAT|0600);

buffer_item *adr;
adr = (buffer_item*)shmat(shm_id,NULL,0);

for(int i=0;i<BUFFER_SIZE;i++)
{
    buffer[i].buf[0]=0;
    buffer[i].buf[1]=0;
    buffer[i].buf[2]=0;
    buffer[i].buf[3]=0;
}

for(int i=0;i<BUFFER_SIZE;i++)
{
    adr[i].buf[0] = buffer[i].buf[0];
    adr[i].buf[1] = buffer[i].buf[1];
    adr[i].buf[2] = buffer[i].buf[2];
    adr[i].buf[3] = buffer[i].buf[3];

}

int consumerNumber = 2;
int produserNumber = 2;

ProcList producer_pids[produserNumber];
ProcList consumer_pids[consumerNumber];

for(int i=0;i<produserNumber;i++)
{
    producer_pids[i]=createProcess("/home/andrey/build-c-unknown-Debug/c");//create sleeping processes 
}

for(int i=0;i<consumerNumber;i++)
{
    consumer_pids[i]=createProcess("/home/andrey/build-p-unknown-Debug/p");
}

    sleep(3);

for(int i=0;i<produserNumber;i++)
{
   kill(producer_pids[i].processPid,SIGCONT);//continue processes
   sleep(1);
}
for(int i=0;i<consumerNumber;i++)
{
   kill(consumer_pids[i].processPid,SIGCONT);
   sleep(1);
}


for(int i=0;i<produserNumber;i++)
    {
        waitpid(producer_pids[i].processPid,&stat,WNOHANG);//wait
    }
for(int i=0;i<consumerNumber;i++)
    {
        waitpid(consumer_pids[i].processPid,&stat,WNOHANG);
    }

shmdt(adr);

semctl(sem_mutex,0,IPC_RMID);
semctl(sem_full,0,IPC_RMID);
semctl(sem_empty,0,IPC_RMID);

}