一个令人惊叹的Linux内核补丁

系统 Linux
我们都知道linux内核中的代码非常精妙,但是有些代码由于历史原因,一个函数为了兼容处理各种情况,到最后可能变得非常糟糕,到处都是goto 和if,看的想跳楼(宋老师口头禅^_^)

[[351277]]

本文转载自微信公众号「相遇Linux」,作者JeffXie。转载本文请联系相遇Linux公众号。   

 我们都知道linux内核中的代码非常精妙,但是有些代码由于历史原因,一个函数为了兼容处理各种情况,到最后可能变得非常糟糕,到处都是goto 和if,看的想跳楼(宋老师口头禅^_^)

如果在系统中读一个文件时会调用

generic_file_buffered_read

这个函数的功能是把磁盘中的数据读到page之后,或者直接获取cache中的page,然后调用copy_page_to_iter把page拷贝到用户层的buffer中。

一天寂静的下午,得空,打开电脑,准备仔细研究一下这个函数,发现这个函数的注释上面就写明了:

  1. * This is really ugly. But the goto's actually try to clarify some 
  2. of the logic when it comes to error handling etc. 

仔细看了一下代码,果然ugly的不像话,到处都是跳转和判断,令人眩晕,而且整个函数达到300行左右(原谅我看了注释才斗胆这样讲:-) ),发现要是把这个函数看下去,今天一整天的心情都不会好了(当时看的是Linux5.10的代码)

  1. ssize_t generic_file_buffered_read(struct kiocb *iocb, 
  2.                 struct iov_iter *iter, ssize_t written) 
  3.  
  4. find_page: 
  5.                 if (fatal_signal_pending(current)) { 
  6.                         error = -EINTR; 
  7.                         goto out
  8.                 } 
  9.                         error = wait_on_page_locked_killable(page); 
  10.                         if (unlikely(error)) 
  11.                                 goto readpage_error; 
  12.                         if (PageUptodate(page)) 
  13.                                 goto page_ok; 
  14.  
  15.                         if (inode->i_blkbits == PAGE_SHIFT || 
  16.                                         !mapping->a_ops->is_partially_uptodate) 
  17.                                 goto page_not_up_to_date; 
  18.                         /* pipes can't handle partially uptodate pages */ 
  19.                         if (unlikely(iov_iter_is_pipe(iter))) 
  20.                                 goto page_not_up_to_date; 
  21.                         if (!trylock_page(page)) 
  22.                                 goto page_not_up_to_date; 
  23.                         /* Did it get truncated before we got the lock? */ 
  24.                         if (!page->mapping) 
  25.                                 goto page_not_up_to_date_locked; 
  26.                         if (!mapping->a_ops->is_partially_uptodate(page, 
  27.                                                         offset, iter->count)) 
  28.                                 goto page_not_up_to_date_locked; 
  29.                         unlock_page(page); 
  30.                 } 

于是就想内核社区这么多牛人,他们整天盯着这些代码,肯定很多人早已经注意到了,于是想去看看有没有人提交patch重构这个函数:

  1. ./scripts/get_maintainer.pl   mm/filemap.c 
  2. linux-kernel@vger.kernel.org (open list) 

然后我就在下面网址中搜索generic_file_buffered_read,果然在10月25号(我看代码那天在11月1号前后),就有人发了相关patch:

  1. https://lore.kernel.org/lkml/ 

然后迫不及待查看patch,并把整个patch 下载下来:

这里推荐一个工具,使用b4工具

  1. https://git.kernel.org/pub/scm/utils/b4/b4.git 

可以直接从

  1. https://lore.kernel.org 

获取原始格式的patch,便于自己git am之后测试。

  1. # b4 am https://lore.kernel.org/lkml/20201025212949.602194-1-kent.overstreet@gmail.com 
  2. v2_20201025_kent_overstreet_generic_file_buffered_read_improvements.cover 
  3. v2_20201025_kent_overstreet_generic_file_buffered_read_improvements.mbx 

然后直接 git am ,非常方便,这样就打上了lore.kernel.org上提交的patch.

  1. git am v2_20201025_kent_overstreet_generic_file_buffered_read_improvements.mbx 
  2. 提示:在git am之前,可以提前git apply --check 一下 
  1. # gitlogdate -3 
  2. fc5608fc9917    2020-10-25      Kent Overstreet fs: generic_file_buffered_read() now uses find_get_pages_contig 
  3. 3bcadc3306be    2020-10-25      Kent Overstreet fs: Break generic_file_buffered_read up into multiple functions 
  4. 3650b228f83a    2020-10-25      Linus Torvalds  Linux 5.10-rc1 
  5.  
  6. alias gitlogdate='git log --pretty=format:"%h%x09%ad%x09%an%x09%s" --date=short' 

打了这个patch之后,generic_file_buffered_read变成了这个样子:

  1. ssize_t generic_file_buffered_read(struct kiocb *iocb, 
  2.                 struct iov_iter *iter, ssize_t written) 
  3. .. 
  4. pg_nr = generic_file_buffered_read_get_pages(iocb, iter, 
  5.                                  pages, nr_pages); 
  6.                                   
  7. ... 
  8. for (i = 0; i < pg_nr; i++) { 
  9.    copied = copy_page_to_iter(pages[i], offset, bytes, iter); 
  10.    } 

而且

  1. generic_file_buffered_read_get_pages 

也非常之清晰:

  1. static int generic_file_buffered_read_get_pages(struct kiocb *iocb, 
  2.                                                 struct iov_iter *iter, 
  3.                                                 struct page **pages, 
  4.                                                 unsigned int nr) 
  5.         nr_got = find_get_pages_contig(mapping, index, nr, pages); 
  6.         if (nr_got) 
  7.                 goto got_pages; 
  8.  
  9.         if (iocb->ki_flags & IOCB_NOIO) 
  10.                 return -EAGAIN; 
  11.  
  12.         page_cache_sync_readahead(mapping, ra, filp, index, last_index - index); 
  13.  
  14.         nr_got = find_get_pages_contig(mapping, index, nr, pages); 
  15.         if (nr_got) 
  16.                 goto got_pages; 
  17. ... 

看完generic_file_buffered_read()之后,整个下午的心情都好多了。

 

责任编辑:武晓燕 来源: 相遇Linux
相关推荐

2023-12-10 14:19:31

JupyterPython编码

2011-03-18 09:56:19

JavaScript

2020-12-22 15:47:02

Python开发工具

2012-03-01 11:32:18

硅谷女性

2011-04-07 11:33:00

HTML 5JavaScript

2022-03-23 15:11:04

Arch LinuxLinuxCutefish 桌

2021-11-29 07:02:24

Python函数操作

2020-02-19 14:47:25

人工智能技术无人驾驶

2012-11-15 09:59:35

HTML5WebHTML5特效

2024-07-31 08:38:36

2024-08-05 08:38:13

2024-05-16 11:09:40

Python字符串代码

2024-02-04 18:20:53

AI模型代码

2022-01-26 13:00:07

Vue.js UI组件Web

2018-06-19 07:49:49

物联网足球科技世界杯

2023-11-18 09:07:59

Go语言技巧

2024-05-31 11:32:32

2020-08-18 17:21:01

开发工具前端开发

2023-07-06 10:31:24

CSS网格生成器

2023-03-16 14:25:43

GPT-4人工智能
点赞
收藏

51CTO技术栈公众号