Linux内核API —— __wake_up

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数原型:

    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。


### ICM42607传感器在Linux驱动移植后的数据读取方法 ICM42607 是 Bosch 推出的一款高性能六轴惯性测量单元(IMU),支持加速度计和陀螺仪功能。为了正确读取其数据,在完成 Linux 驱动移植后,通常需要遵循以下逻辑流程。 #### 1. 初始化设备 初始化过程包括配置硬件接口(如 SPI 或 I&sup2;C)、设置寄存器参数以及启用传感器的功能模块。对于 ICM42607,可以通过调用 `inv_icm42607_init` 类似的函数来完成初始化操作[^2]: ```c int inv_icm42607_init(struct icm42607_device *dev) { // 设置通信协议(SPI/I2C) dev->bus_type = BUS_TYPE_SPI; // 配置芯片使能状态 if (inv_icm42607_write_reg(dev, REG_PWR_MGMT_1, BIT_H_RESET)) { return -EIO; } // 启用加速计和陀螺仪 if (inv_icm42607_set_accel_gyro_enable(dev, true)) { return -EIO; } return 0; } ``` 此代码片段展示了如何通过写入特定寄存器位来重置并启动 IMU 设备。 --- #### 2. 数据读取机制 一旦设备被成功初始化,就可以利用定义好的 API 来获取传感器的数据流。具体来说,可以借助类似于 `inv_icm42607_read_data` 的函数实现数据采集[^2]: ```c int inv_icm42607_read_data(struct icm42607_device *dev, struct sensor_event *event) { uint8_t buffer[6]; // 存储原始数据 // 执行批量读取命令 if (inv_icm42607_read_regs(dev, REG_ACCEL_XOUT_H, buffer, sizeof(buffer))) { return -EIO; } // 解析缓冲区中的高字节与低字节组合成实际数值 event->accel.x = ((buffer[0] << 8) | buffer[1]); event->accel.y = ((buffer[2] << 8) | buffer[3]); event->accel.z = ((buffer[4] << 8) | buffer[5]); return 0; } ``` 上述例子说明了从指定地址范围提取加速度信息的过程,并将其转换为可理解的形式存储到结构体变量中。 --- #### 3. 处理中断信号 如果希望实时捕获事件触发情况下的变化,则需注册相应的中断处理程序。这一步骤涉及修改 GPIO 引脚属性以便接收外部通知消息[^1]: ```c static irqreturn_t icm42607_interrupt_handler(int irq, void *context) { struct icm42607_device *dev = context; // 更新内部标志量表示新样本可用 atomic_set(&dev->new_sample_flag, 1); wake_up_interruptible(&dev->wait_queue); return IRQ_HANDLED; } void setup_interrupt_handling(struct icm42607_device *dev) { request_irq(dev->irq_number, icm42607_interrupt_handler, IRQF_TRIGGER_RISING, "icm42607-interrupt", dev); } ``` 这里展示了一个简单的中断服务例程设计思路——当检测到来自目标器件发出的脉冲时唤醒等待队列上的进程继续执行后续任务。 --- #### 4. 用户空间交互方式 最终目的是让应用程序能够方便地访问底层硬件资源而无需关心复杂的细节问题。因此还需要提供一套完整的字符设备节点管理方案供上层调用[^3]: ```python import fcntl import array def read_sensor_data(device_file='/dev/icm42607'): with open(device_file, 'rb') as fd: data_buffer = array.array('B', [0]*6) # 发送控制指令至内核驱动 fcntl.ioctl(fd.fileno(), IOCTL_READ_SENSOR_DATA_CMD, data_buffer.buffer_info()[0]) accel_x = (data_buffer[0] << 8) | data_buffer[1] accel_y = (data_buffer[2] << 8) | data_buffer[3] accel_z = (data_buffer[4] << 8) | data_buffer[5] return {'x': accel_x, 'y': accel_y, 'z': accel_z} sensor_values = read_sensor_data() print(f"Acceleration values: X={sensor_values['x']}, Y={sensor_values['y']}, Z={sensor_values['z']}") ``` 该 Python 脚本演示了怎样通过 ioctl 系统调用来请求来自定制化字符设备文件描述符关联的对象所返回的结果集。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值