现代CPU通常实现了不同的工作模式,具有不同级别的硬件权限.
Linux系统利用CPU这一特性,使用其中的两级来分别运行内核程序与应用程序,这样使得操作系统本身得到充分的保护.
内核空间与用户空间是程序执行的两种不同状态,通过系统调用和硬件中断能够完成从用户空间到内核空间的转移.
Linux内核源码下载地址
www.kernel.org
Linux内核源码目录结构
内核所支持的每种CPU体系,在该目录下都有对应的子目录。每个CPU的子目录,又进一步分解为boot(系统引导),mm(内存管理),kernel(系统调用)等子目录.
与平台无关的头文件在
include/linux
子目录下;与平台相关的头文件在相应的子目录下;
Linux内核具有可定制的优点,具体步骤如下:
清除临时文件、中间文件和配置文件
make clean
- remove most generated files but keep the config filesmake mrproper
- remove all generated files and config filesmake distclean
- mrproper + remove editor backup and patch files确定目标系统的软硬件配置情况
比如CPU的类型、网卡的型号、所需支持的网络协议等.
使用如下命令之一配置内核:
配置选项说明:
内核配置通常在一个已有的配置文件基础上,通过修改得到新的配置文件, Linux内核提供了一系列可供参考的内核配置文件,位于
arch/<cpu>/configs
.
make config
: 基于文本模式的交互式配置make menuconfig
: 基于文本模式的菜单型配置(推荐),使用方法如下:
Enter
键或快捷键(选项上的高亮字母)进入下一层选单;y
将这个项目编译进内核中,按m
编译为模块,按n
为不选择,按h
可显示该选项的帮助信息; 按空格可在y
m
n
三者间切换; 按Esc
将返回到上层选单;make oldconfig
: 使用已有的配置文件(.config),但会询问新增的配置选项.make xconfig
: 图形化的配置(需安装图形化系统)编译内核
使用如下命令之一编译内核 编译好的内核位于
arch/<cpu>/boot/
目录下
make zImage
- X86平台下只能用于小于512K的内核make bzImage
make zImage V=1
- 获取详细编译信息make bzImage V=1
编译内核模块 - make modules
安装内核模块 - make modules_install
制作init ramdisk - (启动Linux内核专用的一个被压缩过的小型根文件系统)
mkinitrd initrd-$version $version
例:mkinitrd initrd-2.6.29 2.6.29
$version 可以通过查询lib/modules
下的目录得到
内核安装(X86)
cp arch/x86/boot/bzImage /boot/vmlinuz-$version
cp $initrd /boot/
设置启动项,并将启动项关联内核文件及ramdisk
修改/etc/grub.conf
或者/etc/lilo.conf
Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用需要的组件呢?
module_init
宏指定module_exit
宏指定// 范例:
#include <linux/init.h>
#include <linux/module.h>
// 模块加载函数(必需)
static int hello_init(void)
{
printk(KERN_WARNING"Hello, world!\n");
return 0;
}
// 模块卸载函数(必需)
static void hello_exit(void)
{
printk(KERN_INFO"Goodbye, world!\n");
}
module_init(hello_init);
module_exit(hello_exit);
makefile
makefile
, 使得编译中增加内核模块?// 1. 内核模块由一个源文件构成
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /lib/modules/2.6.29/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
// 2. 内核模块由多个源文件构成
// --- main.c ---
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jacey Wu");
MODULE_DESCRIPTION("Hello World Module");
MODULE_ALIAS("a simplest module");
extern int add(int a, int b);
static int __init hello_init()
{
printk("Hello World!\n");
add(1, 2);
return 0;
}
static void __exit hello_exit()
{
printk("<7>hello <0>exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
// --- main.c ---
// --- add.c ---
int add(int a, int b)
{
return a+b;
}
// --- add.c ---
// --- makefile ---
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
hello-objs := main.o add.o
else
KDIR := /lib/modules/2.6.29/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
// --- makefile ---
insmod
rmmod
lsmod
modprobe
modprobe
与insmod
区别在于, 前者会根据文件/lib/modules/<$version>/modules.dep
来查看要加载的模块,是否依赖于其他模块,如果是,将会首先找到这些模块,把它们先加载到内核.
MODULE_LICENSE
MODULE_AUTHOR
MODULE_DESCRIPTION
MODULE_VERSION
MODULE_ALIAS
module_param(name, type, perm)
bool
:布尔型; int
:整型; charp
:字符串型;S_IRUGO
: 任何用户都对/sys/module
中出现的该参数具有读权限;S_IWUSR
: 允许root用户修改/sys/module
中出现的该参数;// --- param.c ---
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static char* name = "Jacey Wu";
static int age = 100;
module_param(age, int, S_IRUGO);
module_param(name, charp, S_IRUGO);
static int hello_init(void)
{
printk(KERN_EMERG" Name:%s\n", name);
printk(KERN_EMERG" Age:%d\n", age);
return 0;
}
static void hello_exit(void)
{
printk(KERN_INFO" Module Exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
// --- param.c ---
/proc/kallsyms
记录了内核中所有导出的符号的名字与地址EXPORT_SYMBOL()
EXPORT_SYMBOL_GPL()
- 只能用于包含GPL许可证的模块.// hello.c
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jacey Wu");
MODULE_DESCRIPTION("Hello World Module");
MODULE_ALIAS("a simplest module");
extern int add_integar(int a, int b);
extern int sub_integar(int a, int b);
static int __init hello_init()
{
int res = add_integar(1, 2);
return 0;
}
static void __exit hello_exit()
{
int res = sub_integar(2, 1);
}
module_init(hello_init);
module_exit(hello_exit);
// calculate.c
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
int add_integar(int a, int b)
{
return a+b;
}
int sub_integar(int a, int b)
{
return a-b;
}
static int __init sym_init()
{
return 0;
}
static int __exit sym_exit()
{
}
module_init(sym_init);
module_exit(sym_exit);
EXPORT_SYMBOL(add_integar); // 内核符号导出
EXPORT_SYMBOL(sub_integar); // 内核符号导出
常见问题
insmod
程序会将内核模块版本与当前正在运行的内核版本比较,如果不一致将会报错.modprobe --force-modversion
强行插入uname -r
查看当前运行的内核版本内核模块与应用程序的区别:
内核打印 - printk
与printf
区别:
printk
在内核中使用; printf
在应用程序中使用;printk
允许根据严重程度,通过附加不同的"优先级"来对消息分类; 按照优先级递减的顺序分别为:
KERN_EMERG
- 用于紧急消息KERN_ALERT
- 需要立即行动的消息KERN_CRIT
- 严重情况KERN_ERR
- 错误情况KERN_WARNING
- 有问题的警告KERN_NOTICE
- 正常情况,但是仍然值得注意KERN_INFO
- 信息型消息KERN_DEBUG
- 用作调试信息没有指定优先级的
printk
默认使用DEFAULT_MESSAGE_LOGLEVEL
优先级, 是一个在kernel/printk.c
中定义的整数控制台优先级配置
/proc/sys/kernel/printk 6 4 1 7
Console_loglevel
Default_message_loglevel
Minimum_console_level
Default_console_loglevel
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。