• linux内核等待队列wait_queue_head_t


    前言

            头文件        

    #include 

    定义并初始化

    1. wait_queue_head_t r_wait;
    2. init_waitqueue_head(&cm_dev->r_wait);

            wait_queue_head_t 表示等待队列头,等待队列wait时,会导致进程或线程被休眠,一个等待队列头中可以有很多的等待队列元素。每个元素绑定一个进程或者线程。这里绑定进程或者线程的目的,是为了在执行wakeup时,知道应该唤醒谁。

            定义并初始化等待队列元素        

    wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait, current);
    1. /*
    2. * Macros for declaration and initialisaton of the datatypes
    3. */
    4. #define __WAITQUEUE_INITIALIZER(name, tsk) { \
    5. .private = tsk, \
    6. .func = default_wake_function, \
    7. .task_list = { NULL, NULL } }

    Core.c (kernel\sched) 

    1. int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags,
    2. void *key)
    3. {
    4. return try_to_wake_up(curr->private, mode, wake_flags);
    5. }
    6. EXPORT_SYMBOL(default_wake_function);

    宏current

    这是一个struct task_struct * 类型的变量。放在线程里,用于获取当前线程的指针。下面这两个函数虽然没有给它传递struct task_struct *参数,但是它却知道当前线程的指针,就是因为他们内部实现都调用了current。

    1. bool kthread_should_stop(void);
    2. bool kthread_should_park(void);
    1. /**
    2. * kthread_should_stop - should this kthread return now?
    3. *
    4. * When someone calls kthread_stop() on your kthread, it will be woken
    5. * and this will return true. You should then return, and your return
    6. * value will be passed through to kthread_stop().
    7. */
    8. bool kthread_should_stop(void)
    9. {
    10. return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)->flags);
    11. }
    1. /**
    2. * kthread_should_park - should this kthread park now?
    3. *
    4. * When someone calls kthread_park() on your kthread, it will be woken
    5. * and this will return true. You should then do the necessary
    6. * cleanup and call kthread_parkme()
    7. *
    8. * Similar to kthread_should_stop(), but this keeps the thread alive
    9. * and in a park position. kthread_unpark() "restarts" the thread and
    10. * calls the thread function again.
    11. */
    12. bool kthread_should_park(void)
    13. {
    14. return test_bit(KTHREAD_SHOULD_PARK, &to_kthread(current)->flags);
    15. }

    一 相关操作

    将等待队列元素加入等待队列头,这个和list_add做相同理解也可以。

    1. void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
    2. {
    3. unsigned long flags;
    4. wait->flags &= ~WQ_FLAG_EXCLUSIVE;
    5. spin_lock_irqsave(&q->lock, flags);
    6. __add_wait_queue(q, wait);
    7. spin_unlock_irqrestore(&q->lock, flags);
    8. }

    将wait从等待队列中删除。

    1. void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
    2. {
    3. unsigned long flags;
    4. spin_lock_irqsave(&q->lock, flags);
    5. __remove_wait_queue(q, wait);
    6. spin_unlock_irqrestore(&q->lock, flags);
    7. }

    设置当前进程或者线程的状态

    set_current_state是有内存屏障的功能。__set_current_state不具备内存屏障功能。

    state_value可取值:

    #define TASK_RUNNING        0
    #define TASK_INTERRUPTIBLE    1
    #define TASK_UNINTERRUPTIBLE    2

    1. /*
    2. * set_current_state() includes a barrier so that the write of current->state
    3. * is correctly serialised wrt the caller's subsequent test of whether to
    4. * actually sleep:
    5. *
    6. * set_current_state(TASK_UNINTERRUPTIBLE);
    7. * if (do_i_need_to_sleep())
    8. * schedule();
    9. *
    10. * If the caller does not need such serialisation then use __set_current_state()
    11. */
    12. #define __set_current_state(state_value) \
    13. do { current->state = (state_value); } while (0)
    14. #define set_current_state(state_value) \
    15. set_mb(current->state, (state_value))

            函数schedule

    asmlinkage void schedule(void);

            这个函数声明前面有个asmlinkage ,这个参数网上说是用于规定传参是通过寄存器传输还是堆栈传输的。我在Linkage.h (include\linux) 中也看到了如下的宏定义,这些对于本次的实验都没有影响。        

    1. #ifdef __cplusplus
    2. #define CPP_ASMLINKAGE extern "C"
    3. #else
    4. #define CPP_ASMLINKAGE
    5. #endif
    6. #ifndef asmlinkage
    7. #define asmlinkage CPP_ASMLINKAGE
    8. #endif

            signal_pending函数,给他传递current参数。

    1. static inline int signal_pending(struct task_struct *p)
    2. {
    3. return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING));
    4. }

    二 阶段测试

    测试线程代码:

            该段代码是一个大模块中的一小段测试代码,该线程中从ready_list中取出内存节点,然后给他赋值,然后放入ok_list链表,直到ready_list链表变成空链表为止。因为暂时没有调用wakeup给他唤醒。所以当ready_list为空时,它一定会处于阻塞的状态。

            这里需要注意的一点是,运行schedule函数后,如果执行kthread_stop停止该线程,signal_pending返回值也是0,在此后要调用kthread_should_stop来判断线程是否可以停止运行。

            在判断ret值非0后,是否continue,还是执行其他操作,这个也要具体情况具体分析,避免锁死在这里。

    1. static int
    2. cm_thread_1(void *arg)
    3. {
    4. int ret = 0;
    5. int send_count = 0;
    6. struct buf_node *pnode;
    7. struct cm_dev_ *cm_dev = (struct cm_dev_ *)arg;
    8. wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait, current);
    9. add_wait_queue(&cm_dev->w_wait,&wait);
    10. while(1){
    11. if(kthread_should_stop()){
    12. DEBUG_CM("exit thread");
    13. break;
    14. }
    15. while(cm_list_size(&cm_dev->ready_list) == 0){
    16. set_current_state(TASK_INTERRUPTIBLE);
    17. DEBUG_CM("start schedule");
    18. schedule();
    19. DEBUG_CM("end schedule");
    20. ret = signal_pending(current);
    21. if(ret){
    22. DEBUG_CM("ret = %d",ret);
    23. continue;
    24. }
    25. //执行kthread_stop函数后,也会调度该线程,此时signal_pending也是0
    26. if(kthread_should_stop()){
    27. DEBUG_CM("exit thread");
    28. goto end_thread_1;
    29. }
    30. }
    31. ++send_count;
    32. DEBUG_CM("send a string:%d",send_count);
    33. pnode = pop_list(&cm_dev->ready_list);
    34. sprintf(pnode->buf,"send_count:%d",send_count);
    35. list_add_tail(&pnode->node,&cm_dev->ok_list);
    36. wake_up_interruptible(&cm_dev->r_wait);
    37. }
    38. end_thread_1:
    39. remove_wait_queue(&cm_dev->w_wait,&wait);
    40. return 0;
    41. }

    运行结果

            从输出结果可知,调用完schedule()函数后,线程就开始睡觉了。而不是调用set_current_state以后才开始睡觉的。从set_current_state的函数实现可知,set_current_state只是设置了flags的标志位

    1. root@hehe:~# insmod csi_buf.ko
    2. [ 27.546429] /big/csi_driver/csi_buf/csi_buf.c:cm_init:192: ready_list size = 5
    3. [ 27.553906] /big/csi_driver/csi_buf/csi_buf.c:cm_init:193: ok_list size = 0
    4. [ 27.561109] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:145: send a string:1
    5. [ 27.569831] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:145: send a string:2
    6. [ 27.578217] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:145: send a string:3
    7. [ 27.585521] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:145: send a string:4
    8. [ 27.592757] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:145: send a string:5
    9. [ 27.600136] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:135: start schedule
    10. /*睡眠在这里*/

    此时执行 ps aux,看到我们的内核线程在睡觉,如下

    1. root@hehe:~# ps aux | grep cm_thread_1
    2. root 569 0.0 0.0 0 0 ? S 01:44 0:00 [cm_thread_1]
    3. root 575 0.0 0.3 3596 1648 ttymxc0 S+ 01:46 0:00 grep cm_thread_1
    4. root@hehe:~#

    执行rmmod xxx.ko卸载该模块

    1. root@hehe:~# rmmod csi_buf.ko
    2. [ 38.326944] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:137: end schedule
    3. [ 38.334002] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:145: exit thread
    4. [ 38.340905] /big/csi_driver/csi_buf/csi_buf.c:cm_exit:240: tsks_1 stop
    5. [ 38.347628] /big/csi_driver/csi_buf/csi_buf.c:cm_exit:247: cm_exit ok
    6. root@hehe:~#

            卸载函数中的代码如下:从上面的执行结果可知,执行kthread_stop后schedule函数返回0,然后判断kthread_should_stop,可以停止,然后结束线程。

    1. kthread_stop(cm_dev->tsks_1);
    2. DEBUG_CM("tsks_1 stop");

    三 唤醒相关操作

    wake_up系列宏

    1. #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
    2. #define wake_up_nr(x, nr) __wake_up(x, TASK_NORMAL, nr, NULL)
    3. #define wake_up_all(x) __wake_up(x, TASK_NORMAL, 0, NULL)
    4. #define wake_up_locked(x) __wake_up_locked((x), TASK_NORMAL, 1)
    5. #define wake_up_all_locked(x) __wake_up_locked((x), TASK_NORMAL, 0)
    6. #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
    7. #define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
    8. #define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
    9. #define wake_up_interruptible_sync(x) __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)

    本次实验,我们使用wake_up_interruptible。

    二 测试实例

    线程1,从ready_list中取出节点,然后写数据,然后放入ok_list。

    线程2,从ok_list中取出数据,输出,然后将节点放ready_list

    1. static int
    2. cm_thread_1(void *arg)
    3. {
    4. int ret = 0;
    5. int send_count = 0;
    6. struct buf_node *pnode;
    7. struct cm_dev_ *cm_dev = (struct cm_dev_ *)arg;
    8. wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait, current);
    9. DEBUG_CM("");
    10. add_wait_queue(&cm_dev->w_wait,&wait);
    11. while(1){
    12. DEBUG_CM("");
    13. if(kthread_should_stop()){
    14. DEBUG_CM("exit thread");
    15. break;
    16. }
    17. DEBUG_CM("");
    18. while(cm_list_size(&cm_dev->ready_list) == 0){
    19. set_current_state(TASK_INTERRUPTIBLE);
    20. schedule();
    21. ret = signal_pending(current);
    22. if(ret){
    23. DEBUG_CM("ret = %d",ret);
    24. continue;
    25. }
    26. //执行kthread_stop函数后,也会调度该线程,此时signal_pending也是0
    27. if(kthread_should_stop()){
    28. DEBUG_CM("exit thread");
    29. goto end_thread_1;
    30. }
    31. }
    32. ++send_count;
    33. DEBUG_CM("send a string:%d",send_count);
    34. if(cm_list_size(&cm_dev->ready_list) > 0){
    35. pnode = pop_list(&cm_dev->ready_list);
    36. sprintf(pnode->buf,"send_count:%d",send_count);
    37. list_add_tail(&pnode->node,&cm_dev->ok_list);
    38. wake_up_interruptible(&cm_dev->r_wait);
    39. }else{
    40. DEBUG_CM("ready_list size == 0");
    41. }
    42. }
    43. end_thread_1:
    44. remove_wait_queue(&cm_dev->w_wait,&wait);
    45. return 0;
    46. }
    47. static int
    48. cm_thread_2(void *arg)
    49. {
    50. int ret = 0;
    51. int send_count = 0;
    52. struct buf_node *pnode;
    53. struct cm_dev_ *cm_dev = (struct cm_dev_ *)arg;
    54. wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait, current);
    55. add_wait_queue(&cm_dev->r_wait,&wait);
    56. while(1){
    57. if(kthread_should_stop()){
    58. DEBUG_CM("exit thread");
    59. break;
    60. }
    61. while(cm_list_size(&cm_dev->ok_list) == 0){
    62. set_current_state(TASK_INTERRUPTIBLE);
    63. schedule();
    64. ret = signal_pending(current);
    65. if(ret){
    66. DEBUG_CM("ret = %d",ret);
    67. continue;
    68. }
    69. //执行kthread_stop函数后,也会调度该线程,此时signal_pending也是0
    70. if(kthread_should_stop()){
    71. DEBUG_CM("exit thread");
    72. goto end_thread_2;
    73. }
    74. }
    75. ++send_count;
    76. if(cm_list_size(&cm_dev->ok_list) > 0){
    77. DEBUG_CM("send a string:%d",send_count);
    78. pnode = pop_list(&cm_dev->ok_list);
    79. DEBUG_CM("pnode->buf = %s",pnode->buf);
    80. list_add_tail(&pnode->node,&cm_dev->ready_list);
    81. wake_up_interruptible(&cm_dev->w_wait);
    82. }else{
    83. DEBUG_CM("ready_list size == 0");
    84. }
    85. }
    86. end_thread_2:
    87. remove_wait_queue(&cm_dev->r_wait,&wait);
    88. return 0;
    89. }

    三 测试结果

    截取一段运行结果:

    1. [ 432.577454] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:200: pnode->buf = send_count:5258
    2. [ 432.585847] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:152: send a string:5259
    3. [ 432.593387] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:198: send a string:5259
    4. [ 432.600879] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:200: pnode->buf = send_count:5259
    5. [ 432.609265] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:131:
    6. [ 432.615217] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:136:
    7. [ 432.621145] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:152: send a string:5260
    8. [ 432.628686] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:198: send a string:5260
    9. [ 432.636204] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:131:
    10. [ 432.642132] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:136:
    11. [ 432.648086] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:200: pnode->buf = send_count:5260
    12. [ 432.656472] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:152: send a string:5261
    13. [ 432.664001] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:198: send a string:5261
    14. [ 432.671492] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:200: pnode->buf = send_count:5261
    15. [ 432.679875] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:131:
    16. [ 432.685825] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:136:
    17. [ 432.691755] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:152: send a string:5262
    18. [ 432.699340] /big/csi_driver/csi_buf/csi_buf.c:cmcsi_buf/csi_buf.c:cm_thread_1:152: send a string:5284
    19. [ 433.476323] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:198: send a string:5284
    20. [ 433.483846] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:131:
    21. [ 433.489777] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_1:136:
    22. [ 433.495737] /big/csi_driver/csi_buf/csi_buf.c:cm_thread_2:200: pnode->buf = send_count:5284

    结束

  • 相关阅读:
    线上展厅设计步骤
    Go语言网络编程(socket编程)TCP
    中间件漏洞(redis)
    基础线段树
    工厂方法模式-原理解析-逐步构建-java实战
    【Linux】线程池
    Redis主从复制集群的介绍及搭建
    数据结构(严蔚敏版)第一章——复数的实现
    .NET 简介:跨平台、开源、高性能的开发平台
    pytorch中的池化函数
  • 原文地址:https://blog.csdn.net/yueni_zhao/article/details/127401165