1.1 Overview
socket.c里面有:
core_initcall(sock_init); /* early initcall */
因此在系统启动的时候,
do_basic_setup(main.c)---->do_initcalls(main.c)会调用sock_init。
同理在af_init.c中的fs_initcall(inet_init)导致inet_init在启动的过程中也被调用,其他重要的的还有net_dev_init(dev.c)
1.2 sock_init
sock_init:没啥好说的,主要就是初始化了sk cache和skb cache
1.3 net_dev_init:
1. 1最重要就是启动了2个softirq
a) open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
b) open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
这2个softirq就是用来发送和接收网络数据包的,以后会详细说明。
2. 初始化2个包类型LIST:
INIT_LIST_HEAD(&ptype_all);
for (i = 0; i < 16; i++)
INIT_LIST_HEAD(&ptype_base[i]);
这2个包类型LIST很重要,包类型指的是第三层协议的类型,IP,ARP等等。这些协议会调用dev_add_pack(struct packet_type*)向数据链路层注册包类型,数据链路层在接收到包后,才可以根据包头的协议类型决定该把包传给那种第三层协议。packet_type结构中有一个函数指针func,链路层就是调用这个函数来向第三层传递数据的,以ARP为例子,dev_add_pack(&arp_packet_type);其中arp_packet_type被定义为,
static struct packet_type arp_packet_type = {
.type = __constant_htons(ETH_P_ARP),
.func = arp_rcv,
};
因此,当数据链路层检查出包是一个ARP包的时候,就会调用arp_rcv向上层传递该包。
实际上我们有2个包类型队列,一般情况下,都注册到ptype_base,但是如果dev_add_pack注册的包类型中type为ETH_P_ALL时,包类型被注册到ptype_all,被注册到ptype_all的包类型,在每一个包被接收的时候它的func函数都会被调用。
3. 初始化包接收队列,讨论包接收的时候会谈到该队列:
for_each_possible_cpu(i) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue);
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
queue->backlog.poll = process_backlog;
queue->backlog.weight = weight_p;
}
1.4 inet_init
inet_init很长
1. 通过调用proto_register注册了tcp_prot,udp_prot, raw_prot
2. 通过调用sock_register注册了inet_family_ops
proto_register实际上就是把协议加到proto_list. 这些协议的定义是这样的:
struct proto udp_prot = {
.name = "UDP",
.owner = THIS_MODULE,
.close = udp_lib_close,
.connect = ip4_datagram_connect,
.disconnect = udp_disconnect,
.ioctl = udp_ioctl,
.destroy = udp_destroy_sock,
.setsockopt = udp_setsockopt,
.getsockopt = udp_getsockopt,
.sendmsg = udp_sendmsg,
.recvmsg = udp_recvmsg,
.sendpage = udp_sendpage,
.backlog_rcv = udp_queue_rcv_skb,
.hash = udp_lib_hash,
.unhash = udp_lib_unhash,
.get_port = udp_v4_get_port,
.obj_size = sizeof(struct udp_sock),
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
#endif
REF_PROTO_INUSE(udp)
};
按照Linux对网络系统的架构,对于应用层来说,是统一的socket接口,而实际如何传递数据是由不同类型的sock决定的,如果按照面向对象的思想来看,sock结构就是基类,具体协议的sock结构就是子类,比如对于UDP来说就是udp_sock.我们察看UDP类型的socket操作,可以发现,创建socket的时候,会调用inet_family_ops里面的create函数inet_create,它会把UDP类型socket的ops指针设置为inet_dgram_ops,还有prot指针设置为udp_prot,当对socket进行操作的时候,比如当调用sock_sendmsg的时候,实际会调用到inet_dgram_ops里面的inet_sendmsg,而它会调用到udp_prot里面的udp_sendmsg.这里只是简单介绍了一下socket操作的流程,具体情况,以后再详细说明.还需要说明的是,这些函数指针不是必须的,这是AF_INET family中为了结构清晰,可扩展性好而使用的一种方式,实际上就好比加入了一新的inet层.在其他family中,比如AF_PACKET,你就会发现注册的协议里面是没有定义这些函数指针的.
static struct proto packet_proto = {
.name = "PACKET",
.owner = THIS_MODULE,
.obj_size = sizeof(struct packet_sock),
};
3. 通过调用inet_add_protocl注册了icmp_protocol,udp_protocol,tcp_protocol,igmp_protocol
inet_add_protocl实际上就是注册需要利用IP层进行传输的层4的协议,类似于dev_add_pack注册层3协议一样,注册层4协议的时候,也需要注册一个接收函数,这样IP层在决定数据传输给具体哪个层4协议的时候,就可以调用这个接收函数来把数据传递给它.比如对于udp_protocol:
static struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
.no_policy = 1,
};
如果IP层发现数据是一个UDP报文,就会调用udp_rcv向上传递数据.
4. 通过调用inet_register_protosw注册了inetsw_array
i. inetsw_array就是为了AF_INET好管理而创造出来的一个结构,很简单,不详述.
5. 初始化ARP模块:arp_init
i. 初始化 neigbhour table
ii. dev_add_pack(&arp_packet_type)向数据链路层注册一下,好接收ARP包.
6. 初始化IP模块:ip_init用来初始化路由表,tcp_v4_init用来创建一个tcp control socket
7. 初始化TCP模块:tcp_init,没研究过TCP模块,以后有时间看看
8. 初始化UDP lite模块:udplite4_register,没用过这个协议
9. 初始化ICMP模块:icmp_init,创建ICMP control socket.
10. 初始化IP fragment:ipfrag_init
11. 通过调用dev_add_pack向数据链路层注册了ip_packet_type以接收IP包.