内核开发

一,linux内核介绍:
linux内核体系结构总体分为两部分:用户空间、内核空间。然而各自两部分也可以细分。
内核开发-第一天 - ds_liang - Ds_liang
用户空间
user app:用户应用应用程序,一般都运行在用户态
glibc:库函数
内核空间
sci:系统调用,应用程序一般通过系统调用进入内核态
kernl:linux内核,里面还可以细分为很多部分
adkc:arch,就是架构。这部分就是内核与体系结构相关的代码
然而更进一步细分如下图,
内核开发-第一天 - ds_liang - Ds_liang
一共七个部分
sci:系统调用,应用程序进入内核态的一个入口
pm:进程管理
vfs:虚拟文件系统,linux对各种文件系统进行封装对最总成为一个统一的借口
mm:内存管理系统,主要是内存的管理
ns:网络栈
arch:与体系结构相关代码
dd:驱动程序

为什么分为用户态、内核态?
arm系统处理器可以处于其中不同的模式,然而一般应用程序都运行在用户模式或者系统模式。另外五种模式拥有 处理器的全部硬件资源。假如程序运行在最高权力之下出来问题还有谁能管?出于对内核的保护,必须让程序运行在一 种权限受限的模式之下,前面所述的两种模式正合。分这两种状态最最要是对内核的保护。

二,linxu内核源代码
linux内核源代码结构基本就是像树一样,从根部向各方向伸展。并且按照一定的规律组织起来。
要注意的主要有两点:
mm是linux通用的内存管理代码,而与体系相关的内存管理代码在arch/xxx/mm目录下
include/linux中的也是linux通用的头文件,与体系相关的头文件在arch/xxx/include目录下
感觉这部分没什么好讲的。

三,linux内核配置与编译
编译linux内核之前就必须对其进行配置,这也是必须的吧。因为你编译都要有相对编译的依据吧,至少那个要编 译那个无须编译都都要描述的一清二楚的吧。其中根目录下.config文件里面的东东就是觉得如何那个应该编译那个不应 该编译那个编译那个又要编译成模块。
有一个指令不得不讲, cp /boot/config-2.6.35-22-generic ./.config复制当前系统的配置到根目录下,这样我们就可
以获取到当前系统的配置。而且编译一个新的属于自己的内核也不是梦了,当然在这前提在在根据自己的需要配置内核 是一个多么爽的事情呢!特别以前看ldd3一开是就要编译内核,啊!哥哥不会啊,这么多配置怎么搞啊?有了这东东老 实说根本不用烦!
假如没修改Makefile默认就是编译x86架构的内核,x86要求内存小于512kb要我自己配置我真的无法达到这高度。 而且x86使用的是bzImage格式镜像,我们的arm用uboot引导的话使用的几时uImage格式。
make clean:删除编译生成的中间文件(*.o)
make mrproper:删除编译生成的中间文件(*.o)并且删除配置所产生的配置文件(.config)
make distclean:删除编译生成的中间文件(*.o)、删除配置所产生的配置文件(.config)并且path、bakup等文 件
make menuconfig:基于文本模式进行配置
推荐这模式,在这模式之下可以选择为y、n、m
y:编译进内核
n:不编译进内核
m:编译成模块,当需要使用的时候动态加载到内核
(y ) a.c ---> a.o 结合其他a.o文件链接成为Image(内核)文件
(m) b.c ---> b.o 编译模块
(   ) c.c       不编译

基于已有.config进行配置
通过 cp /boot/config-2.6.35-22-generic ./.config获得当前系统配置文件这是一种方法。
在arch/xxx/config目录下有很多改架构的配置文件,可以选着一个作为蓝本进行配
(S3C2440:cp arch/arm/configs/s3c2410_defconfig ./config,make s3c2410_defconfig效果一样)
现在直接make可以进行内核编译了。
接着make bzImage编译出bzImage镜像,make modules_install安装模块。默认在装在/lib/module/xxx目录下

                制作init ramdisk
                mkinitrd initrd-xxx xxx
                后面的xxx代表/lib/module目下那个由我们刚刚编译出来的文件夹一定要匹配,在该目录下会生成initrd-xxx文件
(ubuntu方法不一样sudo mkinitramfs -o /boot/initrd.img-3.0.4,直接在/boot下生成initrd.ing-3.0.4)

添加启动引导
复制bzImage到/boot并重命名(vmlinuz-3.0.4)
复制initrd-3.0.4到/boot并重命名(initrd.img-3.0.4)
因为我是用ubuntu跟redhat有点不一样,修改vi /boot/grub/grub.cfg 内核开发-第一天 - ds_liang - Ds_liang
  每一个menuentry代笔一个启动项,我们依瓢画葫就ok了。

因为写的比较乱,下面总结一下:
1, cp /boot/config-2.6.35-22-generic ./.config复制当前系统配置,其后配置可以在这基础上安装自己需要修改配 置
2,make menuconfig 配置内核
3,make 编译
4,make bzImage 编译bzImage格式内核
5,make modules && make modules_install 安装模块
6,mkinitrd initrd-xxx xxx 生成init ramdisk(sudo mkinitramfs -o /boot/initrd.img-3.0.4)
7,复制bzImage,initrd-xxx到/boot
8,修改grub.cfg
到此就已经编译出一个属于自己的linux内核!

