学习Linux驱动时,经常要和内核API打交道。可能讲解Linux驱动的书籍会说明某某函数包含在某某头文件里,但是当要刻意去查找调用某个函数应该包含哪个文件时,却怎么也找不找。再者,如果想要查看某个函数的源代码,却不知从哪找起,不知道在哪个.c文件里。而Linux驱动书上也不一定能找到答案。进入内核源码文件夹,grep一下,噼里啪啦地在终端里打印一大堆信息,还得去粗取精,也挺麻烦的。到在线阅读Linux源代码之类的网站去查找,也不一定方便。ctags工具也不一定能准确锁定到要查找的函数的位置。
据此,写了个shell脚本,以实现快速查找内核函数、内核数据结构、宏定义等。
不过,要借助ctags工具生成tags文件。所以,首先得先安装ctags工具。
在命令行上输入如下命令:
$ sudo apt-get install ctags
然后进入内核源代码目录下,如
$ cd /home/john/Kernel/linux-2.6.35.7
然后,输入如下命令:
$ ctags -R .
经过一段时间的等待后,tags文件生成完毕。
然后保存如下脚本为ksearch.sh。
#! /bin/sh
KERNEL="/home/john/Kernel/linux-2.6.35.7"
INCLUDE="$KERNEL/include"
INCLUDE_LINUX="$INCLUDE/linux"
TAGS="$KERNEL/tags"
TARGET=$1
TYPE=$2
FLAG=0
if [ "$TYPE" = "s" -o "$TYPE" = "struct" ]
then
EXP="struct $TARGET {"
FLAG=1
elif [ "$TYPE" = "m" -o "$TYPE" = "macro" ]
then
EXP="^\<$TARGET\>"
FLAG=2
elif [ "$TYPE" = "f" -o "$TYPE" = "func" ]
then
EXP="\<$TARGET\>([[:alpha:]]"
FLAG=3
elif [ "$TYPE" = "t" -o "$TYPE" = "typedef" ]
then
EXP="\<$TARGET\>;"
FILE_PATH=`grep -nrs "$EXP" $INCLUDE_LINUX | awk -F"\n" '{print $1}'`
if [ -z "$FILE_PATH" ]; then
FILE_PATH=`grep -nrs "$EXP" $INCLUDE | awk -F"\n" '{print $1}'`
fi
if [ -z "$FILE_PATH" ]; then
exit 1
fi
FILE_PATH=`echo $FILE_PATH | awk -F":" '{print $1 " +" $2}'`
vi $FILE_PATH
echo ">>Include:"
echo "$FILE_PATH"
exit 1
else
echo "Usage: $PROGRAM <target> <type>"
echo ">>type: s|struct; m|macro; f|func; t|typedef"
exit 1
fi
echo "+-----------------------------------------------------------------------+"
# 分析tags文件,以获得欲查找内容所在的文件路径
RECORD=`grep -nrs "$EXP" $TAGS | awk -F"\n" '{print $1}'`
FILE=`echo $RECORD | awk -F" " '{print $2}'`
#echo "RECORD: $RECORD"
#echo "FILE: $FILE"
# 拼接成绝对路径
FILE_PATH=$KERNEL/$FILE
echo ">>FILE_PATH: "
echo "$FILE_PATH"
# 此时已经锁定到了具体文件
# 构造正则表达式并模式匹配,锁定到具体行
if [ $FLAG -eq 1 ] #TYPE=struct
then
EXP="^struct $TARGET {"
LINE=`grep -nsr "$EXP" $FILE_PATH | awk -F":" '{print $1}'`
elif [ $FLAG -eq 2 ] #TYPE=macro
then
EXP="^#[[:space:]]*define \<$TARGET\>"
LINE=`grep -nsr "$EXP" $FILE_PATH | awk -F":" '{print $1}'`
elif [ $FLAG -eq 3 ] #TYPE=func
then
# 获得函数原型
PROTOTYPE=`echo $RECORD | awk -F"^" '{print $2}' | awk -F"$" '{print $1}'`
echo "+---------------------------------------------------------+"
echo ">>Function Prototype:"
echo "$PROTOTYPE"
EXP="[[:alpha:]]+[[:space:]]+\**\<$TARGET\>\([[:alpha:]]"
LINE=`grep -E -nsr "$EXP" $FILE_PATH | awk -F":" '{print $1}'`
# 解决形如
# extern inline long
# copy_to_user(void __user *to, const void *from, long n)
# 将返回类型和函数名分行的函数声明
if [ -z "$LINE" ]; then
TMP="^\<$TARGET\>\([[:alpha:]]"
LINE=`grep -E -nsr "$TMP" $FILE_PATH | awk -F":" '{print $1}'`
fi
fi
LINE=`echo $LINE | awk -F" " '{print $1}'`
#echo ">>LINE: $LINE"
vi $FILE_PATH +$LINE
# 函数所在的头文件的绝对路径
if [ $FLAG -eq 3 ] # TYPE=func;
then
echo "+---------------------------------------------------------+"
echo ">>Include:"
HEAD_EXP=$EXP
# 在KERNEL/include/linux目录下查找
HEADER=`grep -E -nrs "$HEAD_EXP" $INCLUDE_LINUX`
# 在KERNEL/include/linux目录下未找到
# 则在KERNEL/include目录下查找
if [ -z "$HEADER" ]; then
HEADER=`grep -E -nrs "$HEAD_EXP" $INCLUDE`
fi
#echo "$HEADER"
HEADER=`echo "$HEADER" | awk -F"\n" '{printf $1}'`
HEADER=`echo "$HEADER" | awk -F":" '{print $1 " +" $2}'`
echo "$HEADER"
fi
echo "+-----------------------------------------------------------------------+"
需要给这个脚本一个可执行权限,命令如下:
$ chmod +x ksearch.sh
不过,有一点要注意,脚本开头的KERNEL要改为你自己的内核所在的文件夹的绝对路径。
现在来看看此脚本如何使用。
如果要查找内核数据结构,比如sk_buff结构体,在命令行上输入:
$ ./ksearch.sh sk_buff s
或者 $ ./ksearch.sh sk_buff struct
按回车,vi编辑器立马就会打开sk_buff所在的文件,并把光标定在sk_buff所在行。
而退出vi编辑器后,发现命令行打印了一些信息,这就是sk_buff所在文件的绝对路径。
如果要查找宏定义,比如MKDEV,在命令行上输入:
$ ./ksearch.sh MKDEV m
或者 $ ./ksearch.sh MKDEV macro
按回车,vi编辑器立马就会打开MKDEV所在的文件,并把光标定在MKDEV所在行。
而退出vi编辑器后,发现命令行也打印了一些信息,其实就是MKDEV所在文件的绝对路径。
如果查找的是typedef类型定义,如irqreturn_t,在命令行输入:
$ ./ksearch.sh irqreturn_t t
或者 $ ./ksearch.sh irqreturn_t typedef
按回车,vi编辑器会打开irqreturn_t所在的文件,并把光标定在irqreturn_t所在行。
退出vi编辑器后,命令行上打印的是包含irqreturn_t的头文件的绝对路径。
如果查找的是函数,如函数i2c_add_adapter(),在命令行输入:
$ ./ksearch.sh i2c_add_adapter f
或者 $ ./ksearch.sh i2c_add_adapter func
也能达到预期的效果,退出vi后,可以看到终端上输出有函数所在文件的绝对路径、函数原型、包含函数的头文件的绝对路径。如图所示:
图中424表示函数i2c_add_adapter在i2c.h的424行,如果想查看头文件,只需输入vi,然后粘帖424所在的整行,即
$ vi /home/john/Kernel/linux-2.6.35.7/include/linux/i2c.h +424
就可以打开vi,并且光标定位在函数i2c_add_adapter所在行处。