以生产者消费者为例实现Linux进程线程控制

操作系统课程设计实验报告


设计题目一:Linux进程线程控制

——以生产者消费者为例实现进程线程控制

——by 


一、设计题目要求

加深理解进程和程序、进程和线程之间的联系与区别;

深入理解进程及线程的重要数据结构及实现机制;

熟悉进程及线程的创建、执行、阻塞、唤醒、终止等控制方法;

学会使用进程及线程开发应用程序。

二、程序设计思路及流程图

程序功能简介:

生产者功能描述:在同一个进程地址空间内执行两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。 

消费者功能描述:消费者线程从缓冲区获得物品,然后释放缓冲区,当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。

程序设计思路:

设计了两个主要函数:生产者函数、消费者函数

设计了三个信号量:full信号量,判断缓冲区是否有值,初值为0;

                  empty信号量,判断缓冲区是否有空缓冲区,初值为缓冲区数;

                  mutex信号量作为互斥信号量,用于互斥的访问缓冲区。

生产者函数通过执行P操作信号量empty减1,判断缓冲区是否有空。有空则互斥的访问缓冲区病放入数据,然后释放缓冲区,执行V操作,信号量full加1.

消费者函数执行P操作,信号量full减1,判断是否有数据,有则互斥的访问缓冲区并取走数据,然后释放缓冲区,执行V操作,empty信号量加1。

程序流程图:

见附录A

三、涉及的背景知识及所用函数简介

1、fork 函数

函数原型 :pid_t fork(void)

头文件 :unistd.h

作用 :创建一个子进程

返回值 :成功后父进程返回子进程的PID号,子进程返回0;失败返回-1

2、pthread_create 函数

函数原型 :int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_rtn) (void*),void * arg)

头文件 :pthread.h

作用 :创建一个线程

参数 :thread         待创建线程的id指针

           pthread_attr_t     创建线程时的线程属性

           *start_rtn(void * ) 返回值是void*类型的指针函数

           arg                函数start_rtn的参数

返回值 :成功返回0;失败返回错误编号

3、pthread_join 函数

函数原型 :int pthread_join( pthread_t thread, void ** rval_ptr)

头文件 :pthread.h

作用 :1)调用者将挂起并等待指定线程终止

                 2)当新线程调用pthread_exit()退出或者return时,进程中的其他线       可通过pthread_join()获得进程的退出状态

参数 :thread 线程的ID

rval_ptr 线程的返回状态

返回值 :成功返回0;失败返回错误编码

    4、wait 函数

函数原型 :pid_t wait(int *status)

头文件 :sys/wait.h

作用 :调用者将挂起并等待子进程终止

参数 :status 进程的返回状态

返回值 :子进程的ID号

5、sem_wait 函数

函数原型 :int sem_wait(sem_t*sem)     

头文件 :semaphore.h       

作用 :从信号量的值减去一个“1”,但它永远会先等待该信号量为一个

零值才开始做减法    

返回值 :所有的函数成功返回0,错误的话信号量的值不改动,返 回-1,error设定来标识错误

6、sem_post 函数

函数原型 :int sem_post(sem_t*sem)

头文件 :emaphore.h  

作用 :给信号量的值加上一个“1”

返回值 :所有的函数成功返回0,错误的话信号量的值不改动,返 回-1,error设定来标识错误

7、sem_init 函数

函数原型 :int sem_init(sem_t*sem, int pshared, unsigned int value);

         头文件 :emaphore.h 

作用 :初始化一个定位在sem的匿名信号量

返回值 :成功是返回0;错误时,返回—1,并把error设置为合适的值。

8、sem_destroy 函数

函数原型 :int sem_destroy(sem_t*sem)

头文件 :emaphore.h  

作用 :销毁有sem指向的匿名信号量

     

 返回值 :成功是返回0;错误时,返回—1,并把error设置为合适的值。

9、signal 函数

函数原型 :void (*signal(int signum,void(*handler)(int)))(int);           

头文件 :signal.h

作用 :设置某一信号的对应动作

参数 :sinnum指明了所要处理的信号类型

               handler描述了与信号关联的动作

返回值 :返回先前的信号处理函数指针,如果有错误则返回 SIG_ERR(-1)

10、alarm 函数

函数原型 :unsigned int alarm (unsigned int seconds);

头文件 :unistd.h

作用 :在进程中设置一个定时器,当定时器指定的时间到时,它向进程 发送SIGALRM信号。如果忽略或者不捕获此信号,则其默认动作      是终止调用该alarm函数的进程。

参数 :seconds  制定秒数

 返回值 :成功:如果调用此alarm()前,进程已经设置了闹钟时间,则返 回上一个闹钟时间的剩余时间,否则返回0。出错:-1

11、getpid 函数

函数原型 :int_getpid(void)

头文件 :unistd.h

作用 :取得进程识别代码

返回值 :目前进程的进程识别码

12、srand 函数

函数原型 :void srand(unsigned seed);         

