LDD3学习9--数据类型和定时器

这部分对应的是第七章和第十一章,因为内容也不是很多,就一起写了。里面的内容基本上就是一个个的点,所以也就一个个点简单总结一下。

1 数据类型

1.1 数据长度

不同操作系统类型长度可能不一样,看图的话最好用u8,u16,u32,u64。

内存页最好使用PAGE_SIZE,而不要使用4K,因为很多平台可能不是4K。

1.2 字节序大小端

这个在网络编程涉及到也很多,用的时候查一下就知道了。

1.3 数据对齐

书里面说的是最后加一个__attribute__ ((packed)) scsi;,这个是取消对齐,不过我记得在一般常用的时候,都是手动指定对齐:

cpp 复制代码
struct BitFieldStruct {
    unsigned int a : 4;  // 占用 4 位
    unsigned int b : 3;  // 占用 3 位
    unsigned int c : 1;  // 占用 1 位
};

具体用的时候再看吧。。

1.4 判断指针

不要用NULL,用ERR_PTR,IS_ERR,PTR_ERR。

1.5 链表

这个不用自己搞,用内核里面的<linux/list.h>

2 定时器

内核通过定时器中断来跟踪时间的流动,大部分平台运行在 100 或者 1000 中断每秒; 流行的 x86 PC 缺省是 1000。

2.1 定时器

一般用的是 jiffies定时器,是在<linux/jiffies.h>。用法就不多写了,要用的时候搜一下或者GPT,答案都很标准。

高进度的定时器,可以用TSC,例子是:

cpp 复制代码
unsigned long ini, end;
rdtscl(ini); rdtscl(end);
printk("time lapse: %li\n", end - ini); 

两者的区别:

特性 TSC 定时器 jiffies 定时器
依赖硬件 依赖 CPU 硬件支持 不依赖硬件,完全由内核实现
精度 纳秒级 毫秒级
性能 非常高效 开销较小,但需依赖时钟中断
多核一致性 可能存在问题 无多核一致性问题
功耗 较高(高频访问可能增加功耗) 较低(只在时钟中断时更新)
时间跨度 通常不适合长时间跨度 可用于长时间跨度(只需考虑溢出)
典型场景 高精度时间戳,性能测量,延迟计算 调度、内核延迟、一般计时需求

2.2 当前时间

获取当前时间,时间戳,这些和应用层好像差不多,就不多说了。

延迟,在应用层,基本上就是一个sleep打天下。在内核好像东西多了不少。

long wait_event_interruptible_timeout(wait_queue_head_t *q, condition, signed long timeout);这个是用在条件变量。

signed long schedule_timeout(signed long timeout);这个会让当前任务进行休眠,但是会被唤醒,比如信号量。

例子:

cpp 复制代码
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/delay.h>

void example_function(void) {
    long timeout = msecs_to_jiffies(100); // 将100毫秒转换为jiffies
    set_current_state(TASK_INTERRUPTIBLE); // 设置当前任务状态为可中断睡眠
    schedule_timeout(timeout); // 让当前任务睡眠指定的时间
}

ndelay,udelay,mdelay。这几个都是让CPU空转,会占用很多CPU资源。所以只能用在短时间。

msleep。基于调度器调度,不会占用太多CPU资源。

2.3 内核定时器

定义是在<linux/timer.h>,让内核在指定时间后执行某个任务,某个事件或函数。通过timer_list结构,使用 init_timer() 或 timer_setup() 初始化定时器。 使用 add_timer() 启动定时器。 使用 del_timer() 删除定时器。

例子:

cpp 复制代码
void setup_my_timer(void) {
    timer_setup(&my_timer, my_timer_callback, 0);
    my_timer.expires = jiffies + msecs_to_jiffies(1000); // 设置定时1秒
    add_timer(&my_timer);
}

2.4 Tasklets机制

Tasklets 是 Linux 内核中一种轻量级的底半部(Bottom Half)机制,专门用于在软中断(SoftIRQ)上下文中执行延迟处理任务。它可以延迟执行某些非时间敏感的任务,而不会阻塞中断处理程序(Top Half)。

