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

问题描述 投票:0回答:1

[我想知道为什么在我所知的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原子的任何可执行文件。

c++ linux x11 xlib
1个回答
0
投票

@@其他人在评论中解释了答案:

“您是否意识到,由于您的-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);
}

我最初添加的进程ID返回中的那个-1,是因为我当时以为它返回了错误的进程ID,因为我以为我在写fork()时应该具有与Popen相同的进程ID,后来我发现情况并非如此。我减去了一个,因此使两个不同的本来正确的进程ID错误地相等。拒绝我的人可以停止滥用堆栈溢出,因为我曾经被禁止的全部原因是因为人们这样做并且不理解我的问题,这是应该禁止的问题,而不是我的问题。如果您对我的问题有疑问,请不要拒绝投票,而不必说原因。或者更好的是,有足够的宽限期仅降低巨魔的票数,而不是普通人。

这是在我的原始代码中做我打算做的正确方法,这导致我提出这个问题;我想知道如何检测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;
}

使用上面的辅助函数,同时替换原始代码中的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++;
  }

正如其他人也指出的那样,某些发行版将返回不同的父进程,因为sh cmd将直接使用run。为了解决这个问题,我尝试执行if或in语句,以查看返回的父进程或“祖父母”进程ID是否相等,同时尝试跳过所有带有sh cmd值的父进程。

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

© www.soinside.com 2019 - 2024. All rights reserved.