头文件 :stdlib.h

作用 :产生伪随机数序列

参数       :seed   改变系统提供的种子植

13、Pthread_setcancel state 函数

函数原型 :int pthread_setcancelstate(int state, int *oldstate)

作用 :设置本线程对cancel信号的反应,state有两种值,分别表示收到     信号后设为canceled状态和忽略cancel信号继续运行;oldstate如果     不为null则存入原来的cancel状态以便恢复。

四、程序所用数据结构简介

int num_producer ,num_consumer    :宏定义中生产者,消费者的数量

int BUFFER_SIZE        :缓冲区大小

int NUM        :产品编号

int total_producer,total_consumer        :每5秒对生产者和消费者数量进行统计

int buffer[BUFFER_SIZE]        :缓冲区模型

int nextp, nextc        :生产者,消费者的指针

pthread_t thrreads_p[100],threads_c[100]  :生产者,消费者线程

sem_t empty,full, mutex  :信号量,以使生产者之间和消费者之间,以及二者之间的互斥

void handler()         :用来循环输出生产者,消费者的数量

五、程序源代码

见附录B

六、调试方案及调试过程记录与分析

输入文件选取:

本程序无需输入,只需按照需要更改生产者,消费者,缓冲区的数量和大小即可。

测试结果、调试过程:

第一次测试结果:显示很多函数文件未定义。

第一次调试:在makefile文件开头加vpath命令,来指明用户自己所定义的头文件的位置,同时在每个用到用户自己定义头文件的.c文件的gcc-c一行加上-I./include

第二次测试结果:头文件被包含进去,但NUM变量出现重复定义错误。

第二次调试:在.h文件中声明全局变量,而全局变量的赋值放在了主函数producer_consumer.c中。

第三次测试结果:使用1个生产者,1个消费者,2块缓冲区,此时无语法错误,但是改变之前的数据为3,3,20之后,生产者和消费者出现次序并没有体现同步与互斥。

第三次调试:使用函数pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL)取消线程,和srand函数,使生产者和消费者可以同步和互斥。

第四次测试结果:程序可以正确执行,无语法错误,并且可以实现同步和互斥。

七、程序运行结果分析

通过四次测试调试运行,程序可以正确执行,可以实现生产者、消费者的同步与互斥,以及对缓冲区的互斥使用。


程序源代码:

 

源文件名:producer_consumer.c
内容:
/* 
************************************************************************************** 
*Project/File :producer_consumer.c 
*By :
*Mail :
*Status :finished 
*version :2.0 
*Created Time :2014年09月24日星期三17时45分50秒 
************************************************************************************** 
*Note: 
*系统里有若干个合作的进程/线程,其中n个生产者线程,m个消费者线程,以及p块缓冲区。 
*任何一个生产者都可以将自己的产品存入缓冲区的任何一个位置; 
*任何一个消费者都可以将缓冲区内的一个产品取出; 
*生产者源源不断地生产并存入产品; 
*消费者周而复始地从缓冲区中取出产品将其消费掉; 
************************************************************************************** 
*/ 
 
#include"producer_consumer.h" 
 
void handler() 
{ 
printf("total_producer = %d, total_consumer = %d\n", total_producer, total_consumer); 
signal(SIGALRM, handler);  //让内核做好准备,一旦接受到SIGALARM信号,就执行 handler; 
alarm(5); //闹钟设为5秒; 
 
} 
 
int main() 
{ 
NUM=1; 
total_producer = 0; 
total_consumer = 0; 
nextp=0; 
nextc=0; 
int p1; 
p1 = fork(); 
if(p1) 
{ 
//parent 
printf("This is parent(%d)!\n", getpid()); 
} 
else 
{ 
//child 
int i; 
handler(); //每隔5秒钟统计一下生产的产品数和消费的产品数; 
sem_init(&empty,0,BUFFER_SIZE);  //初始化信号量; 
sem_init(&full,0,0); 
sem_init(&mutex,0,1); 
 
for(i=0;i<BUFFER_SIZE;i++) //清空缓冲区; 
buffer[i]=0; 
for(i=0;i<num_producer;i++)  //创建生产者,消费者线程; 
pthread_create(&threads_p[i],NULL,producer_thread,(void *)(i+1)); 
for(i=0;i<num_consumer;i++) 
pthread_create(&threads_c[i],NULL,consumer_thread,(void *)(i+1)); 
 
for(i=0;i<num_producer;i++)  //销毁线程; 
pthread_join(threads_p[i],NULL); 
for(i=0;i<num_consumer;i++) 
pthread_join(threads_c[i],NULL); 
 
sem_destroy(&full);   
sem_destroy(&empty); //销毁信号量; 
sem_destroy(&mutex); 
} 
 
return 0; 
} 
 
 
 
