Implement shmat and shmdt
This commit is contained in:
+14
-7
@@ -236,13 +236,6 @@ vm_area_struct_t *create_vm_area(mm_struct_t *mm,
|
||||
uint32_t pgflags,
|
||||
uint32_t gfpflags);
|
||||
|
||||
vm_area_struct_t *create_vm_area_with_phy(mm_struct_t *mm,
|
||||
uint32_t virt_start,
|
||||
size_t size,
|
||||
uint32_t pgflags,
|
||||
uint32_t gfpflags,
|
||||
uint32_t phy_vm_start);
|
||||
|
||||
/// @brief Clone a virtual memory area, using copy on write if specified
|
||||
/// @param mm the memory descriptor which will contain the new segment.
|
||||
/// @param area the area to clone
|
||||
@@ -266,6 +259,20 @@ int destroy_vm_area(mm_struct_t *mm, vm_area_struct_t *area);
|
||||
/// @return a pointer to the area if we found it, NULL otherwise.
|
||||
vm_area_struct_t *find_vm_area(mm_struct_t *mm, uint32_t vm_start);
|
||||
|
||||
/// @brief Checks if the given virtual memory area range is valid.
|
||||
/// @param mm the memory descriptor which we use to check the range.
|
||||
/// @param vm_start the starting address of the area.
|
||||
/// @param vm_end the ending address of the area.
|
||||
/// @return 1 if it's valid, 0 if it's occupied, -1 if it's outside the memory.
|
||||
int is_valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end);
|
||||
|
||||
/// @brief Searches for an empty spot for a new virtual memory area.
|
||||
/// @param mm the memory descriptor which should contain the new area.
|
||||
/// @param length the size of the empty spot.
|
||||
/// @param vm_start where we save the starting address for the new area.
|
||||
/// @return 0 on success, 1 on failure.
|
||||
int find_free_vm_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start);
|
||||
|
||||
/// @brief Creates the main memory descriptor.
|
||||
/// @param stack_size The size of the stack in byte.
|
||||
/// @return The Memory Descriptor created.
|
||||
|
||||
+60
-33
@@ -73,6 +73,7 @@ static inline shm_info_t *__shm_info_alloc(key_t key, size_t size, int shmflg)
|
||||
// Allocate the memory.
|
||||
uint32_t order = find_nearest_order_greater(0, size);
|
||||
shm_info->shm_location = _alloc_pages(GFP_KERNEL, order);
|
||||
pr_crit("page: %p\n", shm_info->shm_location);
|
||||
// Return the shared memory structure.
|
||||
return shm_info;
|
||||
}
|
||||
@@ -130,6 +131,25 @@ static inline shm_info_t *__list_find_shm_info_by_key(key_t key)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// @brief Searches for the shared memory with the given page.
|
||||
/// @param page the page we are searching.
|
||||
/// @return the shared memory with the given page.
|
||||
static inline shm_info_t *__list_find_shm_info_by_page(page_t *page)
|
||||
{
|
||||
shm_info_t *shm_info;
|
||||
// Iterate through the list of shared memories.
|
||||
list_for_each_decl(it, &shm_list)
|
||||
{
|
||||
// Get the current entry.
|
||||
shm_info = list_entry(it, shm_info_t, list);
|
||||
// If shared memories is valid, check the id.
|
||||
if (shm_info && (shm_info->shm_location == page)) {
|
||||
return shm_info;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void __list_add_shm_info(shm_info_t *shm_info)
|
||||
{
|
||||
assert(shm_info && "Received a NULL pointer.");
|
||||
@@ -198,34 +218,12 @@ long sys_shmget(key_t key, size_t size, int shmflg)
|
||||
return shm_info->id;
|
||||
}
|
||||
|
||||
static inline int __find_vm_free_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start)
|
||||
{
|
||||
// Get the stack.
|
||||
vm_area_struct_t *area, *prev_area;
|
||||
list_for_each_prev_decl(it, &mm->mmap_list)
|
||||
{
|
||||
area = list_entry(it, vm_area_struct_t, vm_list);
|
||||
assert(area && "There is a NULL area in the list.");
|
||||
// Check the previous segment.
|
||||
if (area->vm_list.prev != &mm->mmap_list) {
|
||||
prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list);
|
||||
assert(prev_area && "There is a NULL area in the list.");
|
||||
// Compute the available space.
|
||||
unsigned available_space = area->vm_start - prev_area->vm_end;
|
||||
// If the space is enough, return the address.
|
||||
if (available_space >= length) {
|
||||
*vm_start = area->vm_start - length;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *sys_shmat(int shmid, const void *shmaddr, int shmflg)
|
||||
{
|
||||
shm_info_t *shm_info = NULL;
|
||||
task_struct *task = NULL;
|
||||
uint32_t vm_start, phy_start;
|
||||
|
||||
// The id is less than zero.
|
||||
if (shmid < 0) {
|
||||
pr_err("The id is less than zero.\n");
|
||||
@@ -251,26 +249,55 @@ void *sys_shmat(int shmid, const void *shmaddr, int shmflg)
|
||||
// Get the calling task.
|
||||
task = scheduler_get_current_process();
|
||||
assert(task && "Failed to get the current running process.");
|
||||
|
||||
uint32_t phy_start = get_physical_address_from_page(shm_info->shm_location);
|
||||
uint32_t vm_start;
|
||||
__find_vm_free_area(
|
||||
task->mm,
|
||||
shm_info->shmid.shm_segsz,
|
||||
&vm_start);
|
||||
|
||||
// Get the phyisical address from the allocated pages.
|
||||
phy_start = get_physical_address_from_page(shm_info->shm_location);
|
||||
// Find the virtual address for the new area.
|
||||
if (find_free_vm_area(task->mm, shm_info->shmid.shm_segsz, &vm_start)) {
|
||||
pr_err("We failed to find space for the new virtual memory area.\n");
|
||||
return (void *)-EACCES;
|
||||
}
|
||||
mem_upd_vm_area(
|
||||
task->mm->pgd,
|
||||
vm_start,
|
||||
phy_start,
|
||||
shm_info->shmid.shm_segsz,
|
||||
MM_RW | MM_PRESENT | MM_USER | MM_UPDADDR);
|
||||
|
||||
return (void *)vm_start;
|
||||
}
|
||||
|
||||
long sys_shmdt(const void *shmaddr)
|
||||
{
|
||||
shm_info_t *shm_info = NULL;
|
||||
task_struct *task = NULL;
|
||||
uint32_t phy_start;
|
||||
size_t size;
|
||||
page_t *page;
|
||||
|
||||
// Get the calling task.
|
||||
task = scheduler_get_current_process();
|
||||
assert(task && "Failed to get the current running process.");
|
||||
// Get the page.
|
||||
page = mem_virtual_to_page(task->mm->pgd, (uint32_t)shmaddr, &size);
|
||||
// Check if we can find the page.
|
||||
if (page == NULL) {
|
||||
pr_err("Cannot retrieve the page from the give address.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
shm_info = __list_find_shm_info_by_page(page);
|
||||
// Check if no shared memory exists for the given address.
|
||||
if (!shm_info) {
|
||||
pr_err("No shared memory exists for the given address.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
// Get the phyisical address from the allocated pages.
|
||||
phy_start = get_physical_address_from_page(shm_info->shm_location);
|
||||
// Set all virtual pages as not present.
|
||||
mem_upd_vm_area(
|
||||
task->mm->pgd,
|
||||
(uint32_t)shmaddr,
|
||||
phy_start,
|
||||
shm_info->shmid.shm_segsz,
|
||||
MM_GLOBAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/// See LICENSE.md for details.
|
||||
|
||||
// Setup the logging for this file (do this before any other include).
|
||||
#include "sys/kernel_levels.h" // Include kernel log levels.
|
||||
#include "sys/kernel_levels.h" // Include kernel log levels.
|
||||
#define __DEBUG_HEADER__ "[KHEAP ]" ///< Change header.
|
||||
#define __DEBUG_LEVEL__ LOGLEVEL_INFO ///< Set log level.
|
||||
#include "io/debug.h" // Include debugging functions.
|
||||
|
||||
+71
-72
@@ -75,98 +75,46 @@ void paging_flush_tlb_single(unsigned long addr)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
/// @brief
|
||||
/// @param mm
|
||||
/// @param vm_start
|
||||
/// @param vm_end
|
||||
/// @return int
|
||||
static inline int __valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end)
|
||||
{
|
||||
if (vm_end <= vm_start)
|
||||
return -1;
|
||||
// Get the stack.
|
||||
vm_area_struct_t *area;
|
||||
list_for_each_prev_decl(it, &mm->mmap_list)
|
||||
{
|
||||
area = list_entry(it, vm_area_struct_t, vm_list);
|
||||
assert(area && "There is a NULL area in the list.");
|
||||
if ((vm_start >= area->vm_start) && (vm_start <= area->vm_end))
|
||||
return 0;
|
||||
if ((vm_end >= area->vm_start) && (vm_end <= area->vm_end))
|
||||
return 0;
|
||||
if ((vm_start <= area->vm_start) && (vm_end >= area->vm_end))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// @brief Searches for an empty spot for a new virtual memory area.
|
||||
/// @param mm the memory descriptor which should contain the new area.
|
||||
/// @param length the size of the empty spot.
|
||||
/// @param vm_start where we save the starting address for the new area.
|
||||
/// @return 0 on success, 1 on failure.
|
||||
static inline int __find_vm_free_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start)
|
||||
{
|
||||
// Get the stack.
|
||||
vm_area_struct_t *area, *prev_area;
|
||||
list_for_each_prev_decl(it, &mm->mmap_list)
|
||||
{
|
||||
area = list_entry(it, vm_area_struct_t, vm_list);
|
||||
assert(area && "There is a NULL area in the list.");
|
||||
// Check the previous segment.
|
||||
if (area->vm_list.prev != &mm->mmap_list) {
|
||||
prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list);
|
||||
assert(prev_area && "There is a NULL area in the list.");
|
||||
// Compute the available space.
|
||||
unsigned available_space = area->vm_start - prev_area->vm_end;
|
||||
// If the space is enough, return the address.
|
||||
if (available_space >= length) {
|
||||
*vm_start = area->vm_start - length;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
vm_area_struct_t *create_vm_area(mm_struct_t *mm,
|
||||
uint32_t vm_start,
|
||||
size_t size,
|
||||
uint32_t pgflags,
|
||||
uint32_t gfpflags)
|
||||
{
|
||||
uint32_t vm_end, phy_start, order;
|
||||
vm_area_struct_t *segment;
|
||||
|
||||
// Compute the end of the virtual memory area.
|
||||
uint32_t vm_end = vm_start + size;
|
||||
vm_end = vm_start + size;
|
||||
// Check if the range is already occupied.
|
||||
if (__valid_vm_area(mm, vm_start, vm_end) <= 0) {
|
||||
if (is_valid_vm_area(mm, vm_start, vm_end) <= 0) {
|
||||
pr_crit("The virtual memory area range [%p, %p] is already in use.\n", vm_start, vm_end);
|
||||
kernel_panic("Wrong virtual memory area range.");
|
||||
}
|
||||
// Allocate on kernel space the structure for the segment.
|
||||
vm_area_struct_t *new_segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL);
|
||||
segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL);
|
||||
// Find the nearest order for the given memory size.
|
||||
uint32_t order = find_nearest_order_greater(vm_start, size);
|
||||
uint32_t phy_vm_start;
|
||||
order = find_nearest_order_greater(vm_start, size);
|
||||
|
||||
if (pgflags & MM_COW) {
|
||||
pgflags &= ~(MM_PRESENT | MM_UPDADDR);
|
||||
phy_vm_start = 0;
|
||||
pgflags = pgflags & ~(MM_PRESENT | MM_UPDADDR);
|
||||
phy_start = 0;
|
||||
} else {
|
||||
pgflags |= MM_UPDADDR;
|
||||
pgflags = pgflags | MM_UPDADDR;
|
||||
page_t *page = _alloc_pages(gfpflags, order);
|
||||
phy_vm_start = get_physical_address_from_page(page);
|
||||
phy_start = get_physical_address_from_page(page);
|
||||
}
|
||||
|
||||
mem_upd_vm_area(mm->pgd, vm_start, phy_vm_start, size, pgflags);
|
||||
mem_upd_vm_area(mm->pgd, vm_start, phy_start, size, pgflags);
|
||||
|
||||
// Update vm_area_struct info.
|
||||
new_segment->vm_start = vm_start;
|
||||
new_segment->vm_end = vm_end;
|
||||
new_segment->vm_mm = mm;
|
||||
segment->vm_start = vm_start;
|
||||
segment->vm_end = vm_end;
|
||||
segment->vm_mm = mm;
|
||||
|
||||
// Update memory descriptor list of vm_area_struct.
|
||||
list_head_insert_after(&new_segment->vm_list, &mm->mmap_list);
|
||||
mm->mmap_cache = new_segment;
|
||||
list_head_insert_after(&segment->vm_list, &mm->mmap_list);
|
||||
mm->mmap_cache = segment;
|
||||
|
||||
// Sort the mmap_list.
|
||||
list_head_sort(&mm->mmap_list, vm_area_compare);
|
||||
@@ -176,7 +124,7 @@ vm_area_struct_t *create_vm_area(mm_struct_t *mm,
|
||||
|
||||
mm->total_vm += (1U << order);
|
||||
|
||||
return new_segment;
|
||||
return segment;
|
||||
}
|
||||
|
||||
uint32_t clone_vm_area(mm_struct_t *mm, vm_area_struct_t *area, int cow, uint32_t gfpflags)
|
||||
@@ -276,6 +224,57 @@ inline vm_area_struct_t *find_vm_area(mm_struct_t *mm, uint32_t vm_start)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline int is_valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end)
|
||||
{
|
||||
if (vm_end <= vm_start) {
|
||||
return -1;
|
||||
}
|
||||
// Get the stack.
|
||||
vm_area_struct_t *area;
|
||||
list_for_each_prev_decl(it, &mm->mmap_list)
|
||||
{
|
||||
area = list_entry(it, vm_area_struct_t, vm_list);
|
||||
assert(area && "There is a NULL area in the list.");
|
||||
if ((vm_start > area->vm_start) && (vm_start < area->vm_end)) {
|
||||
pr_crit("INSIDE(START): %p <= %p <= %p", area->vm_start, vm_start, area->vm_end);
|
||||
return 0;
|
||||
}
|
||||
if ((vm_end > area->vm_start) && (vm_end < area->vm_end)) {
|
||||
pr_crit("INSIDE(END): %p <= %p <= %p", area->vm_start, vm_end, area->vm_end);
|
||||
return 0;
|
||||
}
|
||||
if ((vm_start < area->vm_start) && (vm_end > area->vm_end)) {
|
||||
pr_crit("WRAPS: %p <= (%p, %p) <= %p", vm_start, area->vm_start, area->vm_end, vm_end);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int find_free_vm_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start)
|
||||
{
|
||||
// Get the stack.
|
||||
vm_area_struct_t *area, *prev_area;
|
||||
list_for_each_prev_decl(it, &mm->mmap_list)
|
||||
{
|
||||
area = list_entry(it, vm_area_struct_t, vm_list);
|
||||
assert(area && "There is a NULL area in the list.");
|
||||
// Check the previous segment.
|
||||
if (area->vm_list.prev != &mm->mmap_list) {
|
||||
prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list);
|
||||
assert(prev_area && "There is a NULL area in the list.");
|
||||
// Compute the available space.
|
||||
unsigned available_space = area->vm_start - prev_area->vm_end;
|
||||
// If the space is enough, return the address.
|
||||
if (available_space >= length) {
|
||||
*vm_start = area->vm_start - length;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __init_pagedir(page_directory_t *pdir)
|
||||
{
|
||||
*pdir = (page_directory_t){ { 0 } };
|
||||
@@ -724,11 +723,11 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t off
|
||||
// Get the current task.
|
||||
task_struct *task = scheduler_get_current_process();
|
||||
// Check if we were asked for a specific spot.
|
||||
if (addr && __valid_vm_area(task->mm, (uintptr_t)addr, (uintptr_t)addr + length)) {
|
||||
if (addr && is_valid_vm_area(task->mm, (uintptr_t)addr, (uintptr_t)addr + length)) {
|
||||
vm_start = (uintptr_t)addr;
|
||||
} else {
|
||||
// Find an empty spot.
|
||||
if (__find_vm_free_area(task->mm, length, &vm_start)) {
|
||||
if (find_free_vm_area(task->mm, length, &vm_start)) {
|
||||
pr_err("We failed to find a suitable spot for a new virtual memory area.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+25
-17
@@ -11,7 +11,7 @@
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int semid, shmid;
|
||||
int shmid;
|
||||
pid_t cpid;
|
||||
int *array;
|
||||
|
||||
@@ -21,40 +21,48 @@ int main(void)
|
||||
perror("shmget");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Create a semaphore set containing one semaphore.
|
||||
semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
|
||||
if (semid == -1) {
|
||||
perror("semget");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("shmid = %d;\n", shmid);
|
||||
|
||||
// Create a child.
|
||||
cpid = fork();
|
||||
|
||||
if (cpid == 0) {
|
||||
// Child attaches the shared memory.
|
||||
array = (int *)shmat(shmid, NULL, 0);
|
||||
if (array == NULL) {
|
||||
perror("shmat");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("C: %p\n", array);
|
||||
array[0] = 1;
|
||||
return 0;
|
||||
} else {
|
||||
array = (int *)shmat(shmid, NULL, 0);
|
||||
printf("F: %p\n", array);
|
||||
array[1] = 2;
|
||||
}
|
||||
|
||||
// Father attaches the shared memory.
|
||||
array = (int *)shmat(shmid, NULL, 0);
|
||||
if (array == NULL) {
|
||||
perror("shmat");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Wait for the child to finish.
|
||||
while (wait(NULL) != -1) continue;
|
||||
|
||||
printf("F: %p\n", array);
|
||||
array[1] = 2;
|
||||
|
||||
printf("array[%d] : %d\n", 0, array[0]);
|
||||
printf("array[%d] : %d\n", 1, array[1]);
|
||||
|
||||
// Detatch the shared memory.
|
||||
if (shmdt(array) < 0) {
|
||||
perror("shmdt");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Remove the shared memory.
|
||||
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
|
||||
perror("shmctl");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Remove the semaphore set.
|
||||
if (semctl(semid, 0, IPC_RMID, NULL) == -1) {
|
||||
perror("semctl");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user