Linux内核编译与开发

简介:

一.Linux内核简介

linux kernel map:

linux kernel map

linux 系统体系结构:

linux 系统体系结构

linux kernel体系结构:

arm有7种工作模式,x86也实现了4个不同级别RING0-RING3,RING0级别最高,

这样linux用户代码运行在RING3下,内核运行在RING0,这样系统本身就得到了

充分的保护

用户空间(用户模式)转到内核空间(系统模式)方法:

  • 系统调用
  • 硬件中断

linux kernel 体系结构:

linux kernel 体系结构

虚拟文件系统VFS:

VFS(虚拟文件系统)隐藏各种文件系统的具体细节,为文件操作提供统一的接口

二.Linux内核源代码

linux内核下载www.kernel.org

目录结构:

解压linux kernel tar后目录

  • arch:根据cpu体系结构不同而分的代码
  • block:部分块设备驱动程序
  • crypto:加密,压缩,CRC校验算法
  • documentation:内核文档
  • drivers:设备驱动程序
  • fs(虚拟文件系统vfs):文件系统
  • include:内核所需的头文件,(与平台无关的头文件在include/linux中)
  • lib:库文件代码(与平台相关的)
  • mm:实现内存管理,与硬件体系结构无关的(与硬件体系结构相关的在arch中)
  • net:网络协议的代码
  • samples:一些内核编程的范例
  • scripts:配置内核的脚本
  • security:SElinux的模块
  • sound:音频设备的驱动程序
  • usr:cpio命令实现,用于制作根文件系统的命令(文件系统与内核放到一块的命令)
  • virt:内核虚拟机

linux DOC 编译生成:

linux源根目录/Documentation/00-INDEX:目录索引

linux源根目录/Documentation/HOWTO:指南

生成linux内核帮助文档:在linux源根目录(Documentation) 执行make htmldocs

ubuntu16下需要执行sudo apt-get install xmlto安装插件才可生成doc文档

后面开发中经常要改的是arch,drivers中的代码

三.Linux内核配置与编译

清理文件(在linux源码根目录):

  • make clean:只清理所有产生的文件
  • make mrproper:清理所有产生的文件与config配置文件
  • make distclean:清理所有产生的文件与config配置文件,并且编辑过的与补丁文件

配置(收集硬件信息如cpu型号,网卡等...):

  • make config:基于文本模式的交互配置
  • make menuconfig:基于文本模式的菜单模式(推荐使用)
  • make oldconfig:使用已有的.config,但会询问新增的配置项
  • make xconfig:图形化的配置(需要安装图形化系统)

配置方法:

1)使用make menuconfig操作方法:

1>按y:编译>连接>镜像文件

2>按m:编译

3>按n:什么都不做

4>按"空格键":y,n轮换

配置完并保存后会在linux源码根目录下生成一个.config文件

注意:在ubuntu11上要执行apt-get install libncurses5-dev来安装支持包

2)利用已有的配置文件模板(.config)

1>linux源码根目录/arch/<cpu架构>/configs/<具体某一的CPU文件>,把里面对应的文件copy并改名为.config至linux源码根目录下

2>利用当前运行已有的文件(要用ls /boot/ -a查看)把/boot/config-2.6.18-53.e15拷贝并改名为.config至linux源码根目录下执行以上操作就可以用make menuconfig在拷贝

.config文件上面修改文件了

编译内核:

1)make zImage

2)make bzImage

区别:在X86平台上,zimage只能用于小于512k的内核

获取详细编译信息:make zimage V=1 或 make bzimage V=1

编译好的内核在:arch/<cpu>/boot/目录下

注意:在把.config配置文件cp到根目录编译内核前,必须进入make menuconfig并保存退出(否则生不了效)

编译并安装模块:

1)编译内核模块:make modules

2)安装内核模块:make modules_install INSTALL_MOD_PATH=/lib/modules

更换本机器内核:将编译好的内核模块从内核源码目录copy至/lib/modules下

制作init ramdisk():输入执行命令mkinitrd initrd-2.6.39(任意) 2.6.39(可通过查询/lib/modules下的目录得到)

注意:

mkinitrd命令为redhat里面的,ubuntu的命令为:mkinitramfs -k /lib/modules/模块安装位置 -o initrd-2.6.39(任意) 2.6.39(可通过查询/lib/modules下的目录得到)

如果ubuntu里面没有mkinitramfs命令可以用apt-get install initrd-tools进行安装

安装内核模块:

1)手动

1>cp linux根目录/arch/x86/boot/bzImage /boot/mylinux-2.6.39

2>cp linux根目录/initrd-2.6.39 /boot/initrd-2.6.39

最后修改/etc/grub.conf或/etc/lilo.conf文件

2)自动

1>make install:这个命令会自动完成上面的操作(查看当前内核版本:uname -r)

-----------------------------------------------------------------------------

四.linux内核模块开发

描述:

linux内核组件非常庞大,内核ximage并不包含某组件,而是在该组件需要被使用的时候,动态的添加到正在运行的内核中(也可以卸载),这种机制叫做“内核模块”的机制。内核模块通常通过使用makefile文件对模块进行编译

