Shared memory
A small interface for printf
Why can't you see any display?
Remember to compile with gcc -E to see the code actually compiled by gcc.POSIX shared memory
In this exercise, you will write a program using a shared memory to synchronize two processes. Firstly, this program is executed in a POSIX environment (ie under Linux).
- Before the creation of the child process:
- The parent creates a shared memory segment of size 8192,
- Then, after the creation of the child process:
- The parent maps this segment into its address space at address 0x10000000 before destroying the mapping,
- The child maps this same segment into its address space, but at the address 0x20000000 before destroying the mapping,
- The parent destroys the shared memory segment after the child terminates.
-
shm_open: opens or creates a shared memory segment.
A use typical is int fd = shm_open("/my-key", O_RDWR | O_CREAT, 0777);
-
ftruncate: allows to specify the size of a memory segment shared previously opened. A typical use is
ftruncate(fd, 65536);
-
mmap: allows you to map the shared memory segment in the address space
of the process (more generally, mmap allows to map
any file). Typical use is:
void* addr = (void*)0x10000000; addr = mmap(addr, 65536, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- to block the parent as long as the first integer shared memory contains 0,
- make sure that the child unlocks the father.
Congratulations, you have just implemented a great inter-process function call! Indeed, the child is the client, sends a request to the parent who executes code, meanwhile, the child waits for the parent's response.
Implementation of a shared memory interface in xv6
Start by getting the code for xv6:
- git clone https://gitlab.inf.telecom-sudparis.eu/csc4508/xv6-tsp.git if you start from a new copy
- or git checkout master if you already have a local copy of the repository
Then create a local branch to work:
- int shm_create(int size) creates a shared memory segment of size size and returns an identifier to this segment. It is the equivalent of a call to the POSIX function shm_open followed by ftruncate
- int shm_attach(int id, void* addr) attaches the segment id at the address addr. This function is the equivalent of the POSIX mmap function.
- int shm_detach(int id) detaches the id segment. This function is the equivalent of the POSIX munmap function.
- int shm_destroy(int id) destroys the id segment. This function is the equivalent of the POSIX shm_unlink function.
Each of these functions should return -1 on error. Add a preliminary implementation of these functions in vm.c which prints function ??? not yet implemented and returns -1.
- Associate a number with the various functions. It is this number that is transmitted by the process to the system during the system call. For this, we must define new constants in syscall.h.
- On the process side, add the code to call the new system
functions. It is necessary to modify:
- user.h, which contains the signatures of the system calls,
- usys.S, which contains the (assembler) code of these functions.
- On the kernel side, you must add the code implementing the new system
calls. It is necessary to modify:
- vm.c to add the preliminary definitions of system calls,
- syscall.c, which contains the table associating the system calls numbers to the implementation functions.
Creation and attachment of a shared memory segment
To implement these functions, we advise you to use the following data structure (to be added to vm.c) to represent the set of shared memory segments:
- pages contains the virtual addresses of the shared pages (see below),
- npages gives the number of pages of the segment (it is equal to 0 if the entry is not used, i.e. if there is no identifier segment id ),
- nused gives the number of times the segment has been attached (this field will be used in the following exercise).
- char* kalloc() allocates a new physical page and returns a pointer to a virtual address at which the page is mapped. This virtual address is only valid in the kernel address space.
- int V2P(char* addr) returns the physical address of the virtual address addr if addr is an address returned by kalloc.
- mappages(myproc()->pgdir, vaddr, PGSIZE, paddr, PTE_W|PTE_U) allows to map the physical address page paddr to the virtual address vaddr in the current process and gives the current process write permission to this page.
- To calculate the number of memory pages to store size bytes, you can use PGROUNDUP(size)/PGSIZE.
The shm_create function must:
- Calculate the number of memory pages to allocate
- Find a free shm (i.e. whose npages is 0)
- Allocate the memory pages (with kalloc) and store their address in the pages array
- Fill the allocated pages with 0s
The shm_attach function must map the pages of the shared memory segment into memory and increment the nused reference count.
To go further: detach and destroy a shared memory segment
To get started, in order to detach the id segment of the p process, you need to know to which address this segment had been mapped. To do this, you need to modify the proc structure in order to keep the (id, addr) associations where id is a segment identifier and addr the projection address. Edit the shm_attach function in order to store this association.
Then, to remove an association between a virtual address and a physical address in a process, there is no function in the kernel. To help you, the following code is used to mark the virtual address vaddr as invalid in the page table of the current process.
Finally, you should know that kfree frees a memory page.
To go even further, you can:
- Prevent the destruction of a segment if it is still mapped in another process using the nused field.
- Automatically detach the attached segments from a process when it ends (function exit in proc.c).