【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

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor