第10章 套接字层实现
Linux内环中支持的套接字结构
套接字的基本功能:
- 传输数据
- 为TCP管理连接
- 控制或调节TCP/IP协议栈的操作
套接字层API构成
- 网络功能 (由struct prot数据结构描述)
- 将网络的操作映射到Linux常规I/O操作上,这样应用程序人员就可以如调用普通文件I/O操作一样使用TCP/IP来传送和接收数据 (struct proto_ops数据结构描述)
管理套接字传送数据的结构是Socket Buffer,struct sk_buff数据结构中存放了套接字接收/发送的数据。
每个程序使用套接字都有一个struct socket数据结构与struct sock数据结构的实例,这两个结构包含套接字通用属性,独立于具体协议;具体的协议族与协议实例继承通用套接字的属性,形成管理协议本身套接字的结构。
struct socket 是一个通用数据结构,存放的是套接字层的控制和状态信息;每个打开的套接字都有一个struct socket数据结构的实例;包含一个文件描述符可由应用程序代码引用,该文件描述符从套接字层返回给应用程序。
struct socket {
socket_state state; //当前套接字状态
short type; //套接字的类型
unsigned long flags; //套接字等待缓冲区的状态信息
const struct proto_ops *ops; //套接字层的操作函数块
struct fasync_struct *fasync_list; //等待被唤醒的套接字列表,该链表用于异步文件调用
struct file *file; //套接字所属的文件描述符
struct sock *sk; //指向存放套接字属性的结构指针
wait_queue_head_t wait; //套接字的等待队列
};
说明:
- *ops 定义了一组套接字层的标志操作函数指针供应用程序调用,函数指针直接与socketcall系统调用存放的函数指针一一对应;
- *file 套接字所属的文件描述符,在创建和打开套接字时,套接字层向应用程序返回该文件描述符,应用程序通过文件描述符访问套接字。
10.1 地址族的值和协议交换表
struct inet_protosw实现协议交换表,管理和描述套接字层对应系统调用套接字操作函数struct proto_ops *ops 、与内核协议相关套接字操作函数块struct proto *prot 。
struct inet_protosw {
struct list_head list;
/* These two fields form the lookup key. */
//套接字类型
unsigned short type; /* This is the 2nd argument to socket(2). */
unsigned short protocol; /* This is the L4 protocol number. */
//指向协议实例实现的套接字操作函数块数据结构
struct proto *prot;
//指向套接字层对应的socketcall系统调用,是套接字操作函数块数据结构的指针。
const struct proto_ops *ops;
//使用该套接字的权限
int capability; /* Which (if any) capability do
* we need to use this socket
* interface?
*/
//对接收、发送数据包是否做校验和
char no_check; /* checksum on rcv/xmit/none? */
//属性的相关标志。
unsigned char flags; /* See INET_PROTOSW_* below. */
};
10.2 套接字API系统调用的实现
实现系统调用的基本过程如下:
- 内核实现了一系列的系统调用,所有系统调用函数以sys_为前缀名,对应用户程序的函数调用;内核为每个系统调用分配一个索引号,索引号与系统调用函数对应关系保存在系统调用表system call table中。
- 用户程序调用参数在用户地址空间,内核功能函数要访问这些参数,需将其从用户地址空间映射到内核地址空间(X86 CPU体系结构,将存放用户数据地址的指针放入CPU的寄存器中,传给内核代码,然后内核代码将用户数据复制到内核地址空间,这项功能由copy_from_user函数完成)
- 如果系统调用成功,则向用户程序返回正整数或0。如果不成功,则系统调用向用户程序返回错误代码。
在应用层程序调用套接字API时,应用层的函数调用转换为内核协议实例函数需经过以下步骤:
- 将用户地址空间参数映射到内核地址空间
- 将常规套接字层的函数调用转换到某协议族的函数
- 将协议族的函数调用传递给协议实例的网络功能函数
10.2.1 系统调用实现原理
基本机制:
Linux下的系统调用是通过0x80实现的,每个系统调用都有相应的系统调用号作为唯一的标识,内核维护一张系统调用表, sys_call_table,表中的元素是系统调用函数的起始地址,而系统调用号就是系统调用在调用表的偏移量。在x86上,系统调用号是通过eax寄存器传递给内核的。
通知内核的机制是靠软件中断实现的。首先,用户程序为系统调用设置参数。其中一个参数是系统调用编号。参数设置完成后,程序执行“系统调用”指令。x86系统上的软中断由int产生。这个指令会导致一个异常:产生一个事件,这个事件会致使处理器切换到内核态并跳转到一个新的地址,并开始执行那里的异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。它与硬件体系结构紧密相关。
新地址的指令会保存程序的状态,计算出应该调用哪个系统调用,调用内核中实现那个系统调用的函数,恢复用户程序状态,然后将控制权返还给用户程序。
https://www.cnblogs.com/alantu2018/p/8991310.html