实验5:信号量的实现与应用

实验5:信号量的实现与应用

本文以记录实验过程为主,具体实验内容参考:实验指导书

1 添加系统调用

1、首先在 unistd.h 文件中添加4个系统调用号:

...
#define __NR_setregid	71
/*添加4个信号量的系统调用号*/
#define __NR_sem_open   72
#define __NR_sem_wait   73
#define __NR_sem_post   74
#define __NR_sem_unlink 75

#define _syscall0(type,name) \
...

2、然后再添加信号量相关定义和声明(也在 unistd.h 文件中)

...
#endif /* __LIBRARY__ */
/* 信号量相关定义和声明 */
#define SEM_NAME_LEN 20   /* 信号量名字的最大长度 */
#define SEM_MAX      20   /* 信号量的数目 */
typedef struct sem_t{
    char name[SEM_NAME_LEN];    /* 信号量名字 */
	int value;
	struct task_struct *wait;
    struct sem_t *next;     //所有的信号量保存在内核空间,是一个链表
}sem_t;

sem_t* sem_open(const char *name, unsigned int value); 
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_unlink(const char *name);
/**********************/
extern int errno;
...

3、在 kernel\system_call.s 中将系统调用总数修改为 76

/*修改总系统调用的个数*/
nr_system_calls = 76

4、在 include\linux\sys.h 中添加系统调用声明,并将函数指针添加到 sys_call_table 中

/*添加信号量的系统调用声明(想一下这里的声明为什么不用加形参?)*/
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_setup, sys_exit, sys_fork, sys_read,
sys_write, ... sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid, sys_sem_open, sys_sem_wait, sys_sem_post, sys_sem_unlink };

2 新增sem.c

在 kernel 目录下新建 sem.c 文件,然后修改 kernel 目录下的 makefile ,在 OBJS 中新增 sched.o,然后在 ### Dependencies: 下 添加 sem.s sem.o 的生成规则:

...

OBJS  = sched.o system_call.o traps.o asm.o fork.o \
	panic.o printk.o vsprintf.o sys.o exit.o \
	signal.o mktime.o sem.o

...

### Dependencies:
sem.s sem.o: sem.c ../include/linux/kernel.h ../include/unistd.h
exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \
 ...

3 sem.c

sem.c 文件内容如下:

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

sem_t *sem_head = &((sem_t *){ "\0", 0, NULL, NULL });   /* 信号量链表的头节点 */

sem_t* sys_sem_open(const char *name, unsigned int value)
{
    sem_t *sem_current = NULL;  
    sem_t *sem_front = NULL; 
    char sem_name[SEM_NAME_LEN] = {'\0'};
    int i = 0;

    printk("sem_open:enter:");
    /* 将name拷贝到内核空间 */
    while( (sem_name[i] = get_fs_byte(name + i)) != '\0')
    {   /*如果name超过SEM_NAME_LEN个字符,就只保留前SEM_NAME_LEN - 1 个字符*/
        if( (++i) >= SEM_NAME_LEN - 1){sem_name[i] = '\0'; break;}
    }
    
    /* 查找信号量是否已经存在 */
    for(sem_front = sem_head, sem_current = sem_head->next; sem_current != NULL; sem_front = sem_current, sem_current = sem_current->next)
    {
        if(0 == strcmp(sem_current->name, sem_name) )
        {
            printk("the semaphore is exist!\n");
            return(sem_current);
        }
    }

    if(NULL == sem_current)
    {   /* name信号量还不存在,新建一个信号量 */
        sem_current = (sem_t *)malloc(sizeof(sem_t));
        strcpy(sem_current->name, sem_name);
        sem_current->value = value;
        /* 尾插法 */
        sem_current->next = NULL;
        sem_front->next = sem_current;
    }
    printk("sem_open:exit\n");
    return(sem_current);
}

int sys_sem_wait(sem_t *sem)
{
    cli();
    while(sem->value <= 0)
        sleep_on( &(sem->wait) );
    sem->value--;
    sti();

    return 0;
}

int sys_sem_post(sem_t *sem)
{
	sem->value++;
	if(sem->wait)  /*检测等待队列是否为空*/
	{
		wake_up( &(sem->wait) );
		return 0;
	}

	return -1;
}


