Linux下如何获取后台运行进程的标准输出
Linux下一切皆文件的思想同样适用于进程。对于很多后台运行的进程,通过查看其procfs我们可以发现,其标准输入(fd0),标准输出(fd1)和标准错误输出(fd2)均指向/dev/null,也就是Linux下的“黑洞”设备。
我们使用这个程序来做一下试验:
#include <cstdio>
int main() { while (true) printf("1\n"); }
在终端执行
g++ test.cpp -o test.out
# 后台运行进程
setsid ./test.out 1>/dev/null 2>&1 </dev/null &
ps aux | grep test.out | grep -v grep
ls -l /proc/<pid>/fd
如果此时我们突然需要重新获取该进程的输出,是否可以直接修改/proc/<pid>/fd的链接呢?答案是不可行的,或者至少笔者没有找到合适的方法。如果有哪位大牛了解,还望不吝赐教。
但是在可以使用gdb的情况下可以“曲线救国”。首先介绍两个Linux下的文件描述符操作函数dup和dup2。
#include <unistd.h>
/* 分配一个与oldfd描述相同的文件描述符,且保证返回的新文件描述符
* 是当前可用文件描述符中的最小值。*注意*,二者共享文件偏移量和
* 文件状态标志等。例如,如果在其中一个文件描述符调用lseek,那么
* 另一个文件描述符的偏移量也会发生变化。但二者不共享文件描述符状态。
*/
int dup(int oldfd);
/* 复制oldfd至newfd,如果newfd已经打开,则先关闭newfd。如果newfd与
* oldfd相同,则直接返回newfd。函数返回后oldfd与newfd指向相同文件,
* 只是文件描述符不同。
*/
int dup2(int oldfd, int newfd);
通过gdb可以动态attach到指定进程上,并执行代码修改进程。(attach失败可能是由于系统ptrace权限设置导致的,可以尝试使用sudo执行gdb,或修改ptrace权限)
gdb
(gdb) attach <pid>
(gdb) call (int)open("/path/to/your/log", 66, 0666)
(gdb) call (int)dup2(fd, 1)
(gdb) call (int)dup2(fd, 2)
(gdb) call (int)close(fd)
(gdb) detach
(gdb) quit
经过上述操作,再执行ls -l /proc/<pid>/fd,我们发现标准输出与标准错误输出已经被重定向到指定的/path/to/your/log位置了。
如果想要重定向回终端,可以通过终端执行tty获取当前终端设备文件名,然后在gdb中打开对应文件并将其复制到标准输出与标准错误输出。
参考链接&自动设置脚本