当前位置: 首页 > Linux

最后!“30岁”的Linux内核C语言将升级到C11

时间:2023-04-06 12:11:24 Linux

上周,Linux内核邮件列表上的信息“社区最近讨论是否要为内核采用现代C语言标准”引起了业界的关注。刚刚,Linux开源社区正式宣布,未来内核C语言版本将升级至C11,预计在今年5月5.18版本后生效。这个突然的决定,终于迎来了拥有30年历史的Linux内核C语言的升级。众所周知,顽固的Linux之父LinusTorvalds并非易事。那么,为什么这次LinusTorvalds终于放手了呢?这里似乎有一点偶然的因素。事件的起因还要追溯到上周Linux社区的讨论。一个bug引发的“连锁反应”据报道,一位名叫JakobKoschel的博士生正在研究与内核链表原语相关的推测执行漏洞。在这个过程中,他发现了一个问题:Linux内核广泛使用双链表:structlist_head{structlist_head*next,*prev;};通常,开发人员将这种结构嵌入到其他结构中,这样任何相关的结构类型都可以创建一个链表。同时,内核还提供了大量的函数和宏,可以用来遍历和操作链表。其中之一是list_for_each_entry(),这是一个伪装成控制结构的宏。碰巧,问题出在这个宏上。我们假设内核包含以下结构:structfoo{intfooness;structlist_headlist;};然后可以使用List中的元素创建foo结构的双向链表。假设一个名为foo_list的结构被声明为这样一个链表的头,这个链表可以使用下面的代码遍历:structfoo*iterator;list_for_each_entry(iterator,&foo_list,list){do_something_with(iterator);}/使用iterator这里的/list参数告诉宏foo结构中list_head结构的名称。该循环将为迭代器指向的列表中的每个元素执行一次。这会导致USB子系统中出现错误:传递给宏的迭代器在宏退出后仍然可用。当然,这是一件非常“危险”的事情。因此,Koschel提交了一个补丁,重写了有问题的代码,通过在循环结束后停止使用迭代器来修复错误。随后,JakobKoschel向LinusTorvalds提交了一个补丁,修复了一个与内核链接表相关的推测执行漏洞(推测安全列表迭代器提议)。Linux之父终于被说服了起初,LinusTorvalds本人似乎不太喜欢这个补丁,也不知道它与推测执行漏洞有什么关系。但是经过Koschel的详细解释,Linus承认这只是一个普通的bug。然而,事情并没有那么简单,Linus很快意识到了真正的问题:传递给链表遍历宏的迭代器必须在循环本身之外的范围内声明。造成这种不可预知的错误的原因是C89中没有“循环中变量的声明”。我们知道,Linux内核虽然发展很快,但也依赖于一些非常古老的工具,其中之一就是它的内核代码还在使用1989版的C语言标准,也就是内核引入时引入的标准projectstarted30很多年前写的。像list_for_each_entry()这样的宏基本上总是将最后一个HEAD条目泄漏出循环,仅仅是因为迭代器变量不能在循环本身中声明。如果你能写一个迭代器列表遍历宏来声明它自己,那么迭代器在循环外是不可见的,你就不会有这个问题。但是,由于内核坚持C89标准,因此无法在循环内声明变量。所以Linus决定,“让我们升级一下”,也许是时候升级到C99标准了,它至少比C89新一点,尽管它也有20多年的历史,并且允许您在循环中声明变量。现在C89已经过时了,为什么这么多年没变?Linus解释说,“这是因为我们遇到了一些旧的gcc编译器版本的奇怪问题,无法随意升级。”不过,现在Linux内核已经将gcc的最低要求提高到了5.1版本。奇怪的错误应该消失了。另一位核心开发者ArndBergmann也关注此事。他认为升级到C11或更高版本是可以的,但是升级到C17或C2x会破坏gcc-5/6/7的支持,所以升级到C11更容易实现。最终,LinusTorvalds支持这个想法,宣布他将“在5.18合并窗口中尽早尝试”。虽然接下来转向C11可能会导致一些意想不到的bug,但如果一切顺利,下一个Linux内核版本将正式转向C11。您对此次升级活动有何看法?也欢迎大家在下方交流互动。