一个socket代表了通信链路的一端,存储或指向与链路有关的所有信息。Linux提供了创建socket的一个系统调用,通过该系统调用,能够得到一个用来访问套接字的描述符:
#include <sys/types.h>
#include <sys/socket.h>
int socket( int domain, int type, int protocol );
内核中的系统调用函数原型是在net/socket.c 1180行:
asmlinkage long sys_socket( int family, int type, int protocol );
该函数主要做了两件事情:创建一个代表通讯端点的结构体struct socket,将这个结构映射到一个文件描述符上,最后将这个描述符返回,也就是我们调用socket得到的套接字描述符。
下面是Linux内核中对结构socket的定义(不同操作系统间,对该结构的定义会有差异):
struct socket {
socket_state state;
unsigned long flags;
struct proto_ops *ops;
struct fasync_struct *fasync_list;
struct file *file;
struct sock *sk;
wait_queue_head_t wait;
short type;
};
state是一个内部状态标志:
typedef enum {
SS_FREE = 0, /* 未分配 */
SS_UNCONNECTED, /* 未连接 */
SS_CONNECTING, /* 正在连接当中 */
SS_CONNECTED, /* 已经连向一个套接字 */
SS_DISCONNECTING /* 正在断开连接 */
} socket_state;
#define SOCK_ASYNC_NOSPACE 0
#define SOCK_ASYNC_WAITDATA 1
#define SOCK_NOSPACE 2
#define SOCK_PASSCRED 3 ops是协议相关的一系例操作的集合,包括listen, bind, connect等常用socket操作,struct proto_ops结构体在include/linux/net.h 123行。 fasync_list是一个异步唤醒的列表,结构体struct fasync_struct在include/linux/fs.h 733行 sk是一个网络层的套接字表示,关于结构体struct sock,下文会有专门介绍。 type是套接字的类型:
enum sock_type {
SOCK_STREAM = 1, /*可靠字节流服务套接字,TCP*/
SOCK_DGRAM = 2, /*传输层数据报服务, UDP*/
SOCK_RAW = 3, /*网络层数据报服务, ICMP, IGMP, 原始IP*/
SOCK_RDM = 4, /*可靠的数据报服务*/
SOCK_SEQPACKET = 5, /*可靠的双向记录流服务*/
SOCK_PACKET = 10, /*已废弃*/
};
暂时放一下struct sock,先来看看sys_socket的第一步创建struct socket中究竟做了些什么(描述越过了一些不是很重要的步骤):
首先,检查传入的用来标识域的协议族变量family是否在合法范围内,关于family,我们只关心其中的几个值,PF_INET表示因特网协议,PF_UNIX是unix文件系统套接字。
然后,对于(family == PF_INET && type == SOCK_PACKET )的情况,因为是已废弃的,给出警告信息。
net_families是一个数组,所有的协议族都在这个数组中注册,数组的项是一个结构体:
struct net_proto_family {
int family;
int (*create)(struct socket *sock, int protocol);
short authentication;
short encryption;
short encrypt_net;
struct module *owner;
};
对于我们要创建的family,我们必须确保能在这个数组中找到相应的项(即内核支持该域)。
在内存中创建一个struct socket,并将其type赋值为传入的type值。
调用net_families[family]->create完成最后的创建工作。返回。
至此,一个socket就创建成功了。但还有两个问题没有明确:struct sock结构体的内容,以及net_families[family]->create如何完成对socket的创建。下一篇将结合inet域的实际例子进行分析。