源文件名:producer.c
内容:
/* 
************************************************************************************** 
*Project/File :producer.c 
*By :
*Mail : 
*Status :finished 
*version :2.0 
*Created Time :2014年09月24日星期三17时45分50秒 
************************************************************************************** 
*Note: 
*当缓冲区中有空闲位置时,允许任何一个生产者把它的产品存入; 
*当缓冲区中无空闲位置时,试图将产品存入缓冲区的任何生产者必须等待; 
************************************************************************************** 
*/ 
 
#include"producer_consumer.h" 
 
void *producer_thread(void *tid) 
{ 
int i; 
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); 
while(1) 
{ 
sem_wait(&empty); //信号量减一,即看看生产者是否可以向当前指针指向的位置放产品, 
//保证生产者之间互斥; 
srand((int)time(NULL) * (int)tid); 
sleep(1); 
sem_wait(&mutex); //信号量减一,看看当前时刻缓冲区是否被占用, 
//保证生产者和消费者之间互斥; 
// srand((int)time(NULL) * (int)tid); 
if(NUM>BUFFER_SIZE) NUM=1; //如果大于20,NUM重新为1 ; 
buffer[nextp]=(NUM++); 
printf("生产者编号:%d\t指针:%d\t\n", (int)tid, nextp); 
nextp=(nextp+1) % BUFFER_SIZE;  //生产者指针后移,指向下一个位置 
total_producer++; 
printf("缓冲区:"); 
for(i=0;i<BUFFER_SIZE;i++) 
{ 
printf("%3d",buffer[i]); 
} 
printf("\n"); 
 
sem_post(&mutex); //释放缓冲区; 
sem_post(&full); //告诉消费者有产品可以消费; 
srand((int)time(NULL) * (int)tid); 
} 
return 0;  
} 
 
 
源文件名:consumer.c
内容:
/* 
************************************************************************************** 
*Project/File :consumer.c 
*By : 
*Mail :
*Status :finished 
*version :2.0 
*Created Time :2014年09月24日星期三17时45分50秒 
************************************************************************************** 
*Note: 
*当缓冲区中尚有未取出的产品时,允许任何一个消费者吧其中的一个产品取出; 
*当缓冲区中没有未取出的产品时,试图从该环内取出产品的任何消费者必须等待; 
************************************************************************************** 
*/ 
 
#include"producer_consumer.h" 
 
void *consumer_thread(void *tid) 
{ 
int i; 
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); 
while(1) 
{ 
sem_wait(&full); // 从信号量减去一个"1"; 
srand((int)time(NULL) * (int)tid); 
sem_wait(&mutex); //对缓冲区互斥使用; 
 
// srand((int)time(NULL) * (int)tid); 
printf("消费者编号:%d\t  指针:%d\t\n",(int)tid,nextc); 
buffer[nextc] = 0; 
nextc =(nextc + 1) % BUFFER_SIZE; //指针后移,指向下一个产品; 
total_consumer++;  
sleep(1);  
printf("缓冲区:"); 
for(i = 0;i < BUFFER_SIZE;i++) 
{ 
printf("%3d",buffer[i]); 
} 
printf("\n");  
 
sem_post(&mutex); //释放缓冲区;  
sem_post(&empty); //信号量加"1",告诉生产者可以放产品;  
srand((int)time(NULL) * (int)tid); 
} 
return 0; 
}
文件名:producer_consumer.h
内容:
/* 
************************************************************************************** 
*Project/File :producer_consumer.h 
*By :
*Mail :
*Status :finished 
*version :2.0 
*Created Time :2014年09月24日星期三17时45分50秒 
************************************************************************************** 
*Note: 
*将所有.c文件所用到的头文件包含进去; 
*包含所有的宏定义; 
*全局变量的定义; 
*函数的声明; 
************************************************************************************** 
*/ 
#ifndef producer_consumer_h 
#define producder_consumer_h 
 
#include "stdio.h" 
#include "stdlib.h" 
#include "string.h" 
#include "pthread.h" 
#include "semaphore.h" 
#include "signal.h" 
#include "unistd.h" 
#define num_producer 5 
#define num_consumer 5 
#define BUFFER_SIZE 20 
int NUM; 
int total_producer, total_consumer; 
int buffer[BUFFER_SIZE]; 
int nextp, nextc; 
pthread_t threads_p[100], threads_c[100]; 
sem_t empty, full, mutex; 
 
void *producer_thread(void *tid); 
void *consumer_thread(void *tid); 
 
#endif
文件名:Makefile
内容:
vpath %.h../producer_consumer 
producer_consumer:producer.o consumer.o producer_consumer.o 
gcc -o producer_consumer -pthread producer.o consumer.o producer_consumer.o 
producer_consumer.o:producer_consumer.c producer_consumer.h 
gcc -c producer_consumer.c -I./include 
producer.o:producer.c producer_consumer.h 
gcc -c producer.c -I./include 
consumer.o:consumer.c producer_consumer.h 
gcc -c consumer.c -I./include 


你可能感兴趣的:(以生产者消费者为例实现Linux进程线程控制)