四,内核模块开发

linux可以将功能编译成模块,在需要使用的时候加到内核。从而实现动态,这不单单决绝了内核文件过大的问题,而且但组件发送变化只需要重新编译模块就可以了。加载过程不需要重启电脑这也是模块最大的好处,十分方便调试!功能模块一般要么编译进内核要么编译成模块。如果编译进内核会造出内核文件变大而且修改不变,发现变化时都必须重新编译内核,但是优点是一直都存在内核中。我觉得也不算是优点吧。编译成模块则在使用时加载就可以实现其功能,并不一直存在内核中。就是使用时加载。特点不编译进内核,在运行时期动态安装、卸载。

         模块框架:

 

         //特定头文件

         #include <linux/init.h>

         #include <linux/moudule.h>

        

         //许可证

         MODULE_LICENSE(“GPL”);

        

         //初始化函数

         static int __init hello_init(void)

         {

        

                   return 0;

         }

 

         //退出函数

         static void __exit hello_exit(void)

         {

 

                   return ;

         }

 

         //两个指定初始化函数、退出函数的宏

         module_init(hello_init);

         module_exit(hello_exit);

        

         makefile框架:

         #第一次在当前目录没定义,在linux代码运行此时有定义并且导出obj-m

         ifneq ($(KERNELRELEASE),)

 

         #obj-m代表模块

         obj-m := hello.o

 

         else

        

         #指定linux代码目录

         KDIR := /lib/module/xxx/build

        

         all:

                   make –C $(KDIR) M=$(PWD) modules

 

         clean:

                   rm -f *.ko *.o *.mod.o *.mod.c *.symvers

         endif

 

         insmod:加载模块

         rmmod:卸载模块

         lmmod:列出所加载的函数

 

         c语言函数与模块区别

         1,c语言指定main函数执行并且执行完后就消失了。但是模块一旦加载就一直存在,           知道将其卸载。通过两个宏指定进出入口。

         2,c语言可以直接编译,但是模块就必须结合linux代码、makef来进行编译

 

         hello例子(module_param)

         #include <linux/init.h>

         #include <linux/module.h>

 

         //使用参数,必须点定义参数module_param再声明参数

         //module_param(, , )第二个参数为类型可以为int,bool,charp.第三个参数为全限

         //假如不传递参数默认为1

         int count  = 1;

         module_param(count, int, S_IRUGO);

 

         MODULE_LICENSE(“GPL”);

 

        static int __init hello_init(void)

         {

                   for(;count > 0;count--)

                   printk(KERN_ERR “hello,Dsliang!\n”);

 

                   reutn 0;

         }                          

 

         static void __exit hello_exit(void)

         {

                   printk(KERN_ERR “GoodBye!\n”);

 

                   return ;

         }

 

         //指定hello_init函数,当调用ismod加载此模块时自动调用hello_init函数

         module_init(hello_init);

         //指定hello_exit函数,当调用rmmod加载此模块时自动调用hello_exit函数

         module_exit(hello_exit);

 

         symbol例子(EXPORT_SYMBOL)

         //symbol.c

         #include <linux/init.h>

         #include <linux/module.h>

 

         MODULE_LICENSE(“GPL”);

 

         extern void myprint(char *ptr);

        

         static int __init symbol_init(void)

         {

                   myprint(“hello!Dsliang”);

 

                   return 0;

         }

 

         static void __exit symbol_exit(void)

         {

                   myprint(“GoodBye!”);    

 

                   return ;

         }

 

         module_init(symbol_init);

         module_exit(symbol_exit);

 

 

         //symbol_export.c

         #include <linux/init.h>

         #include <inux/module.h>

 

         MODULE_LICENSE(“GPL”);

 

         void myprint(char *p)

         {

                   printk(KERN_ERR “%s”, p);

 

                   return ;

         }

 

         static int __init symbol_export_init(void)

         {

 

                   return 0; 

         }

         static void __exit symbol_export_exit(void)

         {

 

                   return ;

         }

        

         module_init(symbol_export_init);

         module_exit(symbol_export_exit);

         EXPORT_SYMBOL(myprint);

 

 

         !!!主要一定要再同一个目录下编译才能

         //obj-m = symbol.o symbol_export.o

 

         通过查看/proc/kallsyms可以看到对于的符号有没在系统

         EXPORT_SYMBOL(xxx)à添加xxx函数关联到/proc/kallsyms文件中

         假如没有这符号,加载模块会出现”unknow symbol in module”并且无法加载

 

         假如有不匹配问题(当前运行内核没其代码):

1,  可以使用modprobe –force-modversion 强制加载(不建议)

2,  使用同一版本内核代码进行编译

 

printk与printf

         printk是内核函数,printf是c库封装了printk函数。在内核只能使用printk。

         printk函数有优先级”<0>” “<1>” ”<2>” “<3>” ”<4>” “<5>” ”<6>” ”<7>”优先级越高值越低

         不加默认为KERN_WARNING,假如优先级低于(Console_loglevel)则不输出终端。但是无论如何在 /var/log/message都会记录所有输出

         通过查看/proc/sys/kernel/printk(可以对其修改,但是重启复位)可以看到对应的值

         4                          4                          1                          7               

         控制台              默认值             

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值