繁体   English   中英

如何正确读取进程的文件描述符?

[英]How to properly read a process's file descriptors?

我正在编写一个小程序来列出特定进程的文件描述符,但我很难理解结果。 我正在检查的过程如下所示:

int main() {
    int fds1[2];
    int fds2[2];
    pipe(fds1);
    pipe(fds2);
    pid_t pid = fork();
    if (pid == 0) {
        dup2(fds1[0], STDIN_FILENO);
        dup2(fds2[1], STDOUT_FILENO);
        close(fds1[0]);
        close(fds1[1]);
        close(fds2[0]);
        close(fds2[1]);
        sleep(2);
        return 0;
    }
    close(fds1[0]);
    close(fds2[1]);
    waitpid(pid, NULL, 0);
    return 0;
}

fd 检查程序代码如下所示:

let path_str = format!("/proc/{}/fd", self.pid);
let dir = Path::new(&path_str);

let mut fds = Vec::new();
for entry in fs::read_dir(dir).ok()? {
    let path = entry.ok()?.path();
    let filename = path.file_name()?;
    let fd = fname.to_str()?.to_string().parse::<usize>().ok()?
    fds.push(fd);
}

一起运行上述程序时ls -l /proc/{pid}/fd的结果给了我这个列表:

0 -> /dev/pts/6
1 -> /dev/pts/6
2 -> /dev/pts/6
22 -> /dev/pts/1
30 -> /dev/pts/4

4 -> /home/{user}/.spectrwm.conf
5 -> 'pipe:[168640]'
6 -> 'pipe:[168641]'

我很困惑为什么/dev/pts/x有 5 个符号链接,为什么底部的 3 个 fds 包含在这个进程的文件描述符中,尤其是我的 WM 的配置文件中。 我对pipefork的工作原理有基本的了解,但我似乎无法理解这里发生了什么。

任何帮助或见解将不胜感激,谢谢!

我要注意的第一件事是与spectrwm.conf关联的文件描述符可能是由您的 window 管理器以某种方式打开的(可能通过 LD_PRELOAD 或类似的东西)。 您可以使用strace查看打开了哪些文件描述符以及如何打开(但是,在这种情况下,您应该使用标志-ff ,因为您正在分叉一个新进程)。

您还可以使用 GDB 来识别何时进行某些系统调用(即打开),以识别您的进程在执行的哪个时间点打开与/home/{user}/.spectrwm.conf关联的文件描述符(参见catchpoints )。

至于其他文件描述符,为了帮助您了解发生了什么,我再次运行了您的代码。 以下是父进程的文件描述符:

user@pop-os:~$ ls -l /proc/39783/fd
total 0
lrwx------ 1 user user 64 Mar 21 14:03 0 -> /dev/pts/0
lrwx------ 1 user user 64 Mar 21 14:03 1 -> /dev/pts/0
lrwx------ 1 user user 64 Mar 21 14:03 2 -> /dev/pts/0
l-wx------ 1 user user 64 Mar 21 14:03 4 -> 'pipe:[544306]'
lr-x------ 1 user user 64 Mar 21 14:03 5 -> 'pipe:[544307]'

我为父进程看到的文件描述符都是有意义的。 回想一下, STDINSTDOUTSTDERR的文件描述符201 所有这些文件描述符都指向/dev/pts/0这是一个伪终端(您用来运行代码的终端)。 接下来,父进程中的文件描述符45分别对应pipe的读写端。 Recall that the libc pipe function fills an array of length two for the reading and writing ends (but you close two of the file descriptors associated with your pipes -- the reading end of pipe fds1 and the writing end of pipe fds2 so the open file描述符在父级中有意义)。

以下是子进程的文件描述符:

user@pop-os:~$ ls -l /proc/39784/fd
total 0
lr-x------ 1 user user 64 Mar 21 14:03 0 -> 'pipe:[544306]'
l-wx------ 1 user user 64 Mar 21 14:03 1 -> 'pipe:[544307]'
lrwx------ 1 user user 64 Mar 21 14:03 2 -> /dev/pts/0

这些文件描述符也很有意义。 回想一下dup2创建文件描述符的副本,但将复制的文件描述符分配给特定编号。 这允许您执行进程间通信(这也是 shell 用来实现 pipe 运算符|的方式——只需派生n进程,创建n - 1管道,并创建适当的dup2调用)。

In your child process, we can see that you duplicate the pipe fds to STDIN and STDOUT , which is why you see the reading end of the first pipe associated with STDIN_FILENO and the writing end of the second pipe associated with STDOUT_FILENO . 最后一个文件描述符,本例中的STDERR_FILENO/dev/pts/0仍然连接到终端,因为您没有dup2调用它。

我不知道你想用你的程序做什么,但我确实有一些问题/注意事项给你:

  • 为什么在 fork 一个子进程时使用两个管道? 要让两个进程相互通信,在大多数情况下,您只需要一个 pipe(回想一下,一个 pipe 有两个文件描述符用于读取和写入)。

暂无
暂无

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

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