我正在尝试使用forkpty
到execvp
一个ls /
,然后从父进程读取其输出并写入stdout。
我得到了它,并且在Valgrind下运行它也没有显示任何错误。
编辑:Valgrind没有显示内存错误。 Valgrind下的最后read
仍会产生EIO
。
但是,上一个read
始终返回-1
并将errno
设置为EIO
。我一直在研究和阅读手册页,但我仍然不明白为什么会发生这种情况:
EIO I/O error. This will happen for example when the process is in a background process group, tries to read from its controlling terminal, and either it is ignoring or blocking SIGTTIN or its process group is orphaned. It may also occur when there is a low-level I/O error while reading from a disk or tape.
我还注意到,如果我抓住SIGCHLD
信号,我可以知道ls
何时退出,但是如果我列出了一个更大的目录,例如/usr/bin
,那么我得到的信号是ls
子进程,即使成功后有很多read
个进程。
以下是输出示例:
$ ./a.out
bin dev initrd.img.old libx32 opt sbin usr
boot etc lib lost+found proc srv var
build home lib32 media root sys vmlinuz
core initrd.img lib64 mnt run tmp vmlinuz.old
# read: Input/output error
我错过了一些明显的东西吗?
#include <pty.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
int master_fd;
pid_t child_pid = forkpty(&master_fd, NULL, NULL, NULL);
if (child_pid == -1) {
perror("# forkpty");
return EXIT_FAILURE;
}
else if (!child_pid) { /* CHILD */
char* ls_argv[] = {
"ls",
"/",
NULL,
};
execvp(ls_argv[0], ls_argv);
perror("# execvp");
abort();
}
else { /* PARENT */
uint8_t buffer[BUFSIZ];
ssize_t read_count;
while ((read_count = read(master_fd, buffer, BUFSIZ)) > 0) {
uint8_t* ptr = buffer;
size_t ptr_len = (size_t) read_count;
while (ptr_len > 0) {
ssize_t write_count = write(STDOUT_FILENO, ptr, ptr_len);
if (write_count == -1) {
perror("# write");
return EXIT_FAILURE;
}
ptr += write_count;
ptr_len -= write_count;
}
}
if (read_count == -1) {
perror("# read");
return EXIT_FAILURE;
}
if (close(master_fd) == -1) {
perror("# close");
return EXIT_FAILURE;
}
int child_status;
if (waitpid(child_pid, &child_status, 0) == -1) {
perror("# waitpid");
return EXIT_FAILURE;
}
if (!WIFEXITED(child_status)) {
fprintf(stderr, "# main: subprocess did not exit normally\n");
return EXIT_FAILURE;
}
return child_status;
}
}
答案 0 :(得分:1)
pty驱动程序调用内核函数使slave pty一方无效,从那时起返回EIO
,直到从属端关闭。这种情况总是在主控方被控制过程关闭时发生。
此过程是当今所有unix变体的标准。这是一种解决方法,可以避免在主端离开后继续使用设备的进程。即使您使用新进程再次附加主端,旧的从进程也会被阻塞,直到它关闭打开的描述符。
答案 1 :(得分:1)
这个问题似乎有三个部分:
a)在这种情况下(上次阅读时)EIO是否是有效错误?
b)为什么SIGCHLD在预期的I / O完成之前收到的很多,
c)为什么Valgrind不会发生错误?
a)由于读取类似于子进程已退出的损坏管道上的读取,因此预计最后一次读取将失败。与EIO相比,EOF似乎是一个更有意义的错误。(当从磁盘或磁带读取时出现低级I / O错误时,也可能出现错误。)
b)CPU处理远远超过I / O引起了这种情况。收到SIGCHLD(并设置了一个标志以表明孩子已经退出),父进程应该在读完孩子写入slave-pty的信息并因此安全地忽略EIO后,期望读取失败。
c)我不确定这一点。虽然Valgrind可能会减慢一些时间来为I / O处理提供足够的时间,但最终的读取仍然会失败(返回-1),在本例中是EIO。