【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_NONBLOCK
    if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
        flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

    // 获取未用的文件描述符fd1
    fd1 = get_unused_fd_flags(flags);
    if (unlikely(fd1 < 0)) // 如果获取失败,返回错误码
        return fd1;

    // 获取未用的文件描述符fd2
    fd2 = 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;

    // 创建第一个socket
    err = sock_create(family, type, protocol, &sock1);
    if (unlikely(err < 0)) // 如果创建失败,跳转到错误处理
        goto out;

    // 创建第二个socket
    err = 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操作集连接两个socket
    err = 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; // 操作成功,返回0

out: // 错误处理部分
    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`下执行,表明过程中出现了错误,需要清理资源并放回未使用的文件描述符,然后返回错误码。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript基础第三天

JavaScript 基础第三天 今天我们学习for循环、while循环、终止循环和无限循环。 1. for 循环 1.1. 语法 // 1. 语法格式 // for(起始值; 结束条件; 累加器) { // // 要重复执行的代码 // }1.2. 示例代码 let sum 0; for (let i 0; i < 100; i) {sum i; } alert(&q…

阿里云服务器4核16G配置报价和CPU内存性能参数表

阿里云4核16G服务器优惠价格ECS云服务器经济型e实例26元1个月、149元半年、79元3个月&#xff0c;4核16G通用算力u1服务器、通用型g7、通用型g8i、AMD通用型g8a、性能增强通用型g8ae、高主频通用型hfg8i、AMD通用型g7a、内存型r7p等均提供4核16G配置。阿里云服务器网aliyunfuwu…

C#通过重写虚方法实现加、减、乘、除运算 通过多态确定人类的说话行为

目录 一、涉及到的知识点1 1.虚方法 2.重写方法 3.重写方法与重载方法的区别 4.通过KeyPressEventArgs.KeyChar限制键盘输入的内容 5.if-else if-else嵌套转换为switch case 二、 涉及到的知识点2 1.多态性 2.使用多态性的注意事项 3. 使用虚方法实现多态性 三、实…

红日靶场2学习

靶场下载来自&#xff1a; http://vulnstack.qiyuanxuetang.net/vuln/detail/3/ 靶场统一登录密码&#xff1a;1qazWSX 按大佬的说法是 环境需要模拟内网和外网两个网段&#xff0c;PC端虚拟机相当于网关服务器&#xff0c;所以需要两张网卡&#xff0c;一个用来向外网提供web…

FT2232调试记录(3)

FT2232调试记录&#xff08;1&#xff09;: FT2232调试记录&#xff08;2&#xff09;: FT2232调试记录&#xff08;3&#xff09;: FT2232 SPI读写函数: 参照SPI提供的文档&#xff1a; 工程&#xff1a; SPI 写函数&#xff1a; FT_STATUS write_byte(FT_HANDLE handle…

剑指offer——数值的整数次方

目录 1. 题目描述2. 一般思路2.1 有问题的思路2.2 全面但不高效的思路2.3 面试小提示 3. 全面又高效的思路 1. 题目描述 题目:实现函数 double Power(double base,int exponent)&#xff0c;求base 的exponent 次方。不得使用库函数&#xff0c;同时不需要考虑大数问题 2. 一般…

4核16g云服务器多少钱?

4核16G服务器租用优惠价格26元1个月&#xff0c;腾讯云轻量4核16G12M服务器32元1个月、96元3个月、156元6个月、312元一年&#xff0c;阿腾云atengyun.com分享4核16服务器租用费用价格表&#xff0c;阿里云和腾讯云详细配置报价和性能参数表&#xff1a; 腾讯云4核16G服务器价…

VSCode汉化以及跳转到函数定义设置

目录 VSCode汉化设置跳转到函数定义 VSCode汉化 一、目前默认是英文状态 二、按下ShiftCtrlP弹出这个框框 三、点击语言显示配置 四、点击下载语言 五、点击下载中文即可 六、下载完后&#xff0c;再按ShiftCtrlP&#xff0c;选择语言配置 七、选择第二个&#xff0c;中…

Swift Combine 级联多个 UI 更新,包括网络请求 从入门到精通十六

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…

点餐|外卖订餐小程序|基于微信小程序的外卖订餐系统设计与实现(源码+数据库+文档)

点餐|外卖订餐小程序目录 目录 基于微信小程序的外卖订餐系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户微信端功能模块 2、管理员服务端功能模块 3、商家务端功能模块 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设…

Spring Boot整合 Cache 以Redis服务 处理数据缓存

目录 一、SpringBoot框架 二、什么是cache 三、redis介绍 四、高速缓存 一、SpringBoot框架 Spring Boot是一个基于Java的开源框架&#xff0c;用于快速构建独立的、可运行的、生产级的Spring应用程序。它简化了Spring应用程序的配置和部署过程&#xff0c;并提供了许多开…

git stash 正确用法

目录 一、背景 二、使用 2.1 使用之前&#xff0c;先简单了解下 git stash 干了什么&#xff1a; 2.2 git stash 相关命令 2.3 使用流程 1. 执行 git stash 2. 查看刚才保存的工作进度 git stash list 3. 这时候在看分支已经是干净无修改的(改动都有暂存到 stash) 4. 现在…

山脉的个数/攀登者

题目描述 攀登者喜欢寻找各种地图&#xff0c;并且尝试攀登到最高的山峰。 地图表示为一维数组&#xff0c;数组的索引代表水平位置&#xff0c;数组的元素代表相对海拔高度。其中数组元素0代表地面。 例如&#xff1a;[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0]&#xff0c;代表如下…

代码随想录刷题笔记 DAY 26 | 组合总和 No.39 | 组合求和 II No.40 | 分割回文串 No.131

文章目录 Day 2501. 组合总和&#xff08;No. 39&#xff09;1.1 题目1.2 笔记1.3 代码 02. 组合求和 II&#xff08;No. 40&#xff09;2.1 题目2.2 笔记2.3 代码 03. 分割回文串&#xff08;No. 131&#xff09;3.1 题目3.2 笔记3.3 代码 Day 25 01. 组合总和&#xff08;No…

【计算机网络】网络层之IP协议

文章目录 1.基本概念2.协议头格式3.网段划分4.特殊的IP地址5.IP地址的数量限制6.私有IP地址和公网IP地址7.路由 1.基本概念 IP地址是定位主机的&#xff0c;具有一个将数据报从A主机跨网络可靠的送到B主机的能力。 但是有能力就一定能做到吗&#xff0c;只能说有很大的概率。…

寒假作业:2024/2/14

作业1&#xff1a;编程实现二维数组的杨辉三角 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, const char *argv[]) {int n;printf("please enter n:");scanf("%d",&n);int a…

【Spring MVC篇】返回响应

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Spring MVC】 本专栏旨在分享学习Spring MVC的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、返回静态页面…

164基于matlab的奇异值分解、小波降噪、zoom细化

基于matlab的奇异值分解、小波降噪、zoom细化。程序已调通&#xff0c;可直接运行。 164 奇异值分解 小波降噪 zoom细化 (xiaohongshu.com)

vue学习106-120

创建项目p106 router&#xff0c;store和app.vue不用删 清一下router里的路由配置 vant组件库p107 目标&#xff1a;认识第三方vue组件库vant-ui&#xff08;cv战士&#xff09; 封装好了的组件整合在一起就是组件库 http://vant-contrib.gitee.io/vant/v2/#/zh-CN/ vue2用va…

python自学...

一、稍微高级一点的。。。 1. 闭包&#xff08;跟js差不多&#xff09; 2. 装饰器 就是spring的aop 3. 多线程