等待队列

add_wait_queue_exclusive();

add_wait_queue();

插入队列的位置不同:一个从尾,一个在头。前者插入元素的优先级较高。

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/list.h>
 
MODULE_LICENSE("GPL");
 
int my_function(void * argc)
{
    printk("<0>in the kernel thread function!\n");   
    printk("<0>the current pid is:%d\n",current->pid);
    printk("<0>out the kernel thread function\n");
    return 0;
}
 
static int __init my_init(void)
{  
    int result, result1, result2;  
    int wait_queue_num = 0;
    wait_queue_head_t head;
    wait_queue_t data, data1, data2, *curr, *next; 
 
    printk("<0>into my_init.\n");
     
                 
    result =kernel_thread(my_function,NULL,CLONE_KERNEL);
    result1=kernel_thread(my_function,NULL,CLONE_KERNEL);
    result2=kernel_thread(my_function,NULL,CLONE_KERNEL);
 
 
    struct pid * kpid = find_get_pid(result);
    struct task_struct * task = pid_task(kpid,PIDTYPE_PID);
 
    struct pid * kpid1 = find_get_pid(result1);
    struct task_struct * task1 = pid_task(kpid1,PIDTYPE_PID);
 
    struct pid * kpid2 = find_get_pid(result2);
    struct task_struct * task2 = pid_task(kpid2,PIDTYPE_PID);
 
    init_waitqueue_head(&head);
 
 
    init_waitqueue_entry(&data, task);
    init_waitqueue_entry(&data1,task1);
    init_waitqueue_entry(&data2,task2);
 
 
    add_wait_queue_exclusive(&head,&data1);
    add_wait_queue_exclusive(&head,&data2);
    add_wait_queue(&head,&data);
 
 
    list_for_each_entry_safe(curr, next, &(head.task_list), task_list)
    {
        wait_queue_num++;
         
        printk("<0>the flag value of the current data of the waitqueue is:%d\n",curr->flags); 
             
        printk("<0>the pid value of the current data of the waitqueue is:%d\n", \
                                                        ((struct task_struct *)(curr->private))->pid);
    }
 
    printk("<0>the value of the wait_queue_num is :%d\n",wait_queue_num);
     
    printk("<0>the result  of the kernel_thread is :%d\n", result);
    printk("<0>the result1 of the kernel_thread is :%d\n",result1);
    printk("<0>the result2 of the kernel_thread is :%d\n",result2);
    printk("<0>the current pid is:%d\n",current->pid);
    printk("<0>out my_init.\n");
    return 0;
}
 
static void __exit my_exit(void)
{  
        printk("<0>Goodbye mytest\n");   
}
 
module_init(my_init);
module_exit(my_exit);

又是一位内核界的明星,通过内核链表遍历一大串结构体的典型。

#define list_for_each_entry_safe(pos, n, head, member) \
for ( pos = list_entry((head)->next, typeof(*pos), member), \
n
= list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos
= n, n = list_entry(n->member.next, typeof(*n), member) )

它都遍历了哪些东西,看看下面的结构体,也就一目了然了。

复制代码
-- include/linux/wait.h --

struct __wait_queue_head {
spinlock_t
lock;
struct list_head task_list;
};
typedef
struct __wait_queue_head wait_queue_head_t;


struct __wait_queue {
unsigned
int flags;
void*private;
wait_queue_func_t func;
struct list_head task_list;
};
typedef
struct __wait_queue wait_queue_t;
复制代码

以下是运行的结果,通过遍历队列打印出的pid可以看出插入队列的方式。

[103891.133838] into my_init.
[103891.133875] the result  of the kernel_thread is :22093
[103891.133878] the result1 of the kernel_thread is :22094
[103891.133880] the result2 of the kernel_thread is :22095
[103891.133883] the current pid is:22092
[103891.133886] the flag value of the current data of the waitqueue is:0
[103891.133889] the pid value of the current data of the waitqueue is:22094
[103891.133891] the flag value of the current data of the waitqueue is:0
[103891.133894] the pid value of the current data of the waitqueue is:22093
[103891.133897] the flag value of the current data of the waitqueue is:1
[103891.133900] the pid value of the current data of the waitqueue is:22095
[103891.133903] the value of the wait_queue_num is :3
[103891.133905] out my_init.
[103891.134519] in the kernel thread function!
[103891.134522] the current pid is:22094
[103891.134525] out the kernel thread function
[103891.134621] in the kernel thread function!
[103891.134623] the current pid is:22095
[103891.134625] out the kernel thread function
[103891.134679] in the kernel thread function!
[103891.134681] the current pid is:22093
[103891.134683] out the kernel thread function

  

