linux的进程/线程/协程系列3:查看linux内核源码——vim+ctags/find+grep

linux的进程/线程/协程系列3:查看linux内核源码——vim+ctags/find+grep

  • 前言
  • 摘要:
  • 1. 下载linux内核源码
  • 2. 打标签方法:vim+ctags
    • 2.1 安装vim
    • 2.2 安装universal-ctags
    • 2.3 查看源码
  • 3. 系统命令:find+grep
    • 3.1 find命令
    • 3.2 grep命令
    • 3.3 find+grep查找字符及源码
  • 参考文献

前言

最近学习自动驾驶系统时,碰到协程的概念。进程和线程已经迷了,又来个协程,看了很多资料后决定作总结,概括三者联系和区别,最后归结到协程在自动驾驶中的应用。初级程序员目标是搞清三者概念并应用到实际中,而资深工程师则需要在系统层面考虑三者的性能及实现代价,直到如今三者仍是Linux内核和各类编程语言持续更新完善的模块之一,所以理清三者的关系、编程应用和考量性能是进阶程序员的必修课。行文的目的,是对进程/线程/协程这一系列繁复的概念和知识点做一个全面的总结,同时尽量做到知识点讲精讲细讲全,甄别模糊概念,同时兼顾源码及编程实现,最后归结到Apollo的协程实现。

本系列文章分九篇讲解:

  1. 《进程到协程的演化》:涉及进程发展的历史和计算机系统结构知识;
  2. 《进程/线程的系统命令》:总结进程/线程有关的系统命令,让大家有一个初步感性认识,而不只是生涩的文字;
  3. 《查看linux内核源码——vim+ctags/find+grep》:如何查看linux系统源码,源码第一手资料,重要性不言而喻;
  4. 《进程/线程相关知识总结》:进程/线程知识串讲,进程、线程和协程一脉相承,对进程理解透彻,线程和协程的难点也会迎刃而解。
  5. 《协程发展史、当前现状及libgo/tbox》:Conway Melvin如何总结出协同工作机制,引出当前协程库现状,分析它们的优劣势,并给出我的推荐:libgo,分析其源码目录。最后提引性能神器tbox。
  6. 《全面弄懂进程/线程/协程的内存调度》:三者在内存中的调度,,带读者领略内存调度的魅力。
  7. libgo/tbox功能及源码详解》:分析libgo/tbox的原理和集成功能,给出源码简读和样例。
  8. 《进程/线程/协程的性质辨析和实现对比》:列表分析三者的性质,同时根据源码,挑重点总结实现区别。
  9. 《Apollo中的协程概述》:协程在Apollo中的应用,展示及分析Apollo协程的源码及优缺点。

摘要:

本章讲解如何在系统中查看进程/线程的内核源码,为下一章方便查看实现方式差异做准备。本章分四节:第一节,讲解如何下载对应版本的内核源码源文件;第二节,使用vim+ctags查看源码;第三节,使用find+grep查看源码。

1. 下载linux内核源码

  1. 首先,查看自己的内核版本,命令如下:
$ apt-cache search linux-source
linux-source - Linux kernel source with Ubuntu patches
linux-source-4.15.0 - Linux kernel source for version 4.15.0 with Ubuntu patches
linux-source-4.18.0 - Linux kernel source for version 4.18.0 with Ubuntu patches
linux-source-5.0.0 - Linux kernel source for version 5.0.0 with Ubuntu patches
linux-source-5.3.0 - Linux kernel source for version 5.3.0 with Ubuntu patches
  1. 下载。目前最新只到5.3.0,所以选择它下载。这里的下载结果是将源码压缩文件放入/usr/src目录下对应版本目录,同时在/usr/src下创建一个软链接:
$ sudo apt install linux-source-5.3.0
/usr/src$ ll
total 32
drwxr-xr-x  8 shaw docker 4096 926 18:43 ./
drwxr-xr-x 11 root root   4096 87  2020 ../
drwxr-xr-x  7 root root   4096 920 19:11 linux-headers-5.4.0-84-generic/
drwxr-xr-x 24 root root   4096 920 19:11 linux-hwe-5.4-headers-5.4.0-84/
drwxr-xr-x  4 root root   4096 919 20:04 linux-source-5.3.0/
lrwxrwxrwx  1 root root     45 715  2021 linux-source-5.3.0.tar.bz2 -> linux-source-5.3.0/linux-source-5.3.0.tar.bz2
  1. 解压。我们需要对源码文件解压才能查看和进一步操作:
/usr/src$ tar xjvf linux-source-5.3.0.tar.bz2 -C ~/code/  ## 根据需要调整解压目录

