Linux内核API —— __wake_up

本文详细介绍了Linux内核中的__wake_up函数,该函数用于唤醒等待队列中的进程。文章通过具体的代码示例展示了如何使用该函数,并分析了其在实际应用中的效果。

函数原型:

    void __wake_up(struct wait_queue_head* q, unsigned int mode, int nr, void* key);

功能:

    此函数用于唤醒等待队列中的处于特定状态中的进程。当该进程的状态为mode时,则有可能被唤醒获得cpu资源,从而被调度执行。

进程状态定义:

    在linux/sched.h中有进程相关的状态定义。

/*
 * Task state bitmask. NOTE! These bits are also
 * encoded in fs/proc/array.c: get_task_state().
 *
 * We have two separate sets of flags: task->state
 * is about runnability, while task->exit_state are
 * about the task exiting. Confusing, but this way
 * modifying one set can't modify the other one by
 * mistake.
 */

/* Used in tsk->state: */
#define TASK_RUNNING                    0
#define TASK_INTERRUPTIBLE              1
#define TASK_UNINTERRUPTIBLE            2
#define __TASK_STOPPED                  4
#define __TASK_TRACED                   8
/* Used in tsk->exit_state: */
#define EXIT_DEAD                       16
#define EXIT_ZOMBIE                     32
#define EXIT_TRACE                      (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_DEAD                       64
#define TASK_WAKEKILL                   128
#define TASK_WAKING                     256
#define TASK_PARKED                     512
#define TASK_NOLOAD                     1024
#define TASK_NEW                        2048
#define TASK_STATE_MAX                  4096

#define TASK_STATE_TO_CHAR_STR          "RSDTtXZxKWPNn"

/* Convenience macros for the sake of set_current_state: */
#define TASK_KILLABLE                   (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED                    (TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED                     (TASK_WAKEKILL | __TASK_TRACED)

#define TASK_IDLE                       (TASK_UNINTERRUPTIBLE | TASK_NOLOAD)

/* Convenience macros for the sake of wake_up(): */
#define TASK_NORMAL                     (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
#define TASK_ALL                        (TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED)

    不同内核会有所区别,具体各状态代表什么意思,大家可以google一下,这里不展开。

    实例Demo:

    __wake_up.c

#include<linux/wait.h>
#include<linux/kthread.h>
#include<linux/list.h>
#include<linux/sched.h>
#include<linux/delay.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("viz");

static int  __init __weakup_test_init(void);
static void __exit __weakup_test_exit(void);

static struct task_struct* old_task;
static struct wait_queue_head head;

int func_weakup(void* args)
{
   printk("func_weakup...\n");
   printk("this thread pid = %\d\n",current->pid);

   //print init status
   printk("init status, old_task->state = %ld\n",old_task->state);

   //__wake_up
   //__wake_up(&head,TASK_ALL,0,NULL);

   printk("after __weak_up, old_task->state = %ld\n",old_task->state);
return 0;
}

static int __init __weakup_test_init(void)
{
   printk("test init...\n");

   char namefrt[] = "__weakup.c:%s";
   long timeout;
   struct task_struct* result;
   struct wait_queue_entry data;
   printk("into weak up...\n");
   result = kthread_create_on_node(func_weakup,NULL,-1,namefrt);
   printk("the new thread pid is : %ld\n",result->pid);
   printk("the current pid is: %ld\n",current->pid);
   init_waitqueue_head(&head);
   init_waitqueue_entry(&data,current);
   add_wait_queue(&head,&data);
   old_task = current;
   //wake_up(&head);
    wake_up_process(result);
   timeout = schedule_timeout_uninterruptible(1000*10);
   printk("sleep timeout = %ld\n",timeout);
   printk("out __weakup_test_init\n");
return 0;
}

static void __exit __weakup_test_exit(void)
{
   printk("test exit...\n");
}

module_init(__weakup_test_init);
module_exit(__weakup_test_exit);

  Makefile

#
# Made from Makefile.org 
#

obj-m += __wake_up.o

编译脚本make.sh

#/bin/sh
ver=`uname -r`
make -C /lib/modules/$ver/build M=$PWD "$@"


分析:

一、先注释__wake_up函数,insmod __wake_up.ko,实践中这个过程需要大概10s时间才能完成,之后dmesg查看log信息。

[ 2087.345261] __wake_up: loading out-of-tree module taints kernel.
[ 2087.345588] __wake_up: module verification failed: signature and/or required key missing - tainting kernel
[ 2087.347020] test init...
[ 2087.347020] into weak up...
[ 2087.347152] the new thread pid is : 5840
[ 2087.347153] the current pid is: 5839
[ 2087.347224] func_weakup...
[ 2087.347226] this thread pid = 5840
[ 2087.347226] init status, old_task->state = 2
[ 2087.347227] after __weak_up, old_task->state = 2
[ 2128.863258] sleep timeout = 0
[ 2128.863261] out __weakup_test_init

二、放开__wake_up函数,执行相同动作,发现insmod立即完成,同时dmesg查看信息。

[ 2323.877494] test init...
[ 2323.877495] into weak up...
[ 2323.877636] the new thread pid is : 6692
[ 2323.877637] the current pid is: 6691
[ 2323.877643] func_weakup...
[ 2323.877643] this thread pid = 6692
[ 2323.877644] init status, old_task->state = 2
[ 2323.877646] after __weak_up, old_task->state = 0
[ 2323.877659] sleep timeout = 10000
[ 2323.877659] out __weakup_test_init

第一个过程,新进程的初始状态是2,即处于不可中断状态,因为__init中调用了schedule_timeout_uninterruptible,所以insmod过程会有10s的等待。

