??那么如何把套接字和文件聯(lián)系起來(lái)呢刊头? 答案就是通過(guò)下面這張圖原杂。
其中
task_struct
表示一個(gè)進(jìn)程污尉,files_struct
中的fd_array[]
表示該進(jìn)程打開(kāi)的所有描述符被碗,對(duì)于套接字來(lái)說(shuō)锐朴,與其他類型文件的區(qū)別就是最終f_op
指向的是socket_file_ops
蔼囊。不過(guò)畏鼓,可以看到,這里的socket_file_ops
只有一些通用的操作膳沽,并沒(méi)有send
和recv
挑社。特有的操作通過(guò) socketcall() 區(qū)分的巡揍。
socket和sock
??終于到今天的主角了腮敌。實(shí)際上,對(duì)每一個(gè)新創(chuàng)建的套接字斗这,內(nèi)核協(xié)議棧都會(huì)創(chuàng)建struct socket
和struct sock
兩個(gè)數(shù)據(jù)結(jié)構(gòu)。這兩個(gè)結(jié)構(gòu)就像孿生兄弟钮莲,struct socket
面向用戶空間,struct sock
面向內(nèi)核空間崔拥。
struct socket
簡(jiǎn)化版的結(jié)構(gòu)如下:
struct socket {
unsigned long flags;
const struct proto_ops *ops;
struct file *file;
struct sock *sk;
short type;
};
??其中type
表示協(xié)議链瓦,這是在創(chuàng)建套接字的時(shí)候的protocol
參數(shù)確定的,
int socket(int domain, int type, int protocol);
??file
指針指向上面那張圖中的struct file
結(jié)構(gòu)慈俯,通過(guò)它,socket
便與文件系統(tǒng)關(guān)聯(lián)了起來(lái)卖子。
??sk
指向?qū)\生的兄弟sock
結(jié)構(gòu)洋闽。
??socket
結(jié)構(gòu)中最重要的要數(shù)ops
指針了诫舅,根據(jù)協(xié)議類型宫患,它指向一種特定協(xié)議的實(shí)現(xiàn)撮奏。比如TCP的就是inet_stream_ops
; ICMP、UDP協(xié)議對(duì)應(yīng)inet_dgram_ops
;RAWIP對(duì)應(yīng)的是inet_sockraw_ops
同樣地泽疆,這些也都在創(chuàng)建套接字的時(shí)候就決定了殉疼。
??struct proto_ops
的簡(jiǎn)化版本的結(jié)構(gòu)如下
struct proto_ops{ int family;
int (*bind)(struct socket *sock, struct sockaddr *myaddr, int sockaddr_len);
int (*connect)(struct socket *sock,struct sockaddr *vaddr,int sockaddr_len, int flags);
int (*accept)(struct socket *sock, struct socket *newsock, int flags);
int (*sendmsg)(struct socket *sock, struct msghdr *m, size_t total_len);
}
其中的接口名字是不是很熟悉瓢娜?是的眠砾,它們和進(jìn)行網(wǎng)絡(luò)編程時(shí)調(diào)用的C庫(kù)中函數(shù)名字是一樣的托酸。以sendmsg為例柒巫,真實(shí)的調(diào)用過(guò)程是這樣
即當(dāng)用戶調(diào)用
sendmsg
時(shí),內(nèi)核會(huì)找到描述符fd
對(duì)應(yīng)的struct socket
結(jié)構(gòu)刨疼,然后調(diào)用sock->ops->sendmsg
執(zhí)行特定協(xié)議的發(fā)送揩慕。那么,ops
字段什么時(shí)候被賦值呢贱案???答案是宝踪,在創(chuàng)建
struct sock
結(jié)構(gòu)前碍扔。struct sock
的簡(jiǎn)化結(jié)構(gòu)如下圖所示
struct sock_common {
struct proto *skc_prot;
};
struct sock {
struct sock_common __sk_common;
struct sk_buff_head sk_receive_queue;
struct sk_buff_head sk_write_queue;
};
其中最重要的字段就是skc_prot
不同,它也是協(xié)議相關(guān)的。作為struct socket
結(jié)構(gòu)的孿生兄弟服鹅,struct sock
結(jié)構(gòu)也是在用戶創(chuàng)建套接字時(shí)就創(chuàng)建的企软。
sock_alloc
創(chuàng)建了struct socket
結(jié)構(gòu)仗哨,隨后厌漂,根據(jù)用戶傳入的family
斟珊,查詢數(shù)組net_families
,找到對(duì)應(yīng)的函數(shù)指針胜嗓,調(diào)用create
函數(shù)钩乍。net_families
保存著內(nèi)核啟動(dòng)時(shí)注冊(cè)(通過(guò)sock_register
)的 socket protocol handler
寥粹,比如以下幾種
static const struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};
static const struct net_proto_family netlink_family_ops = {
.family = PF_NETLINK,
.create = netlink_create,
.owner = THIS_MODULE, /* for consistency 8) */
};
static const struct net_proto_family packet_family_ops = {
.family = PF_PACKET,
.create = packet_create,
.owner = THIS_MODULE,
};
static const struct net_proto_family unix_family_ops = {
.family = PF_UNIX,
.create = unix_create,
.owner = THIS_MODULE,
};
inetsw
中注冊(cè)的每種協(xié)議都有ops
和prot
兩個(gè)字段,前者與struct socket
結(jié)構(gòu)關(guān)聯(lián)到一起岛杀,后者與struct sock
關(guān)聯(lián)到一起类嗤。在inet_create
中,struct socket
的ops
字段和struct sock
的sk_prot
字段被賦值货裹。
本文全部?jī)?nèi)容轉(zhuǎn)載自:套接字的秘密—socket與sock
待整理文章:Linux 網(wǎng)絡(luò)棧剖析