:::info ## Wait Queue A Wait Queue is a synchronization mechanism in the Linux kernel used to put processes to sleep while waiting for a specific condition to be met. Once the condition is satisfied, the processes are awakened. It is commonly used to avoid busy-waiting, thereby improving system efficiency. Under normal circumstances, a function using a wait queue must know the name or the pointer of the wait queue. This is because a wait queue is represented as a variable (usually of type wait_queue_head_t ), and the function needs the address of this variable to perform operations. > In this lab, you need to implement a custom wait queue-like functionality in kernel space, allowing user applications to operate through the system call. ::: ## Sample Code ( User Space ) ```c= #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <sys/syscall.h> #define NUM_THREADS 10 void *enter_wait_queue(void *thread_id) { fprintf(stderr, "enter wait queue thread_id: %d\n", *(int *)thread_id); /* */ // your syscall here syscall( xxx , 1); fprintf(stderr, "exit wait queue thread_id: %d\n", *(int *)thread_id); } void *clean_wait_queue() { /* // your syscall here syscall( xxx , 2); */ } int main() { void *ret; pthread_t id[NUM_THREADS]; int thread_args[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; i++) { thread_args[i] = i; pthread_create(&id[i], NULL, enter_wait_queue, (void *)&thread_args[i]); } sleep(1); fprintf(stderr, "start clean queue ...\n"); clean_wait_queue(); for (int i = 0; i < NUM_THREADS; i++) { pthread_join(id[i], &ret); } return 0; } ``` ## Sample Code ( Kernel Space ) ```c= static int enter_wait_queue(void) { return 0; } static int clean_wait_queue(void) { return 0; } SYSCALL_DEFINE1(call_wait_queue, int, id) { switch (id){ case 1: enter_wait_queue() break; case 2: clean_wait_queue(); break; } return 0; } ``` ## Workflow 1. Declare `my_wait_queue` in kernel space 2. Add a thread to the wait queue to wait 3. Remove threads from the wait queue, allowing them to exit ## Declare `my_wait_queue` in kernel space ```c! static DECLARE_WAIT_QUEUE_HEAD(my_wait_queue) ``` ### Two methods of declaring wait queues #### DECLARE_WAIT_QUEUE_HEAD > https://elixir.bootlin.com/linux/v5.15.137/source/include/linux/wait.h#L61 #### init_waitqueue_head > https://elixir.bootlin.com/linux/v5.15.137/source/include/linux/wait.h#L66 ## POSIX thread library > #include <pthread.h> ### pthread_create - create a new thread > https://man7.org/linux/man-pages/man3/pthread_create.3.html ```c! int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void *), void *restrict arg); ``` ### pthread_join - join with a terminated thread > https://man7.org/linux/man-pages/man3/pthread_join.3.html ```c! int pthread_join(pthread_t thread, void **retval); ``` ## Add a thread to the wait queue to wait ( Kernel Space ) ```c= static int enter_wait_queue(void) { wait_queue_entry_t wq_entry; init_waitqueue_entry(wq_entry, current); add_wait_queue_exclusive(my_wait_queue, wq_entry); return 0; } ``` ```c= static int enter_wait_queue(void) { return wait_event_interruptible(my_wait_queue, false); } ``` ### Waitqueue related functions #### init_waitqueue_entry > https://elixir.free-electrons.com/linux/v5.15.137/source/include/linux/wait.h#L82 ### Functions that add a thread to a wait queue #### add_wait_queue > https://elixir.bootlin.com/linux/v5.15.137/source/kernel/sched/wait.c#L18 ```c= void add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry) { unsigned long flags; wq_entry->flags &= ~WQ_FLAG_EXCLUSIVE; spin_lock_irqsave(&wq_head->lock, flags); __add_wait_queue(wq_head, wq_entry); spin_unlock_irqrestore(&wq_head->lock, flags); } ``` ##### WQ_FLAG_EXLUSIVE > https://elixir.bootlin.com/linux/v5.15.137/source/include/linux/wait.h#L20 ```c! #define WQ_FLAG_EXCLUSIVE 0x01 ``` #### add_wait_queue_exclusive > https://elixir.bootlin.com/linux/v5.15.137/source/kernel/sched/wait.c#L29 ```c= void add_wait_queue_exclusive(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry) { unsigned long flags; wq_entry->flags |= WQ_FLAG_EXCLUSIVE; spin_lock_irqsave(&wq_head->lock, flags); __add_wait_queue_entry_tail(wq_head, wq_entry); spin_unlock_irqrestore(&wq_head->lock, flags); } ``` ## Remove threads from the wait queue, allowing them to exit ```c=(my_wait_queue) static int clean_wait_queue(void) { while (waitqueue_active(&my_wait_queue)) { wake_up(my_wait_queue); sleep(1); } return 0; } ``` ## 問題 ![image](https://hackmd.io/_uploads/rkkYG1jEyg.png) ```c! obj-y := hello.o call_wait_queue.o ``` ## 紀錄 ![image](https://hackmd.io/_uploads/r1CF2NoNyx.png)