TCPIP协议栈初始化流程
显然就是根据每个 block 的优先级把这个 block 排列在一个 block 的链表里面,在 notifier_chain_register 函数里面我们可以发现这个链表是 netdev_chain。实际上这个链表的作用就 是在每个 interface 打开,关闭状态改变或者外界调用相应的 ioctl 的时候通知这个链表上面的所有相 关的设备,而每一个协议都调用 register_netdevice_notifier 注册了一个 netdev_notifier 的结构体, 这样就可以在 interface 改变的时候得到通知了(通过调用每个 notifier_call 函数)。
proto_init 函数在同一个文件里面:
void __init proto_init(void) { extern struct net_proto protocols[]; struct net_proto *pro; pro = protocols; while (pro->name != NULL) { (*pro->init_func)(pro); pro++; } }
struct net_proto 在 include/linux/net.h 里面是这样的:
struct net_proto { const char *name; /* Protocol name */ void (*init_func)(struct net_proto *); /* Bootstrap */ };
这里要说明的是 packet_netdev_notifier 是一个 struct notifier_block 类型,这个 struct 是在 include/linux/notifier.h 里面的:
struct notifier_block { int (*notifier_call)(struct notifier_block *self, unsigned long, void *); struct notifier_block *next; int priority; };
for (i = 0; i < NPROTO; i++) net_families[i] = NULL; sk_init(); proto_init();
其中 net_families 在 include/linux/net.h 里面定义,是这样的:
struct net_proto_family { int family; int (*create)(struct socket *sock, int protocol); /* These are counters for the number of different methods of each we support */ short authentication; short encryption; short encrypt_net; };
下面来看 inet_proto_init 函数,这个函数在 net/ipv4/af_inet.c 中间,里面也有很多 ifdef 的编 译选项,假定下面几个是没有定义的:CONFIG_NET_IPIP,CONFIG_NET_IPGRE,CONFIG_IP_FIREWALL, CONFIG_IP_MASQUERADE,CONFIG_IP_MROUTE
其中有用的只有前两项,那个 create 的 callback 函数是每个协议,例如 AF_INET 等初始化上层协 议如 TCP/ICMP 协议需要的,以后还会遇到的,这里先放着把
sk_init 函数在 net/core/sock.c 里面,没什么说的..
struct sock *sk_alloc(int family, int priority, int zero_it) { struct sock *sk = kmem_cache_alloc(sk_cachep, priority); if(sk) { if (zero_it) memset(sk, 0, sizeof(struct sock)); sk->family = family; } return sk; }
这个 protocols 的数组是在 net/protocols.c 里面定义的,包含了一堆的协议初始化结构体,其中 我只注意两个:AF_INET 和 AF_PACKET 它们的初始化函数分别是 inet_proto_init 和 packet_proto_init
协议初始化-1
下面来看看 IPv4 协议和 PACKET 协议的初始化过程。
首先 sock_init 函数里面有很多 ifdef 这样的东东,我觉得对于一个普通的主机来说,这些都不会 配置的,它们包括: SLAB_SKB,CONFIG_WAN_ROUTER,CONFIG_FIREWALL,CONFIG_RTNETLINK,CONFIG_NETLINK_DEV
去掉了这些编译选项以后就剩下这样的代码:
协议初始化-2
其中的 sock_register 函数的作用已经在前面说Biblioteka Baidu,现在来看看 struct inet_protocol 和 inet_add_protocol 函数。前面的结构体
是在 include/net/protocol.h 里面:
struct inet_protocol { int (*handler)(struct sk_buff *skb, unsigned short len); void (*err_handler)(struct sk_buff *skb, unsigned char *dp, int len); struct inet_protocol *next; unsigned char protocol; unsigned char copy:1; void *data; const char *name; };
假定下面几个是定义了的:CONFIG_INET_RARP,CONFIG_PROC_FS 下面是整理后的代码:
(void) sock_register(&inet_family_ops); for(p = inet_protocol_base; p != NULL;) { struct inet_protocol *tmp=(struct inet_protocol *)p->next; inet_add_protocol(p); printk("%s%s",p->name,tmp?", ":"\n"); p = tmp; }
首先看 PACKET 协议,首先我们假定 PACKET 协议是编译在核心里面的,而不是一个 MODULE,这样得 到 packet_proto_init 函数在 net/packet/af_packet.c 里面是这样的:
void __init packet_proto_init(struct net_proto *pro) { sock_register(&packet_family_ops); register_netdevice_notifier(&packet_netdev_notifier); }
而 register_netdevice_notifier 函数在 net/core/dev.c 里面,是这样的:
int register_netdevice_notifier(struct notifier_block *nb) { return notifier_chain_register(&netdev_chain, nb); }
而 notifier_chain_register 函数在 include/linux/notifier.h 里面,是这样的:
extern __inline__ int notifier_chain_register( struct notifier_block **list, struct notifier_block *n) { while(*list) { if(n->priority > (*list)->priority) break; list= &((*list)->next); } n->next = *list; *list=n; return 0; }
p2 = (struct inet_protocol *) p2->next; } }
显然这个函数就是建立一个 hash 表,然后每个 hash 表项都是一个链表头,然后通过这个 hash 表加 链表的方式访问每个协议结构体。在这里你也见到了 copy 成员的用法了把。
arp_init 函数是在 net/ipv4/arp.c 里面的(假定没有定义 CONFIG_SYSCTL):
TCP/IP 协议栈初始化流程
启动以后 先从 init/main.c 的 start_kernel 函数说起。 在这个函数里面调用 kernel_thread 启动了 init 进程,这个进程对应的函数是同一个文件里面的 init 函数,在 init 函数里面调用了一个 叫 do_basic_setup 的在同一个文件里面的函数,这个函数调用了 net/socket.c 里面的 sock_init 函数, 这个函数就是 TCP/IP 协议栈,也包括 ipx 等的入口。
其中 sock_register 函数在 net/socket.c 里面,就是简单的设置前面说的 net_families 数组中间 对应的值:
int sock_register(struct net_proto_family *ops) { if (ops->family >= NPROTO) { printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family, NPROTO); return -ENOBUFS; } net_families[ops->family]=ops; return 0; }
arp_init(); ip_init(); tcp_v4_init(&inet_family_ops); tcp_init(); icmp_init(&inet_family_ops);
rarp_ioctl_hook = rarp_ioctl; proc_net_register(&proc_net_rarp); proc_net_register(&proc_net_raw); proc_net_register(&proc_net_snmp); proc_net_register(&proc_net_netstat); proc_net_register(&proc_net_sockstat); proc_net_register(&proc_net_tcp); proc_net_register(&proc_net_udp);
第一个函数是用来接收数据的 callback 函数,第二个是错误处理函数,其它的 copy 是用来协议共 享的,这个以后再说,data 当然就是这个结构体的私有数据了。
inet_add_protocol 函数是在 net/ipv4/protocol.c 里面的:
void inet_add_protocol(struct inet_protocol *prot) { unsigned char hash; struct inet_protocol *p2;
hash = prot->protocol & (MAX_INET_PROTOS - 1); prot ->next = inet_protos[hash]; inet_protos[hash] = prot; prot->copy = 0;
p2 = (struct inet_protocol *) prot->next; while(p2 != NULL) { if (p2->protocol == prot->protocol) { prot->copy = 1; break; }