gpt4 book ai didi

c++ - 为什么 if (fork() == 0) { getpid() } 和 popen() 进程返回相同的进程 ID?

转载 作者:太空宇宙 更新时间:2023-11-04 11:42:44 25 4
gpt4 key购买 nike

我想知道当 fork() 中的 getpid() 据我所知应该是与 popen() 生成的进程不同的进程时,为什么这两个进程 ID 匹配。

我被告知我的代码之所以有效,是因为我的解释可能是基于 Ubuntu 的发行版的错误,例如 Xubuntu、Lubuntu 和 KDE neon(我迄今为止测试过的发行版)。您可以从这里轻松编译和测试代码:https://github.com/time-killer-games/XTransientFor如果您不信任它,请忽略该链接上可用的 x64 二进制文件。如果这对他们不起作用,特别欢迎 Arch、RedHat 等测试人员提供反馈。

这里有一个更简单的方法来演示这个问题:

// USAGE: xprocesstest [command]

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>

#include <unistd.h>

#include <thread>
#include <chrono>

#include <iostream>
#include <string>

using std::string;

static inline Window XGetActiveWindow(Display *display) {
unsigned long window;
unsigned char *prop;

Atom actual_type, filter_atom;
int actual_format, status;
unsigned long nitems, bytes_after;

int screen = XDefaultScreen(display);
window = RootWindow(display, screen);

filter_atom = XInternAtom(display, "_NET_ACTIVE_WINDOW", True);
status = XGetWindowProperty(display, window, filter_atom, 0, 1000, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);

unsigned long long_property = prop[0] + (prop[1] << 8) + (prop[2] << 16) + (prop[3] << 24);
XFree(prop);

return (Window)long_property;
}

static inline pid_t XGetActiveProcessId(Display *display) {
unsigned long window = XGetActiveWindow(display);
unsigned char *prop;

Atom actual_type, filter_atom;
int actual_format, status;
unsigned long nitems, bytes_after;

filter_atom = XInternAtom(display, "_NET_WM_PID", True);
status = XGetWindowProperty(display, window, filter_atom, 0, 1000, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);

unsigned long long_property = prop[0] + (prop[1] << 8) + (prop[2] << 16) + (prop[3] << 24);
XFree(prop);

return (pid_t)(long_property - 1);
}

int main(int argc, const char **argv) {
if (argc == 2) {
char *buffer = NULL;
size_t buffer_size = 0;
string str_buffer;

FILE *file = popen(argv[1], "r");

if (fork() == 0) {
Display *display = XOpenDisplay(NULL);
Window window;

unsigned i = 0;
while (i < 10) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
if (XGetActiveProcessId(display) == getpid()) {
window = XGetActiveWindow(display);
break;
}
i++;
}

if (window == XGetActiveWindow(display))
std::cout << "process id's match!" << std::endl;
else std::cout << "process id's don't match!" << std::endl;

XCloseDisplay(display);
exit(0);
}

while (getline(&buffer, &buffer_size, file) != -1)
str_buffer += buffer;

std::cout << str_buffer;
free(buffer);
pclose(file);
}
}

编译:

cd "${0%/*}"
g++ -c -std=c++17 "xprocesstest.cpp" -fPIC -m64
g++ "xprocesstest.o" -o "xprocesstest" -fPIC -lX11

运行:

cd "${0%/*}"
./xprocesstest "kdialog --getopenfilename"

您可以将引号中的命令替换为设置 _NET_WM_PID 原子的任何可执行文件。

最佳答案

@thatotherguy 在他的评论中解释了答案:

“您是否知道,由于您的 -1,您实际上是在检查这两个进程是否具有连续的 pid?这在 Linux 上并不令人惊讶。发行版之间的差异取决于 sh 是否优化了它使用的额外 fork运行命令”

static inline pid_t XGetActiveProcessId(Display *display) {
unsigned long window = XGetActiveWindow(display);
unsigned char *prop;

Atom actual_type, filter_atom;
int actual_format, status;
unsigned long nitems, bytes_after;

filter_atom = XInternAtom(display, "_NET_WM_PID", True);
status = XGetWindowProperty(display, window, filter_atom, 0, 1000, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);

unsigned long long_property = prop[0] + (prop[1] << 8) + (prop[2] << 16) + (prop[3] << 24);
XFree(prop);

return (pid_t)(long_property - 1);
}

我最初添加的进程 id 返回中的 1 是因为我认为当时它返回了错误的进程 id,因为我认为当我写的时候 fork() 应该与 popen 具有相同的进程 id ,后来我发现情况并非如此。我减去一个,从而使两个不同的正确进程 ID 错误地相等。

这是在我的原始代码中做我打算做的事情的正确方法,这导致我问这个问题;我想知道如何检测一个 fork 和 popen 子进程是否来自一个公共(public)父进程(同时从 GetActiveProcessId() 函数的返回中删除减一):

#include <proc/readproc.h>
#include <cstring>

static inline pid_t GetParentPidFromPid(pid_t pid) {
proc_t proc_info; pid_t ppid;
memset(&proc_info, 0, sizeof(proc_info));
PROCTAB *pt_ptr = openproc(PROC_FILLSTATUS | PROC_PID, &pid);
if(readproc(pt_ptr, &proc_info) != 0) {
ppid = proc_info.ppid;
string cmd = proc_info.cmd;
if (cmd == "sh")
ppid = GetParentPidFromPid(ppid);
} else ppid = 0;
closeproc(pt_ptr);
return ppid;
}

使用上面的辅助函数,同时替换原始代码中的 while 循环,让我可以做我想做的事:

  while (i < 10) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
if (GetParentPidFromPid(XGetActiveProcessId(display)) == GetParentPidFromPid(getpid()) ||
GetParentPidFromPid(GetParentPidFromPid(XGetActiveProcessId(display))) == GetParentPidFromPid(getppid())) {
window = XGetActiveWindow(display);
break;
}
i++;
}

正如@thatotherguy 还指出的那样,一些发行版将返回不同的父进程,因为 sh cmd 将直接使用 run。为了解决这个问题,我在 if 语句中执行了一个或检查以查看父进程或“祖 parent ”进程 ID 是否返回相等,同时尝试跳过具有 sh cmd 值的任何父进程。

如果您在基于 Debian 的系统上,辅助函数需要安装 -lprocps 链接器标志和 libprocps-dev 包。包名在其他发行版上会有所不同。

关于c++ - 为什么 if (fork() == 0) { getpid() } 和 popen() 进程返回相同的进程 ID?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58784609/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com