鸣涧 发表于 2023-8-17 09:58:32

让 ChatGPT 写一个 Kernel Module [一]

让 ChatGPT 写一个 Kernel Module [一]


笔者最近看到这样一篇文章(https://www.downtowndougbrown.co ... rnel-module-for-me/),原作者让 ChatGPT 写一个内核模块,要求实现的功能是:每 5 秒向控制台打印一句 "Hello world",并且把编译需要的 Makefile 也一起写出来。AI 最开始的实现方法是:创建一个内核线程,线程主体是一个 while 循环,每隔 1 毫秒检查一次,看时间是不是过去了 5 秒:while (!kthread_should_stop()){    unsigned long time_since_load = jiffies - jiffies_at_load;    unsigned long time_since_load_sec = time_since_load / HZ;    if (time_since_load_sec >= 5)    {      printk(KERN_INFO "Hello world!\n");      jiffies_at_load = jiffies;    }    // Sleep for 1 ms to avoid hogging the CPU    msleep(1);}此处用 msleep(1) 是没有大毛病的,它确实可以通过睡眠暂时让出 CPU,避免 hogging,不像 busy loop 的 mdelay()。但它这里实现的比较曲折,1 毫秒检查一次,5 秒内就要检查 5000 次,虽然没有「霸占」CPU,但是对 CPU 资源也是不小的浪费。于是原作者让它改了一个更减少 CPU 消耗的版本出来:while (!kthread_should_stop()){    printk(KERN_INFO "Hello world!\n");    // Sleep for 5 seconds to avoid hogging the CPU    schedule_timeout(HZ * 5);}看起来是解决了原来存在的问题,但编译出来一试,好家伙,"Hello world" 是突突地往控制台上打啊,根本不是间隔5 秒一次。把这个问题反馈给 AI 后,它立马做出了调整,加入了一句对 process 状态的设置后,就可以 work 了。set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(HZ * 5);查阅 Linux 源码可知,schedule_timeout() 最终会调用 __schedule() 函数,其对 process 切换的判断是这样的:if (!preempt && prev->state)deactivate_task(rq, prev, ...);再由于:#define TASK_RUNNING 0x0000所以如果之前的状态是 RUNNING,process 并不会真的离开 CPU 的 runqueue,岂不就是一直执行,一直框框地打印么。在 https://livegrep.com/search/linux 上查了下 schedule_timeout() 在内核中的具体使用情况,好些对 set_current_state() 为 INTERRUPTIBLE 的设置没有和 schedule_timeout() 挨在一起,所以 AI“理解”不了这两者的关联,笔者觉得是可以接受的。不过其实 Linux 是提供了一个“二合一”的封装函数的:sched schedule_timeout_uninterruptible(signed long timeout){set_current_state(TASK_UNINTERRUPTIBLE);return schedule_timeout(timeout);}而它还有个更上层的封装 "msleep"(希望可被信号打断就用 "msleep_interruptible"):void msleep(unsigned int msecs){unsigned long timeout = msecs_to_jiffies(msecs) + 1;while (timeout)    timeout = schedule_timeout_uninterruptible(timeout);}
咳,绕了这么大一圈,其实一开始直接用 msleep(5000) 最方便啦。后来原作者又提了在「内核模块」开发中颇为常见的两点功能:一是将 5 秒的间隔配置成 module parameter(以供动态调整),这个任务被顺利完成了。二是在 "/proc" 文件系统中加入打印次数的统计功能(以便查询),这里出了点小岔子,AI 用的 "file_operations",而不是 "proc_ops",这在高于 5.7 的内核版本上是编译不过的(参看笔者亲身经历的这个案例)。这也不能怪 AI,你没说内核版本不是。
小结
最后原作者写了下他的感受,大意就是 "half amazing and half terrifying",虽然 AI 中途犯了不少错,但总比自己现查资料来的快不是…除此之外,笔者也有两点感受,一是 ChatGPT 即使有时会出错,但回答地总是非常自信(还好不是那么普通,却那么自信……),二是那个注释一条条地写的真是规范啊,连每个头文件为什么加,都有理有据,这一点就强过很多人。#include <linux/module.h>// Needed for all kernel modules#include <linux/kernel.h>// Needed for KERN_INFO#include <linux/init.h>    // Needed for the macros#include <linux/jiffies.h> // Needed for jiffies#include <linux/delay.h>   // Needed for msleep笔者自己也用这个题目,在 ChatGPT 上试了一把,得出了不太一样的结果,欲知后续,请看下文分解。
补充(为了避免影响主线剧情):那 schedule_timeout() 返回的时候,也需要手动再将状态设置回 TASK_RUNNING 么?不需要,因为 timer 的 callback 在唤醒 process 后会将其状态(自动)设为 RUNNING(参考 Linux 中的等待队列机制 ):void process_timeout(struct timer_list *t){struct process_timer *timeout = from_timer(timeout, t, timer);wake_up_process(timeout->task);}
页: [1]
查看完整版本: 让 ChatGPT 写一个 Kernel Module [一]