【C语言】socketpair 的系统调用

2024-02-16 11:36

本文主要是介绍【C语言】socketpair 的系统调用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、 Linux 内核 4.19socketpair 的系统调用

SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol,int __user *, usockvec)
{return __sys_socketpair(family, type, protocol, usockvec);
}

这段代码定义了一个名为 socketpair 的系统调用。系统调用是操作系统提供给用户程序的接口,允许用户程序请求内核提供的服务。下面解读这段代码:
1. 宏定义 SYSCALL_DEFINE4: 它是一个预处理器宏,用来定义一个带有四个参数的系统调用接口。这个宏展开后,会生成向操作系统注册 socketpair 系统调用所需的所有内核代码。
2. 参数 int, family: 定义了 socket 对的域,这表示网络通信的域。比如 AF_INET 用于 IPv4 通信,`AF_UNIX` 用于本地进程间通信。这个参数告诉内核应当使用哪种网络协议族来创建 socket 对。
3. 参数 int, type: 定义了 socket 对的类型。`SOCK_STREAM` 表示提供序列化的、可靠的、双向的、基于连接的字节流(类似 TCP)。`SOCK_DGRAM` 表示数据报文,用于无连接的消息传递(类似 UDP)。
4. 参数 int, protocol: 表明使用哪种具体的协议。通常情况下,当 family 和 type 已经确定时,`protocol` 可以被设置为 0 来自动选择默认协议。
5. 参数 int __user *, usockvec: 这是一个用户空间的整型数组指针,用来存放内核返回的两个 socket 文件描述符。这里的 __user 是一个类型说明符,用于标示该指针指向的是用户空间的内存,这是为了在内核空间和用户空间中传递数据而使用的一种安全措施。
函数体仅包含了一行代码,它直接调用了 __sys_socketpair 函数,这个函数是真正完成工作的内部函数。它接收上面的四个参数,创建一对相互连接的套接字(sockets),并将文件描述符通过 usockvec 返回给用户空间的程序。
该 socketpair 系统调用与 C 语言中的 socketpair 函数有直接关系。在用户空间编程中,当调用标准的 C 语言库函数 socketpair() 时,该库函数最终会使用一个系统调用来与内核通信,请求创建 socket 对。对于用户程序而言,`socketpair()` 函数是与 socketpair 系统调用的接口。C 函数只是内部对系统调用转换为用户空间的库函数调用,实际的工作是在内核中完成的。因此,这段代码是 socketpair 用户空间函数背后的内核实现。

二、linux内核4.9中的 __sys_socketpair函数

/**	Create a pair of connected sockets.*/int __sys_socketpair(int family, int type, int protocol, int __user *usockvec)
{struct socket *sock1, *sock2; // 定义两个socket结构体指针int fd1, fd2, err; // 分别用于存储文件描述符和错误码struct file *newfile1, *newfile2; // 对应两个socket的文件结构体指针int flags; // 标志位变量// 提取标志位并检查是否包含非法标志flags = type & ~SOCK_TYPE_MASK;if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))return -EINVAL;type &= SOCK_TYPE_MASK; // 保留有效的socket类型// 如果SOCK_NONBLOCK不与O_NONBLOCK相等,并且flags包含SOCK_NONBLOCK,则替换为O_NONBLOCKif (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;// 获取未用的文件描述符fd1fd1 = get_unused_fd_flags(flags);if (unlikely(fd1 < 0)) // 如果获取失败,返回错误码return fd1;// 获取未用的文件描述符fd2fd2 = get_unused_fd_flags(flags);if (unlikely(fd2 < 0)) { // 如果获取失败,清理fd1并返回错误码put_unused_fd(fd1);return fd2;}// 将文件描述符拷贝到用户空间err = put_user(fd1, &usockvec[0]);if (err) // 如果拷贝出错,跳转到错误处理goto out;err = put_user(fd2, &usockvec[1]);if (err) // 如果拷贝出错,跳转到错误处理goto out;// 创建第一个socketerr = sock_create(family, type, protocol, &sock1);if (unlikely(err < 0)) // 如果创建失败,跳转到错误处理goto out;// 创建第二个socketerr = sock_create(family, type, protocol, &sock2);if (unlikely(err < 0)) { // 如果创建失败,释放第一个socket并跳转到错误处理sock_release(sock1);goto out;}// 检查安全性限制err = security_socket_socketpair(sock1, sock2);if (unlikely(err)) { // 如果检查失败,释放两个socket并跳转到错误处理sock_release(sock2);sock_release(sock1);goto out;}// 使用socket操作集连接两个socketerr = sock1->ops->socketpair(sock1, sock2);if (unlikely(err < 0)) { // 如果操作失败,释放两个socket并跳转到错误处理sock_release(sock2);sock_release(sock1);goto out;}// 为两个socket分配文件结构体newfile1 = sock_alloc_file(sock1, flags, NULL);if (IS_ERR(newfile1)) { // 如果分配失败,保存错误码,释放第二个socket,跳转到错误处理err = PTR_ERR(newfile1);sock_release(sock2);goto out;}newfile2 = sock_alloc_file(sock2, flags, NULL);if (IS_ERR(newfile2)) { // 如果分配失败,保存错误码,释放第一个文件结构体,跳转到错误处理err = PTR_ERR(newfile2);fput(newfile1);goto out;}// 记录审计信息audit_fd_pair(fd1, fd2);// 安装两个文件描述符fd_install(fd1, newfile1);fd_install(fd2, newfile2);return 0; // 操作成功,返回0out: // 错误处理部分put_unused_fd(fd2); // 释放第二个文件描述符put_unused_fd(fd1); // 释放第一个文件描述符return err; // 返回错误码
}

