根据socket网络编程的经验,用户在使用TCP协议传输数据时,应用程序调用 int sock_fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
获得socket 描述符sock_fd,之后,同操作文件一样,应用程序可以使操作sock_fd 调用bind,listen,accept,connect,read,write,close,shutdown,getsockopt,setsockopt等函数。
因此,我的疑问是,socket()调用之后,内核逐步使怎么完成的呢?
阅读了很多博客,其中,博客 http://blog.chinaunix.net/uid-25314474-id-3067854.html 解决了我的问题,借由函数传递来表达整个过程可以表示为:
socket( int, family, int, type, int, protocol) -> SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) -> sock_create(family, type, protocol, &sock);
之后,socket系统调用的执行就交给内核中的相应函数来完成了, 此例中为sock_create(family, type, protocol, &sock);
通过追踪sock_create((family, type, protocol, &sock),进一步发现,该函数之后会调用pf->create(net, sock, protocol, kern)去完成主要的创建工作。
pf 的定义及初始化赋值在下列语句中完成,
const struct net_proto_family *pf;
pf = rcu_dereference(net_families[family]);
struct net_proto_family {
intfamily;
int(*create)(struct net *net, struct socket *sock,
int protocol, int kern);
struct module*owner;
};
由此引入了另外一个很重要的全局数组,net_families[],进一步追踪net_families,发现,
该数组定义在/net/socket.c
static const struct net_proto_family *net_families[NPROTO] __read_mostly;
而该数组的成员是由函数 int sock_register(const struct net_proto_family *ops) 进行添加的。
每一类注册的socket都对应一个struct net_proto_family结构体;该结构体都通过sock_register被存储到net_families[]中。
inet_family_ops 对象在 函数static int __init inet_init(void)中通过(void)sock_register(&inet_family_ops);完成了相应的注册工作。
例如:
static const struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner= THIS_MODULE,
};
static const struct net_proto_family ax25_family_ops = {
.family =PF_AX25,
.create =ax25_create,
}
之后,便对inet_family_ops 中的inet_create 进行追踪。inet_create 属于套接口层函数。之后,便可以依次继续向下调用。
此处,对套接口层简单补充下。套接口层位于应用程序和协议栈之间,对应用程序屏蔽了与协议栈实现的具体细节,将应用程序发送的与协议无关的请求映射到与协议相关的实
现,由此为应用程序提供了一个访问网络和进程间通信的通用接口。通常,应用程序调用库函数,而库函数通过系统调用进入套接口层,而在linux中每个传输层协议都对应一个
proto_ops 接口,套接口中有指向该结构的指针,于是,这个接口就
实现了套接口函数到传输层函数的映射,而当函数调用最终进入传输层函数后,便可以沿着协议栈继续向下传递,到达网络层,直至硬件出口。