int sys_sem_unlink(const char *name)
{
    sem_t *sem_current = NULL;  
    sem_t *sem_front = sem_head;
    char sem_name[SEM_NAME_LEN] = {'\0'};
    int i = 0;
    printk("sem_unlink:enter:");
    /* 将name拷贝到内核空间 */
    while( (sem_name[i] = get_fs_byte(name + i)) != '\0')
    {   /*如果name超过SEM_NAME_LEN个字符,就只保留前SEM_NAME_LEN - 1 个字符*/
        if( (++i) >= SEM_NAME_LEN - 1){sem_name[i] = '\0'; break;}
    }

    /* 查找信号量是否存在 */
    for(sem_front = sem_head, sem_current = sem_head->next; sem_current && strcmp(sem_current->name, sem_name); sem_front = sem_current, sem_current = sem_current->next);

    if(sem_current == NULL)
        return -1;

    sem_front->next = sem_current->next;
    free(sem_current);
    printk("unlink a sem\n");
    return 0;
    
}

附录

附录1: Ubuntu下运行的 pc.c:

/*
 * 生成执行文件:gcc -Wall -o pc.out pc.c -lpthread
 */

#include <unistd.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#define  BUFF_SIZE  10                 //共享缓冲区的大小
#define  FILENAME "./fileBuff.txt"     //文件共享缓冲区
#define  PRODUCT_NUM 500               /*生产者需要生产的产品数量*/
#define  CONSUMER_NUM 5                /*消费者人数*/

sem_t *Full, *Empty, *Mutex;           //信号量

//消费者
void consumer()
{
	int fd = -1;
    int buff[BUFF_SIZE];     /*将从文件缓冲区中读出的数据存入buff中*/
    int num = 0;             /*从文件缓冲区中实际读取到的资源数*/
	
	while(1)
	{
        sem_wait(Full);      /*检查是否有资源*/
		sem_wait(Mutex);     /*检查是否有其他进程访问临界区*/
		/*1、先读出所有的内容*/
		fd = open(FILENAME, O_RDONLY);
        num = read(fd, buff, BUFF_SIZE * sizeof(int)) / sizeof(int);
		close(fd);
		/*2、取出第一个数据*/
		if(num > 0)
		{
		    printf("%d : %d\n", getpid(), buff[0]); /*取出第一个数打印到屏幕*/
            fflush(stdout);                         /*刷新终端显示*/
		}
		/*3、将除第一个数外的其他内容写回文件缓冲区*/
		fd = open(FILENAME, O_WRONLY | O_TRUNC); /*打开并清空文件缓冲区*/
		if(num > 1)
		{
	        write(fd, &buff[1], (num - 1) * sizeof(int));/*其余的数字写回文件缓冲区*/
		}
        close(fd);

		sem_post(Mutex);
		sem_post(Empty);
	}
}

// 生产者向文件缓冲区写入500个数字才退出 
void producter()
{
	int fd = -1;    /*文件描述符*/
	int i = 0;

	for(i = 0; i < PRODUCT_NUM; i++)
	{   /*向文件缓冲区写入500个数字*/
		sem_wait(Empty);          //检查是否还有空余空间
		sem_wait(Mutex);          //检查是否有其他进程访问临界区

        fd = open(FILENAME, O_WRONLY | O_APPEND);
	    write(fd, &i, sizeof(int)); //写入数字
		close(fd);

		sem_post(Mutex);
		sem_post(Full);
	}
}

int main()
{
	int i = 0;       
    int fd = -1;   //文件描述符
    //删除信号量, 
	sem_unlink("Full");
	sem_unlink("Empty");
	sem_unlink("Mutex");

    Full = sem_open("Full", O_CREAT, 0664, 0);              //创建满信号量(已使用空间)
    Empty = sem_open("Empty", O_CREAT, 0664, BUFF_SIZE);    //创建空信号量(剩余空间)
    Mutex = sem_open("Mutex", O_CREAT, 0664, 1);            //创建锁信号量(用于控制临界区同时只能有1个进程访问)
    fd = open(FILENAME, O_RDWR | O_CREAT | O_TRUNC, 0666);  //创建文件缓冲区
    close(fd);
	for(i = 0; i < CONSUMER_NUM; i++)
	{   //创建5个子进程
	    if(!fork()) break;    //子进程则退出
	}

	if(5 != i)
	{   //子进程是消费者
	    consumer();
	}
	else
	{   //父进程是生产者
	    producter();
        sleep(10);    //睡眠10s,
	}

    //删除信号量
	sem_unlink("Full");
	sem_unlink("Empty");
	sem_unlink("Mutex");
    kill(0, SIGKILL);      /*杀死所有子进程*/
    return 1;
}

