:::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;
}
```
## 問題

```c!
obj-y := hello.o call_wait_queue.o
```
## 紀錄
