Linux内核网络子系统框架介绍
文章目录
- 1)术语
- 2)概述
- 3)源码目录介绍
- 4)linux内核网络协议栈分层概述
- 1、linux风味的层次结构
- 2、linux风味的层次结构(源文件表示)
- 3、计算机网络五层模型以及对应的常见协议
- 4、各个层级简析
- 5)深入socket通信
- 1、socket网络编程
- 2、重要的数据结构
- 1)socket套接字
- 2)netdevice
- 3、在Linux下一切皆文件,但是唯独网络设备不是?
- 4、socket相关的系统调用源码分析
- 6)网络数据包数据通道
- 1)接收通道大致流程
- 2)发送通道
1)术语
网络世界充斥着大量术语!VPN : Virtual Private Network
hdr : header
INET : internet
if : Interface
QoS : Quality of Servicephy的多重含义
1、在网络协议栈中表示physical 物理层;
2、phy也用于表示以太网芯片(实现物理层的集成芯片);实现MAC层和PHY层的集成芯片用ethenet来表示;IPsec子系统提供了一种网络层安全解决方案 - 嵌入在ipv6/ipv4中
2)概述
1、Linux内核网络协议栈的 实现很庞大复杂,专业的网络工程师才需要精通,驱动工程师专注于 根据驱动模型 开发对应驱动即可,网络协议栈底下有很多子系统(IPV4路由选择子系统、邻接子系统、Netfilter子系统、Linux无线子系统、InfiniBand子系统、蓝牙子系统、NFC子系统、PCI子系统);
2、传统内核网络包括有线网络(即以太网)、无线网络(即WIFI),无线子系统是后期新增的(在现有网络栈基础上新增差异化部分),介绍资料比较少,后续再开专题讨论,这里重点讨论以太网;
3、linux内核网络栈实现了计算机网络协议中的三个层级
1)网络设备驱动层 就位于数据链路层,网络设备驱动程序 将接收来自网络层的数据包sk_buff或传递给网络层;
2)网络协议栈本质上就是在封装sk_buff或解析sk_buff;
4、以设备驱动工程师来看Linux内核网络,需要掌握哪些?
1)掌握内核网络的整体框架和用户到驱动的调用流程;
2)协议细节本身不需要深入;
3)源码目录介绍
1、基本目录
1、Network configuration - 网络协议栈实现(包含多种子系统 - PHY/WIRELESS/NFC等)
android\kernel\fusion\4.19\net1) 核心实现
android\kernel\fusion\4.19\net\core2)网络协议栈实现
android\kernel\fusion\4.19\net\ipv4
android\kernel\fusion\4.19\net\ipv63)无线栈实现
android\kernel\fusion\4.19\net\wireless
android\kernel\fusion\4.19\net\mac80211
linux无线子系统代码分支是单独维护的,无线栈有常规网络设备没有的特性,使用的驱动框架是mac80211(无线独有的)2、Network device configuration - 网络设备驱动程序实现
android\kernel\fusion\4.19\drivers\net\phy - 只实现phy层的网卡芯片
android\kernel\fusion\4.19\drivers\net\ethenet -集成phy层、MAC层的网卡芯片
android\kernel\fusion\4.19\drivers\net\wireless -无线设备头文件介绍
1、Global definitions for the INET interface module
/android/vendor/amlogic/common/kernel/common_5.4/include/uapi/linux/if.h
struct ifaddr \ ifreq \ ifmap \ ifconf2、Definitions of the socket-level I/O control calls.
/android/vendor/amlogic/common/kernel/common_5.4/include/uapi/linux/sockios.h
1)Socket configuration controls#define SIOCGIFNAME 0x8910 /* get iface name */
2)device private ioctl calls
#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */3、中断相关
/android/vendor/amlogic/common/kernel/common_5.4/include/linux/interrupt.h
devm_request_irq() / devm_free_irq()4、网络层使用的重要结构体和函数
/android/vendor/amlogic/common/kernel/common_5.4/include/linux/net.h
struct socket \ socket_wq \ proto_ops
int kernel_sendmsg() \ int kernel_bind() \ int kernel_listen()5、协议相关
ip.h/ip_fw.h
ipx.h(internet packet exchange)
tcp.h / udp.h
2、网络协议栈的源码版本
网络协议栈的内核源码目录都集中在/net 下,对比Linux内核1.3和2.6的差异
https://elixir.bootlin.com/linux/v2.6.39.4/source/net
1)研究框架,建议是选择稳定的低版本(不能过早 - 特性和框架未完善,也不宜太新 - 太多辅助功能 影响分析)
以tcp.c文件为例(1.3 - 2000行代码,2.6 - 3400行代码,5.4 - 4000行代码),如果使用1.3版本,则大大减少代码分析负担,当然,要深入分析尽量使用1.3,如果只是过一下重点,使用项目中对应的版本就好!
2)net源码目录下有很多子系统(IPV4路由选择子系统、邻接子系统、Netfilter子系统、Linux无线子系统、InfiniBand子系统、蓝牙子系统、NFC子系统、PCI子系统),要注意区分;
3、网络模块源码组织
4)linux内核网络协议栈分层概述
1、linux风味的层次结构
1)系统调用接口和套接口层 不属于计算机网络体系层级,是linux系统用户态和内核态通信接口的封装实现;
2)非网络工程岗位,大多数只会接触到系统调用接口层和网络设备驱动程序;
3)可以看出,计算机协议除了应用层(HTTP/FTP),其它层级均由内核实现;
2、linux风味的层次结构(源文件表示)
1)表示层和会话层仅是类比,在linux充当过渡层使用;
2)dev.c文件规模的快速增长(1.3内核 1000行代码,2.6内核 6500行代码,5.4内核 10000行代码)
3、计算机网络五层模型以及对应的常见协议
介绍:https://blog.csdn.net/xx16755498979/article/details/132489516
4、各个层级简析
BSD socket
位于最上面的内核层接口,对接用户层的socket接口
/android/vendor/amlogic/common/kernel/common_5.4/net/socket.c
int __init sock_init(void)
{net_sysctl_init();skb_init();init_inodecache();register_filesystem(&sock_fs_type);kern_mount(&sock_fs_type);
}
core_initcall(sock_init);
INET socket
处理来自BSD层的请求,完成检查工作后将请求发送下层传输层进行具体的处理(可以理解为 是过渡层)
/android/vendor/amlogic/common/kernel/common_5.4/net/ipv4/af_inet.c
传输层
/android/vendor/amlogic/common/kernel/common_5.4/net/ipv4/tcp.c
void __init tcp_init(void)
{tcp_init_mem();tcp_v4_init();tcp_metrics_init();tcp_tasket_init();
}
网络层
/android/vendor/amlogic/common/kernel/common_5.4/net/ipv4/ip_output.c
void __init ip_init(void)
{ip_rt_init();inet_initpeers();
}
设备接口层
android/vendor/amlogic/common/kernel/common_5.4/net/core/dev.c
static int __init net_dev_init(void)
{dev_proc_init();netdev_kobject_init();register_pernet_subsys(&netdev_net_ops);register_pernet_device(&loopback_net_ops);register_pernet_device(&default_device_ops);
}
subsys_initcall(net_dev_init);
硬件驱动层
/android/vendor/amlogic/common/kernel/common_5.4/drivers/net/ethernet/davicom/dm9000.c
网卡DM9000裸机驱动详解:
https://zhuanlan.zhihu.com/p/374941834
5)深入socket通信
1、socket网络编程
1、socket是什么?
1)socket通信不仅只用于收发网络数据,还可以用于:应用->驱动,应用->应用间的IPC (本地和远程)2)socket通讯是内核实现?是的
android\kernel\fusion\4.19\net\socket.c2、使用ioctl访问wlan0
https://tvgit.gz.cvte.cn/c/MTK9256_AN14/source/base/android/+/1737666
struct ifreq ifr;
int ctl_sock = -1;
ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
ioctl(ctl_sock, TXRX_PARA, &ifr);3、应用<->应用通信 (通过什么凭证指定通讯对象? ip + 端口)
TCP/UPD编程示例:1)TCP服务器
#include <sys/socket.h>
#include <netinet/in.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int server_fd, new_socket;struct sockaddr_in address;int addrlen = sizeof(address);char buffer[BUFFER_SIZE] = {0};// 1. 创建 TCP socketserver_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0);// 2. 配置服务器地址address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY; // 接受任意IP的连接address.sin_port = htons(PORT); // 端口转换为网络字节序// 3. 绑定socket到地址bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0);// 4. 监听连接 (最多5个等待连接)listen(server_fd, 5) < 0);// 5. 接受客户端连接new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen);// 6. 读取客户端数据并回显read(new_socket, buffer, BUFFER_SIZE);printf("Received: %s\n", buffer);// 发送响应const char *response = "Message received!";send(new_socket, response, strlen(response), 0);// 7. 关闭socketclose(new_socket);close(server_fd);return 0;
}2)TCP客户端
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define PORT 8080
#define SERVER_IP "127.0.0.1" // 本地回环地址
#define BUFFER_SIZE 1024int main() {int sock = 0;struct sockaddr_in serv_addr;char buffer[BUFFER_SIZE] = {0};// 1. 创建TCP socketsock = socket(AF_INET, SOCK_STREAM, 0));// 2. 配置服务器地址serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(PORT);// 转换IP地址为二进制格式inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr);// 3. 连接到服务器connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr);// 4. 发送消息const char *hello = "Hello from TCP client!";send(sock, hello, strlen(hello), 0);printf("Message sent\n");// 5. 接收响应read(sock, buffer, BUFFER_SIZE);printf("Server response: %s\n", buffer);// 6. 关闭socketclose(sock);return 0;
}3)UDP服务端
#include <sys/socket.h>
#include <netinet/in.h>#define PORT 8081
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr, cliaddr;socklen_t len = sizeof(cliaddr);char buffer[BUFFER_SIZE];// 1. 创建UDP socketsockfd = socket(AF_INET, SOCK_DGRAM, 0);// 2. 配置服务器地址memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = INADDR_ANY; // 接受任意IPservaddr.sin_port = htons(PORT);// 3. 绑定socket到地址bind(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr));// 4. 接收数据报int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&cliaddr, &len);buffer[n] = '\0'; // 确保字符串终止printf("Received: %s\n", buffer);// 5. 发送响应const char *response = "UDP message received!";sendto(sockfd, response, strlen(response), 0,(const struct sockaddr*)&cliaddr, len);// 6. 关闭socketclose(sockfd);return 0;
}4)UDP客户端
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define PORT 8081
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr;char buffer[BUFFER_SIZE];// 1. 创建UDP socketsockfd = socket(AF_INET, SOCK_DGRAM, 0);// 2. 配置服务器地址memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT);servaddr.sin_addr.s_addr = inet_addr(SERVER_IP); // 直接转换IP// 3. 发送消息const char *hello = "Hello from UDP client!";sendto(sockfd, hello, strlen(hello), 0,(const struct sockaddr*)&servaddr, sizeof(servaddr));printf("Message sent\n");// 4. 接收响应socklen_t len = sizeof(servaddr);int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0,(struct sockaddr*)&servaddr, &len);buffer[n] = '\0';printf("Server response: %s\n", buffer);// 5. 关闭socketclose(sockfd);return 0;
}
2、重要的数据结构
1)socket套接字
android/vendor/amlogic/common/kernel/common_5.4/include/linux/net.h1、sock_type
enum sock_type {SOCK_STREAM = 1, //流式套接字,一般用于TCPSOCK_DGRAM = 2, //datagram 报文, 一般用于UDPSOCK_RAW = 3, //原始套接字,会绕过传输层SOCK_RDM = 4,SOCK_SEQPACKET = 5, //序列包套接字,比STREAM更安全SOCK_DCCP = 6,SOCK_PACKET = 10, //绕过所有协议栈,直接从链路层接收原始帧 已废弃
};1) 定义了网络通信的“服务类型”,它和domain地址族、具体协议protocol一起,为应用程序提供不同层次的网络通信能力;
2) SOCK_STREAM 和 SOCK_DGRAM 囊括了99%的应用场景2、地址族domain
supported address families
#define AF_UNSPEC 0
#define AF_UNIX 1 //用于同一台主机上的进程间通信(IPC),数据不经过网络协议栈,通过文件系统路径名寻址
#define AF_LOCAL 1
#define AF_INET 2 //IPV4网络协议,使用32位IPV4地址和16位端口号
#define AF_INET6 10
#define AF_NETLINK 16
#define AF_ROUTE AF_NETLINK
#define AF_PACKET 17 //用于底层网络操作和监控
#define AF_NFC 39
1)socket接口是个大杂烩,支持不同类型的网络(如internet、NFC、NETLINK等),使用domain来进行区分;
2)NETLINK是linux平台下的基于socket的IPC通信机制(既可以 应用->kernel,也可以是应用->应用),当下无线网络已采用NETLINK通信,实现用户空间与内核空间双向通信机制,比如supplicant与内核通信;3、PF_ 用于指定协议族
protocal families, same as address families
#define PF_UNSPEC AF_UNSPEC
...
与AF_的联系?
但在实际实现中,一个协议族使用的地址格式通常也是唯一的,所以它们的值完全一致。在现代编程中(如Linux),它们可以完全互换使用,不会产生任何区别。遵循 BSD 传统的代码通常在 socket() 调用中使用 PF_,而在操作地址时使用 AF_,但这仅仅是风格问题。4、地址族、协议族、sock_type的常见应用
#include <sys/socket.h>
// 创建一个用于TCP通信的IPv4 socket
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);// 创建一个用于本地进程间通信的socket
int local_socket = socket(AF_UNIX, SOCK_STREAM, 0);// 创建一个用于监听所有网络流量的raw socket(需要root权限)
int packet_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));// 创建一个用于配置网络路由的netlink socket
int netlink_socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);5、socket
struct socket {socket_state state;short type;unsigned long flags;struct file *file;struct sock *sk;const struct proto_ops *ops; //网络通讯方法集struct socket_wq wq; //等待队列
}struct proto_ops {int family;struct module *owner;int (*bind) (struct socket *sock, struct sockaddr *myaddr, int sockaddr_len);int (*connect) (struct socket *sock, struct sockaddr *vaddr, int sockaddr_len, ..);int (*accept) (struct socket *sock, struct sockaddr *newsock, ...);int (*ioctl) (struct socket *sock, unsigned int cmd, unsigned long arg);int (*listen) (struct socket *sock, int len);int (*sendmsg) (struct socket *sock, struct msghdr *m, size_t total_len);int (*recvmsg)(struct socket *sock, struct msghdr *m, size_t total_len, ..)
}
2)netdevice
1、网络设备
android/vendor/amlogic/common/kernel/common_5.4/include/linux/netdevice.h
struct net_device {char name[IFNAMSIZ];struct list_head dev_list;struct net_device_stats stats;
#if CONFIG_WIRELESS_EXTstruct iw_handler_def *wireless_handlers;struct iw_public_data *wireless_data;
#endifstruct net_device_ops *netdev_ops;struct ethtool_ops *ethtool_ops;struct header_ops *header_ops;struct wireless_dev *ieee80211_ptr;unsigned char *dev_addr;struct netdev_rx_queue *_rx;struct device dev;struct phy_device *phydev;
}struct net_device_ops {int (*ndo_init)(struct net_device *dev); //netdevice do initint (*ndo_open)(struct net_device *dev);netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb, struct net_device *dev);void (*ndo_set_rx_mode)(struct net_device *dev);int (*ndo_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);int (*ndo_set_config)(struct net_device *dev, struct ifmap *map);
}
可以看出从net_device结构包含了多种网络设备(WIFI、phy、其它等),开发者根据实际开发的目标设备实现对应的网络设备接口;2、wifi设备
/android/vendor/amlogic/common/kernel/common_5.4/include/net/cfg80211.h
struct wireless_dev {struct wiphy *wiphy;enum nl80211_iftype iftype;struct net_device *netdev;struct cfg80211_conn *conn;
#ifdef CONFIG_CFG80211_WEXTstruct {struct cfg80211_connect_params connect;}
#endif
}struct wiphy {struct mac_address *addresses;enum cfg80211_signal_type signal_type;
#ifdef CONFIG_PMconst struct wiphy_wowlan_support *wowlan;struct cfg80211_wowlan *wowlan_config;
#endifstruct device dev;struct list_head wdev_list;struct wiphy_vendor_command *vendor_commands;struct nl80211_vendor_cmd_info *vendor_events;
}3、以太网设备
/android/vendor/amlogic/common/kernel/common_5.4/include/linux/phy.h
struct phy_device {struct phy_driver *drv;u32 phy_id;enum phy_state state;phy_interface_t interface;void *priv;struct phylink *phylink;struct net_device *attached_dev;
}struct phy_driver {u32 phy_id;char *name;void *driver_data;int (*probe)(struct phy_device *phydev);int (*suspend)(struct phy_device *phydev);int (*resume)(struct phy_device *phydev);int (*set_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);int (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum);int (*read_page)(struct phy_device *dev);int (*set_loopback)(struct phy_device *dev, bool enable);
}4、网络设备注册接口
/android/vendor/amlogic/common/kernel/common_5.4/net/core/dev.c
register_netdevice(struct net_device *dev)
--dev_net(dev) //net namespace,用于隔离不同的协议栈
--dev->netdev_ops->ndo_init(dev); //调用dev的init
--call_netdevice_notifiers(NETDEV_POST_INIT, dev);
--netdev_register_kobject(dev);
--list_netdevice(dev); //将网络设备插入链表5、网络数据收发接口
/android/vendor/amlogic/common/kernel/common_5.4/include/linux/netdevice.h
1)
int netif_rx(struct sk_buff *skb); //驱动上报skb给网络协议栈
int netif_rx_ni(struct sk_buff *skb); //ni no interrupt 不可中断
2)
int dev_queue_xmit(struct sk_buff *skb); //协议栈下发skb给驱动
3、在Linux下一切皆文件,但是唯独网络设备不是?
1、先来看上层应用如何访问eth0/wlan0设备?
1)上层应用使用socket获取网络数据,上层不会感知底层是哪种设备(eth0/wlan0…),因此系统同一时间只能使用一种网络设备;
2)socket = open?与通过打开设备文件获取设备句柄,区别是什么?
》网络设备它不是通过设备节点来访问的,而是通过套接字动态创建文件句柄;
2、虽然Limnux中几乎所有的接口都是以文件形式组织的,但对于网络栈在/dev目录下却无这样的对应关系。不过内核网络栈实现仍然提供了对于网络数据的普通文件操作方式,如write、read函数可直接用于读写网络数据,在socket.c文件中可以看到内核提供的针对网络数据的文件操作函数集合的实现。
3、在socket.c源文件中,定义了网络协议对应的普通文件操作函数集合,从而使得read、write、ioct等这些常见普通文件操作函数也可以被使用在网络接口的处理上。值得注意的是,这些提供的普通文件接口中并没有定义open函数,因为对于网络而言,socket函数已经完成了类似的功能,并且open函数基本调用语义是通过文件路径进行操作,对于网络栈内核并无这样的对应文件。
4、网络协议栈 socket_file_ops
/android/vendor/amlogic/common/kernel/common_5.4/net/socket.c
1)ops定义
static const struct file_operations socket_file_ops = {.owner = THIS_MODULE,.llseek = no_llseek,.read_iter = sock_read_iter,.write_iter = sock_write_iter,.poll = sock_poll,.unlocked_ioctl = sock_ioctl,
}
2)ops的挂接
sock_map_fd
--sock_alloc_file(sock, flags, dname)
----alloc_file_pseudo(.., &socket_file_ops)
5、总的来说,网络设备不是通过open来产生对应的fd文件句柄,而是由socket接口动态创建fd文件句柄!
1)对于上层来说,使用上差异很小,无非就是建立连接 -> 数据传输;
2)对于底层来说,实现的机制有较大差异,因此被内核界称为“网络设备”独树一帜;
4、socket相关的系统调用源码分析
1、socket系统调用
1)定义
/android/bionic/libc/kernel/uapi/linux/net.h
/android/external/kernel-headers/original/uapi/linux/net.h#include <linux/socket.h>
#include <asm/socket.h>
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
#define SYS_SEND 9 /* sys_send(2) */
#define SYS_RECV 10 /* sys_recv(2) */
#define SYS_SENDTO 11 /* sys_sendto(2) */
#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
#define SYS_ACCEPT4 18 /* sys_accept4(2) */
#define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */
#define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */2)每个socket对应的系统调用函数如下
SYS_SOCKET 1 socket()
SYS_BIND 2 bind()
SYS_CONNECT 3 connect()
SYS_LISTEN 4 listen()
SYS_ACCEPT 5 accept()
SYS_GETSOCKNAME 6 getsockname()
SYS_GETPEERNAME 7 getpeername()
SYS_SOCKETPAIR 8 socketpair()
SYS_SEND 9 send()
SYS_RECV 10 recv()
SYS_SENDTO 11 sendto()
SYS_RECVFROM 12 recvfrom()
SYS_SHUTDOWN 13 shutdown()
SYS_SETSOCKOPT 14 setsockopt()
SYS_GETSOCKOPT 15 getsockopt()比如:bind()
bind(sockfd, &addr, addrlen)
int bind(int fd, const struct sockaddr *addr, socklen_t len) {return socketcall_cp(SYS_BIND, fd, (long)addr, len, 0, 0, 0);
}2、socket()接口源码调用过程
参考:https://www.cnblogs.com/573583868wuy/p/17937322对应内核接口__sys_socket
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{return __sys_socket(family, type, protocol);
}
android/vendor/amlogic/common/kernel/common_5.4/net/socket.c
int __sys_socket(int family, int type, int protocol)
{int retval;struct socket *sock;sock_create(family, type, protocol, &sock);return sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
}
1)
sock_create(family, type, protocol, &sock);
--__sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
--security_socket_create(family, type, protocol, kern); //分配socket、sock结构体
--sock_alloc();
--pf->create(net, sock, protocol, kern);
android/vendor/amlogic/common/kernel/common_5.4/net/ipv4/af_inet.c
----inet_create(net, sock, protocol, kern)
--security_socket_post_create(sock, family, type, protocol, kern);
2)
sock_map_fd()
--fd = get_unused_fd_flags(flags); //分配文件描述符
--sock_alloc_file(sock, flags, NULL); //分配inode、file结构用于普通文件操作3、bind()接口源码调用过程
SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
{return __sys_bind(fd, umyaddr, addrlen);
}__sys_bind
--sockfd_lookup_light(fd, ..)
--move_addr_to_kernel(umyaddr, addrlen, &address);
--sock->ops->bind(sock,(struct sockaddr *)&address, addrlen);
android/vendor/amlogic/common/kernel/common_5.4/net/ipv4/af_inet.c
----inet_bind(sock, uaddr, addr_len); //完成s_addr和s_port二元组绑定4、read()接口源码调用过程
1)对应内核函数入口
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{struct fd f = fdget_pos(fd);ret = vfs_read(f.file, buf, count, &pos);
}2)虚拟文件系统接口
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{if (file->f_op->read_iter) // 检查文件操作集是否有read_iter方法retval = file->f_op->read_iter(kio, iocb); //对于 Socket,struct file->f_op 指向 socket_file_ops。
}3)网络协议栈 socket_file_ops
/android/vendor/amlogic/common/kernel/common_5.4/net/socket.c
1、ops定义
static const struct file_operations socket_file_ops = {.owner = THIS_MODULE,.llseek = no_llseek,.read_iter = sock_read_iter,.write_iter = sock_write_iter,.poll = sock_poll,.unlocked_ioctl = sock_ioctl,
}
4) ops的挂接
sock_map_fd
--sock_alloc_file(sock, flags, dname)
----alloc_file_pseudo(.., &socket_file_ops)5)sock_read_iter()
--sock_recvmsg(sock, &msg, msg.msg_flags);
----sock_recvmsg_nosec(sock, msg, flags);
------INDIRECT_CALL_INET(sock->ops->recvmsg, inet6_recvmsg,inet_recvmsg, sock, msg, msg_data_left(msg),flags);
android/vendor/amlogic/common/kernel/common_5.4/net/ipv4/af_inet.c -INET层
--------inet_recvmsg(sock, msg, size, flags);
----------sk->sk_prot->recvmsg();
android/vendor/amlogic/common/kernel/common_5.4/net/ipv4/tcp.c -传输层,极其复杂
------------tcp_recvmsg()
--------------sk_wait_data(sk, &timeo, last)
--------------skb_copy_datagram_msg(skb, offset, msg, used);5、ioctl()接口源码调用过程
sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
1)分支1
//cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)
/android/vendor/amlogic/common/kernel/common_5.4/net/core/dev_ioctl.c
--dev_ioctl(net, cmd, &ifr, &need_copyout);
2)分支2
--sock_do_ioctl(net, sock, cmd, arg);
----sock->ops->ioctl(sock, cmd, arg);
android/vendor/amlogic/common/kernel/common_5.4/net/ipv4/af_inet.c
------inet_ioctl()
--------sk->sk_prot->ioctl() --传输层tcp
6)网络数据包数据通道
1)接收通道大致流程
1、硬件监听物理层传输介质,进行数据的接收,当完全接收一个数据包后,产生中断信号给到CPU;
2、进去驱动程序注册的中断处理函数,完成数据从硬件缓冲区拷贝到内核缓冲区,将其封装为sk_buff结构;
3、驱动调用netif_rx,将数据包上报到链路层;
4、层层上报后(比如ip_rcv->tcp_rcv),最终存放在sock结构receive_queue指向的队列中;
5、等待用户从内核中读取;
2)发送通道
发送通道相对容易理解,用户将数据准备好,调用发送,经过层层下沉,最终物理层发送出去;