附录2:Linux0.11下运行的 pc.c:

#define __LIBRARY__
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/stat.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  BUFF_SIZE  10                 /*共享缓冲区的大小*/
#define  FILENAME "./fileBuff.txt"     /*文件共享缓冲区*/
#define  DISPLAY  "/usr/root/disFile.txt"  /*存放显示结果*/
#define  PRODUCT_NUM  100               /*生产者需要生产的产品数量*/
#define  CONSUMER_NUM 5                /*消费者人数*/

sem_t *Full, *Empty, *Mutex;           /*信号量*/
int displayFd;                         /*显示文件的文件描述符*/

/*消费者*/
void consumer()
{
    int fd;
    char buff[BUFF_SIZE] = {'\0'};     /*将从文件缓冲区中读出的数据存入buff中*/
    int count;           /*从文件缓冲区中实际读取到的资源数*/
    char pid;
    int i = 0;
    while(1)
    {
        sem_wait(Full);      /*检查是否有资源*/
        sem_wait(Mutex);     /*检查是否有其他进程访问临界区*/

        fd = open(FILENAME, O_RDONLY);
        count = read(fd, buff, BUFF_SIZE * sizeof(char)) / sizeof(char);
        close(fd);
        /*for(i = 0; i <count; i++)
            printf("%d,", buff[i]);*/
        if(count > 0)
        {
            pid = getpid();
            write(displayFd, &pid, 1);
            write(displayFd, ":", 1);
            write(displayFd, &buff[0], 1);
            write(displayFd, "\n", 1);
            /*printf("%d : %d\n", getpid(), buff[0]); 
            fflush(stdout);    */                
        }

        fd = open(FILENAME, O_WRONLY | O_TRUNC); /*打开并清空文件缓冲区*/
        if(count > 1)
        {
	    write(fd, &buff[1], (count - 1) * sizeof(char));/*其余的数字写回文件缓冲区*/
        }
        close(fd);
        sem_post(Mutex);
        sem_post(Empty);
    }
}

/* 生产者向文件缓冲区写入500个数字才退出*/ 
void producter()
{
	int fd = -1;    /*文件描述符*/
	char i = 0;

	for(i = 0; i < PRODUCT_NUM; i++)
	{   /*向文件缓冲区写入500个数字*/
		sem_wait(Empty);          /*检查是否还有空余空间*/
		sem_wait(Mutex);          /*检查是否有其他进程访问临界区*/

        fd = open(FILENAME, O_WRONLY | O_APPEND);
	    write(fd, &i, sizeof(char)); /*写入数字*/
		close(fd);

		sem_post(Mutex);
		sem_post(Full);
	}
}

void main()
{
	int i = 0;       
    int fd = -1;   /*文件描述符*/

    Full = sem_open("Full",  0);              /*创建满信号量(已使用空间)*/
    Empty = sem_open("Empty",  BUFF_SIZE);    /*创建空信号量(剩余空间)*/
    Mutex = sem_open("Mutex", 1);            /*创建锁信号量(用于控制临界区同时只能有1个进程访问)*/
    displayFd = open(DISPLAY, O_RDWR | O_CREAT | O_TRUNC, 0666);  /*创建显示文件*/

    fd = open(FILENAME, O_RDWR | O_CREAT | O_TRUNC, 0666);  /*创建文件缓冲区*/
    close(fd);
	for(i = 0; i < CONSUMER_NUM; i++)
	{   /*创建5个子进程*/
	    if(!fork()) break;    /*子进程则退出*/
	}

	if(5 != i)
	{   /*子进程是消费者*/
	    consumer();
	}
	else
	{   /*父进程是生产者*/
	    producter();
        sleep(10);    /*睡眠10s,*/
	}

    /*删除信号量*/
	sem_unlink("Full");
	sem_unlink("Empty");
	sem_unlink("Mutex");
      
	close(displayFd);
    kill(0, SIGKILL);      /*杀死所有子进程*/
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值