__wake_up函数功能描述:此函数用于唤醒等待队列中处于特定状态的进程,此特定状态是此函数的第二个参数mode定义的。当进程的状态满足此特定状态时就有可能被唤醒,获得CPU资源,从而被调度执行。
__wake_up文件包含
#include <linux/wait.h>
__wake_up函数定义
在内核源码中的位置:linux-3.19.3/kernel/sched/wait.c
函数定义格式:
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key)
__wake_up输入参数说明
此函数的第一个输入参数是wait_queue_head_t类型的指针,代表等待队列的头指针,其定义见文件linux-3.19.3/include/linux/wait.h,如下:
typedef struct __wait_queue_head wait_queue_head_t;
此类型等价于__wait_queue_head, __wait_queue_head的定义如下:
struct __wait_queue_head {
spinlock_t lock; /*资源访问锁*/
struct list_head task_list; /*等待队列链表元素*/
};
其中struct list_head的定义见文件linux-3.19.3/include/linux/types.h,如下:
struct list_head {
struct list_head *next, *prev;
};
字段next和prev分别指向等待队列链表的下一个和前一个元素。
此函数的第二个参数mode是无符号的整型变量,代表能够被唤醒的进程所处的状态,即只有处于此状态的进程才能够被唤醒,可能的取值为宏TASK_NORMAL和TASK_ALL,此二者的定义见文件linux-3.19.3/include/linux/sched.h,如下:
#define TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
#define TASK_ALL (TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED)
其中TASK_NORMAL代表唤醒等待队中处于可中断的等待状态的进程及处于不可中断的等待状态的进程;TASK_ALL代表唤醒处于TASK_NORMAL状态的进程及处于暂停状态和跟踪状态的进程。
此函数的第三个参数是一个整型变量,代表唤醒等待队列中进程的个数,当此参数小于等于0时,此函数能够唤醒所有满足第二个参数条件的进程;当此参数大于0时,函数执行中止有两种可能,第一种等待队列扫描完毕,即可唤醒的进程已被完全唤醒,第二种比较复杂如下所示:
(curr->func(curr, mode, sync, key) &&(flags & WQ_FLAG_EXCLUSIVE)&& ! --nr_exclusive)
当此条件为真时唤醒进程结束,即curr->func( )
返回值非0, f lags为WQ_FLAG_EXCLUSIVE, nr_exclusive值为1时进程唤醒结束。其中curr->func( )
代表唤醒进程的函数,在此为default_wake_function( ),返回值为0或1, f lags是wake_queue_t的字段,代表是否是高优先级进程,值一般为0或1。
此函数的第四个参数是一个void型的指针变量,代表唤醒进程时执行的函数,一般传递NULL。
__wake_up返回参数说明
此函数的返回结果是一个void型的变量,即没有任何返回结果。
__wake_up实例解析
编写测试文件:__wake_up.c
头文件引用及全局变量定义:
/*头文件引用*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <linux/delay.h>
MODULE_LICENSE("GPL");
/*全局变量定义*/
static wait_queue_head_t head; //等待队列头元素
struct task_struct * old_thread; //保存进程描述符信息
子进程处理函数定义:
int my_function(void * argc)
{
printk("in the kernel thread function! \n");
printk("the current pid is:%d\n", current->pid); //显示当前进程的PID值
// 显示init进程的状态
printk("the state of the init function is :%ld\n", old_thread->state);
//__wake_up(&head, TASK_ALL,0, NULL); //调用函数唤醒等待队列中的进程
//显示函数__wake_up( )调用之后的init进程的状态
printk("the state of the init function after __wake_up is :%ld\n", old_thread->state);
printk("out the kernel thread function\n");
return 0;
}
模块加载函数定义:
static int __init __wake_up_init(void)
{
char namefrm[]="__wake_up.c%s"; //线程的输出类型名,在此程序中无影响
long time_out;
struct task_struct * result; //进程信息结构体变量
wait_queue_t data; //等待队列元素
printk("into __wake_up_init.\n");
result=kthread_create_on_node(my_function, NULL, -1, namefrm); //创建新进程
printk("the pid of the new thread is:%d\n", result->pid); //显示新线程的PID值
printk("the current pid is:%d\n", current->pid); //显示当前进程的PID值
init_waitqueue_head(&head); //初始化等待队列头元素
init_waitqueue_entry(&data, current); //用当前进程初始化等待队列中的一个元素
add_wait_queue(&head, &data); //将等待队列元素加入等待队列中
old_thread=current; //记录当前进程的信息
wake_up_process(result); //唤醒新创建的线程
time_out=schedule_timeout_uninterruptible(1000*10); //让当前进程进入睡眠状态
printk("the schedule timeout is:%ld\n", time_out); //显示函数返回结果
printk("out __wake_up_init.\n");
return 0;
}
模块退出函数定义:
static void __exit __wake_up_exit(void)
{
printk("Goodbye __wake_up\n");
}
模块加载、退出函数调用:
module_init(__wake_up_init);
module_exit(__wake_up_exit);
实例运行结果及分析:
在进行测试之前,先把语句“__wake_up(&head, TASK_ALL,0, NULL); ”
注释掉,然后编译模块,执行命令insmod __wake_up.ko插入内核模块,此过程终端会停止一段时间,不会立即返回,大概等待10s。模块插入成功之后,终端会返回到命令模式,输入命令dmesg -c查看模块插入结果,会出现如图A
所示的结果。
取消对语句“__wake_up(&head, TASK_ALL,0, NULL); ”
的注释掉,重新编译模块,执行命令insmod __wake_up.ko插入内核模块,此时终端会立即返回,无需象图A
所示的实验一样等待10s。模块插入成功之后,终端会返回到命令模式,输入命令dmesg -c查看模块插入结果,会出现如下图所示的结果。
结果分析:
由图A
结果可以看出新的进程创建成功,在模块初始化进程被中断之后,新进程开始执行,在创建的新进程中,模块初始化进程的状态值为2,即处于不可中断的等待状态,因为模块初始化进程中执行了函数schedule_timeout_uninterruptible( ),所以第一次实验过程中插入模块之后会等待10s。图B
的实验结果显示,函数__wake_up( )执行之前,模块初始化进程的状态值为2,函数__wake_up( )执行之后,父进程的状态值变为0,并且函数schedule_timeout_uninterruptible( )的返回结果是10000,说明模块初始化进程是被强制唤醒的,而非等待超时唤醒,所以在插入模块之后,不会出现第一次实验的等待10s的结果。由此可以说明函数__wake_up( )能够唤醒等待队列中的满足条件的进程,此条件是进程所处的状态在函数的第二个参数所定义的范围内。
进程状态说明:
进程能够处于的状态在linux-3.19.3内核中有以下几种情况,定义见文件linux-3.19.3/include/linux/sched.h:
1)可运行状态(TASK_RUNNING),处于此状态的进程要么在CPU上执行,要么准备执行,此状态的定义值为0。
2)可中断的等待状态(TASK_INTERRUPTIBLE),处于此状态的进程处于睡眠状态,直到某个条件变为真,进程才有可能被调度执行,函数interruptible_sleep_on( )的作用就是使进程处于此种状态,此状态的定义值为1。
3)不可中断的等待状态(TASK_UNINTERRUPTIBLE),此状态和可中断的等待状态相似,但处于此状态的进程的状态是不能被改变的,将一直处于此状态,函数uninterruptible_sleep_on( )能够使等待队列中的进程进入此种状态,此状态的定义值为2。
4)暂停状态(__TASK_STOPPED),此状态的进程的执行被暂停,当进程接收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU信号后,进入暂停状态,此状态的定义值为4。
5)跟踪状态(__TASK_TRACED),此状态的进程的执行已由debugger程序暂停,当一个进程被另一个进程监控时,任何信号都可以把这个进程置于TASK_TRACED状态,此状态的定义值为8。
6)僵死撤销状态(EXIT_DEAD),也称最终状态,由于父进程刚发出wait4()或waitpid()系统调用,因而进程由系统删除,此状态的定义值为16。
7)僵死状态(EXIT_ZOMBIE),此状态的进程的执行被终止,但是父进程还没有发布wait4( )或waitpid( )系统调用来返回有关死亡进程的信息,即此进程的描述符数据还存在,仍然可被父进程使用,此状态的定义值为32。
8)退出跟踪(EXIT_TRACE),此状态是一个复合状态,是新内核增加的新状态,定义如下:
#define EXIT_TRACED (EXIT_ZOMBIE | EXIT_DEAD)
9)死亡状态(TASK_DEAD),进程死亡进入的状态,函数do_exit( )使进程进入此状态,此状态的定义值为64。
10)唤醒杀死状态(TASK_WAKEKILL),此状态是新内核增加的新状态,进程处于此状态与处于TASK_UNINTERRUPTIBLE状态相似,但可以接受致命信号唤醒线程,此状态的定义值是128。
11)唤醒状态(TASK_WAKING),此状态是新内核增加的新状态,进程收到唤醒信号处于唤醒状态,此状态的定义值是256。
12)休眠状态(TASK_PARKED),此状态是新内核增加的新状态,此状态的进程主语休眠状态,此状态的定义值是512。
13)TASK_STATE_MAX状态的定义值是1024,预留,暂时无用。
14)可杀死状态(TASK_KILLABLE),此状态是一个复合状态,是新内核增加的新状态,定义如下:
#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
15)暂停状态(TASK_STOPPED),此状态是一个复合状态,是新内核增加的新状态,定义如下:
#define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED)
16)跟踪状态(TASK_TRACED),此状态是一个复合状态,是新内核增加的新状态,定义如下:
#define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)
17)普通状态(TASK_NORMAL),此状态是一个复合状态,是新内核增加的新状态,定义如下:
#define TASK_NORMAL (TASK_INTERRUPTIBLE |TASK_UNINTERRUPTIBLE)
18)TASK_ALL,此状态是一个复合状态,是新内核增加的新状态,定义如下:
#define TASK_ALL (TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED)
19)TASK_REPORT,此状态是一个复合状态,是新内核增加的新状态,定义如下:
#define TASK_REPORT (TASK_RUNNING | TASK_INTERRUPTIBLE | \
TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \
__TASK_TRACED | EXIT_ZOMBIE | EXIT_DEAD)