【C语言】Linux内核accept 系统调用代码

一、Linux 4.19内核accept 系统调用代码中文注释

/** 在使用accept时,我们尝试创建一个新的socket,与客户端建立连接,* 唤醒客户端,然后返回新的连接文件描述符(fd)。我们在内核空间收集* 连接方的地址,并在最后将其移到用户空间。这样做不干净是因为,我们* 打开socket后可能返回一个错误。** 1003.1g标准增加了通过recvmsg()查询连接挂起状态的能力。我们* 需要在重构accept时,也以一种干净的方式增加这个支持。*/int __sys_accept4(int fd, struct sockaddr __user *upeer_sockaddr,int __user *upeer_addrlen, int flags)
{struct socket *sock, *newsock;struct file *newfile;int err, len, newfd, fput_needed;struct sockaddr_storage address;// 校验传入的flags标志if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))return -EINVAL;// 将SOCK_NONBLOCK转换为对应的O_NONBLOCK标志if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;// 通过文件描述符找到对应的socket,fd为文件描述符sock = sockfd_lookup_light(fd, &err, &fput_needed);if (!sock)goto out;// ENFILE表示文件表溢出err = -ENFILE;newsock = sock_alloc(); // 分配一个新的socketif (!newsock)goto out_put;// 设置新socket的类型和操作函数newsock->type = sock->type;newsock->ops = sock->ops;/** 我们不需要调用try_module_get,因为监听socket(sock)* 已经有了协议模块(sock->ops->owner)。*/__module_get(newsock->ops->owner);// 获得未使用的文件描述符newfd = get_unused_fd_flags(flags);if (unlikely(newfd < 0)) {err = newfd;sock_release(newsock); // 释放socket资源goto out_put;}// 分配新socket文件表项newfile = sock_alloc_file(newsock, flags, sock->sk->sk_prot_creator->name);if (IS_ERR(newfile)) {err = PTR_ERR(newfile);put_unused_fd(newfd);goto out_put;}// 安全检查err = security_socket_accept(sock, newsock);if (err)goto out_fd;// 调用实际的accept操作err = sock->ops->accept(sock, newsock, sock->file->f_flags, false);if (err < 0)goto out_fd;// 如果用户提供了地址存储,则获取并复制给用户if (upeer_sockaddr) {len = newsock->ops->getname(newsock,(struct sockaddr *)&address, 2);if (len < 0) {err = -ECONNABORTED;goto out_fd;}err = move_addr_to_user(&address,len, upeer_sockaddr, upeer_addrlen);if (err < 0)goto out_fd;}/* 与某些操作系统不同,文件标志不是通过accept()继承的。 */// 将新的文件描述符安装到文件表中fd_install(newfd, newfile);err = newfd;out_put:fput_light(sock->file, fput_needed); // 释放文件表项
out:return err; // 返回错误码或新的文件描述符
out_fd:fput(newfile); // 释放文件表项put_unused_fd(newfd); // 释放未使用的文件描述符goto out_put;
}// 处理accept4系统调用,将用户态参数转到内核态处理
SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,int __user *, upeer_addrlen, int, flags)
{return __sys_accept4(fd, upeer_sockaddr, upeer_addrlen, flags);
}// 处理accept系统调用,flags默认为0
SYSCALL_DEFINE3(accept, int, fd, struct sockaddr __user *, upeer_sockaddr,int __user *, upeer_addrlen)
{return __sys_accept4(fd, upeer_sockaddr, upeer_addrlen, 0);
}

这段代码定义了两个系统调用函数:
- SYSCALL_DEFINE4(accept4, ...): 这是一个宏,它定义了`accept4`系统调用的接口。`accept4`允许用户程序在创建一个新的socket连接时指定额外的选项,比如`SOCK_CLOEXEC`和`SOCK_NONBLOCK`。它接收四个参数:文件描述符`fd`、用户空间的指向`sockaddr`结构的指针`upeer_sockaddr`、指向地址长度变量的指针`upeer_addrlen`、以及标志位`flags`。内核会将这个调用委托给`__sys_accept4`处理。
- SYSCALL_DEFINE3(accept, ...): 这个宏定义了标准的`accept`系统调用,它不接收`flags`参数(默认为0),只在`accept4`的基础上省略了最后一个参数。其余的参数和`accept4`相同。内核也会将这个调用委托给`__sys_accept4`处理,只不过`flags`参数提供了默认值0。
这两个宏最终都会导致`__sys_accept4`函数被调用来执行处理连接请求的实际工作。
- upeer_sockaddr和`upeer_addrlen`是指向用户空间的指针,表明`accept`和`accept4`在连接成功之后,可以返回新连接端点的地址信息给用户程序。
这部分的代码是Linux内核网络栈中处理接受新连接请求的关键组成部分。这些函数直接与用户空间的应用程序交互,允许它们在监听的网络端口上接受新的连接。

