Linux 网络初始化流程
1. 系统初始化
(1) X86 PC初始化过程
1) CPU自身初始化
2) BIOS加电自检POST
完成系统硬件的检测,包括内存检测、系统总线检测等
3) BIOS加载内核主引导程序
MBR中的主引导程序包括446字节的程序代码和64字节的分区表,最后两个字节为0xAA55
4) 内核主引导程序加载次引导程序
次引导程序负责加载Linux内核映像,并将控制权交给内核
5) 启动内核
1>.Start()汇编例程:进行一些基本的硬件设置
2>Startup_32()汇编例程,设置一个基本的运行环境(堆栈等),并清除BSS,解压内核
3>第二个Startup_32()对页表进行初始化,启用内存分页功能,并为任何可选的浮点单元(FPU)检测CPU的类型,将其存储起来供以后使用
4>init/main.c中的start_kernel函数
内核的初始化过程
(2) 内核初始化
内核初始化由start_kernel开始,至第一个用户进程init结束
1) start_kernel()
page_address_init()初始化页地址,使用链表将其链接起来
sched_init()初始化进程调度器
init_timers()初始化定时器
softirq_init()初始化tasklet_softirq和hi_softirq
time_init()初始化系统时钟
rest_init()
2) rest_init()
启动内核线程kernel_init和kthreadd,运行kthread_create_list全局链表中的kthread
3) kernel_init
调用do_basic_setup函数初始化设备,完成外设及其驱动程序(直接编译进内核的模块)加载和初始化
static void __init do_basic_setup(void)
{
cpuset_init_smp();
usermodehelper_init();
init_tmpfs();
driver_init();
init_irq_proc();
do_ctors();
do_initcalls();
}
4) init_post
运行第一个用户空间进程init
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
2. 网络协议栈初始化
位于__initcall_start和__initcall_end之间的区域组成了.initcall.init节,其中保存了由xxx_initcall形式的宏标记的函数地址,do_initcall函数可以取得函数地址并执行其指向的函数。
(1) core_initcall:sock_init
分配一些内存空间,以及创建了一个sock_fs_type的文件系统。
(创建一个套接字缓冲区,最常用的操作是alloc_skb,它在skbuff_head_cache中创建一个struct sk_buff,如果要在skbuff_fclone_cache中创建,可以调用__alloc_skb,通过特定参数进行。)
文件系统:首先是调用register_filesystem(&sock_fs_type);把文件系统类型注册到file_system链表上,然后调用kern_mount(&sock_fs_type);把该文件系统注册到super_blocks上。
(2) fs_initcall:inet_init
(3) subsys_initcall:net_dev_init
网络子系统的初始化,这一层主要提供一些设备无关的处理流程,也提供一些公用的函数给底层的Device Driver调用。为网络协议提供统一的发送、接收接口。
1) 创建在/proc文件系统下的入口
2) 流量管理初始化
为每个CPU建立网络数据包的接收/发送队列;向系统注册接收网络数据包的上层协议栈的接收处理函数,这些协议处理函数在向量表ptype_base中进行管理;向系统注册网络子系统中接收/发送数据包的两个软件中断处理函数(NET_TX_SOFTIRQ,NET_RX_SOFTIRQ)
3) 设备与事件初始化
遍历网络设备实例列表,将初始化失败的设备从设备链表中移走;
向CPU热插拔的事件通知链注册网络子系统的回调函数,一旦CPU热插拔事件发生,该回调函数dev_cpu_callback处理CPU事件
4) 初始化路由与其他初始化过程
独立于协议栈的目的路由Cache(DST),由dst_init函数初始化
注册网络子系统
(4) device_initcall:设备驱动初始化
3. send 系统调用
内核代码:sys_sendto(fd,buff,len,flags,NULL,0);
应用层待发送的数据放在自己申请的Buff中,而在INET Socket层使用msghdr{}结构组织数据,而在INET Socket层以下都使用sk_buff{}结构组织数据。
struct msghdr {
void * msg_name; /* Socket name */
int msg_namelen; /* Length of name */
struct iovec * msg_iov; /* Data blocks */
__kernel_size_t msg_iovlen; /* Number of blocks */
void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
__kernel_size_t msg_controllen; /* Length of cmsg list */
unsigned msg_flags;
};
4.
5.