------------------------------------------------------------------------------

现在便有了一个真真切切的队列,作为进程调度的东西,离不开休眠、唤醒、休眠、唤醒……智商为正的大伙都能看出来:这玩意是成对儿的嘛。函数的设计也确实如此。

int my_function(void * argc)
{
    printk("<0>the state of the real_parent is :%ld\n",current->real_parent->state);
    __wake_up(&head,TASK_ALL,0,NULL);   //唤醒
    printk("<0>the state of the real_parent after __wake_up is :%ld\n",current->real_parent->state);
    printk("<0>out the kernel thread function\n");
    return 0;
}
 
static int __init my_init(void)
{
    int result=0;
    long left_time=0;
    wait_queue_t data;
 
    printk("<0>into my_init.\n");
    result=kernel_thread(my_function, NULL, CLONE_KERNEL);
             init_waitqueue_head(&head);
    init_waitqueue_entry(&data, current);
    add_wait_queue(&head, &data);
 
    left_time=sleep_on_timeout(&head, 100);     //休眠
    printk("<0>the return result of the sleep_on_timeout is:%ld\n", left_time);
 
    printk("<0>out my_init.\n");
    return 0;
}

主进程生了个子进程后,将自己放入队列里,睡午觉去了。

子进程运行过程中唤醒老爸,于是乎,闹钟还未响却被孩子吵醒,如此看来,带孩子的男人确实不容易a。

  

当然,从这个sleep_on_timeout的参数也能看出,这一睡就睡了整个一个队列。我想单独控制队列个别元素该怎么办?

static int __init my_init(void)
{  
    int result, result1;   
    int  wait_queue_num=0;
    wait_queue_head_t head;
    wait_queue_t data, data2, *curr, *next;
 
    printk("<0>into my_init.\n");        
             
    result1=kernel_thread(my_function,NULL,CLONE_KERNEL);
    result2=kernel_thread(my_function,NULL,CLONE_KERNEL);
 
    struct pid * kpid1 = find_get_pid(result);
    struct task_struct * task1 = pid_task(kpid1,PIDTYPE_PID);
    struct pid * kpid2 = find_get_pid(result1);
    struct task_struct * task2 = pid_task(kpid2,PIDTYPE_PID);
 
    init_waitqueue_head(&head);
 
    init_waitqueue_entry(&data1, task1);
    data.task_list.next=&data1.task_list;    //这里注意了,要使用prepare_to_wait,这里就必须这样初始化即将插入的队列元素
    printk("<0>the state of the current thread is:%ld\n",current->state);
    prepare_to_wait(&head, &data1, 130);  //插入队列后,同时也将当下的进程的状态改为了130
    printk("<0>the state of the current thread is:%ld\n",current->state);
 
    init_waitqueue_entry(&data2, task2);
    data2.task_list.next=&data2.task_list;
    prepare_to_wait_exclusive(&head, &data2, 2);    //又插了一个
    printk("<0>the state of the current thread is:%ld\n",current->state);
     
    list_for_each_entry_safe(curr, next, &(head.task_list), task_list)
    {
        wait_queue_num++;
    }      
    printk("<0>the value of the wait_queue_num is :%d\n",wait_queue_num);    //打印后发现,队列里确实有两个元素
    finish_wait(&head,&data2);
    printk("<0>the state of the current thread is:%ld\n",current->state);
 
    wait_queue_num=0;
    list_for_each_entry_safe(curr, next, &(head.task_list), task_list)
    {
        wait_queue_num++;
    }
    printk("<0>the value of the wait_queue_num is :%d\n",wait_queue_num);        //删掉一个后,当然就剩下一个咯
 
    printk("<0>out my_init.\n");
    return 0;
}

首先先解释下什么是130,当然就是128+2啦,对应的第二位和第八位。也就是说内核用一位代表了一种进程状态。在这里,

2:    TASK_INTERRUPTIBLE         128: TASK_WAKEKILL

2.6.32的版本,设计了九种状态,咋是九种?不是只有八位不?——@##% ,不是还有个零么!

  

当然,内核api里还有许多函数会对队列进行更精细的操作,比如 wake_up_process,sleep_on_interrupt_timeout 什么的,看多了,也就发现其中有着相当的规律,尤其是函数名字的命名方式,这样,对内核api的学习也算是一点点慰藉。

posted @   郝壹贰叁  阅读(604)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示