You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在服务器-客户端程序中使用sendmsg()系统调用并通过ftrace追踪流程

服务器-客户端中sendmsg的使用与ftrace追踪指南

一、sendmsg系统调用核心特点

sendmsg是比send/sendto更灵活的套接字发送接口,支持一次性发送多个缓冲区的数据,还能附带控制信息(比如文件描述符传递),适合复杂的网络数据传输场景。

二、服务器-客户端示例代码

服务器端代码(调用sendmsg发送响应)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080
#define BUF_SIZE 1024

int main() {
    int sockfd, new_fd;
    struct sockaddr_in serv_addr, cli_addr;
    socklen_t cli_len = sizeof(cli_addr);
    char buffer[BUF_SIZE];

    // 创建TCP套接字
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 初始化服务器地址
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(PORT);

    // 绑定套接字到端口
    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 监听连接请求
    if (listen(sockfd, 3) == -1) {
        perror("listen failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("服务器监听端口 %d...\n", PORT);

    // 接受客户端连接
    if ((new_fd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len)) == -1) {
        perror("accept failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 接收客户端消息
    ssize_t n = recv(new_fd, buffer, BUF_SIZE-1, 0);
    if (n == -1) {
        perror("recv failed");
        close(new_fd);
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    buffer[n] = '\0';
    printf("收到客户端消息: %s\n", buffer);

    // 构造sendmsg的多缓冲区数据
    struct iovec iov[2];
    char msg1[] = "服务器响应Part1: ";
    char msg2[] = "已接收你的请求";
    iov[0].iov_base = msg1;
    iov[0].iov_len = strlen(msg1);
    iov[1].iov_base = msg2;
    iov[1].iov_len = strlen(msg2);

    // 初始化msghdr结构体
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = iov;
    msg.msg_iovlen = 2;

    // 调用sendmsg发送数据
    if (sendmsg(new_fd, &msg, 0) == -1) {
        perror("sendmsg failed");
        close(new_fd);
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("sendmsg调用完成,已发送响应\n");

    // 清理资源
    close(new_fd);
    close(sockfd);
    return 0;
}

客户端代码(触发服务器的sendmsg调用)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUF_SIZE 1024

int main() {
    int sockfd;
    struct sockaddr_in serv_addr;
    char buffer[BUF_SIZE];

    // 创建TCP套接字
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 初始化服务器地址
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // 转换本地IP地址格式
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("无效的服务器IP");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 连接服务器
    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
        perror("连接服务器失败");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 发送消息给服务器
    char *msg = "Hello, Server!";
    if (send(sockfd, msg, strlen(msg), 0) == -1) {
        perror("send failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("已发送消息到服务器: %s\n", msg);

    // 接收服务器响应
    ssize_t n = recv(sockfd, buffer, BUF_SIZE-1, 0);
    if (n == -1) {
        perror("recv failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    buffer[n] = '\0';
    printf("收到服务器响应: %s\n", buffer);

    // 清理资源
    close(sockfd);
    return 0;
}

编译与运行

  • 编译服务器:gcc server.c -o server
  • 编译客户端:gcc client.c -o client
  • 启动服务器:./server
  • 另开终端启动客户端:./client

三、用ftrace追踪sendmsg执行流程

1. 前置准备

确保系统已挂载debugfs(无权限则加sudo):

mount -t debugfs none /sys/kernel/debug

进入ftrace追踪目录:

cd /sys/kernel/debug/tracing

2. 配置ftrace追踪规则

  • 设置追踪的核心函数:
echo "sys_sendmsg" > set_graph_function
  • 启用函数调用图模式:
echo function_graph > current_tracer
  • 清空历史追踪日志:
echo > trace

3. 触发sendmsg调用

保持ftrace终端开启,在另一个终端运行服务器和客户端,触发sendmsg执行。

4. 查看追踪结果

回到ftrace终端,查看完整调用链:

cat trace

四、解读sendmsg内核执行流程

从ftrace输出中,可看到sendmsg的核心调用链(不同内核版本略有差异):

  • sys_sendmsg:用户态系统调用入口,完成参数校验与转换
  • __sys_sendmsg:实际处理sendmsg请求的核心函数
  • sock_sendmsg:分发请求到对应套接字类型的处理逻辑
  • tcp_sendmsg:TCP套接字的发送实现,处理滑动窗口、重传队列等协议逻辑
  • __tcp_sendmsg_locked:加锁确保并发安全的TCP发送逻辑
  • skb_queue_tail:将待发送数据封装为套接字缓冲区(skb)并加入发送队列
  • tcp_write_xmit:尝试将发送队列中的数据推送至网络层
  • dev_queue_xmit:将数据包交给网络设备驱动完成硬件发送

内容的提问来源于stack exchange,提问作者mittu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 14:30:24