Почему if (fork () == 0) {getpid ()} и процесс popen () возвращают один и тот же идентификатор процесса? - PullRequest
0 голосов
/ 10 ноября 2019

Я хочу знать, почему два идентификатора процесса совпадают, когда, как мне известно, getpid () в fork () должен быть процессом, отличным от процесса popen ().

Iмне сообщили, что мой код работает только из-за того, что, как я понимаю, это может быть ошибка в дистрибутивах на основе 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.

1 Ответ

0 голосов
/ 10 ноября 2019

@ thatotherguy объясняет ответ в своем комментарии:

"Знаете ли вы, что из-за вашего - 1 вы на самом деле проверяете, что два процесса имеют последовательные pids? Это неудивительное поведение в 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);
}

То - 1 в возвращаемом идентификаторе процесса, который я первоначально добавил, потому что я думал, что когда он возвращалневерный идентификатор процесса, потому что я думал, что когда писал, что fork () должен иметь тот же идентификатор процесса, что и popen, что позже я обнаружил, что это не так. Я вычел один, таким образом делая два разных в противном случае правильных идентификатора процесса, неправильно равных.

Вот правильный способ сделать то, что я намеревался сделать, в моем исходном коде, что привело меня к заданию этого вопроса;Я хотел знать, как определить, происходят ли дочерние процессы 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++;
  }

Как указывало @thatotherguy, некоторые дистрибутивы возвращают другой родительский процесспотому что sh cmd будет использовать run напрямую. Чтобы решить эту проблему, я выполнил или проверил в операторе if, чтобы увидеть, равны ли идентификаторы родительского или «родительского» процесса равным, при попытке пропустить любые родительские процессы со значением sh cmd.

Требуется вспомогательная функцияфлаг компоновщика -lprocps и пакет libprocps-dev установлены, если вы работаете в системе на основе Debian. На других дистрибутивах имя пакета будет другим.

...