为什么if(fork()==0){getpid()}和popen()进程返回相同的进程id
Why is if (fork() == 0) { getpid() } and a popen() process returning the same process id?
据我所知,当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原子的可执行文件。
@thatherguy在他的评论中解释了答案:
"你知道吗,由于你的-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;
}
使用上面的helper函数,同时用它替换原始代码中的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++;
}
正如@thathotherguy也指出的那样,一些发行版将返回不同的父进程,因为sh cmd将直接使用run。为了解决这个问题,我在if语句中执行了或检查,以查看父进程或"祖父母"进程id是否返回相等,同时尝试跳过任何具有sh cmd值的父进程。
如果您在基于Debian的系统上,则helper函数需要安装-lprocps
链接器标志和libprocps-dev
包。包名称在其他发行版上会有所不同。
- 将返回值从 exe 传递到 bat,并将其传递给 C# 中的进程
- 枚举进程模块在有效句柄上返回无效句柄
- 进程退出,返回值3221226356写入系统( "cls" )。(已解决)
- 进程返回 -1073741819 (0xC0000005) 错误
- 进程在我的 c++ 代码上返回 -1073741571 (0xC00000FD)
- C++进程在 4.017 秒后退出,返回值3221225477
- kill() 总是返回 0(成功),即使在子进程已经结束之后?
- 为什么我不能返回向量<向量<int>>(进程返回 -1073741819 (0xC0000005))
- 进程为包含double但不包含int的模板返回了-103741819(0xC0000005)
- 为什么if(fork()==0){getpid()}和popen()进程返回相同的进程id
- 进程返回 -1073741819 (0xC0000005) (为什么??
- 分段错误进程返回139(0x8b)
- 为什么进程返回 -1
- 如何将退出代码从 Ada 进程返回到调用的 Windows 进程
- 得到错误分割错误(核心转储)进程返回139 (0x8B)
- 程序结束时进程返回0xC0000005
- 树二进制C/ c++进程返回-1073741819 (0xC0000005)
- 创建进程并在进程返回消息后运行代码(c++)
- 进程返回 255 <0xFF>,C++,程序停止工作
- GlobalAllocPtr()总是为不同的进程返回相同的地址