下面我们看如何使用两种方式高效地查找源码。

2. 打标签方法:vim+ctags

vim+ctags虽然查找方便,格式简洁,但是只能查看打了tag的函数,如果查找没被打tag的函数或者某行代码是不行的,这时可以使用下一节的find+grep。下面分步骤讲解如何使用vim+ctags。

2.1 安装vim

vim安装直接使用命令即可:

$ sudo apt install vim

同时推荐一个vim配置,命令如下:

//这个会直接帮你配置vimrc和vim各种插件
$ wget -qO- https://raw.github.com/ma6174/vim/master/setup.sh | sh -x 

2.2 安装universal-ctags

对于ctags,目前有两个版本,一个是exuberant-ctags,一个是universal-ctags。exuberant-ctags目前已停止维护,其原作者Reza Jelveh后来转投到universal-ctags旗下,所以推荐universal-ctags。其安装步骤如下:

  1. 安装autoconf,autoconf用于生成configure脚本:
$ sudo apt install autoconf
  1. 从github上克隆代码,可自选存放路径:
$ cd /tmp
$ git clone https://github.com/universal-ctags/ctags
  1. 安装universal-ctags,根据自己需要调整安装路径
$ cd /tmp/ctags
$ ./autogen.sh
$ ./configure --prefix=/opt/software/universal-ctags
$ make -j8
$ sudo make install
  1. 使用创建链接的方式,把新编译安装的universal-ctags链接过来:
$ sudo ln -s /opt/software/universal-ctags/bin/ctags /usr/bin/ctags
  1. 使用ctags生成tag:
$ cd ~/code/linux-source-5.3.0$    # 进入到之前的源码文件中
~/code/linux-source-5.3.0$ ctags -R .  #递归地生成.tags文件
  1. 在vim中配置ctags,以便支持vim -t命令。在/etc/vim/vimrc加入配置:
$ sudo vim /etc/vim/vimrc
set tags=tags
set autochdir

set tags+=/home/shaw/code/linux-source-5.3.0/tags

2.3 查看源码

以上就是vim+ctags的安装配置步骤,让我们试一下效果,比如查找源码中创建进程的函数fork:

~/code/linux-source-5.3.0$ vim -t fork 

查找结果如下图所示,键入对应数字后回车即可进入相应文件查看,键入:q退出。
linux的进程/线程/协程系列3:查看linux内核源码——vim+ctags/find+grep_第1张图片

注意:因为生成的tag已配置在vim目录中,所以vim -t在任意目录均可查找。但find+grep只能在指定的目录中查找。

3. 系统命令:find+grep

find和grep是大家非常熟悉的两个linux系统命令,两者都是查找命令,但是有一些差别:find的查找对象是目录,返回结果是目录和文件名;而grep的查找对象是文件,返回结果是某行的字符串,而两者结合就可以搜索范围文件内的字符串。由于linux系统“一切皆文件”的特性,查找功能丰富的find和grep命令就显得尤为重要,下面简要介绍下两者用法:

3.1 find命令

find用来在指定目录下查找文件,并且将查找到的子目录和文件全部进行显示。find使用格式如下:

    find [-H] [-L] [-P] [-D debugopts] [-Olevel] [path...] [expression]

其中[-H] [-L] [-P] [-D debugopts] [-Olevel]这几个选项并不常用,可以省略掉。path:find命令所查找的目录路径,例如用.来表示当前目录,用/来表示系统根目录。expression:可以分解为“-options [-print -exec -ok …],”,-options:指定find命令的常用选项,-print:将匹配的文件输出到标准输出,-exec:对匹配的文件执行该参数所给出的shell命令,-ok:和-exec的作用相同,不过在执行每一个命令之前,都会给出提示,让用户来确定是否执行 。options常用参数列举如下:

  1. -ipath p, -path p : 路径名称符合 p 的文件,ipath 会忽略大小写;
  2. -name name, -iname name : 文件名称符合 name 的文件,iname 会忽略大小写。可使用正则表达式,正则表达式的用法在grep中介绍;
  3. -size ±n : 文件大小是(无符号)大于(+)小于(-)n 单位,b 代表 512 位元组的区块,c 表示字元数,k 表示 kilo bytes,w 是二个位元组;
  4. -type d: 目录,c: 字型设备文件,b: 区块设备文件,p: 管道文件,f: 一般文件,l: 符号连结,s: socket;
  5. -user/group:根据文件所属用户或组来查找文件;
  6. -uid/gid:根据文件所属用户ID或组ID查找文件;
  7. -a,-o和–not:-a:and连接条件(两个同时满足);-o:or或条件;-not:取反;
  8. -atime,-mtime,-ctime,-amin,-mmin,-cmin,±n:根据文件时间戳的相关属性来查找文件,a/m/c表示最近一次访问/修改内容/修改属性;time单位为天,min单位为分钟;+n表示n time/min内没有做过a/m/c操作,-n表示n time/min内做过a/m/c操作。另外,我们可以通过stat命令来查看一个文件的时间信息;
  9. -depth:在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找;
  10. -prune 使用这一选项可以使find命令不在当前指定的目录中查找,如果同时使用-depth选项,那么-prune将被find命令忽略;
  11. -perm:根据文件权限查找文件。r 读权限read 4;w 写权限write 2;x 操作权限execute 1。另外:文件权限格式为:【文件-或文件夹d】【owner权限】【group权限】【others权限】,如drwxrw-r–(767);
  12. -exec,xargs:-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行。但有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出现溢出错误。而find命令把匹配到的文件传递给xargs命令,xargs命令每次只获取一部分文件而不是全部,不像-exec选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。 find命令配合使用exec和xargs可以使用户对所匹配到的文件执行几乎所有的命令。

