网络编程常用接口的内核实现----sys_socket()


最近在开发一个内核模块,主要的功能是在集群的节点之间迁移TCP连接,从而实现基于内容的调用。因此,花了很多时间和精力研究linux的网络协议栈,但是还是有很多地方没有串起来。网络协议栈是为用户层的应用开发服务的,因此决定从用户层常用的编程接口入手,通过学习这些接口的实现,来理清整个过程,加深对网络协议栈的理解。

网络编程通常是基于客户端-服务端模型。首先启动服务器,稍后的某个时刻启动客户,它要连接到此服务器上。假设客户给服务器发送一个请求,服务器处理这个请求,并且给客户发送一个响应。为了执行网络I/O,第一件事情就是调用socket()函数来创建套接字。socket()函数对应的系统调用时sys_socket(),其源码及分析如下所示:

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
 int retval;
 struct socket *sock;
 int flags;

 /* Check the SOCK_* constants for consistency.  */
 /*
  * 下面的检查是在编译的时候进行的,如果这些
  * 变量的值不一致,编译时会报错。
  */
 BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
 BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
 BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
 BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);

 /*
  * 从linux 2.6.27开始,参数type除了指定套接字类型外,还
  * 可以通过或运算来指定SOCK_CLOEXEC和SOCK_NONBLOCK标志
  * 来改变套接字的行为。可以通过man socket命令查看详情。
  * 首先通过SOCK_TYPE_MASK掩码来获取type中设置的标志(当然
  * 也可能没有设置)。如果type中有标志设置,但是不是
  * SOCK_CLOEXEC和SOCK_NONBLOCK对应的位,则返回EINVAL错误。
  */
 flags = type & ~SOCK_TYPE_MASK;
 if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
  return -EINVAL;
 /*
  * 获取套接字类型
  */
 type &= SOCK_TYPE_MASK;

 /*
  * 如果SOCK_NONBLOCK不等于O_NONBLOCK并且设置了SOCK_NONBLOCK
  * 标志,则将flags中的SOCK_NONBLOCK替换为O_NONBLOCK。
  */
 if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
  flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

 /*
  * 根据协议族类型和套接字类型创建套接字。
  */
 retval = sock_create(family, type, protocol, &sock);
 if (retval < 0)
  goto out;

 /*
  * 创建一个新的文件描述符,将新创建的
  * socket实例关联上去。
  */
 retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
 if (retval < 0)
  goto out_release;

out:
 /* It may be already another descriptor 8) Not kernel problem. */
 return retval;

out_release:
 sock_release(sock);
 return retval;
}

  • 1
  • 2
  • 3
  • 下一页

相关内容