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

2024-02-17 01:04

本文主要是介绍【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`行为。

这篇关于【C语言】Linux内核accept 系统调用代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/716227

相关文章

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll