实验4-信号量的实现和应用

信号量的实现和应用

对应实验手册:https://hoverwinter.gitbooks.io/hit-oslab-manual/content/sy4_sem.html

补充知识

信号量操作

参考链接:

  • https://c.biancheng.net/view/8632.html

  • https://www.cnblogs.com/Suzkfly/p/14351449.html

  • 头文件:

    #include<fcntl.h>
    #include<sys/stat.h>
    #include<semaphore.h>
    
  • 操作函数:

    //打开有名信号量
    sem_t * sem_open(const char * name,
    				int oflag,
    				mode_t mode,
    				unsigned int value);
    //关闭信号量
    int sem_close(sem_t * sem);
    //删除信号量文件
    int sem_unlink(const cahr * name);
    //测试信号量
    int sem_wait(sem_t * sem);
    //信号量++
    int sem_post(sem_t * sem);
    

Linux下C语言文件操作

参考链接:

  • https://blog.csdn.net/Mculover666/article/details/104817798

  • 头文件:

    #include <sys/types.h>		//定义了一些常用数据类型,比如size_t
    #include <fcntl.h>			//定义了open、creat等函数,以及表示文件权限的宏定义
    #include <unistd.h>			//定义了read、write、close、lseek等函数
    #include <errno.h>			//与全局变量errno相关的定义
    #include <sys/ioctl.h>		//定义了ioctl函数 
    
  • 操作函数:

    //打开文件
    int open(const char * path, int flags, mode_t mode)
    //读取文件
    size_t read(int fd, void * buf, size_t count);
    //写入文件
    size_t write(int fd, const void * buf, sieze_t count);
    //关闭文件
    int close(int fd);
    

实验代码及结果

Ubuntu下用信号量解决消费者-生产者问题

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<semaphore.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/ioctl.h>
#include<sys/wait.h>

#define PRODUCER 1
#define CUSTOMER 5
#define BUFF_SIZE 10
#define NUMBER 500

sem_t * full, * empty, * mutex;
int fd; //文件描述符

int main()
{
    int i, j, k;
    int data;
    pid_t p; //进程描述符
    int buff_out = 0; //从缓冲区读取的位置
    int buff_in = 0; //向缓冲区读入的位置
    char file_name[] = {"buffer.txt"};

    //打开信号量
    if ((empty = sem_open("sys_empty", O_RDWR | O_CREAT, 0666, BUFF_SIZE )) == SEM_FAILED) {
		printf("fail to sem_open the semaphore : empty\n");
		return -1;
	}
	if ((full = sem_open("sys_full", O_RDWR | O_CREAT, 0666, 0)) == SEM_FAILED) {
		printf("fail to sem_open the semaphore : full\n");
		return -1;
	}
	if ((mutex = sem_open("sys_mutex", O_RDWR | O_CREAT, 0666, 1)) == SEM_FAILED) {
		printf("fail to sem_open the semaphore : mutex\n");
		return -1;
	}

    //打开文件
    fd = open(file_name, O_RDWR | O_CREAT, 0664);
    //存入读取位置buff_out
    lseek(fd, BUFF_SIZE * sizeof(int), SEEK_SET);
    write(fd, (char *)&buff_out, sizeof(int));

    //生产者进程
    if ((p = fork()) == 0) {
		printf("This is the Producer (pid : %d)\n", getpid());
		for (i = 0;i < NUMBER;i ++) {
			sem_wait(empty);
			sem_wait(mutex);
			//写入整数
            printf("producer (pid : %d) write : %d\n", getpid(), i);
            lseek(fd, buff_in * sizeof(int), SEEK_SET);
            write(fd, (char *)&i, sizeof(int));
            buff_in = (buff_in + 1) % BUFF_SIZE;

			sem_post(mutex);
			sem_post(full);
		}
		return 0;
	}
	else if (p < 0) {
		printf("Fail to fork the Producer\n");
		return -1;
	}

    //消费者进程
    for (j = 0;j < CUSTOMER;j ++) {
		if ((p = fork()) == 0) {
            printf("This is the Customer (pid : %d)\n", getpid());
			for (k = 0;k < NUMBER / CUSTOMER;k ++) {
                sem_wait(full);
			    sem_wait(mutex);
			    //文件操作
			    //获取读取位置
                lseek(fd, BUFF_SIZE * sizeof(int), SEEK_SET);
                read(fd, (char *) &buff_out, sizeof(int));
                //读取数据
                lseek(fd, buff_out * sizeof(int), SEEK_SET);
                read(fd, (char *) &data, sizeof(int));
                printf("customer (pid : %d) read -> %d\n", getpid(), data);
                //写入读取位置
                buff_out = (buff_out + 1) % BUFF_SIZE;
                lseek(fd, BUFF_SIZE * sizeof(int), SEEK_SET);
                write(fd, (char *) &buff_out, sizeof(int));
			    sem_post(mutex);
			    sem_post(empty);
            }
			return 0;
		}
		else if (p < 0) {
			printf("Fail to fork the Customer (num : %d)\n", i);
			return -1;
		}
	}

    //进程等待
	wait(NULL);
	//释放信号量
	sem_unlink("sys_empty");
	sem_unlink("sys_full");
	sem_unlink("sys_mutex");
	//关闭文件
	close(fd);

	printf("\r the pc.c is finished\n");
	return 0;
}

在这里插入图片描述

Linux-0.11中实现信号量

include/unistd.h中,增加:

...
#define __NR_sem_open 	72
#define __NR_sem_wait	73
#define __NR_sem_post	74
#define __NR_sem_unlink 75

#define QUE_LEN 16
#define SEM_FAILED  (void*) 0
struct semaphore_queue
{
	int front;
	int rear;
	struct task_struct *wait_tasks[QUE_LEN];
};
typedef struct semaphore_queue sem_queue;

struct semaphore_t
{
    int value;
    int occupied;
    char name[16];
    struct semaphore_queue wait_queue;
};
typedef struct semaphore_t sem_t;
...

include/linux/sys.h中,增加:

...
extern int sys_sem_open();
extern int sys_sem_wait();
extern int sys_sem_post();
extern int sys_sem_unlink();

fn_ptr sys_call_table[] = {...sys_sem_open,sys_sem_wait,sys_sem_post,sys_sem_unlink };

kernel/system_call.s中,修改:

nr_system_calls = 76

新建krenel/sem.c:

#define __LIBRARY__  
#include <unistd.h>  
#include <linux/sched.h>  
#include <linux/kernel.h>  
#include <asm/segment.h>  
#include <asm/system.h>   

#define SEM_COUNT 32 
sem_t semaphores[SEM_COUNT]; 
/*队列相关操作,rear始终是下一个待写入的位置,front始终是队列第一个元素*/
void init_queue(sem_queue* q)  
{  
    q->front = q->rear = 0; 
}

int is_empty(sem_queue* q)
{
	return q->front == q->rear?1:0;
}
/*留下标QUE_LEN-1不用,判断是否慢*/
int is_full(sem_queue* q)
{    
	return (q->rear+1)%QUE_LEN == q->front?1:0;
}
/*获得队列头第一个任务*/
struct task_struct * get_task(sem_queue* q)
{
	if(is_empty(q)) 
	{
		printk("Queue is empty!\n");
		return NULL;
	}
	struct task_struct *tmp = q->wait_tasks[q->front]; 
	q->front = (q->front+1)%QUE_LEN;
	return tmp;
}
/*任务插入队列尾*/
int insert_task(struct task_struct *p,sem_queue* q)
{
	// printk("Insert %d",p->pid);
	if(is_full(q))
	{
		printk("Queue is full!\n");
		return -1;
	}
	q->wait_tasks[q->rear] = p;
	q->rear = (q->rear+1)%QUE_LEN;
	return 1;
}
/*信号量是否已打开,是返回位置*/
int sem_location(const char* name)
{  
    int i;
    for(i = 0;i < SEM_COUNT; i++)  
    {  
        if(strcmp(name,semaphores[i].name) == 0 && semaphores[i].occupied == 1) 
        {     
            return i;  
        }  
    }  
    return -1; 
}  
/*打开信号量*/
sem_t* sys_sem_open(const char* name,unsigned int value)
{
	char tmp[16];
	char c;
	int i;
	for( i = 0; i<16; i++)
	{
		c = get_fs_byte(name+i);
		tmp[i] = c;
		if(c =='\0') break;
	}
	if(c >= 16) 
	{ 	
		printk("Semaphore name is too long!");
		return NULL;
	}
	if((i = sem_location(tmp)) != -1)
	{
		return &semaphores[i];
	}
	for(i = 0;i< SEM_COUNT; i++)
	{
		if(!semaphores[i].occupied)
		{
			strcpy(semaphores[i].name,tmp);
			semaphores[i].occupied = 1;
			semaphores[i].value = value;
			init_queue(&(semaphores[i].wait_queue));
			// printk("%d %d %d %s\n",semaphores[i].occupied,i,semaphores[i].value,semaphores[i].name);
			// printk("%p\n",&semaphores[i]); 
			return &semaphores[i];
		}
	}	
	printk("Numbers of semaphores are limited!\n");
	return NULL;
}  
/*P原子操作*/
int sys_sem_wait(sem_t* sem)
{
	cli();
	sem->value--;
	if(sem->value < 0)
	{
		/*参见sleep_on*/
		current->state = TASK_UNINTERRUPTIBLE;
		insert_task(current,&(sem->wait_queue));
		schedule();
	}
	sti();
	return 0;
}
/*V原子操作*/
int sys_sem_post(sem_t* sem)
{
	cli();
	struct task_struct *p;
	sem->value++;
	if(sem->value <= 0)
	{
		p = get_task(&(sem->wait_queue));
		if(p != NULL)
		{
			(*p).state = TASK_RUNNING;
		}
	}
	sti();
	return 0;
}
/*释放信号量*/
int sys_sem_unlink(const char *name)  
{  
    char tmp[16];
    char c;
	int i;
	for( i = 0; i<16; i++)
	{
		c = get_fs_byte(name+i);
		tmp[i] = c;
		if(c =='\0') break;
	}
	if(c >= 16) 
	{
		printk("Semphore name is too long!");
		return -1;
	}
    int ret = sem_location(tmp); 
    if(ret != -1)
    {
    	semaphores[ret].value = 0;
    	strcpy(semaphores[ret].name,"\0");
    	semaphores[ret].occupied = 0;
    	return 0;
    }   
    return -1;  
}  

