信号量的实现和应用
对应实验手册: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;
}