模块安装与卸载:

1)加载:insmod hello.ko

2)卸载:rmmod hello

3)查看:lsmod

4)加载(自动寻找模块依赖):modprobe hello

modprobe会根据文件/lib/modules/version/modules.dep来查看要加载的模块,看它是否还依赖于其他模块,如果是,会先找到这些模块,把它们先加载到内核

实例分析:

1)moduleDep/1(一个模块的编译)

 
 
  1.  1 #include <linux/module.h> 2 #include <linux/init.h> 3  4 //模块入口函数 5 //__init:表示代码段中的子段,里面的内容只运行一次并且回收内存. 6 static int __init hello_init(void) 7 { 8     printk(KERN_EMERG "hello world!\n"); 9     return 0;10 }11 //模块卸载函数12 //__exit:13 static void __exit hello_exit(void)14 {15     printk(KERN_EMERG "hello exit!\n");16 }17 //内核符号导出 函数18 int add_integar(int a,int b)19 {20     return a+b;  
  2. 21 }22 int sub_integar(int a,int b)23 {24     return a-b;  
  3. 25 }26 27 module_init(hello_init);28 module_exit(hello_exit);29 //函数导出30 EXPORT_SYMBOL(add_integar);31 EXPORT_SYMBOL(sub_integar); 

makefile:

 
 
  1. #第一次执行KERNELRELEASE是空的,所以执行else里面的 
  2. ifneq ($(KERNELRELEASE),) 
  3.  
  4. obj-m :=hello.o 
  5.  
  6. #else块 
  7.  
  8. elseKDIR:= /lib/modules/2.6.18-53.el5/build 
  9.  
  10. all
  11. #KDIR    依赖内核模块源代码路径(内核编译安装路径) 
  12. #PWD     表示内核代码在哪(当前目录) 
  13. #modules 编译的是模块 
  14.     make -C $(KDIR) M=$(PWD) modules  
  15.  
  16. clean: 
  17.     rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order 
  18.  
  19. endif 

2)moduleDep/2(两个模块的编译)

 
 
  1. #include <linux/module.h>  
  2. #include <linux/init.h>  
  3. //模块可选信息  
  4. MODULE_LICENSE("GPL");//许可证声明  
  5. MODULE_AUTHOR("liyuan");//作者声明  
  6. MODULE_DESCRIPTION("This module is a param example.");//模块描述  
  7. MODULE_VERSION("V1.0");//模块别名  
  8. MODULE_ALIAS("a simple module");//模块别名  
  9.  
  10. //模块参数 
  11. static char *name = "liyuan arg"
  12. static int age = 30; 
  13. //S_IRUGO是参数权限,也可以用数字 
  14. module_param(age,int,S_IRUGO); 
  15. module_param(name,charp,S_IRUGO); 
  16.  
  17.  
  18. //使用外部文件函数 
  19. extern int add(int a,int b); 
  20.  
  21.  
  22. //声明 外部内核符号 函数 
  23. extern int add_integar(int a,int b); 
  24. extern int sub_integar(int a,int b); 
  25.  
  26. static int __init mains_init(void) 
  27.      //多文件编译 
  28.  
  29.     printk(KERN_EMERG"param hi"); 
  30.     int vle=add(1,2); 
  31.     printk(KERN_EMERG"add value:%d\n",vle); 
  32.     //模块参数 
  33.  
  34.      printk(KERN_EMERG" name : %s\n",name); 
  35.      printk(KERN_EMERG" age : %d\n",age); 
  36.  
  37.     //使用其他模块的函数(内核符号导出) 
  38.     int adds=add_integar(3,1); 
  39.     int subs=sub_integar(3,1); 
  40.     printk(KERN_EMERG" add_integar : %d\n",adds); 
  41.     printk(KERN_EMERG" sub_integar : %d\n",subs); 
  42.     return 0; 
  43.  
  44. static void __exit mains_exit(void) 
  45.     printk("param exit!"); 
  46.  
  47. module_init(mains_init);52 module_exit(mains_exit); 

add.c

 
 
  1. int add(int a,int b) 
  2.      return a+b; 

makefile

 
 
  1. ifneq ($(KERNELRELEASE),) 
  2. #两个以上内核源文件 生成单独的内核模块名ma 
  3.  
  4. #内核ma 
  5. obj-m :=ma.o 
  6. #下面的ma-objs前面必须和上面一样为ma 
  7. ma-objs := mains.o add.oelseKDIR:= /lib/modules/2.6.18-53.el5/build 
  8.  
  9. all
  10.         make -C $(KDIR) M=$(PWD) modules  
  11. clean: 
  12.     rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order 
  13.  
  14. endif 

运行带参模块:insmod hello.ko name=yuan age=12

内核符号导出(/proc/kallsyms记录了内核中所有导出的符号的名字与地址):

一个内核模块的运行依赖另一个内核模块的函数实现,必须先运行第一个内核模块,这样就需要进行内核符号导出。

注意:

错误信息:disagrees about version of symbol struct_module insmod:error inserting ...

开发内核模块时会出现,内核模块不匹配的情况.是你当前运行的linux内核与编译连接所依赖的

