本文以记录实验过程为主,具体实验内容参考:实验指导书。
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); /*杀死所有子进程*/
}