C 生产者/消费者任务。正确写入共享缓冲区时出现问题
我正在做一个项目,它解决了生产者/消费者日程安排的经典问题。 Linux开放Suse 42.3 Leep,API系统V,C语言 该项目由三个程序组成:生产者、消费者和调度员。 调度器的目的是创建3个信号量,即共享内存,其中有一个缓冲区(数组),其中包含写入(生产者)和读取(消费者),并运行n个生产者和m个消费者进程 每个生产者必须对缓冲区执行k个写入周期,消费者必须执行k个读取周期 使用了3个信号量:互斥、空和满。完整信号量的值在程序中用作数组中的索引 问题在于:例如,当缓冲区大小为3时,生产者写入4部分数据,当缓冲区大小为4-5部分数据时(尽管应该有4部分数据) 消费者阅读正常 此外,调用get_semVal Function时,程序的行为不可预测 请帮忙,我会非常非常感谢你的回答 制作人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时,生产者
#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);
}