内核版本不匹配,解决方法:

  • 使用modprobe --force-modversion强行插入
  • 可使用uname -r进行查看当前运行的内核版本

printk内核打印:

在<linux/kernel.h>中printk有8个优先级,按优先级递减的是:

  • KERN_EMERG 0

用于紧急的消息,常常是那些崩溃的消息

  • KERN_ALERT 1

需要立刻行动的消息

  • KERN_CRIT 2

严重情况

  • KERN_ERR 3

错误情况

  • KERN_WARNING(printk默认级别) 4

有问题的警告

  • KERN_NOTICE 5

正常情况,但是仍然值得注意

  • KERN_INFO 6

信息消息

  • KERN_DEBUG 7

用作调试消息

不管是哪个级别的都会在/var/log/messages里面打印出来(messages可以删除后,运行内核进行测试内核打印情况)控制台打印(优先级配置/proc/sys/kernel/printk)

6 4 1 7

  • Console_loglevel
  • Default_message_loglevel
  • Minimum_console_level
  • Default_console_loglevel

在vm+redhat安装2.6.39内核时出现的错误

启动时报could not find filesystem '/dev/root'

解决方法

a.通过make menuconfig选中以下对应的选项

General setup -->

[*] enable deprecated sysfs features to support old userspace tools

成功时下面那个也*了的

b.修改.config文件

修改.config文件中CONFIG_SYSFS_DEPRECATED_V2,将原本被注释掉的

CONFIG_SYSFS_DEPRECATED_V2 改成CONFIG_SYSFS_DEPRECATED_V2=y





本文作者:佚名
来源:51CTO
目录
相关文章
|
5天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
16天前
|
Linux C语言
Linux内核队列queue.h
Linux内核队列queue.h
|
1月前
|
存储 Shell Linux
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
32 0
|
1月前
|
Shell Linux C语言
【Shell 命令集合 系统设置 】⭐Linux 卸载已加载的内核模块rmmod命令 使用指南
【Shell 命令集合 系统设置 】⭐Linux 卸载已加载的内核模块rmmod命令 使用指南
29 1
|
9天前
|
算法 Linux 调度
深入理解Linux内核的进程调度机制
【4月更文挑战第17天】在多任务操作系统中,进程调度是核心功能之一,它决定了处理机资源的分配。本文旨在剖析Linux操作系统内核的进程调度机制,详细讨论其调度策略、调度算法及实现原理,并探讨了其对系统性能的影响。通过分析CFS(完全公平调度器)和实时调度策略,揭示了Linux如何在保证响应速度与公平性之间取得平衡。文章还将评估最新的调度技术趋势,如容器化和云计算环境下的调度优化。
|
14天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【4月更文挑战第12天】 在多任务操作系统如Linux中,进程调度机制是系统的核心组成部分之一,它决定了处理器资源如何分配给多个竞争的进程。本文深入探讨了Linux内核中的进程调度策略和相关算法,包括其设计哲学、实现原理及对系统性能的影响。通过分析进程调度器的工作原理,我们能够理解操作系统如何平衡效率、公平性和响应性,进而优化系统表现和用户体验。
20 3
|
21天前
|
Linux API C语言
FFmpeg开发笔记(一)搭建Linux系统的开发环境
本文指导初学者如何在Linux上搭建FFmpeg开发环境。首先,由于FFmpeg依赖第三方库,可以免去编译源码的复杂过程,直接安装预编译的FFmpeg动态库。推荐网站<https://github.com/BtbN/FFmpeg-Builds/releases>提供适用于不同系统的FFmpeg包。但在安装前,需确保系统有不低于2.22版本的glibc库。详细步骤包括下载glibc-2.23源码,配置、编译和安装。接着,下载Linux版FFmpeg安装包,解压至/usr/local/ffmpeg,并设置环境变量。最后编写和编译简单的C或C++测试程序验证FFmpeg环境是否正确配置。
37 8
FFmpeg开发笔记(一)搭建Linux系统的开发环境
|
21天前
|
负载均衡 算法 Linux
深度解析:Linux内核调度器的演变与优化策略
【4月更文挑战第5天】 在本文中,我们将深入探讨Linux操作系统的核心组成部分——内核调度器。文章将首先回顾Linux内核调度器的发展历程,从早期的简单轮转调度(Round Robin)到现代的完全公平调度器(Completely Fair Scheduler, CFS)。接着,分析当前CFS面临的挑战以及社区提出的各种优化方案,最后提出未来可能的发展趋势和研究方向。通过本文,读者将对Linux调度器的原理、实现及其优化有一个全面的认识。
|
21天前
|
Ubuntu Linux
Linux查看内核版本
在Linux系统中查看内核版本有多种方法:1) 使用`uname -r`命令直接显示版本号;2) 通过`cat /proc/version`查看内核详细信息;3) 利用`dmesg | grep Linux`显示内核版本行;4) 如果支持,使用`lsb_release -a`查看发行版及内核版本。
36 6
|
24天前
|
Linux 内存技术
Linux内核读取spi-nor flash sn
Linux内核读取spi-nor flash sn
18 1