我正在学习设备驱动程序和内核编程。根据 Jonathan Corbet 的书,设备驱动程序中没有任何main()
功能。
所以我有两个问题:
- 为什么我们不需要
main()
设备驱动程序中的函数? - 内核本身有
main()
功能吗?
谁可以给我解释一下这个?
答案1
在用户空间程序中,main()
是程序的入口点由libc初始化代码调用当二进制文件被执行时。内核代码没有奢侈地依赖 libc,因为 libc 本身依赖于内核系统调用接口来进行内存分配、I/O、进程管理等。
main()
也就是说,在内核代码中相当于start_kernel()
,即由引导加载程序调用加载内核映像后,将其解压缩到内存中并设置必要的硬件和内存分页。start_kernel()
执行大部分系统设置并最终生成 init 进程。
Linux 内核模块的入口点是一个 init 函数,它通过调用宏向内核注册module_init()
。然后注册的模块初始化函数是由内核代码调用do_initcalls()
在内核启动期间通过该函数。
答案2
内核没有main
函数。 main
是C语言的一个概念。内核是用C 和汇编语言编写的。内核的入口代码是用汇编语言编写的。
启动顺序的组织如下:
- BIOS 通常从引导块设备加载引导加载程序。现在流行的引导加载程序是 grub。
- Grub 将内核映像加载到 RAM 中,可以使用初始根设备 (
initrd
)。然后执行某个地址处的代码。 - 内核映像有一些内核模块,例如:文件系统模块、设备驱动程序。内核映像使用文件系统模块来挂载根文件系统。现在内核可以从磁盘加载并运行所有内核模块。
- 内核运行初始化任务。例如:遍历PCI总线并找到所有PCI设备,初始化所有设备驱动程序。
- 最后内核创建进程0和进程1(进程
init
),将CPU的上下文从ring 0切换到ring 3,并启动init进程(进程id为1)。现在内核启动完成了! - 该
init
程序运行所有初始化脚本。所有服务均已启动。称为壳牌。用户可以登录。
该main
函数是一个 C 函数。实际上main方法并不是C程序的入口点。 C运行时之前调用了许多函数main
。 GCC 有一个扩展功能:构造函数。声明为“构造函数”的函数在之前调用main
。
例如:
/* This should not be used directly. Use block_init etc. instead. */
#define module_init(function, type) \
static void _attribute__((constructor)) do_qemu_init ## function(void) { \
register_module_init(function, type); \
}
该宏来自 qemu 项目。
答案3
例如,有一个 main() 函数拱门/x86/boot/main.c用于准备系统从实模式切换到保护模式,但其他体系结构没有这样的代码。有一个不错的概述Linux 内核 2.6.x 在 x86 平台上的引导是如何工作的。确实值得一读。
根据文件如何进行 Linux 内核开发,Linux内核是
独立的 C 环境,不依赖于标准 C 库,因此不支持 C 标准的某些部分。
顺便说一句,根据 C 标准意味着什么
独立环境中的程序是否需要定义“主”函数是由实现定义的。