这里有一个例子:

cpp 复制代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Tasklet Example");

// Tasklet 函数
void my_tasklet_func(unsigned long data) {
    printk(KERN_INFO "Tasklet executed! Data: %lu\n", data);
}

// 定义 Tasklet,初始化时指定执行函数和参数
DECLARE_TASKLET(my_tasklet, my_tasklet_func, 42);

// 模块加载时调用
static int __init tasklet_example_init(void) {
    printk(KERN_INFO "Tasklet example module loaded.\n");

    // 调度 Tasklet
    tasklet_schedule(&my_tasklet);
    printk(KERN_INFO "Tasklet scheduled.\n");

    return 0;
}

// 模块卸载时调用
static void __exit tasklet_example_exit(void) {
    // 确保 Tasklet 在卸载前被销毁
    tasklet_kill(&my_tasklet);
    printk(KERN_INFO "Tasklet example module unloaded.\n");
}

module_init(tasklet_example_init);
module_exit(tasklet_example_exit);

运行结果如下:

bash 复制代码
[553563.141606] Tasklet example module loaded. //insmod
[553563.141615] Tasklet scheduled.
[553563.141618] Tasklet executed with data: 42
[553595.066381] Tasklet example module unloaded. //rmmod

看看代码就基本明白,tasklet其实就是将任务提交给CPU调度。比如说收到一个网络包,中断处理中收到包,之后还有繁琐的解包操作,如果还是占用中断,会阻塞其它任务。这个是可以调用tasklet来处理,不用全部占用CPU,提高系统整体性能。

这样中断的前半部分就是硬中断,后半部分就是软中断,tasklet这些。

例子:

cpp 复制代码
irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    // 检查中断源是否来自预期设备
    if (check_device_irq(irq)) {
        // 读取设备状态寄存器
        unsigned int status = read_device_status();
        // 清除中断标志
        clear_device_irq(irq);
        // 做一些简单的处理,如将数据从设备缓冲区拷贝到内存的临时位置
        copy_data_from_device();
        // 触发中断下半部处理
        schedule_delayed_work(&my_work, msecs_to_jiffies(10));
        return IRQ_HANDLED;
    }
    return IRQ_NONE;
}

2.5 工作队列

在#include <linux/workqueue.h>中。create_workqueue,create_singlethread_workqueue,DECLARE_WORK,INIT_WORK,PREPARE_WORK。。。

说实话之前也没用过这个,查了一下,本质上就是优先级更低的tasklet,可以被内核调度,适用于耗时更长,可以阻塞的任务。不过貌似现在要被kthread替代了。

最后:之前知乎看到一篇写定时器的,写的哇塞:C/C++中如何稳定地每隔5ms执行某个函数?

相关推荐
鹿屿二向箔25 分钟前
搭建一个基于Spring Boot的书籍学习平台
spring boot·后端·学习
有梦想有行动1 小时前
kafka学习
学习
别致的影分身1 小时前
Linux网络 TCP socket
linux·网络·tcp/ip
Kasen's experience2 小时前
Ubuntu linux 文件权限
linux·ubuntu
命里有定数2 小时前
Ubuntu -- 几行命令使用Ollama部署本地AI大模型, 仅调用api, 快速测试api效果
linux·运维·ubuntu·ollama
V丶Chao2 小时前
Syncthing在ubuntu下的安装使用
linux·运维·ubuntu
大龙@、2 小时前
Ubuntu22.4挂载大于2.2T磁盘(27T大磁盘)
linux·服务器·前端
三月七(爱看动漫的程序员)2 小时前
Active Prompting with Chain-of-Thought for Large Language Models
数据库·人工智能·深度学习·学习·语言模型·自然语言处理
jiuri_12152 小时前
Linux UDP 编程详解
linux·udp
半路程序员2 小时前
kubernetes学习-Service(七)
学习·容器·kubernetes