Linux内核源文件提取---内核学习之革命性方法

一. 前言:

   Linux内核是当今最流行的操作系统, 没有之一, 已经运行在数十亿甚至上百亿的设备上, 如台式电脑, 嵌入式设备, 智能手机(android)等等, 基本上能跑操作系统的,除了几个特别的产家如苹果或微软外(也不完全是这两家), 就基本都是Linux操作系统了, 若你的工作中涉及Linux内核源代码的修改, 这其中或许是内核操作系统部分, 又或是Linux设备驱动类, 你若涉及这些工作, 都不可避免地需要学习,修改或为Linux内核增加代码, 在此之前, 肯定得看内核源码或学习相关内核源码(包括内核驱动部分, 后续不再赘述). Linux之所以如此流程,当然也少不了太多的Linux代码的支持了,一个如此流程且功能全面的操作系统,当然是由太多太多的内核源代码组成,且随着时间的推移,功能越来越多, 支持的平台也越来越多, 内核源码也越来越庞大, 无论对于初学者还是工作多年的老鸟, 要想确定项目中或学习中所使用到的内核源码中, 哪些源码是被使用到, 是一个无法避开的问题, 否则你无法知道需要更改哪个文件, 查找哪些函数或功能或变量在哪些地方被使用到, 这是作为内核程序员无法回避的问题. 在如此庞大的内核源文件中, 快速找到对应的源码, 是个非常实际且非常重要的工作. 如你是否考虑把使用到的内核的源码放到一个工程中来学习或查看, 如Source Insight, 或vim+ctags+global? 这样便于快速定位或查看代码, 或学习或修改, 若把整个内核源代码都加入到查看源码的工程中, 天啦, 如此庞大的源码怎么行呢? 无论加到任何一个代码工具(SI 或 VIM)中, 如此庞大的Linux内核, 如此方法简单让人抓狂, 是否有一方法把只编译到的内核源文件提取出来,加到源码工程种, 便于查看或修改? 那这样就太友好不过了---当然可以, 在此本人把多年前钻研出的绝招分享给大家,希望对大家在Linux内核源码的学习或工作上带来帮助.

二.内核源文件提取原理

  不知读者是否知道,一个对应的内核编译后, 在生成vmlinux文件之前, 依赖于所有源文件,每个所源的内核源文件编译时会产生一个 .xx.o.cmd 的隐藏文件. 以 kernel/workqueue.c 文件为例, 内核编译后会生成 .workqueue.o.cmd 文件,其文件内容大致如下, 这样是不是可以发现很多信息了, Linux内核不可能不知道Makefile吧,以及不可能不知道GNU工具链中GCC命令不编译,只生成相应的依赖文件列表,用于Makefile编译,只是该文件已经经过Kernel对应的工具处理过了.

通过上面的示例, 不难知道, 所有编译时生成该文件的, 不就是对应内核配置依赖的源文件了,以及对应源文件所依赖的头文件, 这样,就可通过工具---在此笔者自已实现的, 提取出对应的内核配置所对应内核源码文件列表, 原理是不是很简单?

在此提供提取整指定配置对应的内核源文件列表的脚本kernel_src_dump.py, 脚本源码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ************************************************************************ 
# * @File       : kernel_src_dump.py 
# * @Author     : LuoQiaoFa@163.com 
# * @Date       : 2018-11-10 19:02 
# * @Version    : 1.0  
# * @Description: Python Script 
# * @License    : Copyright (C) 2018-, LuoQiaoFa all rights reserved 
#************************************************************************* 
import sys
import os
import os.path
import time
import re
import argparse

KERNEL_BASE  = os.getcwd()
KERNEL_SRC = os.getcwd()
KERNEL_OUT = os.getcwd()

parser = argparse.ArgumentParser()
parser.add_argument("-p", "--print", help="Only print system command process", action="store_true")
parser.add_argument("-d", "--debug", help="Debug print", action="store_true")
parser.add_argument("-s", "--src", help="kernel source dir")
parser.add_argument("-o", "--out", help="build output dir")
parser.add_argument("-b", "--base", help="kernel base dir")
args = vars(parser.parse_args())

if args["base"]:
    kernel_base = args["base"]
    KERNEL_BASE = kernel_base
    # print("base=%s" % kernel_base)

