【问题标题】:Can not get correct pid in WSL2在 WSL2 中无法获得正确的 pid
【发布时间】:2021-11-17 07:36:23
【问题描述】:

我正在学习 Linux 编程。
当我尝试编写一个简单的模块来获取进程家族时,我发现我无法获取进程及其父进程的当前 pid。如何解决?
这是我的代码的一部分。

static pid_t pid = 1;
module_param(pid, int, 0644);

static int hello_init(void) {
    struct task_struct *p;
    struct list_head *pp;
    struct task_struct *psibling;
    struct pid *kpid;

    kpid = find_get_pid(pid);
    p = pid_task(kpid, PIDTYPE_PID);
    printk("me: %d %s\n", pid, p->comm);
    if (p->parent == NULL) {
        printk("No Parent\n");
    }
    else {
        printk("Parent: %d %s\n", p->parent->pid, p->parent->comm);
    }
    list_for_each(pp, &p->parent->children) {
        psibling = list_entry(pp, struct task_struct, sibling);
        printk("sibling %d %s \n", psibling->pid, psibling->comm);
    }
    list_for_each(pp, &p->children) {
        psibling = list_entry(pp, struct task_struct, sibling);
        printk("children %d %s \n", psibling->pid, psibling->comm);
    }
    return 0;
}

结果:

sudo insmod module.ko pid=1
dmesg
[ 6396.170631] me: 237 systemd
[ 6396.170633] Parent: 235 unshare
[ 6396.170633] sibling 237 systemd
[ 6396.170633] children 286 systemd-journal
[ 6396.170634] children 306 systemd-udevd
[ 6396.170635] children 314 systemd-network
[ 6396.170635] children 501 snapfuse
[ 6396.170636] children 508 dbus-daemon
[ 6396.170636] children 509 NetworkManager
[ 6396.170637] children 632 systemd-logind
[ 6396.170637] children 639 systemd
[ 6396.170638] children 665 rtkit-daemon
[ 6396.170638] children 671 polkitd
[ 6396.170638] children 711 udisksd
[ 6396.170639] children 761 upowerd

【问题讨论】:

    标签: linux windows-subsystem-for-linux wsl-2


    【解决方案1】:

    我不是 Linux 系统开发专家,但我会根据我看到您的尝试尝试提供帮助。

    首先,您没有在问题中提及它,但您显然正在运行某种 Systemd 支持。如您所知,WSL 通常不支持 Systemd。概括地说,在 WSL 上启用 Systemd 的脚本都有两个基本功能:

    • 创建一个新的 PID 命名空间,其中 Systemd 作为 PID1 运行。在最基本的层面上,这可以通过以下方式完成:

      sudo -b unshare --pid --fork --mount-proc /lib/systemd/systemd --system-unit=basic.target
      

      我们可以在返回的进程列表中看到unshare,所以至少它会被调用。

    • 等待 Systemd 完全启动,然后进入上面创建的命名空间。这通常是这样的:

      sudo -E nsenter --all -t $(pgrep -xo systemd) $SHELL
      

      实际的脚本通常会稍微复杂一些,以便处理多个 shell、发行版等。它们还尝试在命名空间内保留更多 WSL 环境,以启用互操作功能,例如运行 Windows .exe s。但核心概念始终相同。

    所以,在这里猜测一下(再次,作为一个非系统开发人员),似乎:

    • kpid=find_get_pid(1) 正在返回命名空间内的systemd 进程

    • pid_task(kpid, PIDTYPE_PID) 正在从根命名空间返回“真实”的进程信息。

      在我看来,代码必须在命名空间之外运行,因为您将 unshare 视为其中的一部分。在命名空间中,unshare 不存在。您可以使用ps -ef | grep unshare 验证这一点(在命名空间内)。

    至少有两种可能的解决方案:

    • 如果这不是问题(并且从 cmets 中,它不是),那么只需从根 pid 命名空间运行您的代码。我假设你的 Systemd 脚本是通过你的 shell 启动文件运行的,所以你应该能够通过像wsl ~ -e bash --noprofile --norc 这样的东西启动来回到根命名空间。这将在没有任何启动脚本的情况下启动 shell。

      当然,禁用 Systemd 脚本的其他技术可能记录在您使用的任何脚本中。

    • 如果您确实希望代码在 PID 命名空间内正常工作,那么您可能需要找到命名空间(我将从 lsns 的源代码开始作为示例)。 然后在该命名空间中找到任务结构(可能是find_task_by_pid_ns?)。

    【讨论】:

    • 非常感谢。我没有意识到这是由命名空间引起的。我发现我之前创建了一个 systemd 环境作为我的默认命名空间,以便使用 Gnome Shell。这个问题可以通过进入wsl2的默认命名空间(可能是叫它)来解决。
    • @rxwooo 很高兴我能帮上忙。这是一个很好的观点——我假设您的意思是让这段代码在 Systemd pid 命名空间中运行。但是绝对,如果这不是问题,那么从默认/全局命名空间执行代码也会得到您期望的结果。我会相应地编辑我的答案!
    • @rxwooo 根据man pid_namespaces,事实证明“根命名空间”显然是正确的术语:-)
    猜你喜欢
    • 2016-12-29
    • 2019-02-25
    • 2020-04-09
    • 1970-01-01
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 2021-03-09
    • 1970-01-01
    相关资源
    最近更新 更多