无法在 WSL2 中获得正确的 pid
Can not get correct pid in WSL2
我正在学习 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 系统开发专家,但我会根据您的尝试尝试提供帮助。
首先,您没有在问题中提及它,但您显然 运行正在启用某种 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
为了处理多个 shells、分布等,实际的脚本通常要复杂一些。它们还试图在命名空间内保留更多的 WSL 环境,以启用互操作功能比如运行ning Windows .exe
s。但核心概念始终相同。
所以,在这里猜测(同样,作为一个非系统开发人员),似乎是:
kpid=find_get_pid(1)
正在返回命名空间
内的 systemd
进程
pid_task(kpid, PIDTYPE_PID)
正在从根名称空间返回“真实”进程信息。
在我看来,代码必须 运行ning 在名称空间之外,因为您将 unshare
视为其中的一部分。在命名空间中,unshare
不存在。您可以使用 ps -ef | grep unshare
.
验证这一点(在命名空间内)
至少有两种可能的解决方案:
如果这不是问题(从评论来看,它不是),那么只需 运行 来自根 pid 命名空间的代码。我假设您的 Systemd 脚本是 运行ning 通过您的 shell 启动文件,因此您应该能够通过使用 wsl ~ -e bash --noprofile --norc
之类的东西启动来返回到根命名空间。这将在没有任何启动脚本的情况下启动 shell。
当然,您使用的任何脚本都可能记录了其他禁用 Systemd 脚本的技术。
如果您确实希望您的代码在 PID 命名空间内正常工作,那么您可能需要找到命名空间(我将从 lsns
的源开始一个例子)。
然后在该命名空间中找到任务结构(可能 find_task_by_pid_ns
?)。
我正在学习 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 系统开发专家,但我会根据您的尝试尝试提供帮助。
首先,您没有在问题中提及它,但您显然 运行正在启用某种 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
为了处理多个 shells、分布等,实际的脚本通常要复杂一些。它们还试图在命名空间内保留更多的 WSL 环境,以启用互操作功能比如运行ning Windows
.exe
s。但核心概念始终相同。
所以,在这里猜测(同样,作为一个非系统开发人员),似乎是:
内的kpid=find_get_pid(1)
正在返回命名空间systemd
进程pid_task(kpid, PIDTYPE_PID)
正在从根名称空间返回“真实”进程信息。在我看来,代码必须 运行ning 在名称空间之外,因为您将
验证这一点(在命名空间内)unshare
视为其中的一部分。在命名空间中,unshare
不存在。您可以使用ps -ef | grep unshare
.
至少有两种可能的解决方案:
如果这不是问题(从评论来看,它不是),那么只需 运行 来自根 pid 命名空间的代码。我假设您的 Systemd 脚本是 运行ning 通过您的 shell 启动文件,因此您应该能够通过使用
wsl ~ -e bash --noprofile --norc
之类的东西启动来返回到根命名空间。这将在没有任何启动脚本的情况下启动 shell。当然,您使用的任何脚本都可能记录了其他禁用 Systemd 脚本的技术。
如果您确实希望您的代码在 PID 命名空间内正常工作,那么您可能需要找到命名空间(我将从
lsns
的源开始一个例子)。 然后在该命名空间中找到任务结构(可能find_task_by_pid_ns
?)。