第二个过程,__wake_up执行之前,进程状态为2,执行之后变为0,说明模块初始化进程被强制唤醒,因而insmod的动作并不会等待10s。


你的问题聚焦在如何将 `volcano_ms_slp_wakelock_lock` 和 `unlock` 的行为与 Linux 原有的 `/sys/power/wake_lock` 机制对齐,并确认“持锁唤醒”和“解锁休眠”的具体实现逻辑。我们可以基于传统 Android/Linux wakelock 模型来设计这套接口,同时也可以提出更现代、更安全的替代思路。 --- ### 设计逻辑详解 #### ✅ `volcano_ms_slp_wakelock_create(name, len)` **目的:声明一个可被管理的唤醒源(wakelock)** - 功能类比:`echo name > /sys/power/wake_lock` 中的“注册名字” - 实际操作: - 验证 `name` 合法性(非空、长度 ≤ len) -内核中分配一个 `wake_source` 结构体(或等价对象),以 `name` 标识 - 将其加入全局链表/哈希表管理 - **注意:此时并不激活该锁!只是预注册资源** - 返回值:成功返回句柄或0,失败返回负错误码 > 📌 提示:与直接写 `/sys/power/wake_lock` 不同的是,`create` 只是准备资源,不触发唤醒动作 —— 更安全可控 --- #### ✅ `volcano_ms_slp_wakelock_lock()` **目的:真正让设备保持唤醒状态(防止进入 suspend)** 要实现“保持设备持锁唤醒”,应执行以下步骤: 1. 查找已创建的 wakelock 对象(通过 name 或 handle) 2. 如果尚未 active: - 调用内核 PM 接口激活它,例如: ```c pm_stay_awake(dev); // 或使用 wake_up() ``` 或者使用传统的 `wake_lock()`(如果使用 wake_source 机制) 3. 增加引用计数(支持嵌套 lock) 4. 记录时间戳用于调试 ✅ **效果**:只要有一个 active wakelock 存在,电源管理系统就不会允许系统进入 suspend 状态。 📌 类比行为: ```bash echo my_lock > /sys/power/wake_lock ``` 这会立即创建并激活一个名为 `my_lock` 的唤醒源,阻止休眠。 --- #### ✅ `volcano_ms_slp_wakelock_unlock()` **目的:释放唤醒请求,允许系统休眠** 要实现“解锁后可能进入休眠”,应: 1. 查找对应的 wakelock 2. 引用计数减一 3. 若计数归零: - 调用内核接口释放唤醒状态: ```c pm_relax(dev); // 或 wake_down() ``` - 标记为 inactive 4. 触发一次电源状态评估(如 `pm_wakeup_pending()` + 尝试 schedule suspend) 📌 类比行为: ```bash echo my_lock > /sys/power/wake_unlock ``` ⚠️ 注意:只有当所有 active wakelock 都被 unlock 后,系统才能真正进入 suspend。 --- #### ✅ `volcano_ms_slp_wakelock_destroy()` **目的:彻底销毁这个唤醒源** - 必须确保当前 wakelock 已经处于 unlocked 状态(引用计数为 0) - 否则返回 `-EBUSY` 错误(避免资源泄漏) - 从全局结构中移除 - 释放内存 - 不需要再通知 PM 子系统,因为已经 unlock 📌 相比之下,原生 `/sys/power/wake_unlock` 并没有显式的 destroy 概念 —— 所以这是你接口的优势:更清晰的生命周期管理! --- ### 🔁 对比原生 `/sys/power/{wake_lock,wake_unlock}` 的缺点 | 特性 | 原生 sysfs 接口 | 你的 virtual 接口改进点 | |------|------------------|--------------------------| | 安全性 | 任意进程可随意写入,易滥用 | 可增加权限控制、引用计数 | | 生命周期 | 无 create/destroy,难以追踪 | 显式创建销毁,便于 debug | | 嵌套支持 | 多次 write 同名 → 多次 hold?行为模糊 | 支持引用计数,精确匹配 | | 调试能力 | 需解析 dmesg 或 dump_sysrq | 可提供 debugfs 列出所有 active locks | --- ### 💡 更好的思路建议(现代化方案) 虽然 `/sys/power/wake_lock` 曾广泛使用,但 Google 已在较新内核中弃用它,转而推荐: #### ✅ 使用 `struct wakeup_source` + `pm_wakeup_*` API(推荐) ```c struct wakeup_source *ws; ws = wakeup_source_register(NULL, "my_device_lock"); wakeup_source_prepare(ws); __pm_stay_awake(ws); // lock: 持有唤醒 __pm_relax(ws); // unlock: 释放唤醒 wakeup_source_unregister(ws); // destroy ``` 优点: - 内核原生支持,线程安全 - 支持超时自动释放(`pm_schedule_wakeup_event`) - 可通过 `/d/wakeup_sources` 查看统计信息 #### ✅ 或使用 `dev->power.wakeup`(设备级唤醒源) 适用于驱动模型中的设备,由设备是否活跃决定是否阻止 suspend。 --- ### 总结:你应该怎么做? | 接口 | 应该做什么 | |------|-----------| | `create` | 分配 `wakeup_source` 并注册,不激活 | | `lock` | 调用 `__pm_stay_awake(ws)`,增加 refcount | | `unlock` | 调用 `__pm_relax(ws)`,减少 refcount,可能触发 auto-suspend | | `destroy`| 确保已 unlock 后调用 `wakeup_source_unregister()` | 这样既兼容传统语义,又具备更好的可维护性和安全性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值