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

186 篇文章 1 订阅
67 篇文章 0 订阅

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

Linux内核中处理接收消息的系统调用的实现代码,对代码关键部分添加中文注释:

// 内核辅助函数,用于执行接收消息的操作
static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg,
             struct msghdr *msg_sys, unsigned int flags, int nosec)
{
    // 声明一些局部变量
    struct compat_msghdr __user *msg_compat = (struct compat_msghdr __user *)msg;
    // 为IO提供一个快速的栈上局部iovec数组
    struct iovec iovstack[UIO_FASTIOV];
    struct iovec *iov = iovstack;
    unsigned long cmsg_ptr;
    int len;
    ssize_t err;
    // 本地socket地址存储结构体
    struct sockaddr_storage addr;
    // 用户模式的地址指针
    struct sockaddr __user *uaddr;
    int __user *uaddr_len = COMPAT_NAMELEN(msg);

    msg_sys->msg_name = &addr;

    // 根据flags判断是否使用兼容模式的消息头
    if (MSG_CMSG_COMPAT & flags)
        err = get_compat_msghdr(msg_sys, msg_compat, &uaddr, &iov);
    else
        err = copy_msghdr_from_user(msg_sys, msg, &uaddr, &iov);
    if (err < 0)
        return err;

    cmsg_ptr = (unsigned long)msg_sys->msg_control;
    msg_sys->msg_flags = flags & (MSG_CMSG_CLOEXEC|MSG_CMSG_COMPAT);

    // 假设所有内核代码都知道sockaddr_storage的大小
    msg_sys->msg_namelen = 0;

    // 检查文件标志是否为非阻塞,并设置相应的标志
    if (sock->file->f_flags & O_NONBLOCK)
        flags |= MSG_DONTWAIT;
    // 接收消息,nosec标志决定是否跳过安全检查
    err = (nosec ? sock_recvmsg_nosec : sock_recvmsg)(sock, msg_sys, flags);
    if (err < 0)
        goto out_freeiov;
    len = err;

    // 如果有地址信息,将它从内核空间复制回用户空间
    if (uaddr != NULL) {
        err = move_addr_to_user(&addr, msg_sys->msg_namelen, uaddr, uaddr_len);
        if (err < 0)
            goto out_freeiov;
    }
    // 更新用户提供的msg_flags
    err = __put_user((msg_sys->msg_flags & ~MSG_CMSG_COMPAT), COMPAT_FLAGS(msg));
    if (err)
        goto out_freeiov;
    // 更新控制信息的长度
    if (MSG_CMSG_COMPAT & flags)
        err = __put_user((unsigned long)msg_sys->msg_control - cmsg_ptr,
                 &msg_compat->msg_controllen);
    else
        err = __put_user((unsigned long)msg_sys->msg_control - cmsg_ptr,
                 &msg->msg_controllen);
    if (err)
        goto out_freeiov;
    err = len;

out_freeiov:
    // 释放分配的iovec,如果它不是使用栈分配的
    kfree(iov);
    return err;
}

// 通过系统调用接口的recvmsg函数实现
long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned int flags,
           bool forbid_cmsg_compat)
{
    // 声明变量用于文件描述符引用计数和错误处理
    int fput_needed, err;
    struct msghdr msg_sys;
    struct socket *sock;

    // 如果禁止了兼容模式,但flags指定了兼容模式,则返回错误
    if (forbid_cmsg_compat && (flags & MSG_CMSG_COMPAT))
        return -EINVAL;

    // 通过文件描述符查找对应的socket对象
    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (!sock)
        goto out;

    // 调用内核辅助函数执行真正的接收操作
    err = ___sys_recvmsg(sock, msg, &msg_sys, flags, 0);

    // 减少文件描述符引用计数
    fput_light(sock->file, fput_needed);
out:
    return err;
}

// 定义recvmsg系统调用的接口函数
SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg,
        unsigned int, flags)
{
    // 调用内部接口函数处理接收信息的请求,并返回结果
    return __sys_recvmsg(fd, msg, flags, true);
}

这段代码主要包含两个部分:`___sys_recvmsg()` 是接收消息的内部实现函数,它负责从用户空间复制消息头到内核空间,接收消息,然后将结果传回到用户空间。这个函数主要被 __sys_recvmsg() 调用,后者是适配系统调用接口的函数。
`__sys_recvmsg()` 函数是对 ___sys_recvmsg() 的简单封装,多了一些检查与文件描述符处理,最终通过 SYSCALL_DEFINE3 宏定义了 recvmsg 系统调用的接口函数,该函数将调用 __sys_recvmsg() 同时要求 flags 参数不能包含兼容模式 MSG_CMSG_COMPAT。当用户空间的程序调用 recvmsg 时,它就会进入内核空间执行这段代码,完成消息的接收。

二、代码讲解:

这段代码是Linux内核中处理接收消息(`recvmsg`系统调用)的C语言代码。该代码主要用于socket通信,以接收从指定socket中来的消息。
接下来,将逐步解释代码的主要功能和组件:

___sys_recvmsg 函数

