让ChatGPT写一个内核模块

嵌入式技术

1356人已加入

描述

笔者最近看到这样一篇文章  ,原作者让 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!
");
        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!
");
    // 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   // Needed for all kernel modules
#include   // Needed for KERN_INFO
#include     // Needed for the macros
#include  // Needed for jiffies
#include    // 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);
}

 

  审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分