贡献者: admin; addis
如果不改变环境变量也可以用 execv(),完整的例子如
#include <unistd.h> // For execv()
#include <sys/wait.h> // For wait()
#include <iostream> // For std::cout
#include <cstdio> // For perror()
int main() {
// Define the command and its arguments
char *args[] = {(char *)"/bin/ls",
(char *)"-l", (char *)NULL};
// Fork a new process
pid_t pid = fork();
if (pid == -1) {
// Fork failed
perror("fork failed");
return 1;
} else if (pid == 0) {
std::cout << "running ls command" << std::endl;
// Replace the child process with a new program (ls -l)
execv("/bin/ls", args);
// If execv() returns, an error occurred
perror("execv failed");
return 1;
} else {
// Parent process
int status;
// Wait for the child to complete. pid is now of the child process
waitpid(pid, &status, 0);
std::cout << "Child process finished with status: "
<< status << std::endl;
}
}
该程若运行成功会把 ls 的输出打到 stdout。
int execve(const char *path, char *const argv[], char *const envp[]);
char *args[] = {"/bin/ls", "-l", NULL};
char *env[] = {"HOME=/usr/home", "LOGNAME=home", NULL};
execve("/bin/ls", args, env);
\item getpid() 可以获取当前进程的 pid
\item 子进程中用 getppid() 获取父进程的 pid
\item 对子进程和父进程, pid_t pid = fork() 分别返回 0 和子进程的 pid。
pid_t wait(int *status); 返回结束的子进程的 pid,如果子进程都结束了,返回 -1。
status 是个 bit field,不同范围的 bit 用于表示不同信息,可以判断 exit code(即 exit() 的参数),或者各种 signal。
WIFEXITED(status): Returns true if the child exited normally (i.e., via exit() or _exit()).
WEXITSTATUS(status): Returns the exit status of the child if WIFEXITED(status) is true. This is the value that the child passed to exit() or _exit().
WIFSIGNALED(status): Returns true if the child process was terminated by a signal.
WTERMSIG(status): Returns the signal number that caused the child to terminate if WIFSIGNALED(status) is true.
WIFSTOPPED(status): Returns true if the child process was stopped (but not terminated) by a signal.
WSTOPSIG(status): Returns the signal number that caused the child to stop if WIFSTOPPED(status) is true.
WCOREDUMP(status): (Non-standard, but common) Returns true if the child process produced a core dump when it terminated.
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
_exit() 比 exit() 更低级,没有任何 cleanup。无论 exit() 参数是什么数字,都叫做 Normal Exit
Signal Termination 见 Linux 进程的信号(笔记)
int kill(pid_t pid, int sig); 给某个 pid 或则进程组发送信号。pid > 0 时发送给指定进程,pid == 0 时发送给当前程序的进程组的所有进程。pid == -1 发给所有有权限发送的进程。pid < -1 发给 gpid 为 -pid 进程组中的所有进程。返回 0 则成功,否则返回 -1 并设置 errno。
fort() 产生的子进程默认具有和主进程相同的进程组。
ps -o pgid -p <PID> 可以查看一个进程的 pgid
pgid 会等于 pid。所以二者相等时,通常意味着该进程是进程组中的第一个进程。
ls | grep foo 的两个命令也是一个进程组。
Ctrl+Z
int setpgid(pid_t pid, pid_t pgid) 可以设置指定进程的 pgid。如果 pid 或 pgid 为 0,代表当前进程的 pid。所以 setpgid(0, 0) 把当前进程的 pgid 设置为 pid。例如在 fork() 之后可以对子进程这么做。
popen() 可以用管道读或写入 stdin/stdout,但不能又读又写,也不能直接读 stderr(需要在命令中用 2>&1)
写管道
#include <cstdio>
#include <cstdlib>
int main() {
FILE* fp = popen("wc -w", "w");
if (fp == nullptr) {
perror("popen failed");
exit(EXIT_FAILURE);
}
const char* input = "Hello, how many words are here?\n";
fprintf(fp, "%s", input);
pclose(fp);
return 0;
}
读管道
FILE* fp = popen("ls -l", "r");
if (fp) {
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp) != nullptr) {
printf("%s", buffer);
}
int ret = pclose(fp);
if (ret == -1) {
perror("pclose failed");
return -1;
}
int ret = WEXITSTATUS(ret);
}
如果要同时读写管道,需要用 pipe() 和 dup2()
int pipe(int pipefd[2]); 可以创建一个管道,使一个进程可以从 pipefd[1] 写入,另一个进程从 pipefd[0] 读取。
dup2(源fd,目标fd)(fd 是 file descriptor),把 目标fd 打开的文件(如果有)先关掉,然后将其指向 源fd 的文件。成功后,两个 fd 就指向同一个内核中的 file description,包括错误状态,文件中的当前位置全部都共享。
close(fd) 并不一定会删除内核中的 file description,只会解绑 file descriptor。只有当所有指向它的 descriptor 都被 close() 才会释放(类似于 shared_ptr)。
pclose() 和 wait() 类似,同样等待子进程结束,返回相同的 bit field,要用 WEXITSTATUS() 等宏函数翻译。
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int pipe_stdin[2]; // Pipe for child's stdin
int pipe_stdout[2]; // Pipe for child's stdout
pid_t pid;
// Create pipes
if (pipe(pipe_stdin) == -1 || pipe(pipe_stdout) == -1) {
perror("pipe failed");
exit(EXIT_FAILURE);
}
// Fork a child process
pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// Child process
// Redirect child's stdin to pipe_stdin[0]
dup2(pipe_stdin[0], STDIN_FILENO);
close(pipe_stdin[0]);
close(pipe_stdin[1]); // Close unused write end
// Redirect child's stdout to pipe_stdout[1]
dup2(pipe_stdout[1], STDOUT_FILENO);
close(pipe_stdout[0]); // Close unused read end
close(pipe_stdout[1]);
// Execute the command
// Replace 'cat' with your command
execl("/bin/sh", "sh", "-c", "cat", NULL);
// If execl fails
perror("execl failed");
exit(EXIT_FAILURE);
}
else {
// Parent process
close(pipe_stdin[0]); // Close unused read end of stdin pipe
close(pipe_stdout[1]); // Close unused write end of stdout pipe
// Write to the child's stdin
const char* input = "Hello, child process!\n";
write(pipe_stdin[1], input, strlen(input));
// Close stdin write end after writing
close(pipe_stdin[1]);
// Read the child's stdout
char buffer[128];
ssize_t count;
while ((count = read(pipe_stdout[0],
buffer, sizeof(buffer) - 1)) > 0) {
buffer[count] = '\0'; // Null-terminate the buffer
printf("Child output: %s", buffer);
}
close(pipe_stdout[0]); // Close stdout read end
// Wait for the child to terminate
wait(NULL);
}
return 0;
}