这段代码是Linux内核中负责创建socket对(两个相互连接的socket)的系统调用`__sys_socketpair`的实现。下面是解读:
1. 定义了一些变量,包括两个socket结构体指针`sock1`和`sock2`,两个文件描述符`fd1`和`fd2`,一个错误码`err`,以及一个`newfile1`和`newfile2`文件结构体指针。
2. 通过`type & ~SOCK_TYPE_MASK`提取标志位,并检查是否有除了`SOCK_CLOEXEC`和`SOCK_NONBLOCK`之外的未知标志位,如果有,则返回错误`-EINVAL`。
3. 然后,将`type`限制为仅包含有效的socket类型。
4. 如果`SOCK_NONBLOCK`标志和`O_NONBLOCK`不是同一个值,并且`flags`中有`SOCK_NONBLOCK`,则替换为`O_NONBLOCK`标志。
5. 调用`get_unused_fd_flags`获取两个未使用的文件描述符`fd1`和`fd2`,如果获取失败,则返回对应的错误码。
6. 使用`put_user`功能将两个文件描述符复制到用户空间指定的数组。
7. 使用`sock_create`函数创建两个新的socket,如果这一步或者之后的步骤出错,则会进行清理并返回错误码。
8. 检查安全性相关的问题,如果`security_socket_socketpair`返回错误,则释放socket资源并跳转到错误处理。
9. 使用socket的操作集中的`socketpair`函数连接这两个socket,如果出错,则释放socket资源并跳转到错误处理。
10. 分别为这两个socket分配文件结构体`newfile1`和`newfile2`,这些文件结构体可以在文件系统中表示一个打开的文件。
11. 如果在分配文件结构体过程中出现错误,将错误保存在`err`中,并释放相应资源,然后跳转到错误处理。
12. 使用`audit_fd_pair`记录审计信息。
13. 使用`fd_install`将新的文件结构体安装到之前获取的文件描述符上,这使得这两个文件描述符实际上指向两个连接的socket。
14. 如果代码执行成功地到达这里,就返回0,表示socket对创建成功。
15. 如果在标签`out`下执行,表明过程中出现了错误,需要清理资源并放回未使用的文件描述符,然后返回错误码。

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



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

相关文章

Go 语言中的select语句详解及工作原理

《Go语言中的select语句详解及工作原理》在Go语言中,select语句是用于处理多个通道(channel)操作的一种控制结构,它类似于switch语句,本文给大家介绍Go语言中的select语... 目录Go 语言中的 select 是做什么的基本功能语法工作原理示例示例 1:监听多个通道示例 2:带

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用