TCP是传输层协议,网络层使用IPv4或IPv6协议,即TCP报文作为IPv4或IPv6报文的数据部分。本文中将使用IPv4协议的TCP称为TCPv4,将使用IPv6的TCP称为TCPv6。使用不同的IP协议对于TCP的影响主要是连接查找、检验和计算等与IP地址相关的部分,核心功能并无差异。故此在以下的代码分析过程中会以TCPv4为主。
先来看看socket系统调用的函数原型:
int socket(int domain, int type, int protocol);
参数解析:
domain参数用来指定通信协议族,对于TCP而言其值应为AF_INET(IPv4协议)或AF_INET6(IPv6协议);
type参数用来指定通信语义,此参数应选择SOCK_STREAM,其含义是“Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported”,TCP的特性正好符合此语义;
protocol参数指定socket所使用的协议,此参数取值IPPROTO_IP(值为0)或IPPROTO_TCP(值为6)代表TCP协议。取值为0也能代表TCP协议,是因为protocol为0时代表取给定domain的type中的默认协议,而TCP是AF_INET|AF_INET6中SOCK_STREAM类型的默认协议。
socket系统调用成功的话返回一个文件描述符,其它API就通过这个描述符操控socket。
问题来了:socket系统调用究竟对内核做了什么?socket对于TCP究竟有何意义?下面我们一起通过分析Linux内核代码来找到这些问题的答案。
Socket系统调用对应的内核函数为:
1360 SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
1361 {
1362 int retval;
1363 struct socket *sock;
1364 int flags;
1365
...
1380 retval = sock_create(family, type, protocol, &sock);
1381 if (retval < 0)
1382 goto out;
1383
1384 retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
1385 if (retval < 0)
1386 goto out_release;
1387
1388 out:
1389 /* It may be already another descriptor 8) Not kernel problem. */
1390 return retval;
...
1395 }
只要弄明白sock_create函数和sock_map_fd函数做了什么,自然就能明白socket系统调用的功能。先来看sock_create函数:
1235 int __sock_create(struct net *net, int family, int type, int protocol,
1236 struct socket **res, int kern)
1237 {
1238 int err;
1239 struct socket *sock;
...
1269 /*
1270 * Allocate the socket and allow the family to set things up. if
1271 * the protocol is 0, the family is instructed to select an appropriate
1272 * default.
1273 */
1274 sock = sock_alloc();
1275 if (!sock) {
1276 net_warn_ratelimited("socket: no more sockets\n");
1277 return -ENFILE; /* Not exactly a match, but its the
1278 closest posix thing */
1279 }
1280
1281 sock->type = type;
...
1295 pf = rcu_dereference(net_families[family]);
...
1310 err = pf->create(net, sock, protocol, kern);
...
1329 *res = sock;
1330
1331 return 0;
...
sock_create函数申请了一个struct socket结构体sock,又调用pf->create指向的函数对通过sock进行初始化。分析inet_init等网络子系统初始化函数得知,pf->