Shared memory based producer consumer programs:
- To carry out this experiment, Experiment 5 needs to be completed first: the implementation and application of semaphores
- Instead of using files as buffers, use shared memory
- Instead of placing the producer and consumer in the same file pc.c, the producer c. Consumer c. Both programs are single process and communicate between processes through semaphores and shared buffers.
Under Linux, shared memory can be used through two system calls shmget() and shmat(). Of course, Linux 0.11 itself does not have these two system calls, so it should be implemented by itself.
Function prototype of shmget() system call:
int shmget(key_t key, size_t size, int shmflg);
The function prototype of shmat() system call is:
void * shmat(int shmid, const void * shmaddr, int shmflg);
Both processes call shmat and can be associated to the same page of physical memory. At this time, the two processes read and write p pointers are reading and writing the same page of memory, so as to realize the inter process communication based on shared memory.
Shared memory structure
The function int shmget(key_t key, size_t size, int shmflg) creates or opens a page of memory, and then returns the shmid of the page's shared memory.
After ignoring the shmflg parameter, you can see that the information to be saved in a page of shared memory is
- Unique identifier key
- Shared memory size
- Then you also need a parameter to save the address of the shared memory page
The structure of shared memory information is as follows:
typedef struct shm_ds { unsigned int key; unsigned int size; unsigned long page; }shm_ds;
shmget() function
As required, the system call will create or open a page of physical memory as shared memory, and return the shmid of the shared memory, that is, the identification of the page of shared memory in the operating system. If multiple processes call shmget() with the same key, they will get the same shmid, that is, the identity of the same block of shared memory.
If the memory corresponding to the key has been established, return shmid directly; otherwise, create a new one and then return.
If the size exceeds one page of memory, return - 1 and set errno to EINVAL.
If the system has no free memory, return - 1 and set errno to ENOMEM.
Function get_free_page() can (1) get an idle physical page and return the starting physical address of the page for the implementation of shmget().
#define __LIBRARY__ #include <unistd.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <errno.h> static shm_ds shm_list[SHM_SIZE] = {{0,0,0}}; int sys_shmget(unsigned int key, size_t size) { int i; unsigned long page; if(size>PAGE_SIZE)/* Memory size exceeds one page */ { printk("shmget: size %u cannot be greater than the page size %ud. \n", size, PAGE_SIZE); return -ENOMEM; } if(key==0) { printk("shmget: key cannot be 0.\n"); return -EINVAL; } /* If the shared memory descriptor already exists, the index is returned directly */ for(i=0; i<SHM_SIZE; i++) { if(shm_list[i].key == key) return i; } /* Get free physical memory page */ page = get_free_page(); if(!page) return -ENOMEM; printk("shmget get memory's address is 0x%08x\n",page); /* Find an unused shared memory descriptor to initialize and return the index */ for(i=0; i<SHM_SIZE; i++) { if(shm_list[i].key==0) { shm_list[i].key = key; shm_list[i].size = size; shm_list[i].page = page; return i; } } return -1; /* The amount of shared memory is full */ }
shmat() function
1. Physical page - > linear address space
The system call will map the shared memory page corresponding to shmid to the virtual address space of the current process and return a logical address P. the calling process can read and write the logical address p to read and write this page of shared memory.
Both processes call shmat and can be associated to the same page of physical memory. At this time, the two processes read and write p pointers are reading and writing the same page of memory, so as to realize the inter process communication based on shared memory.
If shmid is incorrect, return - 1 and collocate errno to EINVAL.
Function put_page() can map (2) physical pages to the specified linear address space for the implementation of shmat().
2. Spatial distribution of process data segments
With free physical pages, there is also the mapping between linear address and physical pages, but to complete this experiment (3), we also need to be able to obtain a free virtual address space.
To delimit a space from the data segment, you first need to understand the distribution of the process data segment space, which is obviously determined by the Exec System call, so take a detailed look at the core code of exec, do_execve (in file fs/exec.c).
In function do_ In execve(), change_ldt is used to modify the data segment (LDT of course). The function change_ldt is implemented as follows:
static unsigned long change_ldt(unsigned long text_size,unsigned long * page) { /*Where text_size is the length of the code segment, taken from the header of the executable file, and page is the parameter and environment page*/ unsigned long code_limit,data_limit,code_base,data_base; int i; code_limit = text_size+PAGE_SIZE -1; code_limit &= 0xFFFFF000; //code_limit is the code segment length limit = text_ Number of pages corresponding to size (rounded up) data_limit = 0x4000000; //Data segment length limit 64MB code_base = get_base(current->ldt[1]); data_base = code_base; // Data segment base address = code segment base address set_base(current->ldt[1],code_base); set_limit(current->ldt[1],code_limit); set_base(current->ldt[2],data_base); set_limit(current->ldt[2],data_limit); __asm__("pushl $0x17\n\tpop %%fs":: ); // Start at the end of the data segment data_base += data_limit; // Forward processing for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) { // Process one page at a time data_base -= PAGE_SIZE; // Establish the mapping of linear address to physical page if (page[i]) put_page(page[i],data_base); } // Return segment limit return data_limit; }
It can be seen that the kernel virtualizes an address space for each process, and then allocates data segments, code segments and stack segments by the function do_execve() is implemented, and the allocation of virtual space is as follows:
Where start_code is the starting address of the code segment, brk is the total length of the code segment and data segment, bss is the uninitialized data segment of the process, start_stack is the starting address of the stack, and these values are saved in the task of the process_ Struct. brk and start_ The space between stacks is prepared for the stack. The bottom of the stack is idle, and the shared memory can be mapped to this space.
3. Realize
Dr. Zhao's book gives the following figure
You see, the useful length is brk, and the back is where you want to apply.
Here's a hint,
/*Get brk length * / current - > brk
/*Get the data base address, the virtual address of the current process * /, get_ base(current->ldt[2])
/*Virtual address of current process + offset of brk in virtual memory = virtual address of brk*/
brk = current->brk + data_base;
/*The next free page is used as shared memory space/
current->brk += PAGE_SIZE;
/Establish the mapping between physical address and linear address*/
put_page(shm_list[shmid].page, brk);
void * sys_shmat(int shmid) { unsigned long data_base, brk; /*Judge whether shmid is legal*/ if(shmid < 0 || SHM_SIZE <= shmid || shm_list[shmid].page==0 || shm_list[shmid].key <= 0) return (void *)-EINVAL; data_base = get_base(current->ldt[2]); printk("current's data_base = 0x%08x,new page = 0x%08x\n",data_base,shm_list[shmid].page); brk = current->brk + data_base; current->brk += PAGE_SIZE; /* Establish the mapping between linear address and physical address*/ if(put_page(shm_list[shmid].page, brk) == 0) return (void *)-ENOMEM; /* The first address is returned when returning, so you need to subtract a page size */ return (void *)(current->brk - PAGE_SIZE); }
last
- Modify / include / unistd h. Add the number of the new system call:
/* Add system call number */ #define __NR_whoami 72 / * Experiment 2*/ #define __NR_iam 73 #define __ NR_ sem_ Open 74 / * Experiment 5*/ #define __NR_sem_wait 75 #define __NR_sem_post 76 #define __NR_sem_unlink 77 #define __ NR_ Shmget 78 / * Experiment 6*/ #define __NR_shmat 79 #define SHM_SIZE 64 typedef struct shm_ds { unsigned int key; unsigned int size; unsigned long page; }shm_ds; int sys_shmget(unsigned int key,size_t size); void * sys_shmat(int shmid);
- Modify / kernel/system_call.s. The and values of the total system calls need to be modified:
nr_system_calls = 80
- Modify / include / Linux / sys h. Declare new function
extern int sys_shmget(); extern int sys_shmat(); fn_ptr sys_call_table[] = { //...sys_setreuid,sys_setregid,sys_whoami,sys_iam, sys_sem_open,sys_sem_wait,sys_sem_post,sys_sem_unlink, sys_shmget, sys_shmat};
- Modify Makefile in linux-0.11/kernel directory
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 who.o sem.o shm.o // ... ### Dependencies: shm.s shm.o shm.c: ../include/asm/segment.h ../include/linux/kernel.h \ ../include/linux/sched.h ../include/linux/mm.h ../include/unistd.h \ ../include/string.h
-
Recompile kernel: make all
-
Modify producer C and consumer c. For linux-0.11 operation:
HIT-OS-LAB reference:
1. Principle, implementation and practice of operating system - edited by Li Zhijun and Liu Hongwei
2. Linux kernel complete notes
3. Experimental report of students from Harbin Institute of Technology
4.Linux-0.11 source code