这是一个静态辅助函数,用来从用户空间接收消息头(`user_msghdr`),将其复制到内核空间并接收消息。
参数解释:
- struct socket *sock: 指向与文件描述符相关联的socket结构的指针。
- struct user_msghdr __user *msg: 用户空间提供的消息头指针,包含了消息的各种控制信息。
- struct msghdr *msg_sys: 内核空间的消息头结构,它将被填充接收到的数据。
- unsigned int flags: 指定接收行为的标志位,如 MSG_DONTWAIT,`MSG_CMSG_CLOEXEC` 等。
- int nosec: 这是一个布尔标识,表示是否绕过安全模块检查。
函数的主要步骤:
1. 分配 iovec 结构,用于描述接收缓冲区的位置和大小。
2. 检查 flags 是否指定了兼容模式(`MSG_CMSG_COMPAT`),并从用户空间拷贝消息头到 msg_sys 结构。
3. 设定消息标志位和地址空间大小。
4. 根据socket文件的非阻塞标志位设置相应的标志位。
5. 调用 sock_recvmsg 或 sock_recvmsg_nosec 来从socket实际接收数据。
6. 若有地址信息,将其拷贝回用户空间。
7. 更新用户空间的消息相关控制信息。
8. 释放分配的iovec结构并返回接收的长度或错误码。

__sys_recvmsg 函数

这个函数是BSD风格的接收消息的接口。
参数解释:
- int fd: 文件描述符,指向需要接收消息的socket。
- struct user_msghdr __user *msg: 用户空间的消息头指针。
- unsigned int flags: 同上。
- bool forbid_cmsg_compat: 这是一个布尔标志,表示是否禁止使用兼容模式消息控制头。
函数的主要步骤:
1. 检查参数是否要禁止兼容模式。
2. 通过文件描述符获取对应的socket结构。
3. 使用`___sys_recvmsg`函数从上一步获取的socket中接收消息。
4. 确保调用结束后释放socket的引用。
5. 返回操作的结果。

SYSCALL_DEFINE3(recvmsg, ...)

这是recvmsg系统调用的定义,它是系统调用接口的一个宏,它创建了一个接收三个参数的系统调用。
函数的作用是简单地调用`__sys_recvmsg`函数,传递文件描述符、消息头指针和标志位,并指定禁止兼容模式消息控制头。
注:这里的 "兼容模式" 通常是指32位程序在64位系统上运行时,内核数据结构与用户空间数据结构大小不一致的情况。
代码中涉及到用户空间和内核空间的交互,以及对临界资源的操作,在实际执行过程中需要考虑数据的同步和安全性。需要确保所有从用户空间复制的数据都经过了正确的验证和处理,防止潜在的安全漏洞或内核崩溃。
这段代码的主要应用场景在于Linux网络编程,特别是涉及到基于socket的TCP/IP或UDP/IP通信。用户空间的程序通过文件描述符来引用打开的socket,当应用程序需要从网络上读取数据时,它会调用`recvmsg`系统调用。此系统调用会将用户空间提供的参数传递到内核空间,内核空间负责从网络接口卡读取数据,处理它们,并将结果复制回用户空间。
关键点是,`recvmsg`系统调用提供了一种灵活的消息接收机制,允许用户程序获取关于接收到的数据的详细信息,比如发送者的地址等。这在开发网络服务器和客户端程序时是非常有用的。

  • 32
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Linux内核中的Socket是一种用于网络通信的抽象接口,它允许应用程序通过网络进行数据传输。在Linux内核中,Socket通信的实现主要依赖于回调函数。 回调函数是一种特殊的函数,它在某个事件发生时被调用。在Socket编程中,回调函数主要用于处理网络事件,例如接收到新的连接、接收到数据等。 在Linux内核中,Socket通信的实现主要涉及以下几个关键概念和回调函数: 1. Socket创建:应用程序通过调用socket()系统调用创建一个Socket对象。在内核中,会调用sock_create()函数创建一个新的Socket对象,并将其与相应的协议族关联起来。 2. Socket绑定:应用程序通过调用bind()系统调用将Socket对象与一个特定的地址和端口进行绑定。在内核中,会调用sock_bind()函数将Socket对象与指定的地址和端口进行关联。 3. Socket监听:应用程序通过调用listen()系统调用将Socket对象设置为监听状态,等待客户端的连接请求。在内核中,会调用sock_listen()函数将Socket对象设置为监听状态,并注册一个回调函数来处理新的连接请求。 4. Socket接收连接:当有新的连接请求到达时,内核调用之前注册的回调函数来处理该连接请求。在内核中,会调用sock_accept()函数接受新的连接,并调用相应的回调函数进行处理。 5. Socket接收数据:当有数据到达时,内核调用之前注册的回调函数来处理接收到的数据。在内核中,会调用sock_recvmsg()函数接收数据,并调用相应的回调函数进行处理。 6. Socket发送数据:应用程序通过调用send()或者write()系统调用将数据发送给远程主机。在内核中,会调用sock_sendmsg()函数发送数据,并调用相应的回调函数进行处理。 以上是Linux内核中Socket通信的基本流程和相关回调函数的介绍。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

109702008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值