在之前的文章中,我們介紹了VFS、VFS與程序模組之間的程式碼分析,也分析了devtmpfs檔案系統。
本次我們介紹socketfs,說明下socketfs與vfs之間的關聯。
在分析VFS以及分析devtmpfs時,其檔案系統變數file_system_type->mount介面實現超級塊的創
建以及根目錄相關的dentry、inode的建立,在root_inode的i_op介面中,實現了檔案及目錄inode建立的介面(i_op->mkdir實現目錄的建立、i_op->create實現檔案的建立)。而針對socket而言,其並不需要建立真實的檔案,因此,其root_inode的i_op介面中並沒有mkdir、create介面,其主要將程序描述符的struct file型別的指標與socket相關的結構體關聯。
關於sockfs,主要分析如下幾個方面的內容:
一、sockfs檔案系統型別的定義、註冊及掛載
二、sockfs相關的結構體
三、socket fd建立的過程
四、socket相關的系統呼叫簡要介紹
五、socket的ioctl處理流程
ioctl的處理流程2. ifconfig up/down
一、sockfs檔案系統型別的定義及註冊針對sockfs,其在sock_init介面中,進行檔案系統的註冊以及掛載操作,sock_init的介面流程圖如下:
通過呼叫register_filesystem實現檔案系統的註冊(檔案系統的註冊之前文章中已經介紹過,可在此處檢視)。
其中sockfs檔案系統的定義如下,其mount介面為sockfs_mount
static struct file_system_type sock_fs_type = {
.name = "sockfs",
.mount = sockfs_mount,
.kill_sb = kill_anon_super,
};
針對sockfs,呼叫kern_mount進行sockfs的掛載操作,而kern_mount通過呼叫vfs_kern_mount進行掛載(但沒有呼叫do_add_mount,將該sockfs掛載到具體目錄中,因sockfs無需掛載具體目錄)。在vfs_kern_mount->mount_fs->type->mount時,即呼叫sockfs_mount介面,進行超級塊、根root、根dentry相關的建立及初始化操作。如下介面定義,其中sockfs_ops為sockfs的超級塊變數相關的操作介面,而sockfs_dentry_operations為sockfs的根dentry的操作介面。
static struct dentry *sockfs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_pseudo(fs_type, "socket:", &sockfs_ops,
&sockfs_dentry_operations, SOCKFS_MAGIC);
}
/*socketfs 超級塊的操作介面,主要包括inode的alloc介面、destroy介面等*/
static const struct super_operations sockfs_ops = {
.alloc_inode = sock_alloc_inode,
.destroy_inode = sock_destroy_inode,
.statfs = simple_statfs,
};
/*dentry_ops*/
static const struct dentry_operations sockfs_dentry_operations = {
.d_dname = sockfs_dname,
};
經以上操作,即完成了sockfs的註冊以及掛載。
二、sockfs相關的結構體及結構體之間的關聯對於網路協議簇而言,包括ax25協議簇、inet4協議簇、inet6協議簇、ipx協議簇等,而每一個協議
簇中又包含多個協議,以inet4協議簇為例,又包含ipv4、tcp、udp、icmp等協議。
而針對網路協議簇與網路協議之間的關係,linux sockfs的架構也按此進行了劃分。
針對sockfs,其也相容VFS架構,但相比於普通檔案及裝置檔案而言,也有不同,針對socket檔案的
操作,linux提供了一系列專用的系統呼叫介面,包括socket、listen、shutdown、connect、bind、accept、recvmsg、getsockname、getpeername、sendmsg、socketpair、send、setsockopt、getsockopt、recvfrom、sendto等介面,使用這些介面可實現上述所說協議簇中的各協議的socket通訊。
針對sockfs而言,其系統呼叫socket與open實現的功能類似,不同的是socket fd中增加了針對網路
協議簇相關的一套框架,用於實現對上述所說的packet協議簇、ipc socket、netlink、ipv4、ipv6等協議的socket通訊。這些結構體包括struct sock、struct socket、struct net_proto、struct inet_protosw、struct proto、struct proto_ops,首先我們分析這些結構體的定義以及這些結構體的關聯,待我們理解這些結構體之後,再分析具體的實現介面。
1.struct socket分析結構體socket用於描述一個socket,該結構體的定義如下:
state定義了socket的狀態(包括SS_UNCONNECTED、SS_CONNECTING、SS_CONNECTED、SS_DISCONNECTING等);file表示檔案描述符,用於與vfs關聯;struct sock *sk為socket結構體的關鍵成員變數,後面介紹;struct proto_ops *ops用於執行協議相關的socket處理介面。struct socket {
socket_state state;
kmemcheck_bitfield_begin(type);
short type;
kmemcheck_bitfield_end(type);
unsigned long flags;
struct socket_wq __rcu *wq;
struct file *file;
struct sock *sk;
const struct proto_ops *ops;
};
2.struct sock分析該結構體的定義如下(由於該結構體的內容較多,僅顯示主要的幾個成員變數),主要包括__sk_common變數包括源、目的ip地址等、socket收發的佇列等資訊
struct sock {
struct sock_common __sk_common;
#define sk_prot __sk_common.skc_prot
struct sk_filter __rcu *sk_filter;
struct socket_wq __rcu *sk_wq;
struct proto *sk_prot_creator;
struct sk_buff_head sk_receive_queue;
struct sk_buff_head sk_write_queue;
…
}
struct sock_common {
/* 源、目的ip地址 */
union {
__addrpair skc_addrpair;
struct {
__be32 skc_daddr;
__be32 skc_rcv_saddr;
};
};
union {
unsigned int skc_hash;
__u16 skc_u16hashes[2];
};
/* 目的埠號等*/
union {
__portpair skc_portpair;
struct {
__be16 skc_dport;
__u16 skc_num;
};
};
unsigned short skc_family;//所屬協議簇
volatile unsigned char skc_state;//socket狀態
unsigned char skc_reuse:4;
unsigned char skc_reuseport:4;
int skc_bound_dev_if;
union {
struct hlist_node skc_bind_node;
struct hlist_nulls_node skc_portaddr_node;
};
struct proto *skc_prot;//傳輸層之上的協議處理介面
#ifdef CONFIG_NET_NS
struct net *skc_net;
#endif
};
3.struct net_proto_family分析該結構體主要用於說明網路協議簇,即針對ax25協議簇、inet4協議簇、inet6協議簇、ipx協議簇等
相關的定義,該結構體的定義如下:
family用於說明協議簇的型別,目前linux支援的協議簇型別包括AF_UNIX、AF_LOCAL、AF_INET、AF_INET6、AF_NETLINK等;create介面函式,該介面用於初始化strcut sock型別的指標變數,根據傳遞的協議號,設定協議相關的介面函式。struct net_proto_family {
int family;
int (*create)(struct net *net, struct socket *sock,
int protocol, int kern);
struct module *owner;
};
在socketfs中定義了struct net_proto_family型別的全域性指標陣列net_families,該陣列中包括了所有
linux核心支援的協議簇,在sockfs初始化時,呼叫介面sock_register將協議簇的指標儲存至該陣列中,該全域性的變數的關聯如下所示,在進行socket的建立時,會根據傳遞的協議簇型別,從該結構體中獲取對應struct net_proto_family型別變數,並呼叫其create介面進行socket的初始化與操作介面的設定等操作。
4.struct inet_protosw分析(inet協議簇)因本文主要介紹inet相關的實現,因此本處介紹struct inet_protosw結構體,該結構體的定義如下:
其中type用於指示socket的型別,socket的型別包括SOCK_STREAM、SOCK_DGRAM具體的型別在下面有說明;protocol用於說明協議的型別(包括ip、tcp、udp、icmp、dccp等);prot用於指向協議相關的處理介面;proto_ops用於指向socket型別的處理介面,即SOCK_STREAM、SOCK_DGRAM等對應的處理介面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;
const struct proto_ops *ops;
char no_check; /* checksum on rcv/xmit/none? */
unsigned char flags; /* See INET_PROTOSW_* below. */
};
enum sock_type {
SOCK_STREAM = 1,
SOCK_DGRAM = 2,
SOCK_RAW = 3,
SOCK_RDM = 4,
SOCK_SEQPACKET = 5,
SOCK_DCCP = 6,
SOCK_PACKET = 10,
};
5.struct proto_ops分析(inet協議簇)該結構體主要用於描述socket型別(SOCK_STREAM、SOCK_DGRAM等)的處理介面,該結構體定義如下:
family用於說明協議簇的型別
release、bind、connect、accept、poll、ioctl、listen、shutdown、setsockopt等介面,這些介面與sockfs提供的系統呼叫介面對應。
struct proto_ops {
int family;
struct module *owner;
int (*release) (struct socket *sock);
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 (*socketpair)(struct socket *sock1,
struct socket *sock2);
int (*accept) (struct socket *sock,
struct socket *newsock, int flags);
int (*getname) (struct socket *sock,
struct sockaddr *addr,
int *sockaddr_len, int peer);
unsigned int (*poll) (struct file *file, struct socket *sock,
struct poll_table_struct *wait);
int (*ioctl) (struct socket *sock, unsigned int cmd,
unsigned long arg);
#ifdef CONFIG_COMPAT
int (*compat_ioctl) (struct socket *sock, unsigned int cmd,
unsigned long arg);
#endif
int (*listen) (struct socket *sock, int len);
int (*shutdown) (struct socket *sock, int flags);
int (*setsockopt)(struct socket *sock, int level,
int optname, char __user *optval, unsigned int optlen);
int (*getsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen);
#ifdef CONFIG_COMPAT
int (*compat_setsockopt)(struct socket *sock, int level,
int optname, char __user *optval, unsigned int optlen);
int (*compat_getsockopt)(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen);
#endif
int (*sendmsg) (struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len);
int (*recvmsg) (struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len,
int flags);
int (*mmap) (struct file *file, struct socket *sock,
struct vm_area_struct * vma);
ssize_t (*sendpage) (struct socket *sock, struct page *page,
int offset, size_t size, int flags);
ssize_t (*splice_read)(struct socket *sock, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, unsigned int flags);
void (*set_peek_off)(struct sock *sk, int val);
};
6.struct proto 分析(inet協議簇)該結構體主要用於具體協議相關的操作介面,對於大多數協議的socket而言,只需要使用上述5中註冊的struct proto_ops提供的操作介面,即可完成對sockfs的系統呼叫的實現,而對一些協議(如udp、icmp、tcp協議),則需要特定的處理函式,這些處理函式的定義即使用struct proto結構體進行定義,該結構體的定義如下:
struct proto {
void (*close)(struct sock *sk,
long timeout);
int (*connect)(struct sock *sk,
struct sockaddr *uaddr,
int addr_len);
int (*disconnect)(struct sock *sk, int flags);
struct sock * (*accept)(struct sock *sk, int flags, int *err);
int (*ioctl)(struct sock *sk, int cmd,
unsigned long arg);
int (*init)(struct sock *sk);
void (*destroy)(struct sock *sk);
void (*shutdown)(struct sock *sk, int how);
int (*setsockopt)(struct sock *sk, int level,
int optname, char __user *optval,
unsigned int optlen);
int (*getsockopt)(struct sock *sk, int level,
int optname, char __user *optval,
int __user *option);
…
int *memory_pressure;
long *sysctl_mem;
int *sysctl_wmem;
int *sysctl_rmem;
int max_header;
bool no_autobind;
…
struct list_head node;
};
結構體之間的關聯上面已經介紹了sockfs相關的結構體,下面描述下這些結構體之間的關聯。
struct inet_protosw、struct proto及struct proto_ops之間的關聯在af_inet4中,這三個結構體之間的關聯如上所示,另外針對strcut inet_protosw型別,定義了全域性指標inetsw,用於連結inet4相關的所有strcut inet_protosw型別的變數,af_inet4註冊的變數如下所示,包含了tcp、udp、icmp、raw socket等struct inet_protosw型別的變數。
struct inet_protosw、struct proto、struct proto_ops、struct socket、struct sock、struct file之間的關聯針對上面介紹的這幾個結構體,並針對檔案描述符結構體struct file,將vfs與sockfs進行了關聯,且將socket與具體協議處理介面、具體的socket type處理介面完成了關聯操作,下圖便是這幾個結構體的關聯。
本小節主要講述了各結構體的定義以及結構體的關聯,通過將這些結構體之間的關係理清,能幫助我們更好的理解sockfs。