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

如何获取特定用户进程列表、处理uid_t类型及进程网络信息?

问题解决方案

一、读取/proc/status中的UID并转换为uid_t类型进行比较

/proc/[pid]/status文件里的Uid行包含四个值:真实UID、有效UID、保存设置UID、文件系统UID,格式类似:

Uid: 1000 1000 1000 1000

要和geteuid()返回的有效用户ID对比,需提取第二个值。具体实现如下:

  1. 从status文件逐行读取内容,提取Uid对应的字段;
  2. 分割字段得到有效UID的字符串值,转换为uid_t类型(uid_t本质是无符号整数,用std::stoul或strtoul转换更安全);
  3. 与geteuid()的返回值比较,筛选出当前用户启动的进程。

修改后的代码片段:

ProcessInfo getInfoFromFileByPID(char pid[], const char* filename)
{
    printf("getInfoFromFileByPID param = %s %s\n", pid, filename);
 
    char            path[256];
    FILE*           fp;
    char            line[256];
    ProcessInfo     process;
    uid_t current_euid = geteuid();  // 获取当前进程的有效用户ID
 
    snprintf(path, sizeof(path), filename, pid);
 
    fp = fopen(path, "r");
    if (fp)
    {
        // 逐行读取避免文件内容截断
        while (fgets(line, sizeof(line), fp) != NULL) {
            std::string str_line(line);
            size_t colon_pos = str_line.find(':');
            if (colon_pos == std::string::npos) continue;

            std::string key = str_line.substr(0, colon_pos);
            std::string value = str_line.substr(colon_pos + 1);
            // 清除值前后的空白字符
            value.erase(0, value.find_first_not_of(" \t\n"));
            value.erase(value.find_last_not_of(" \t\n") + 1);

            if (key == "Name") {
                process.Name = value;
            } else if (key == "Pid") {
                process.PID = std::stoi(value);
            } else if (key == "Uid") {
                // 分割UID的四个部分,取第二个(有效UID)
                std::istringstream iss(value);
                std::vector<std::string> uid_parts;
                std::string part;
                while (iss >> part) uid_parts.push_back(part);
                
                if (uid_parts.size() >= 2) {
                    uid_t process_euid = static_cast<uid_t>(std::stoul(uid_parts[1]));
                    // 对比当前用户的有效UID
                    process.is_current_user = (process_euid == current_euid);
                }
            }
        }
        fclose(fp);
        return process;
    }
    return process;
}

二、获取进程的IP地址、端口及目标主机名

进程的网络连接信息存放在/proc/[pid]/net/tcp(TCP连接)和/proc/[pid]/net/udp(UDP连接)文件中,每行记录一个连接,格式示例(TCP):

sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 0100007F:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 123456

解析步骤:

  1. 读取对应文件,跳过首行表头;
  2. 分割每行字段,提取本地/远程地址(IP:端口的十六进制格式);
  3. 将十六进制IP转换为十进制字符串,十六进制端口转换为十进制数值;
  4. 用getnameinfo函数将远程IP解析为主机名(编译时需链接-lresolv库)。

示例代码片段:

#include <netdb.h>
#include <arpa/inet.h>
#include <sstream>
#include <vector>

// 十六进制字符串转字节
unsigned char hex_to_byte(const std::string& hex) {
    return static_cast<unsigned char>(std::stoul(hex, nullptr, 16));
}

// 十六进制IP转十进制格式(如0100007F → 127.0.0.1)
std::string hex_ip_to_decimal(const std::string& hex_ip) {
    if (hex_ip.length() != 8) return "";
    // 十六进制IP按字节反转存储,需调整顺序
    unsigned char bytes[4] = {
        hex_to_byte(hex_ip.substr(6, 2)),
        hex_to_byte(hex_ip.substr(4, 2)),
        hex_to_byte(hex_ip.substr(2, 2)),
        hex_to_byte(hex_ip.substr(0, 2))
    };
    char ip_str[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, bytes, ip_str, sizeof(ip_str));
    return std::string(ip_str);
}

// 十六进制端口转十进制
uint16_t hex_port_to_decimal(const std::string& hex_port) {
    return static_cast<uint16_t>(std::stoul(hex_port, nullptr, 16));
}

// IP地址转主机名
std::string ip_to_hostname(const std::string& ip) {
    struct sockaddr_in sa;
    char hostname[NI_MAXHOST];
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    inet_pton(AF_INET, ip.c_str(), &sa.sin_addr);
    
    if (getnameinfo((struct sockaddr*)&sa, sizeof(sa), hostname, sizeof(hostname), nullptr, 0, 0) == 0) {
        return std::string(hostname);
    }
    return ip; // 解析失败则返回原IP
}

// 读取进程TCP连接信息
void get_process_network_info(const std::string& pid) {
    std::string path = "/proc/" + pid + "/net/tcp";
    FILE* fp = fopen(path.c_str(), "r");
    if (!fp) return;

    char line[512];
    fgets(line, sizeof(line), fp); // 跳过表头行
    while (fgets(line, sizeof(line), fp) != NULL) {
        std::istringstream iss(line);
        std::vector<std::string> fields;
        std::string field;
        while (iss >> field) fields.push_back(field);
        if (fields.size() < 4) continue;

        // 解析本地地址
        size_t local_colon = fields[1].find(':');
        std::string local_ip = hex_ip_to_decimal(fields[1].substr(0, local_colon));
        uint16_t local_port = hex_port_to_decimal(fields[1].substr(local_colon + 1));

        // 解析远程地址
        size_t rem_colon = fields[2].find(':');
        std::string rem_ip = hex_ip_to_decimal(fields[2].substr(0, rem_colon));
        uint16_t rem_port = hex_port_to_decimal(fields[2].substr(rem_colon + 1));

        // 获取远程主机名
        std::string rem_hostname = ip_to_hostname(rem_ip);

        printf("本地: %s:%u | 远程: %s(%s):%u\n", 
               local_ip.c_str(), local_port, 
               rem_hostname.c_str(), rem_ip.c_str(), rem_port);
    }
    fclose(fp);
}

注意事项:

  • 读取其他用户进程的/proc/[pid]/net目录需要对应权限(如root权限);
  • UDP为无连接协议,部分记录的远程地址可能为空;
  • 编译时需链接网络库:g++ 代码文件.cpp -o 可执行文件 -lresolv

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 08:45:05