if args["src"]:
    kernel_src = args["src"]
    # print("src=%s" % kernel_src)
    KERNEL_SRC = kernel_src

if args["out"]:
    kernel_out = args["out"]
    KERNEL_OUT = kernel_out
    # print("out=%s" % kernel_out)

# print(KERNEL_OUT)
if "ANDROID_PRODUCT_OUT" in os.environ:
    KERNEL_VERSION = "msm-3.18"
    ANDROID_PRODUCT_OUT = os.environ["ANDROID_PRODUCT_OUT"]
    relative_path = os.path.join("kernel", KERNEL_VERSION)
    kernel_src = os.path.join(os.environ["ANDROID_BUILD_TOP"], relative_path)
    kernel_out = os.path.join(ANDROID_PRODUCT_OUT, "obj", "KERNEL_OBJ")
    KERNEL_BASE = os.environ["ANDROID_BUILD_TOP"]
    idx_start = len(KERNEL_BASE) + 1
    KERNEL_SRC = kernel_src[idx_start:]
    # print(KERNEL_SRC)
    KERNEL_OUT = kernel_out[idx_start:]
    # print("KERNEL_OUT=%s" % KERNEL_OUT)

def kernel_depfile_2_flist(depfile, filelist) :
    # depfile = "out/target/product/msm8937_64/obj/kernel/msm-3.18/drivers/input/touchscreen/.ft5x06_ts.o.cmd"
    # print("processing file: %s ..." % depfile)
    fobj = open(depfile, "r")
    for line in fobj :
        txt = line.strip()
        if 0 == len(txt) :
            continue
        if txt.startswith("cmd_") :
            continue
        if txt.startswith("deps_") :
            continue
        if txt.startswith("$(deps_") :
            continue
        if txt.endswith(".o)") :
            continue

        if txt.endswith(" \\") :
            txt = txt[:-2]

        if txt.startswith("$(wildcard ") and txt.endswith(")"):
            idx_start = len("$(wildcard ")
            tmp_txt = txt[idx_start : -1]
            pre_path = KERNEL_OUT
            relative_path = os.path.join(pre_path, tmp_txt)
            abs_path = relative_path
            if KERNEL_BASE in KERNEL_OUT:
                pre_path = KERNEL_OUT[len(KERNEL_BASE) + 1::]
                relative_path = os.path.join(pre_path, tmp_txt)
                abs_path = os.path.join(KERNEL_BASE, relative_path)
            if os.path.exists(abs_path) : 
                print(relative_path)
                pass
        else :
            tmp_txt = re.sub(r".*:= *","", txt)
            if len(tmp_txt) == 0 :
                continue
            if tmp_txt.find(KERNEL_BASE) >= 0 :
                idx_start = len(KERNEL_BASE) + 1
                full_txt = tmp_txt[idx_start:]
            else :
                if tmp_txt.endswith(".mod.c") :
                    continue
                pre_path = KERNEL_OUT
                relative_path = os.path.join(pre_path, tmp_txt)
                if KERNEL_BASE in KERNEL_OUT:
                    pre_path = KERNEL_OUT[len(KERNEL_BASE) + 1::]
                    relative_path = os.path.join(pre_path, tmp_txt)
                full_txt = relative_path
            print(full_txt)
    fobj.close()
 
def depfile_find(path, callback, args):
    for root, dirs, files in os.walk(path):  # path 为根目录
        for fname in files :
            rootname = str(root)
            if rootname.startswith(os.path.join(KERNEL_OUT,"scripts")) :
                continue
            full_name = os.path.join(rootname, fname)
            matchObj = re.search( r'.*\.o\.cmd', full_name, re.M|re.I)
            if matchObj :
                # print(full_name)
                # print("callback type: %s" % str(type(callback)))
                # if "<class 'function'>" == str(type(callback)) :
                # py_ver = sys.version[:3]
                if "function" in str(type(callback)) :
                    callback(full_name, args)

