谷动谷力

 找回密码
 立即注册
查看: 872|回复: 0
打印 上一主题 下一主题
收起左侧

让 ChatGPT 写一个 Kernel Module [一]

[复制链接]
跳转到指定楼层
楼主
发表于 2023-8-17 09:58:32 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
让 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);
}

+10
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|深圳市光明谷科技有限公司|光明谷商城|Sunshine Silicon Corpporation ( 粤ICP备14060730号|Sitemap

GMT+8, 2024-12-27 23:36 , Processed in 0.099025 second(s), 39 queries .

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表