分类: C/C++
2014-11-19 19:34:11
转载自http://blog.chinaunix.net/uid-13746440-id-3547287.html
socket是用户API与网络协议栈之间的一个中间接口层,用户通过调用socket api将报文传给协议栈,或者从协议栈读取报文。
对于用户来说,socket就是一个打开的文件,linux内核中为socket定义了sockfs文件类型:
static struct file_system_type sock_fs_type = {
.name = "sockfs",
.get_sb = sockfs_get_sb,
.kill_sb = kill_anon_super,
};
在sock_init中调用sock_fs_type的注册,文件系统存在后,创建socket,实际上是在sockfs文件系统中创建一个文件节点(inode),并建立struct inode和struct socket结构。
struct socket_alloc {
struct socket socket;
struct inode vfs_inode;
};
内核中实现了socket_alloc结构,将socket和vfs_inode封装在一起,这样通过vfs_inode和socket都可以互相找到对方,同时又保证了inode的独立性,适用于各种文件类型。
在sock_init中首先调用register_filesystem将sock_fs_type挂载全局的file_system链表上,然后调用vfs_kern_mount挂载该文件系统,这个过程会把sock文件系统的一些函
数操作集与超极块关联起来,这样当创建inode节点时,实际上最终使用的是sock文件系统提供的相关函数,这里不细述了,最终会一下申请处一个socket_alloc结构,同时
存在socket和vfs_inode。
接着看看通过task_struct怎么找到socket
current->file->fd_array[fd],fd是文件描述符,不能超过NR_OPEN_DEFAULT(32),通过fd找到file结构,这个结构中包含了目录项f_dentry,而f_dentry中包含了inode,
f_dentry通过inode刚好可以找到socket,这样通过进程描述符就找到了socket了。
下面看看具体的几个关键数据结构:
/**
* struct socket - general BSD socket
* @state: socket state (%SS_CONNECTED, etc)
* @type: socket type (%SOCK_STREAM, etc)
* @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc)
* @ops: protocol specific socket operations
* @fasync_list: Asynchronous wake up list
* @file: File back pointer for gc
* @sk: internal networking protocol agnostic socket representation
* @wait: wait queue for several uses
*/
struct socket {
socket_state state; // 未分配、未连接、正连接、已连接、断连接,只对TCP生效s
short type; // socket类型,如SOCK_DGRAM=1,SOCK_STREAM=2,SOCKET_RAW=3
unsigned long flags; // 无使用
struct fasync_struct *fasync_list;
wait_queue_head_t wait;
struct file *file;
struct sock *sk; // 网络层sock的表示,这个是重点,整个协议栈都是围绕这个成员展开
const struct proto_ops *ops; // 操作集,分别对应流协议、数据报和原始套接口协议
};
这样通过文件描述符,可以找到中间层socket,通过socket可以找到网络层的sock,进而操作数据,收发包,这样就展开了。
sock的结构比较复杂,单独进行分析,下面主要讲socket、sock、tcp sock、inet_connection_sock、inet socket等的关系理一下
1. 通过fd可以找到socket,socket->sk指向了struct sock结构
2. struct sock->sk_socket指向了BSD socket,这样socket和sock可以轻松的互相查询
3. 数据报文在tcp入口,通过ip、端口等信息查询到struct sock信息
4. 如果是tcp协议,tcp_sock = struct tcp_sock *tp = tcp_sk(sk);可以强制转换,看一下这几个结构
struct tcp_sock {
/* inet_connection_sock has to be the first member of tcp_sock */
struct inet_connection_sock inet_conn;
......
struct udp_sock {
/* inet_sock has to be the first member */
struct inet_sock inet;
......
struct inet_connection_sock {
/* inet_sock has to be the first member! */
struct inet_sock icsk_inet;
......
struct inet_sock {
/* sk and pinet6 has to be the first two members of inet_sock */
struct sock sk;
......
inet_sock是INET域专用的一个sock表示,在struct sock上扩展而来,除基本sock属性,提供了INET域专用的属性,如TTL、IP地址、端口等
inet_connection_sock是所有面向连接的协议的socket的相关信息,第一个域是inet_sock,可以方便转换
tcp_sock是tcp协议的专用socket表示,在inet_connection_sock基础上扩展,主要增加了滑动窗口协议、拥塞控制算法等TCP专有属性
udp_sock第一个域也是inet_socket,包括一些加密相关的信息接口,暂时不分析