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

如何在C++中无依赖找到监听SOCK_SEQPACKET(@Foo)的进程ID?

无需依赖net-tools,定位并终止监听特定Unix域套接字的进程

核心思路

Linux的/proc文件系统直接暴露了系统和进程的关键运行信息,我们可以通过以下两步精准定位目标进程:

  1. 从/proc/net/unix中找到监听@Foo抽象套接字的inode编号
  2. 遍历/proc/[pid]/fd目录,找到持有该inode套接字的进程,结合用户身份确认目标PID

关键细节说明

  • 抽象Unix套接字的路径:@Foo属于抽象命名空间套接字,在/proc/net/unix中存储时,路径的第一个字节是\0(空字符),后续为Foo。解析时需跳过第一个空字节再做字符串对比。
  • /proc/net/unix的格式:每行包含inode、引用计数、套接字类型、状态、路径等字段,我们需要筛选状态为LISTENING且路径匹配的条目。
  • 进程权限:每个用户仅能访问自身进程的/proc/[pid]/fd目录,遍历过程中遇到权限不足的PID可直接跳过,完全适配“每个用户对应一个实例”的场景。

C++实现示例

#include <iostream>
#include <fstream>
#include <string>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <cstring>
#include <cctype>

// 从/proc/net/unix获取监听@Foo的套接字inode(十六进制转十进制)
unsigned long long get_target_inode() {
    std::ifstream unix_socks("/proc/net/unix");
    std::string line;
    std::getline(unix_socks, line); // 跳过表头行

    while (std::getline(unix_socks, line)) {
        size_t pos = 0;
        // 提取inode字段(十六进制字符串)
        std::string inode_str = line.substr(pos, line.find(' ') - pos);
        pos = line.find(' ') + 1;
        // 跳过ref_count、flags、type字段
        pos = line.find(' ', pos) + 1;
        pos = line.find(' ', pos) + 1;
        pos = line.find(' ', pos) + 1;
        // 提取状态字段
        std::string state = line.substr(pos, line.find(' ') - pos);
        pos = line.find(' ', pos) + 1;
        // 提取路径(抽象套接字开头是\0,需跳过第一个字节)
        const char* path_ptr = line.c_str() + pos;
        if (state == "LISTENING" && path_ptr[0] == '\0' && strcmp(path_ptr + 1, "Foo") == 0) {
            return stoull(inode_str, nullptr, 16);
        }
    }
    return 0;
}

// 遍历/proc下的PID,找到持有目标inode套接字的当前用户进程
pid_t find_target_pid(unsigned long long target_inode) {
    uid_t current_uid = getuid();
    DIR* proc_dir = opendir("/proc");
    if (!proc_dir) {
        perror("opendir /proc failed");
        return -1;
    }

    dirent* entry;
    while ((entry = readdir(proc_dir)) != nullptr) {
        // 仅处理数字命名的PID目录
        if (!isdigit(entry->d_name[0])) continue;

        pid_t pid = atoi(entry->d_name);
        // 检查进程所属用户是否与当前用户一致
        std::string stat_path = "/proc/" + std::string(entry->d_name) + "/stat";
        std::ifstream stat_file(stat_path);
        if (!stat_file) continue;

        std::string dummy;
        uid_t proc_uid;
        // 跳过前8个字段,读取第9个字段(进程UID)
        for (int i = 0; i < 8; ++i) stat_file >> dummy;
        stat_file >> proc_uid;
        if (proc_uid != current_uid) continue;

        // 遍历进程的文件描述符目录
        std::string fd_dir_path = "/proc/" + std::string(entry->d_name) + "/fd";
        DIR* fd_dir = opendir(fd_dir_path.c_str());
        if (!fd_dir) continue;

        dirent* fd_entry;
        while ((fd_entry = readdir(fd_dir)) != nullptr) {
            if (fd_entry->d_name[0] == '.') continue; // 跳过.和..目录

            std::string fd_path = fd_dir_path + "/" + fd_entry->d_name;
            char link_buf[256];
            ssize_t link_len = readlink(fd_path.c_str(), link_buf, sizeof(link_buf)-1);
            if (link_len <= 0) continue;
            link_buf[link_len] = '\0';

            // 检查是否为目标套接字的inode
            if (strncmp(link_buf, "socket:[", 8) == 0) {
                unsigned long long inode = stoull(link_buf + 8, nullptr, 10);
                if (inode == target_inode) {
                    closedir(fd_dir);
                    closedir(proc_dir);
                    return pid;
                }
            }
        }
        closedir(fd_dir);
    }
    closedir(proc_dir);
    return -1;
}

int main() {
    unsigned long long target_inode = get_target_inode();
    if (target_inode == 0) {
        std::cerr << "未找到监听@Foo的套接字" << std::endl;
        return 1;
    }

    pid_t target_pid = find_target_pid(target_inode);
    if (target_pid == -1) {
        std::cerr << "未找到目标进程" << std::endl;
        return 1;
    }

    std::cout << "找到目标PID: " << target_pid << std::endl;
    // 发送SIGTERM终止进程,如需强制终止可替换为SIGKILL
    if (kill(target_pid, SIGTERM) == -1) {
        perror("终止进程失败");
        return 1;
    }
    std::cout << "已向PID " << target_pid << " 发送SIGTERM信号" << std::endl;
    return 0;
}

注意事项

  • 编译无需额外依赖库,直接执行:g++ -o kill_foo_server kill_foo_server.cpp
  • 若需要强制终止进程,将kill(target_pid, SIGTERM)替换为kill(target_pid, SIGKILL)
  • 遍历/proc时可能遇到进程中途退出的情况,代码已通过continue跳过此类异常,保证运行健壮性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 11:21:03