首页 > 解决方案 > 在 C 中调用 execv("/bin/sh", NULL) 时,如何与子进程通信(例如,通过写入运行命令)?

问题描述

我正在尝试使我的程序能够与另一个需要输入的程序进行交互。我通过在子进程中fork()执行来尝试过。execv("/bin/sh", NULL)现在应该用新进程替换它,/bin/sh. 现在我希望我的父进程与/bin/sh类似发送命令进行通信。我尝试使用管道,但我想我做错了什么。这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
  int pipefd[2];
  pipe(pipefd);
  pid_t pid = fork();

  if(pid == 0) {               // child code
    close(0);                  // close stdin fd
    dup(pipefd[0]);            // duplicate the pipe fd (will return 0)
    close(pipefd[1]);          // close the pipefd 1 (I dont need to write for child now)
    execv("/bin/sh", NULL);    // execute /bin/sh
  } else {                     // parent code
    close(1);                  // close stdout fd
    dup(pipefd[1]);            // duplicate the pipe fd (will return 1)
    close(pipefd[0]);          // close the pipefd 0 (I dont need to read right now)
  }

  write(0, "touch xxx\n", 10); // try to run a command with the parent process
  waitpid(-1, WNOHANG, 0);     // wait for the child to exit
}

我试图从父级写入标准输入,因为我认为/bin/sh从标准输入获取输入我可以写入它。可悲的是我的命令没有执行。之后我可以输入一些东西,但它什么也不做。这是 strace 的输出:

execve("./a.out", ["./a.out"], 0x7fffffffe870 /* 41 vars */) = 0
brk(NULL)                               = 0x555555559000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffffffe710) = -1 EINVAL (Das Argument ist ungültig)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (Datei oder Verzeichnis nicht gefunden)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=210476, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 210476, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7f92000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`|\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0@\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 80, 848) = 80
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0T\246\344\4\347\334\35\347\301CJ\0\267\261\2552"..., 68, 928) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2154488, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7f90000
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 1884632, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7dc3000
mmap(0x7ffff7de9000, 1359872, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7ffff7de9000
mmap(0x7ffff7f35000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x172000) = 0x7ffff7f35000
mmap(0x7ffff7f81000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7ffff7f81000
mmap(0x7ffff7f87000, 33240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7f87000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7dc1000
arch_prctl(ARCH_SET_FS, 0x7ffff7f91580) = 0
mprotect(0x7ffff7f81000, 12288, PROT_READ) = 0
mprotect(0x555555557000, 4096, PROT_READ) = 0
mprotect(0x7ffff7ffb000, 8192, PROT_READ) = 0
munmap(0x7ffff7f92000, 210476)          = 0
pipe([3, 4])                            = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ffff7f91850) = 29114
close(1)                                = 0
dup(4)                                  = 1
close(3)                                = 0
write(0, "touch xxx\n", 10touch xxx
)             = 10
wait4(-1,

我希望有人能帮帮忙。

标签: cforkx86-64

解决方案


您的父进程(else块)正在关闭并替换 fd 1,对应于stdout,这意味着写入自己的进程stdout将转到子进程stdin。但是你write父级的未修改 fd 0, stdin.

更改代码,以便父级写入 fd 1, stdout,它连接到通向子级 fd 的管道0stdin

这里还有其他问题(编译时启用任何级别的警告都会清楚)。修复这些,这应该可以工作(假设,正如你所说,你的系统允许execv接受NULL作为第二个参数)。


推荐阅读