if __name__ == '__main__' :
    # depfile = "out/target/product/msm8937_64/obj/kernel/msm-3.18/crypto/asymmetric_keys/.x509-asn1.o.cmd"
    # kernel_depfile_2_flist(depfile, "filelist")
    # path = "out/target/product/msm8937_64/obj/KERNEL_OBJ/crypto"
    path = KERNEL_OUT
    depfile_find(path, kernel_depfile_2_flist, "undefined")
    # dump all files in path: include/generated
    path = os.path.join(KERNEL_OUT,"include", "generated")
    for root, dirs, files in os.walk(path):  # path 为根目录
        rootname = str(root)
        for fname in files :
            full_txt = os.path.join(rootname, fname)
            strip_path = full_txt
            if KERNEL_BASE in full_txt:
                strip_path = full_txt[len(KERNEL_BASE) + 1::]
            print(strip_path)

该脚本使用帮助命令如下:

以本人工作中使用的内核配置为例, 进入内核源码根目录

python kernel_src_dump.py -s $PWD -o $PWD/zynqmp_defconfig -b $PWD | uniq -u > kernel.txt

参数说明:

-s 指内核源码的根路径, 绝对路径

-o 指内核编译时输出或临时文件位置, 对应内核编译命令中make O=xxx 中的 xxx 对应的绝对路径.

-b 由于历史原因, 固定与 -s 参数相同, 除非读者有兴趣自已修改脚本, 这完全没问题.

其实很多年前(2012年前后)本人是使用bash shell 编写的脚本来提取内核源文件列表的, 只是后来学会python后, 发现python实在太过强大, 一般工作相关的小工具都改为使用python了.

这样对应内核配置在该内核配置编译后, 例可通过执行上述脚本提取出所有的内核源文件, 保存在kernel.txt文件中, 然后便可以通过

1. Source Insight 工程, 【Project】--->【Add and Remove Project Files】--->【Add from list…】---> kernel.txt 添加到SI内核源码工程中, 这样无论在源码符号(函数或变量)上的查找或源码阅读,跳转, 非常的快速, 方便, 准确, 对于工作或学习,效率上有极大提高.

2. Vim+Ctags+global对于喜欢只在Linux 下工程的大神们来说---当然本人也更多是以这样的方式工作. 下述命令便是如何生成在

ctags --tag-relative=yes --sort=yes --output-format=u-ctags -f kernel.txt

gtags --gtagslabel new-ctags -f kernel.txt

注: 这里ctags是 Universal Ctags, 不是 Exuberant Ctags, 通常Linux发行版可能不支持, 需要通过源码的方式编译出来

另外global 工具包的使用请查阅相关网络资料, 通过本文及具体示例, 希望读者能举一反三, 学会学习, 会思考会钻研, 这样才可能更深入及提高. 在此不对这些工具及与VIM配合使用专门介绍,作者在此分享自已平常使用的相关脚本及配置

gen_tags.sh

#!/bin/bash - 
#===============================================================================
#          FILE:  gen_tags.sh
#         USAGE:  ./gen_tags.sh 
#   DESCRIPTION:  
#       OPTIONS:  ---
#  REQUIREMENTS:  ---
#          BUGS:  ---
#         NOTES:  ---
#        AUTHOR: LuoQiaofa (Luoqf), luoqiaofa@163.com
#  ORGANIZATION: 
#       CREATED: 07/03/2019 09:31:17 PM EDT
#      REVISION:  ---
#===============================================================================
set -o nounset                              # Treat unset variables as an error

filelist="zynqmp_defconfig/kernel.txt"
# script_path=`dirname $0`
# echo script_path=${script_path}
# cd ${script_path}
export GTAGSROOT=${PWD}
export GTAGSDBPATH=${PWD}/zynqmp_defconfig
echo tags_path:${GTAGSDBPATH}
# cd ${GTAGSDBPATH}/../..
echo src_root=${GTAGSROOT}
ctags --tag-relative=yes --sort=yes --output-format=u-ctags -f ${GTAGSDBPATH}/tags -L ${GTAGSROOT}/${filelist}
gtags --gtagslabel new-ctags -f ${GTAGSROOT}/${filelist} ${GTAGSDBPATH}

xref_kernel.sh

#!/bin/bash - 
prj=kernel
export GTAGSROOT=${PWD}
export GTAGSDBPATH=${GTAGSROOT}/zynqmp_defconfig
export TAGS=${GTAGSDBPATH}/tags
#vim/gvim -c "set tags=${TAGS}"
#vim/gvim -t <symbol> --cmd "set tags=${TAGS}"

使用时 source xref_kernel.sh

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值