Linux-0.11中用信号量解决生产者-消费者问题

编写测试程序pc.c:

#define   __LIBRARY__
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

_syscall2(sem_t*,sem_open,const char *,name,unsigned int,value);
_syscall1(int,sem_wait,sem_t*,sem);
_syscall1(int,sem_post,sem_t*,sem);
_syscall1(int,sem_unlink,const char *,name);

#define NUMBER 520 /*打出数字总数*/
#define CHILD 5 /*消费者进程数*/
#define BUFSIZE 10 /*缓冲区大小*/

sem_t   *empty, *full, *mutex;
int fno; /*文件描述符*/

int main()
{
    int  i,j,k;
    int  data;
    pid_t p;
    int  buf_out = 0; /*从缓冲区读取位置*/
    int  buf_in = 0; /*写入缓冲区位置*/
    /*打开信号量*/
    if((mutex = sem_open("carmutex",1)) == SEM_FAILED)
    {
        perror("sem_open() error!\n");
        return -1;
    }
    if((empty = sem_open("carempty",10)) == SEM_FAILED)
    {
        perror("sem_open() error!\n");
        return -1;
    }
    if((full = sem_open("carfull",0)) == SEM_FAILED)
    {
        perror("sem_open() error!\n");
        return -1;
    }
    fno = open("buffer.dat",O_CREAT|O_RDWR|O_TRUNC,0666);
    /* 将待读取位置存入buffer后,以便 子进程 之间通信 */
    lseek(fno,10*sizeof(int),SEEK_SET);
    write(fno,(char *)&buf_out,sizeof(int));
    /*生产者进程*/
    if((p=fork())==0)
    {
        for( i = 0 ; i < NUMBER; i++)
        {
            sem_wait(empty);
            sem_wait(mutex);
            /*写入一个字符*/
            lseek(fno, buf_in*sizeof(int), SEEK_SET); 
            write(fno,(char *)&i,sizeof(int));  
            buf_in = ( buf_in + 1)% BUFSIZE;
            printf("producer (pid : %d) write -> %d\n", getpid(), i);

            sem_post(mutex);
            sem_post(full);
        }
        return 0;
    }else if(p < 0)
    {
        perror("Fail to fork!\n");
        return -1;
    }

    for( j = 0; j < CHILD ; j++ )
    {
        if((p=fork())==0)
        {
            for( k = 0; k < NUMBER/CHILD; k++ )
            {
                sem_wait(full);
                sem_wait(mutex);
                /*获得读取位置*/
                lseek(fno,10*sizeof(int),SEEK_SET);
                read(fno,(char *)&buf_out,sizeof(int));
                /*读取数据*/
                lseek(fno,buf_out*sizeof(int),SEEK_SET);
                read(fno,(char *)&data,sizeof(int));
                /*写入读取位置*/
                buf_out = (buf_out + 1) % BUFSIZE;
                lseek(fno,10*sizeof(int),SEEK_SET);
                write(fno,(char *)&buf_out,sizeof(int));
                printf("customer (pid : %d) read -> %d\n", getpid(), data);
                fflush(stdout);

                sem_post(mutex);
                sem_post(empty);
            }
           return 0;
        }else if(p<0)
        {
            perror("Fail to fork!\n");
            return -1;
        }
    }
    wait(NULL);
    /*释放信号量*/
    sem_unlink("carfull");
    sem_unlink("carempty");
    sem_unlink("carmutex");
    /*释放资源*/
    close(fno);
	printf("\nTHE PROCESS DONE!\n");
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值