简体   繁体   English

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

[英]Why is if (fork() == 0) { getpid() } and a popen() process returning the same process id?

I want to know why the two process id's match when the the getpid() in the fork() to my knowledge is supposed to be a different process than the one produced by popen().我想知道为什么当我知道fork() 中的getpid() 应该是与popen() 产生的进程不同的进程时,这两个进程ID 匹配。

I was informed that my code only works because of what I interpret might be a bug with Ubuntu based distro's such as Xubuntu, Lubuntu, and KDE neon, (which are the distro's I've tested so far).我被告知我的代码只能工作,因为我解释的可能是基于 Ubuntu 的发行版的错误,例如 Xubuntu、Lubuntu 和 KDE neon(这是我迄今为止测试过的发行版)。 You can easily compile and test the code from here: https://github.com/time-killer-games/XTransientFor Ignore the x64 binary available at that link if you don't trust it.您可以从此处轻松编译和测试代码: https://github.com/time-killer-games/XTransientFor如果您不信任它,请忽略该链接上可用的 x64 二进制文件。 Arch, RedHat, etc. testers are especially welcome to give feedback if this happens to not work for them.如果这对他们不起作用,特别欢迎 Arch、RedHat 等测试人员提供反馈。

Here's a much more minimal approach to demonstrating the issue:这是演示该问题的一种更简单的方法:

// 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);
  }
}

Compile with:编译:

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

Run with:运行:

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

You can replace the command in the quotes with any executable which sets the _NET_WM_PID atom.您可以将引号中的命令替换为设置 _NET_WM_PID 原子的任何可执行文件。

@thatotherguy explains the answer in his comment: @thatotherguy 在他的评论中解释了答案:

"Are you aware that due to your - 1 you're actually checking that the two processes have sequential pids? This is not surprising behavior on Linux. Differences between distros would be down to whether sh optimizes out the extra fork it uses to run the command" “您是否知道由于您的 - 1 您实际上正在检查两个进程是否具有顺序 pid?这在 Linux 上并不令人惊讶。发行版之间的差异将取决于 sh 是否优化了它用于运行命令”

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);
}

That - 1 in the process id return I initially added because I thought at the time it was returning the wrong process id, because I thought when I wrote that the fork() was supposed to have the same process id as the popen, which later on I discovered wasn't the case.那 - 1 在我最初添加的进程 id return 中是因为我当时认为它返回了错误的进程 id,因为我认为当我写的时候 fork() 应该与 popen 具有相同的进程 id,后来我发现并非如此。 I subtracted one, thus making two different otherwise correct process id's, incorrectly equal.我减去了一个,从而使两个不同的否则正确的进程 id 不正确地相等。

Here's the correct way to do what I intended to do, in my original code, which lead to me asking this question;这是在我的原始代码中做我想做的事情的正确方法,这导致我问这个问题; I wanted to know how to detect if a fork and popen child processes stem from a common parent process (while removing the subtraction of one from the return of the GetActiveProcessId() function):我想知道如何检测一个 fork 和 popen 子进程是否来自一个共同的父进程(同时从 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;
}

Using the above helper function, while replacing the while loop, in the original code, with this, allows me to do what I was after:使用上面的帮助程序 function,在替换原始代码中的 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++;
  }

As @thatotherguy also pointed out, some distros will return a different parent process because the sh cmd will use run directly.正如@thatotherguy 还指出的那样,一些发行版将返回不同的父进程,因为 sh cmd 将直接使用 run。 To solve this, I did an or check in the if statement for seeing whether the parent or "grandparent" process id's returned equal, while attempting to skip any parent processes with the sh cmd value.为了解决这个问题,我在 if 语句中执行或检查以查看父进程或“祖父”进程 id 是否返回相等,同时尝试跳过任何具有 sh cmd 值的父进程。

The helper function needs the -lprocps linker flag and libprocps-dev package installed if you are on a Debian based system. The helper function needs the -lprocps linker flag and libprocps-dev package installed if you are on a Debian based system. Package name will be different on other distros. Package 名称在其他发行版上会有所不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM