首頁>技術>

在之前的文章中,我們介紹了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。

29

Linux

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 常用的幾種大資料架構分析