|
| 1 | +## **1、Linux内核简介** |
| 2 | + |
| 3 | +**linux kernel map:** |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +**linux 系统体系结构:** |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | +**linux kernel体系结构:** |
| 12 | +arm有7种工作模式,x86也实现了4个不同级别RING0-RING3,RING0级别最高, |
| 13 | +这样linux用户代码运行在RING3下,内核运行在RING0,这样系统本身就得到了 |
| 14 | +充分的保护 |
| 15 | + |
| 16 | +**用户空间(用户模式)转到内核空间(系统模式)方法:** |
| 17 | +·系统调用 |
| 18 | +·硬件中断 |
| 19 | + |
| 20 | +**linux kernel 体系结构:** |
| 21 | + |
| 22 | + |
| 23 | + |
| 24 | +**虚拟文件系统VFS:** |
| 25 | +VFS(虚拟文件系统)隐藏各种文件系统的具体细节,为文件操作提供统一的接口 |
| 26 | + |
| 27 | +## **2、Linux内核源代码** |
| 28 | + |
| 29 | +linux内核下载***[http://www.kernel.org](https://link.zhihu.com/?target=http%3A//www.kernel.org)\*** |
| 30 | +**目录结构:** |
| 31 | +解压linux kernel tar后目录 |
| 32 | +·arch:根据cpu体系结构不同而分的代码 |
| 33 | +·block:部分块设备驱动程序 |
| 34 | +·crypto:加密,压缩,CRC校验算法 |
| 35 | +·documentation:内核文档 |
| 36 | +·drivers:设备驱动程序 |
| 37 | +·fs(虚拟文件系统vfs):文件系统 |
| 38 | +·include:内核所需的头文件,(与平台无关的头文件在include/linux中) |
| 39 | +·lib:库文件代码(与平台相关的) |
| 40 | +·mm:实现内存管理,与硬件体系结构无关的(与硬件体系结构相关的在arch中) |
| 41 | +·net:网络协议的代码 |
| 42 | +·samples:一些内核编程的范例 |
| 43 | +·scripts:配置内核的脚本 |
| 44 | +·security:SElinux的模块 |
| 45 | +·sound:音频设备的驱动程序 |
| 46 | +·usr:cpio命令实现,用于制作根文件系统的命令(文件系统与内核放到一块的命令) |
| 47 | +·virt:内核虚拟机 |
| 48 | + |
| 49 | +**linux DOC 编译生成:** |
| 50 | + |
| 51 | +linux源根目录/Documentation/00-INDEX:目录索引 |
| 52 | +linux源根目录/Documentation/HOWTO:指南 |
| 53 | +·生成linux内核帮助文档:在linux源根目录(Documentation) 执行make htmldocs |
| 54 | + |
| 55 | +ubuntu16下需要执行sudo apt-get install xmlto安装插件才可生成doc文档 |
| 56 | + |
| 57 | +后面开发中经常要改的是arch,drivers中的代码 |
| 58 | + |
| 59 | +## **3、Linux内核配置与编译** |
| 60 | + |
| 61 | +**清理文件(在linux源码根目录):** |
| 62 | +·make clean:只清理所有产生的文件 |
| 63 | +·make mrproper:清理所有产生的文件与config配置文件 |
| 64 | +·make distclean:清理所有产生的文件与config配置文件,并且编辑过的与补丁文件 |
| 65 | +↓ |
| 66 | +**配置(收集硬件信息如cpu型号,网卡等...):** |
| 67 | +·make config:基于文本模式的交互配置 |
| 68 | +·make menuconfig:基于文本模式的菜单模式(推荐使用) |
| 69 | +·make oldconfig:使用已有的.config,但会询问新增的配置项 |
| 70 | +·make xconfig:图形化的配置(需要安装图形化系统) |
| 71 | +**配置方法:** |
| 72 | +1)使用make menuconfig操作方法: |
| 73 | +1>按y:编译>连接>镜像文件 |
| 74 | +2>按m:编译 |
| 75 | +3>按n:什么都不做 |
| 76 | +4>按"空格键":y,n轮换 |
| 77 | +配置完并保存后会在linux源码根目录下生成一个.config文件 |
| 78 | +注意:在ubuntu11上要执行apt-get install libncurses5-dev来安装支持包 |
| 79 | +2)利用已有的配置文件模板(.config) |
| 80 | +1>linux源码根目录/arch/<cpu架构>/configs/<具体某一的CPU文件>,把里面对应的文件copy并改名为.config至linux源码根目录下 |
| 81 | +2>利用当前运行已有的文件(要用ls /boot/ -a查看)把/boot/config-2.6.18-53.e15拷贝并改名为.config至linux源码根目录下执行以上操作就可以用make menuconfig在拷贝 |
| 82 | +.config文件上面修改文件了 |
| 83 | +↓ |
| 84 | +**编译内核:** |
| 85 | +1)make zImage |
| 86 | +2)make bzImage |
| 87 | +区别:在X86平台上,zimage只能用于小于512k的内核 |
| 88 | +获取详细编译信息:make zimage V=1 或 make bzimage V=1 |
| 89 | +编译好的内核在:arch/<cpu>/boot/目录下 |
| 90 | +注意:在把.config配置文件cp到根目录编译内核前,必须进入make menuconfig并保存退出(否则生不了效) |
| 91 | +↓ |
| 92 | +**编译并安装模块:** |
| 93 | + |
| 94 | +1)编译内核模块:make modules |
| 95 | +2)安装内核模块:make modules_install INSTALL_MOD_PATH=/lib/modules |
| 96 | +更换本机器内核:将编译好的内核模块从内核源码目录copy至/lib/modules下 |
| 97 | +制作init ramdisk():输入执行命令mkinitrd initrd-2.6.39(任意) 2.6.39(可通过查询/lib/modules下的目录得到) |
| 98 | +注意: |
| 99 | +mkinitrd命令为redhat里面的,ubuntu的命令为:mkinitramfs -k /lib/modules/模块安装位置 -o initrd-2.6.39(任意) 2.6.39(可通过查询/lib/modules下的目录得到) |
| 100 | +如果ubuntu里面没有mkinitramfs命令可以用apt-get install initrd-tools进行安装 |
| 101 | +↓ |
| 102 | +**安装内核模块:** |
| 103 | +1)手动 |
| 104 | +1>cp linux根目录/arch/x86/boot/bzImage /boot/mylinux-2.6.39 |
| 105 | +2>cp linux根目录/initrd-2.6.39 /boot/initrd-2.6.39 |
| 106 | +最后修改/etc/grub.conf或/etc/lilo.conf文件 |
| 107 | +2)自动 |
| 108 | +1>make install:这个命令会自动完成上面的操作(查看当前内核版本:uname -r) |
| 109 | +\----------------------------------------------------------------------------- |
| 110 | + |
| 111 | +## **4、linux内核模块开发** |
| 112 | + |
| 113 | +描述: |
| 114 | +linux内核组件非常庞大,内核ximage并不包含某组件,而是在该组件需要被使用的时候,动态的添加到正在运行的内核中(也可以卸载),这种机制叫做“内核模块”的机制。内核模块通常通过使用makefile文件对模块进行编译 |
| 115 | + |
| 116 | +**模块安装与卸载:** |
| 117 | +1)加载:insmod hello.ko |
| 118 | +2)卸载:rmmod hello |
| 119 | +3)查看:lsmod |
| 120 | +4)加载(自动寻找模块依赖):modprobe hello |
| 121 | +modprobe会根据文件/lib/modules/version/modules.dep来查看要加载的模块,看它是否还依赖于其他模块,如果是,会先找到这些模块,把它们先加载到内核 |
| 122 | + |
| 123 | +**实例分析:** |
| 124 | +1)moduleDep/1(一个模块的编译) |
| 125 | + |
| 126 | +```text |
| 127 | +#include <linux/module.h> |
| 128 | +#include <linux/init.h> |
| 129 | +
|
| 130 | +//模块入口函数 |
| 131 | +//__init:表示代码段中的子段,里面的内容只运行一次并且回收内存. |
| 132 | +static int __init hello_init(void) |
| 133 | +{ |
| 134 | + printk(KERN_EMERG "hello world!\n"); |
| 135 | + return 0; |
| 136 | +} |
| 137 | +//模块卸载函数 |
| 138 | +//__exit: |
| 139 | +static void __exit hello_exit(void) |
| 140 | +{ |
| 141 | + printk(KERN_EMERG "hello exit!\n"); |
| 142 | +} |
| 143 | +//内核符号导出 函数 |
| 144 | +int add_integar(int a,int b) |
| 145 | +{ |
| 146 | + return a+b; |
| 147 | +} |
| 148 | +int sub_integar(int a,int b) |
| 149 | +{ |
| 150 | + return a-b; |
| 151 | +} |
| 152 | +
|
| 153 | +module_init(hello_init); |
| 154 | +module_exit(hello_exit); |
| 155 | +//函数导出 |
| 156 | +EXPORT_SYMBOL(add_integar); |
| 157 | +EXPORT_SYMBOL(sub_integar); |
| 158 | +``` |
| 159 | + |
| 160 | +makefile: |
| 161 | + |
| 162 | +```text |
| 163 | +#第一次执行KERNELRELEASE是空的,所以执行else里面的 |
| 164 | +ifneq ($(KERNELRELEASE),) |
| 165 | +
|
| 166 | +obj-m :=hello.o |
| 167 | +
|
| 168 | +#else块 |
| 169 | +else |
| 170 | +
|
| 171 | +KDIR:= /lib/modules/2.6.18-53.el5/build |
| 172 | +
|
| 173 | +all: |
| 174 | +#KDIR 依赖内核模块源代码路径(内核编译安装路径) |
| 175 | +#PWD 表示内核代码在哪(当前目录) |
| 176 | +#modules 编译的是模块 |
| 177 | + make -C $(KDIR) M=$(PWD) modules |
| 178 | +
|
| 179 | +clean: |
| 180 | + rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order |
| 181 | +
|
| 182 | +endif |
| 183 | +``` |
| 184 | + |
| 185 | +2)moduleDep/2(两个模块的编译) |
| 186 | + |
| 187 | +```text |
| 188 | +#include <linux/module.h> |
| 189 | +#include <linux/init.h> |
| 190 | +//模块可选信息 |
| 191 | +MODULE_LICENSE("GPL");//许可证声明 |
| 192 | +MODULE_AUTHOR("liyuan");//作者声明 |
| 193 | +MODULE_DESCRIPTION("This module is a param example.");//模块描述 |
| 194 | +MODULE_VERSION("V1.0");//模块别名 |
| 195 | +MODULE_ALIAS("a simple module");//模块别名 |
| 196 | +
|
| 197 | +//模块参数 |
| 198 | +static char *name = "liyuan arg"; |
| 199 | +static int age = 30; |
| 200 | +//S_IRUGO是参数权限,也可以用数字 |
| 201 | +module_param(age,int,S_IRUGO); |
| 202 | +module_param(name,charp,S_IRUGO); |
| 203 | +
|
| 204 | +
|
| 205 | +//使用外部文件函数 |
| 206 | +extern int add(int a,int b); |
| 207 | +
|
| 208 | +
|
| 209 | +//声明 外部内核符号 函数 |
| 210 | +extern int add_integar(int a,int b); |
| 211 | +extern int sub_integar(int a,int b); |
| 212 | +
|
| 213 | +static int __init mains_init(void) |
| 214 | +{ |
| 215 | + //多文件编译 |
| 216 | +
|
| 217 | + printk(KERN_EMERG"param hi"); |
| 218 | + int vle=add(1,2); |
| 219 | + printk(KERN_EMERG"add value:%d\n",vle); |
| 220 | + //模块参数 |
| 221 | +
|
| 222 | + printk(KERN_EMERG" name : %s\n",name); |
| 223 | + printk(KERN_EMERG" age : %d\n",age); |
| 224 | +
|
| 225 | + //使用其他模块的函数(内核符号导出) |
| 226 | + int adds=add_integar(3,1); |
| 227 | + int subs=sub_integar(3,1); |
| 228 | + printk(KERN_EMERG" add_integar : %d\n",adds); |
| 229 | + printk(KERN_EMERG" sub_integar : %d\n",subs); |
| 230 | + return 0; |
| 231 | +} |
| 232 | +
|
| 233 | +static void __exit mains_exit(void) |
| 234 | +{ |
| 235 | + printk("param exit!"); |
| 236 | +} |
| 237 | +
|
| 238 | +module_init(mains_init); |
| 239 | +module_exit(mains_exit); |
| 240 | +``` |
| 241 | + |
| 242 | +add.c |
| 243 | + |
| 244 | +```text |
| 245 | +int add(int a,int b) |
| 246 | +{ |
| 247 | + return a+b; |
| 248 | +} |
| 249 | +``` |
| 250 | + |
| 251 | +makefile |
| 252 | + |
| 253 | +```text |
| 254 | +ifneq ($(KERNELRELEASE),) |
| 255 | +#两个以上内核源文件 生成单独的内核模块名ma |
| 256 | +
|
| 257 | +#内核ma |
| 258 | +obj-m :=ma.o |
| 259 | +#下面的ma-objs前面必须和上面一样为ma |
| 260 | +ma-objs := mains.o add.o |
| 261 | +
|
| 262 | +else |
| 263 | +
|
| 264 | +KDIR:= /lib/modules/2.6.18-53.el5/build |
| 265 | +
|
| 266 | +all: |
| 267 | + make -C $(KDIR) M=$(PWD) modules |
| 268 | +clean: |
| 269 | + rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order |
| 270 | +
|
| 271 | +endif |
| 272 | +``` |
| 273 | + |
| 274 | +**运行带参模块**:insmod hello.ko name=yuan age=12 |
| 275 | +内核符号导出(/proc/kallsyms记录了内核中所有导出的符号的名字与地址): |
| 276 | +一个内核模块的运行依赖另一个内核模块的函数实现,必须先运行第一个内核模块,这样就需要进行内核符号导出。 |
| 277 | + |
| 278 | +**注意:** |
| 279 | +错误信息:disagrees about version of symbol struct_module insmod:error inserting ... |
| 280 | +开发内核模块时会出现,内核模块不匹配的情况.是你当前运行的linux内核与编译连接所依赖的 |
| 281 | +内核版本不匹配,解决方法: |
| 282 | +·使用modprobe --force-modversion强行插入 |
| 283 | +·可使用uname -r进行查看当前运行的内核版本 |
| 284 | + |
| 285 | +**printk内核打印:** |
| 286 | +在<linux/kernel.h>中printk有8个优先级,按优先级递减的是: |
| 287 | +·KERN_EMERG 0 |
| 288 | +用于紧急的消息,常常是那些崩溃的消息 |
| 289 | +·KERN_ALERT 1 |
| 290 | +需要立刻行动的消息 |
| 291 | +·KERN_CRIT 2 |
| 292 | +严重情况 |
| 293 | +·KERN_ERR 3 |
| 294 | +错误情况 |
| 295 | +·KERN_WARNING(printk默认级别) 4 |
| 296 | +有问题的警告 |
| 297 | +·KERN_NOTICE 5 |
| 298 | +正常情况,但是仍然值得注意 |
| 299 | +·KERN_INFO 6 |
| 300 | +信息消息 |
| 301 | +·KERN_DEBUG 7 |
| 302 | +用作调试消息 |
| 303 | + |
| 304 | +不管是哪个级别的都会在/var/log/messages里面打印出来(messages可以删除后,运行内核进行测试内核打印情况)控制台打印(优先级配置/proc/sys/kernel/printk) |
| 305 | + |
| 306 | +6 4 1 7 |
| 307 | +·Console_loglevel |
| 308 | +·Default_message_loglevel |
| 309 | +·Minimum_console_level |
| 310 | +·Default_console_loglevel |
| 311 | + |
| 312 | +在vm+redhat安装2.6.39内核时出现的错误 |
| 313 | +启动时报could not find filesystem '/dev/root' |
| 314 | +解决方法 |
| 315 | +a.通过make menuconfig选中以下对应的选项 |
| 316 | +General setup --> |
| 317 | +[*] enable deprecated sysfs features to support old userspace tools |
| 318 | +成功时下面那个也*了的 |
| 319 | +b.修改.config文件 |
| 320 | +修改.config文件中CONFIG_SYSFS_DEPRECATED_V2,将原本被注释掉的 |
| 321 | +CONFIG_SYSFS_DEPRECATED_V2 改成CONFIG_SYSFS_DEPRECATED_V2=y |
| 322 | + |
| 323 | +---- |
| 324 | + |
| 325 | +版权声明:本文为知乎博主「[极致Linux内核](https://www.zhihu.com/people/linuxwang-xian-sheng)」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 |
| 326 | + |
| 327 | +原文链接:https://zhuanlan.zhihu.com/p/549065925 |
0 commit comments