日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

socket 源码分析

發布時間:2023/12/20 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 socket 源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Socket 源碼分析

我們使用 socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 語句創建了一個 socket ,那么實際上發生了什么呢?

從下面的注釋可以看的出來,之前是通過調用 sys_socketcall 再跳轉, 現在是直接調用 sys_socket 函數。

下面的宏就是完成 sys_socket 到 SYSCALL_DEFINE3 宏包圍的函數的調用關系。如果一大堆宏比較麻煩,可以直接參考 -->Linux系統調用之SYSCALL_DEFINE 的分析。

/include/linux/syscalls.h/* obsolete: net/socket.c */asmlinkage long sys_socketcall(int call, unsigned long __user *args);/* net/socket.c */asmlinkage long sys_socket(int, int, int);asmlinkage long sys_socketpair(int, int, int, int __user *);asmlinkage long sys_bind(int, struct sockaddr __user *, int);asmlinkage long sys_listen(int, int);asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *);//omit...#define SYSCALL_DEFINEx(x, sname, ...) \SYSCALL_METADATA(sname, x, __VA_ARGS__) \__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)/** The asmlinkage stub is aliased to a function named __se_sys_*() which* sign-extends 32-bit ints to longs whenever needed. The actual work is* done within __do_sys_*().*/#ifndef __SYSCALL_DEFINEx#define __SYSCALL_DEFINEx(x, name, ...) \__diag_push(); \__diag_ignore(GCC, 8, "-Wattribute-alias", \"Type aliasing is used to sanitize syscall arguments");\asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \__attribute__((alias(__stringify(__se_sys##name)))); \ALLOW_ERROR_INJECTION(sys##name, ERRNO); \static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \{ \long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\__MAP(x,__SC_TEST,__VA_ARGS__); \__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \return ret; \} \__diag_pop(); \static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))#endif /* __SYSCALL_DEFINEx */#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

關于 SYSCALL_DEFINE 解釋比較詳細的一篇文章

到此為止,用戶 調用 socket 已經進行到了內核中此處的函數

代碼位置: net/socket.c

SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol){int retval;struct socket *sock;int flags;//Omit...flags = type & ~SOCK_TYPE_MASK;if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))return -EINVAL;type &= SOCK_TYPE_MASK;if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;// 可以看出就是這里進行的 socket 的創建工作,接下來重點對這個代碼進行分析retval = sock_create(family, type, protocol, &sock);if (retval < 0)goto out;retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));if (retval < 0)goto out_release;out:/* It may be already another descriptor 8) Not kernel problem. */return retval;out_release:sock_release(sock);return retval;}sock_create 實際上是調用了 __socket_create int sock_create(int family, int type, int protocol, struct socket **res){return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);}int __sock_create(struct net *net, int family, int type, int protocol,struct socket **res, int kern){int err;struct socket *sock;const struct net_proto_family *pf;/** Check protocol is in range*/if (family < 0 || family >= NPROTO)return -EAFNOSUPPORT;if (type < 0 || type >= SOCK_MAX)return -EINVAL;/* Compatibility.This uglymoron is moved from INET layer to here to avoiddeadlock in module load.*/if (family == PF_INET && type == SOCK_PACKET) {pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",current->comm);family = PF_PACKET;}/** Allocate the socket and allow the family to set things up. if* the protocol is 0, the family is instructed to select an appropriate* default.*/// 申請一個與 inode 綁定的 socket 。sock = sock_alloc();if (!sock) {net_warn_ratelimited("socket: no more sockets\n");return -ENFILE; /* Not exactly a match, but its theclosest posix thing */}sock->type = type;rcu_read_lock();// 從 RCU 中找到協議對應的數據pf = rcu_dereference(net_families[family]);err = -EAFNOSUPPORT;if (!pf)goto out_release;/* Now protected by module ref count */rcu_read_unlock();// 調用其中的 create 函數err = pf->create(net, sock, protocol, kern);if (err < 0)goto out_module_put;// Omit...*res = sock;return 0;// Omit...}

其中 net_families[family] 這個結構數組的定義可以下面,其中注釋中已經說了,所有協議都將被注冊到這個數組中。所以從 rcu 中找到的就是這個結構,調用的就是這個結構中的 create 函數。

/** The protocol list. Each protocol is registered in here.*/static const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly;struct net_proto_family {int family;int (*create)(struct net *net, struct socket *sock,int protocol, int kern);struct module *owner;};

回到開始,我們創建 socket 使用的 family 參數是 AP_PACKET, 于是找到對應的 net_proto_family 結構。

位置: net/packet/af_packet.c

static const struct net_proto_family packet_family_ops = {.family = PF_PACKET,.create = packet_create,.owner = THIS_MODULE,};

這個是kernel中隊 sock 結構的解釋。接下來繼續分析 packet_create 函數。

// struct sock - network layer representation of sockets static int packet_create(struct net *net, struct socket *sock, int protocol,int kern){struct sock *sk;struct packet_sock *po;__be16 proto = (__force __be16)protocol; /* weird, but documented */int err;if (!ns_capable(net->user_ns, CAP_NET_RAW))return -EPERM;if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW &&sock->type != SOCK_PACKET)return -ESOCKTNOSUPPORT; sock->state = SS_UNCONNECTED; /* unconnected to any socket */err = -ENOBUFS;//根據參數創建 sock 對象。sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto, kern);if (sk == NULL)goto out;// 這里面有關于套接字的各種操作,比如 bind、accept 等。sock->ops = &packet_ops;if (sock->type == SOCK_PACKET)sock->ops = &packet_ops_spkt;// 經過前面的初始化過程后,此處將 sock 結構 與 socket 發生關聯(將 socket 綁定到 sock 上),并初始化 sock 結構。sock_init_data(sock, sk);po = pkt_sk(sk);sk->sk_family = PF_PACKET;po->num = proto; // 將協議添加到此處po->xmit = dev_queue_xmit; //設置發送數據包函數err = packet_alloc_pending(po);if (err)goto out2;packet_cached_dev_reset(po);sk->sk_destruct = packet_sock_destruct; // 銷毀數據包sk_refcnt_debug_inc(sk);/** Attach a protocol block*/spin_lock_init(&po->bind_lock);mutex_init(&po->pg_vec_lock);po->rollover = NULL;// 指定 hook 的函數po->prot_hook.func = packet_rcv;// 如果參數使用的是 SOCK_PACKET 那么函數就要使用 packet_rcv_spktif (sock->type == SOCK_PACKET)po->prot_hook.func = packet_rcv_spkt;po->prot_hook.af_packet_priv = sk;// 這里就可以看出指定的參數中的協議類型的作用,將會將 hook 的類型設置為協議類型if (proto) {po->prot_hook.type = proto;register_prot_hook(sk); // 這個函數還可以分析一下}mutex_lock(&net->packet.sklist_lock);sk_add_node_rcu(sk, &net->packet.sklist);mutex_unlock(&net->packet.sklist_lock);preempt_disable();sock_prot_inuse_add(net, &packet_proto, 1);preempt_enable();return 0;out2:sk_free(sk);out:return err;}```這樣就創建出了一個sock結構,客戶端的句柄實際上在內核中就是這個的對應對應關系。接下來的 bind、 send 等函數就是調用 packet_ops 結構中的函數。## 拓展[關于 Linux RCU 的詳細介紹](http://www2.rdrop.com/users/paulmck/RCU/)[Linux網絡之設備接口層:發送數據包流程dev_queue_xmit](https://blog.csdn.net/wdscq1234/article/details/51926808)

總結

以上是生活随笔為你收集整理的socket 源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。