分类:
2007-12-26 11:45:33
socket系统调用的内核分析
王耀(wangyao@cs.hit.edu.cn)
socket函数是所有网络编程的第一个函数,这里我们来分析一下socket系统调用在内核中的实现。所有的网络系统调用,最终都会调用sys_socketcall这个系统调用,由它来进行多路复用分解,分别调用相应的处理函数,socket函数对应调用sys_socket函数。
sys_socket函数调用sock_create函数来创建struct socket,并初始化它。调用后得到一个struct socket。最后,调用sock_map_fd找一个空闲的文件描述符,映射到这个创建好的套接字上,将这个文件描述符返回。
socket调用关系图(以TCP Socket进行分析):
看上面的调用关系图,看到在sys_socket函数中,有一步是调用net_families[family]->create完成最后的创建工作,下面就以inet域的创建来解释这最后一步的创建工作:
1、设socket->state = SS_UNCONNECTED。
2、从数组inetsw中匹配套接字类型和协议类型。inetsw是一个链表数组,也就是说数组的每一项是一个链表,同套接字类型的在同一个链表中。比如,用户这样创建一个TCP协议的套接字:
socket(
AF_INET, SOCK_STREAM, IPPROTO_TCP )
最终,内核在inetsw中匹配到的是这样一个结构体:
static
struct inet_protosw inetsw_array[] =
{
{
.type =
SOCK_STREAM,
.protocol = IPPROTO_TCP,
.prot =
&tcp_prot,
.ops = &inet_stream_ops,
.capability =
-1,
.no_check = 0,
.flags = INET_PROTOSW_PERMANENT,
}
}
这里最关键的是prot成员和ops成员,tcp_prot将提供TCP协议相关的全部操作,inet_stream_ops将提供域相关的全部操作,包括listen,
connect等。
注意:
这里需要注意proto和proto_ops的数据区别,proto中的操作函数,是针对sock结构体的,在网络层;proto_ops是针对socket结构体的,在BSD套接字层。proto_ops中的操作函数的实现,是通过调用proto中的操作函数来实现的。proto中的操作函数更底层一些。
struct proto中的accept函数:
struct sock * (*accept) (struct sock *sk, int flags, int *err);
struct proto_ops中的accept函数:
int (*accept) (struct socket *sock,struct socket *newsock, int flags);
我们这里分析一个操作函数:
inet_stream_ops中的accept函数,指定的是inet_accept函数;tcp_prot中的accept函数,指定的是inet_csk_accept函数。
inet_accept函数中:
struct sock *sk1 = sock->sk;
struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err);
inet_accept函数中调用了tcp_prot中的accept函数,即inet_csk_accept函数。
3、socket->ops
= 匹配到的那个ops。
4、分配socket->sock。
5、让struct
inet_sock指向socket->sock,struct
inet_sock是struct
sock的超集。其头部内容即为struct
sock。
6、为inet_sock和socket->sock的成员赋初始值。这里,我们可以看到一些平时我们比较关心的问题,比如:inet->mc_ttl
=
1。
7、调用socket->sock->sk_prot->init(...)完成整个创建过程。
描述到这里,一个最为粗糙的socket创建过程算是完成了,但留下的问题却更多了,因为它涉及到很多宠大的结构体,其内容与socket的正常工作都息息相关,希望此文能够对大家了解socket的内核实现有所帮助。
分析中常用的数据结构及其常用引用名:
struct socket *sock;
struct sock *sk;
struct sk_buff *skb;
struct proto tcp_prot;
struct proto_ops inet_stream_ops;
static struct list_head inetsw[SOCK_MAX];
static struct inet_protosw inetsw_array[];
分析中一些初始化函数:
static int __init sock_init(void)
static int __init inet_init(void)
proto_register(&tcp_prot, 1);
sock_register(&inet_family_ops);
inet_add_protocol(&tcp_protocol, IPPROTO_TCP);
void inet_register_protosw(struct inet_protosw *p);
dev_add_pack(&ip_packet_type);
一些数据结构的头部包含关系: