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执行某个函数?

相关推荐
Apipi*4 小时前
30天速通Linux 第六章信号及信号处理
linux·运维·信号处理
敬往事一杯酒哈5 小时前
海康 AGV 导航读码器学习
学习
天启HTTP5 小时前
爬虫防封核心原理:IP轮换机制与风控规避逻辑
linux·服务器·网络·爬虫·tcp/ip
qq_284274057 小时前
机械原理笔记:平面机构自由度计算(复合铰链、局部自由度、虚约束)与四杆机构入门(含考点)
笔记·学习·平面·自动化·制造
广州浮点FLOATLIC7 小时前
许可证服务器迁移后软件打不开:研发 IT 怎样定位连接问题
linux·服务器·数据库
qq_284274058 小时前
数控铣床与加工中心笔记:铣刀与刀柄系统、G54试切对刀、镜像加工指令、孔加工工艺(钻扩铰镗)与固定循环G73/G83/G81/G76/G87/G84
笔记·学习·自动化·制造
BLUcoding8 小时前
接口服务公网超时排查记录:SecureRandom 阻塞问题分析与解决
java·linux·springboot·aes·securerandom
AIgorithmGEEK8 小时前
[Linux]从手写报头到内核套路:序列化、反序列化与自定义协议全链路
linux·运维·服务器·网络·序列化·反序列化
Nil2088 小时前
leetcode 139单词拆分
linux·运维·服务器
Starry-sky(jing)8 小时前
BUG: unable to handle kernel paging request 完整排查:dmesg 四要素与三路定罪
linux·运维·服务器·内核·排障