二、代码解释

这段代码是Linux内核4.19中处理`accept`系统调用的实现,其作用是允许服务器接受一个来自客户端的连接请求。以下是解读:
这个函数尝试创建一个新的socket,设置与客户端的链接,唤醒客户端,然后返回新创建的已连接的文件描述符(fd)。它会在内核空间收集连接方的地址信息,并将信息复制到用户空间。
这里定义了两个系统调用的接口函数:`SYSCALL_DEFINE3(accept, ...)`和`SYSCALL_DEFINE4(accept4, ...)。`accept`是基本的接受连接的系统调用,而`accept4`是一个增强版,允许用户通过额外的`flags`参数设置一些选项。
__sys_accept4函数做了以下工作:
1. 首先检查传入的`flags`参数,确保用户没有设置不合法的标志,如果是就返回`-EINVAL`(表示无效的参数)。
2. 调用`sockfd_lookup_light`函数,尝试获取与给定文件描述符`fd`关联的socket对象。如果成功,继续后续操作;否则,返回错误。
3. 分配一个新的socket结构给新的连接,并拷贝被监听socket的一些属性。
4. 获取一个未使用的文件描述符`newfd`,分配并初始化一个新的文件对象`newfile`。如果这些操作发生错误,清理并返回错误码。
5. 调用`security_socket_accept`和`sock->ops->accept`进行安全检查和实际的接受操作。这些步骤涉及底层协议的操作,以处理连接建立等细节。
6. 如果用户提供了地址存储空间(`upeer_sockaddr`),将新socket的地址信息从内核空间复制到用户空间。
7. 使用`fd_install`将新的文件对象`newfile`绑定到新的文件描述符`newfd`。
8. 进行清理工作,如果在任何一步出错,释放之前分配的资源,并返回错误码;若无误,返回新的连接文件描述符`newfd`。
该函数考虑了错误处理路径,在任何可能的错误点,都会释放占用的资源以避免内存泄露。执行成功时将返回一个代表新连接的文件描述符。
这段代码是在Linux内核中处理`accept`与`accept4`系统调用的实现,这些系统调用是由服务器程序使用的,它们允许服务器接受来自客户端的连接请求。继续分步解释这个函数的执行流程。
1. 检查传入的`flags`参数:
   - 若`flags`包含了不是`SOCK_CLOEXEC`或`SOCK_NONBLOCK`的其他标志位,则返回错误`-EINVAL`。
   - 如果`flags`指定了非阻塞标志`SOCK_NONBLOCK`,并且这个标志和系统定义的`O_NONBLOCK`不相同,则将`flags`中的`SOCK_NONBLOCK`标志替换成`O_NONBLOCK`。
2. 调用`sockfd_lookup_light`函数来查找与文件描述符`fd`对应的socket结构体。如果没有找到,设置错误号`err`,并立即跳到`out`标签处处理退出。
3. 尝试分配一个新的socket(`newsock`)给即将建立的连接。如果分配失败,设置错误号`err`为`-ENFILE`(文件表溢出),然后跳到`out_put`标签处释放已引用的socket并处理退出。
4. 尝试获取一个未使用的文件描述符`newfd`,并为这个文件描述符创建一个新的文件对象`newfile`对应于新的socket。如果获取文件描述符失败或者创建文件对象失败,设置相应的错误号,释放资源,然后退出。
5. 调用`security_socket_accept`函数进行安全检查,并调用原始socket的`accept`方法以完成连接的建立。如果这些步骤中任何一个失败了,跳到`out_fd`来处理错误。
6. 如果用户程序提供了一个地址用于存储客户端的信息(`upeer_sockaddr`),那么获取新socket的地址信息,并通过`move_addr_to_user`函数将地址信息复制到用户空间。如果这个过程中出现错误,设置错误号并跳到`out_fd`处理错误。
7. 通过`fd_install`函数将文件对象`newfile`与文件描述符`newfd`关联起来,完成文件描述符的安装。
8. 得到成功的结果`err`,这是新的文件描述符,这意味着可以通过这个描述符与客户端通信。
如果在这个过程的任何一步中出现了错误,代码会跳到错误处理的部分,执行清理工作,确保不会有资源泄露。正确执行的流程结束时,返回新打开的文件描述符`newfd`用于后续通信。
最后,定义了`SYSCALL_DEFINE4`和`SYSCALL_DEFINE3`宏,这两个宏是用来声明接受4个参数和3个参数的系统调用接口。它们确保了`accept`和`accept4`系统调用可以通过系统调用接口被用户空间的程序访问。这些宏最终将调用`__sys_accept4`来完成实际的工作。如果使用`accept`而非`accept4`,那么`flags`参数默认为0,表示标准的`accept`行为。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/471635.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

laravel_框架结构和文件夹_简单分析

文章目录 简单看一下laravel框架的目录结构illuminate_一柳梅内特的含义illuminate下面有多少文件夹的&#xff1f;介绍一下illuminate下面的文件夹功能我的分类 简单看一下laravel框架的目录结构 illuminate_一柳梅内特的含义 作为及物动词&#xff0c;它的意思是“阐明&…

leetcode hot 100最小花费爬楼梯

本题和之前的爬楼梯类似&#xff0c;但是需要考虑到花费的问题&#xff01;**注意&#xff0c;只有在爬的时候&#xff0c;才花费体力&#xff01;**那么&#xff0c;我们还是按照动态规划的五部曲来思考。 首先我们要确定dp数组的含义&#xff0c;那么就是我们爬到第i层所花费…

【机器学习案例5】语言建模 - 最常见的预训练任务一览表

自监督学习 (SSL) 是基于 Transformer 的预训练语言模型的支柱,该范例涉及解决有助于建模自然语言的预训练任务 (PT)。本文将所有流行的预训练任务放在一起,以便我们一目了然地评估它们。 SSL 中的损失函数 这里的损失函数只是模型训练的各个预训练任务损失的加权和。 以BE…

计算组合数C(n,k)即从n个不同元素中选取k个元素的方法数binom()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算组合数C(n,k) 即从n个不同元素中 选取k个元素的方法数 binom() [太阳]选择题 以下代码的输出结果中正确的是? from scipy.special import binom print("【执行】binom(3, 2)"…

LabVIEW虚拟测试与分析仪

LabVIEW虚拟测试与分析仪 在现代工程技术领域&#xff0c;虚拟仪器的开发和应用已成为一种趋势。利用LabVIEW软件平台开发的虚拟测试与分析仪器进行展开&#xff0c;实现工程测试和分析中的实际需求。通过结合LabVIEW的强大功能和灵活性&#xff0c;成功实现了一套高效、精确的…

嵌入式STM32 单片机 GPIO 的工作原理详解

STM32的 GPIO 介绍 GPIO 是通用输入/输出端口的简称&#xff0c;是 STM32 可控制的引脚。GPIO 的引脚与外部硬件设备连接&#xff0c;可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。 以 STM32F103ZET6 芯片为例子&#xff0c;该芯片共有 144 脚芯片&#xff0c…

算法沉淀——BFS 解决 FloodFill 算法(leetcode真题剖析)

算法沉淀——BFS 解决 FloodFill 算法 01.图像渲染02.岛屿数量03.岛屿的最大面积04.被围绕的区域 BFS&#xff08;广度优先搜索&#xff09;解决 Flood Fill 算法的基本思想是通过从起始点开始&#xff0c;逐层向外扩展&#xff0c;访问所有与起始点相连且具有相同特性&#xf…

CF1845 D. Rating System [思维题+数形结合]

传送门:CF [前题提要]:自己在做这道题的时候思路完全想错方向,导致怎么做都做不出来,看了题解之后感觉数形结合的思考方式挺好的(或者这种做法挺典的),故写篇题解记录一下 题目很简单,不再解释.先不考虑 k k k,想想是一种什么情况?很显然应该是跟下图一样是一个折线图的变化.…

2024-02-16 AIGC-数字人-平台调研-记录

摘要: 2024-02-16 AIGC-数字人-平台调研 需求分析: 数字人-平台调研 南京硅基智能北京风平智能[风平科技]品达集团[杭州品达企服科技(集团)有限公司]花脸数字技术灰豚数字人[温州专帮信息科技有限公司]魔珐科技数字栩生公司官网guiji-ows风平智能 - 领先的AIGC解决方案提供商。…

【Linux】Linux编译器-gcc/g++ Linux项目自动化构建工具-make/Makefile

目录 Linux编译器-gcc/g使用 1.背景知识 Linux中头文件的目录在 Linux 库 条件编译的典型应用 2.gcc如何完成 动态库 vs 静态库 debug && release Linux项目自动化构建工具-make/Makefile 背景 用法 特殊符号 Linux编译器-gcc/g使用 1.背景知识 预处理&am…

【STM32 CubeMX】I2C层次结构、I2C协议

文章目录 前言一、I2C的结构层次1.1 怎样在两个设备之间传输数据1.2 I2C如何传输数据1.3 硬件框图1.4 软件层次 二、IIC协议2.1 硬件连接2.2 I2C 总线的概念2.3 传输数据类比2.3 I2C信号2.4 I2C数据的含义 总结 前言 在STM32 CubeMX环境中&#xff0c;I2C&#xff08;Inter-In…

C++ 模板进阶

C 模板进阶 一.非类型模板参数1.概念2.实例3.注意事项 二.模板的特化1.引出2.函数模板的特化1.语法和使用2.建议 3.类模板的特化1.全特化2.偏特化1.部分特化2.对参数进行进一步的限制 4.匹配顺序 三.模板的分离编译1.什么是分离编译2.模板的分离编译3.解决方法1.显式实例化(不推…