下面列举几个例子:

  1. 将当前目录及其子目录下所有文件后缀为 .c 的文件列出来:
$ find . -name "*.c"
  1. 查找系统中所有文件长度为 0 的普通文件,并列出它们的完整路径:
$ find / -type f -size 0 -exec ls -l {} \;    #{} \;为固定写法:一对大括号+空格+\+
  1. 查找过去30天内名字为任意数字的没修改过内容的文件,并强制删除文件,包括目录:
$ find -mtime +30 -name "*[0-9]*" -exec rm –rf {} \;    #等同于
$ find -mtime +30 -name "*[0-9]*" | xargs rm -f –r 
  1. 查找系统中的每一个普通文件,通过管道过滤,然后使用xargs命令来测试它们分别属于哪类文件
$ find . -type f -print | xargs file

3.2 grep命令

Linux系统中grep命令 (Global search Regular Expression and Print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。
基本格式如下:

grep [OPTIONS] PATTERN [FILE...] 

上式中[OPTIONS]代表参数选项;PATTERN指定搜索模式,如果是简单字符串不需要引号,如果包含正则表达式,此时patter必须用单引号或双引号括起来,[FILE…] 即为要搜索的文件。
[options]主要参数

  1. -?,–number:同时显示匹配行上下的number行
  2. -c,–count:只打印匹配的行数,不显示匹配的内容
  3. -i,–ignore-case:忽略大小写差别
  4. -h,–no-filename:当搜索多个文件时,不显示匹配文件名
  5. -q,–quiet:取消显示,只返回退出状态,0则表示找到了匹配的行
  6. -l,–files-with-matches:打印匹配模板的文件清单
  7. -L,–files-without-match:打印不匹配模板的文件清单
  8. -n,–line-number:在匹配的行前面打印行号
  9. -s,–silent:不显示关于不存在或者无法读取文件的错误信息
  10. -v,–revert-match:反检索,只显示不匹配的行
  11. -w,–word-regexp:把表达式做为一个单词搜索,而不是字符串的一部分
  12. -o :只显示被模式匹配到的字符串。
  13. –color :将匹配到的内容以颜色高亮显示。

下面再列出一些pattern正则表达式的元字符集(基本集)

  1. ^ :锚定行的开始,如’^grep’匹配所有以grep开头的行
  2. $:锚定行的结束,如’grep$'匹配所有以grep结尾的行
  3. .:匹配一个非换行符的字符 如’gr.p’匹配gr后接一个任意字符,然后是p
  4. *:匹配零个或多个先前字符 如’ *grep’匹配所有一个或多个空格后紧跟grep的行
  5. []:匹配一个指定范围内的字符,如’[Gg]rep’匹配Grep和grep。
  6. [^]:匹配一个不在指定范围内的字符,如:’[^A-FH-Z]rep’匹配不包含A-F和H-Z的一个字母开头,紧跟rep的行。
  7. (…):标记匹配字符,如:’(love)’,love被标记为1
  8. <:锚定单词的开始,如:’
  9. >:锚定单词的结束,如’grep>'匹配包含以grep结尾的单词的行
  10. x{m}:连续重复字符x,m次,如’o{5}'匹配包含连续5个o的行
  11. x{m,}:连续重复字符x,至少m次,如’o{5,}'匹配至少连续有5个o的行
  12. x{m,n}:连续重复字符x,至少m次,不多于n次,如’o{5,10}'匹配连续5–10个o的行
  13. \w:匹配一个文字和数字字符,也就是[A-Za-z0-9],如’G\w*p’匹配以G后跟零个或多个文字或数字字符,然后是p
  14. \W:w的反置形式,匹配一个非单词字符,如点号句号等。\W*则可匹配多个
  15. \b:单词锁定符,如: '\bgrep\b’只匹配grep,即只能是grep这个单词,两边均为空格

下面举几个例子:

  1. 通过管道过滤ls -l输出的内容,只显示以a开头的行
$ ls -l | grep '^a' 
  1. 显示所有以d开头的文件中包含test的行
$ grep 'test' d*
  1. 不区分大小写地搜索()默认情况区分大小写
$ grep -i pattern files
  1. 只列出匹配的文件名
$ grep -l pattern files 
  1. 显示匹配pattern1或pattern2的行
$ grep pattern1 | pattern2 files

6.显示既匹配pattern1又匹配pattern2的行

$ grep pattern1 files | grep pattern2

3.3 find+grep查找字符及源码

有了上面知识的铺垫,就可以结合find和grep命令从特定文件查找特定字符。使用find查找特定文件,然后连接管道符号|,过滤文件内容,最后通过xargs将文件表作为输入参数传给grep,根据grep的模式定位到符合要求的文件的某行字符。举例如下:

  1. 用grep命令在目录/usr/include中搜索线程创建函数pthread_create。因为线程不属于linux的系统库,其原函数在/usr/include,指定目录也为了避免结果过多
find /usr/include -type f -print | xargs grep "pthread_create"
$ find /usr/include/ -type f -print | xargs grep "pthread_create"
/usr/include/thread_db.h:					   pthread_create().  */
/usr/include/thread_db.h:					   pthread_create().  */
/usr/include/pthread.h:extern int pthread_create (pthread_t *__restrict __newthread,
/usr/include/pthread.h:/* Get the default attributes used by pthread_create in this process.  */
/usr/include/pthread.h:/* Set the default attributes to be used by pthread_create in this
/usr/include/c++/7/thread:	// Create a reference to pthread_create, not just the gthr weak symbol.
/usr/include/c++/7/thread:	auto __depend = reinterpret_cast(&pthread_create);

  1. find+grep查找源码中的vsnprintf,如下所示。说明:(1)由于作者生成了tags,在tags中检索时产生很多无用信息,因此用-prune去掉tags;(2)-type f,即排除无用的目录;(3)注意,这里路径参数只能用完整路径,否则排除不掉;(4)检索printf和vsprintf时会有问题,虽然会搜索到对的文件,但是产生很多不匹配的行,而且行号显示也不对,可能和命令本身有关系,作者没弄清楚问题所在,知道的读者可以留言讨论;(5)命令后边可以跟>(创建文件并清空文件内容)或>>(在文件后追加)将搜索结果保存到文件。
$ find /home/shaw/code/linux-source-5.3.0 -path "/home/shaw/code/linux-source-5.3.0/tags" -prune -type f | xargs grep -n "vsnprintf" > rlt.txt 
2778274:__color_vsnprintf	tools/perf/util/color.c	/^static int __color_vsnprintf(char *bf, size_t size, const char *color,$/;"	f	typeref:typename:int	file:
3056917:color_vsnprintf	tools/perf/util/color.c	/^int color_vsnprintf(char *bf, size_t size, const char *color,$/;"	f	typeref:typename:int
4583419:vsnprintf	drivers/acpi/acpica/utprint.c	/^int vsnprintf(char *string, acpi_size size, const char *format, va_list args)$/;"	f	typeref:typename:int
4583420:vsnprintf	lib/vsprintf.c	/^int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)$/;"	f	typeref:typename:int

本章我们了解了如何查看进程和线程的源码。下一篇我们将初步介绍协程,包括协程历史、当前各协程库现状及优劣势分析。

本打算行文尽量简洁,但达不到讲精讲细的目的,所以我对本系列文章的定位是复杂知识点详细总结,在此基础上做到尽量简练。由于查阅了大量资料,耗费了很多精力,虽然谈不上尽善尽美,但也希望您的小手能支持作者一下,来个一键四联(点赞、收藏、评论、转发),希望帮助到不停探索的你。

参考文献

  1. Ubuntu16.04安装配置和使用ctags
  2. Ubuntu下查看Linux内核源码(vim+ctags)
  3. linux下find命令的使用和总结
  4. linux 中强大且常用命令:find、grep

你可能感兴趣的:(linux,linux源码,ctags,vim,find,grep)