Implementation and application of semaphore
Corresponding experiment manual: https://hoverwinter.gitbooks.io/hit-oslab-manual/content/sy4_sem.html
Supplementary knowledge
Semaphore operations
Reference links:
-
https://c.biancheng.net/view/8632.html
-
https://www.cnblogs.com/Suzkfly/p/14351449.html
-
head File:
#include<fcntl.h> #include<sys/stat.h> #include<semaphore.h>
-
Operation function:
//Open the named semaphore sem_t * sem_open(const char * name, int oflag, mode_t mode, unsigned int value); //Close the semaphore int sem_close(sem_t * sem); //Delete semaphore file int sem_unlink(const cahr * name); //test semaphore int sem_wait(sem_t * sem); //Semaphore + + int sem_post(sem_t * sem);
C language file operation under Linux
Reference links:
-
https://blog.csdn.net/Mculover666/article/details/104817798
-
head File:
#include <sys/types.h> //Defines some common data types, such as size_t #include <fcntl.h> //Defines open, creat and other functions, as well as macro definitions indicating file permissions #include <unistd.h> //Definition of read, write, close, lseek and other functions #include <errno.h> //Definitions related to the global variable errno #include <sys/ioctl.h> //The ioctl function is defined
-
Operation function:
//Open file int open(const char * path, int flags, mode_t mode) //read file size_t read(int fd, void * buf, size_t count); //Write to file size_t write(int fd, const void * buf, sieze_t count); //Close file int close(int fd);
Experimental code and results
Ubuntu
uses semaphores to solve the consumer-producer problem
#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 #defineBUFF_SIZE 10 #define NUMBER 500 sem_t * full, * empty, * mutex; int fd; //File descriptor int main() {<!-- --> int i, j, k; int data; pid_t p; //process descriptor int buff_out = 0; //The position read from the buffer int buff_in = 0; //The position to read into the buffer char file_name[] = {<!-- -->"buffer.txt"}; //Open the semaphore if ((empty = sem_open("sys_empty", O_RDWR | O_CREAT, 0666, BUFF_SIZE )) == SEM_FAILED) {<!-- --> printf("fail to sem_open the semaphore : empty\ "); return -1; } if ((full = sem_open("sys_full", O_RDWR | O_CREAT, 0666, 0)) == SEM_FAILED) {<!-- --> printf("fail to sem_open the semaphore : full\ "); return -1; } if ((mutex = sem_open("sys_mutex", O_RDWR | O_CREAT, 0666, 1)) == SEM_FAILED) {<!-- --> printf("fail to sem_open the semaphore : mutex\ "); return -1; } //open a file fd = open(file_name, O_RDWR | O_CREAT, 0664); //Save the reading position buff_out lseek(fd, BUFF_SIZE * sizeof(int), SEEK_SET); write(fd, (char *) & amp;buff_out, sizeof(int)); //Producer process if ((p = fork()) == 0) {<!-- --> printf("This is the Producer (pid : %d)\ ", getpid()); for (i = 0;i < NUMBER;i + + ) {<!-- --> sem_wait(empty); sem_wait(mutex); //write integer printf("producer (pid : %d) write : %d\ ", getpid(), i); lseek(fd, buff_in * sizeof(int), SEEK_SET); write(fd, (char *) & amp;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\ "); return -1; } //Consumer process for (j = 0;j < CUSTOMER;j + + ) {<!-- --> if ((p = fork()) == 0) {<!-- --> printf("This is the Customer (pid : %d)\ ", getpid()); for (k = 0;k < NUMBER / CUSTOMER;k + + ) {<!-- --> sem_wait(full); sem_wait(mutex); //File operations //Get the reading position lseek(fd, BUFF_SIZE * sizeof(int), SEEK_SET); read(fd, (char *) & amp;buff_out, sizeof(int)); //Read data lseek(fd, buff_out * sizeof(int), SEEK_SET); read(fd, (char *) & amp;data, sizeof(int)); printf("customer (pid : %d) read -> %d\ ", getpid(), data); //Write the reading position buff_out = (buff_out + 1) % BUFF_SIZE; lseek(fd, BUFF_SIZE * sizeof(int), SEEK_SET); write(fd, (char *) & amp;buff_out, sizeof(int)); sem_post(mutex); sem_post(empty); } return 0; } else if (p < 0) {<!-- --> printf("Fail to fork the Customer (num : %d)\ ", i); return -1; } } //process waits wait(NULL); //Release the semaphore sem_unlink("sys_empty"); sem_unlink("sys_full"); sem_unlink("sys_mutex"); //Close file close(fd); printf("\r the pc.c is finished\ "); return 0; }
Implementing semaphores in Linux-0.11
In include/unistd.h
, add:
... #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; ...
In include/linux/sys.h
, add:
... 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};
In kernel/system_call.s
, modify:
nr_system_calls = 76
Create new krenel/sem.c
:
#define __LIBRARY__ #include <unistd.h> #include <linux/sched.h> #include <linux/kernel.h> #include <asm/segment.h> #include <asm/system.h> #defineSEM_COUNT 32 sem_t semaphores[SEM_COUNT]; /* Queue related operations, rear is always the next position to be written, and front is always the first element of the queue*/ void init_queue(sem_queue* q) {<!-- --> q->front = q->rear = 0; } int is_empty(sem_queue* q) {<!-- --> return q->front == q->rear?1:0; } /*Leave the flag QUE_LEN-1 unused to determine whether it is slow*/ int is_full(sem_queue* q) {<!-- --> return (q->rear + 1)%QUE_LEN == q->front?1:0; } /*Get the first task at the head of the queue*/ struct task_struct * get_task(sem_queue* q) {<!-- --> if(is_empty(q)) {<!-- --> printk("Queue is empty!\ "); return NULL; } struct task_struct *tmp = q->wait_tasks[q->front]; q->front = (q->front + 1)%QUE_LEN; return tmp; } /*Insert the task into the end of the queue*/ int insert_task(struct task_struct *p,sem_queue* q) {<!-- --> // printk("Insert %d",p->pid); if(is_full(q)) {<!-- --> printk("Queue is full!\ "); return -1; } q->wait_tasks[q->rear] = p; q->rear = (q->rear + 1)%QUE_LEN; return 1; } /*Whether the semaphore has been opened, it is the return position*/ int sem_location(const char* name) {<!-- --> int i; for(i = 0;i < SEM_COUNT; i + + ) {<!-- --> if(strcmp(name,semaphores[i].name) == 0 & amp; & amp; semaphores[i].occupied == 1) {<!-- --> return i; } } return -1; } /*Open semaphore*/ 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( & amp;(semaphores[i].wait_queue)); // printk("%d %d %d %s\ ",semaphores[i].occupied,i,semaphores[i].value,semaphores[i].name); // printk("%p\ ", & amp;semaphores[i]); return &semaphores[i]; } } printk("Numbers of semaphores are limited!\ "); return NULL; } /*P atomic operations*/ int sys_sem_wait(sem_t* sem) {<!-- --> cli(); sem->value--; if(sem->value < 0) {<!-- --> /*See sleep_on*/ current->state = TASK_UNINTERRUPTIBLE; insert_task(current, & amp;(sem->wait_queue)); schedule(); } sti(); return 0; } /*V atomic operations*/ int sys_sem_post(sem_t* sem) {<!-- --> cli(); struct task_struct *p; sem->value + + ; if(sem->value <= 0) {<!-- --> p = get_task( & amp;(sem->wait_queue)); if(p != NULL) {<!-- --> (*p).state = TASK_RUNNING; } } sti(); return 0; } /*Release semaphore*/ 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; }
Use semaphores to solve the producer-consumer problem in Linux-0.11
Write test program 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 /*Type the total number*/ #define CHILD 5 /*Number of consumer processes*/ #define BUFSIZE 10 /*buffer size*/ sem_t *empty, *full, *mutex; int fno; /*file descriptor*/ int main() {<!-- --> int i,j,k; int data; pid_t p; int buf_out = 0; /*Read position from buffer*/ int buf_in = 0; /*Write buffer location*/ /*Open semaphore*/ if((mutex = sem_open("carmutex",1)) == SEM_FAILED) {<!-- --> perror("sem_open() error!\ "); return -1; } if((empty = sem_open("carempty",10)) == SEM_FAILED) {<!-- --> perror("sem_open() error!\ "); return -1; } if((full = sem_open("carfull",0)) == SEM_FAILED) {<!-- --> perror("sem_open() error!\ "); return -1; } fno = open("buffer.dat",O_CREAT|O_RDWR|O_TRUNC,0666); /* Store the position to be read in the buffer to facilitate communication between child processes */ lseek(fno,10*sizeof(int),SEEK_SET); write(fno,(char *) & amp;buf_out,sizeof(int)); /*Producer process*/ if((p=fork())==0) {<!-- --> for( i = 0 ; i < NUMBER; i + + ) {<!-- --> sem_wait(empty); sem_wait(mutex); /*Write a character*/ lseek(fno, buf_in*sizeof(int), SEEK_SET); write(fno,(char *) & amp;i,sizeof(int)); buf_in = (buf_in + 1)% BUFSIZE; printf("producer (pid : %d) write -> %d\ ", getpid(), i); sem_post(mutex); sem_post(full); } return 0; }else if(p < 0) {<!-- --> perror("Fail to fork!\ "); 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); /*Get the reading position*/ lseek(fno,10*sizeof(int),SEEK_SET); read(fno,(char *) & amp;buf_out,sizeof(int)); /*Read data*/ lseek(fno,buf_out*sizeof(int),SEEK_SET); read(fno,(char *) & amp;data,sizeof(int)); /*Write the read position*/ buf_out = (buf_out + 1) % BUFSIZE; lseek(fno,10*sizeof(int),SEEK_SET); write(fno,(char *) & amp;buf_out,sizeof(int)); printf("customer (pid : %d) read -> %d\ ", getpid(), data); fflush(stdout); sem_post(mutex); sem_post(empty); } return 0; }else if(p<0) {<!-- --> perror("Fail to fork!\ "); return -1; } } wait(NULL); /*Release semaphore*/ sem_unlink("carfull"); sem_unlink("carempty"); sem_unlink("carmutex"); /*Release resources*/ close(fno); printf("\ THE PROCESS DONE!\ "); return 0; }