C fork循环执行无限命令(urandom / tail在活动文件上)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C fork循环执行无限命令(urandom / tail在活动文件上)相关的知识,希望对你有一定的参考价值。
我试图用C重现unix(OSX)shell(比如bash)中管道的行为。我没有问题来处理一个非无限的命令(例如:ls | wc -c
)。
但是你可以想象,我有一个问题,我无法处理一个无限的命令,例如:base64 /dev/urandom" | head -c 1000
。此命令的输出立即是urandom的前1000个字符。
我的函数只是等待urandom的结束,这是一个infite ...所以,我需要用“CTRL + C”来杀死这个过程(并且只处理孩子的信号而不是我的函数)来打印前1000个字符。
我执行所有管道的功能:
#define READ_END 0
#define WRITE_END 1
void exec_pipes(char ***argv)
{
int p[2];
int fd_save = 0;
while (*argv)
{
pipe(p);
if (fork() == 0)
{
dup2(fd_save, STDIN_FILENO);
if (*(argv + 1))
dup2(p[WRITE_END], STDOUT_FILENO);
close(p[READ_END]);
execvp(**argv, *argv);
exit(EXIT_FAILURE);
}
else
{
wait(NULL);
fd_save = p[READ_END];
close(p[WRITE_END]);
(*argv)++;
}
}
}
在这里,您可以使用main:https://www.onlinegdb.com/Hkbjd3WOz检查我的整个代码
提前致谢。
不,你没有。当读取过程刚刚结束时,它会关闭管道的读取端,因此不允许写入器再写入管道,并从系统中获取错误EPIPE
(无法写入管道)。
对于ocurr,没有任何进程必须打开它才能读取,因为内核会计算读取和写入进程的数量,并且在至少有一个进程将其打开以供读取时不会发出此错误(这意味着第一个进程)链必须关闭管道的读文件描述符,如果它不会从中读取)
然后,您必须关闭未使用的描述符(这取决于您是否希望父级是作者或读者)在父母和孩子中,并且dup2(2)
另一个描述符(再次,在父母和孩子中)到文件描述符0
(in)或1
(out)。 (首先要说的是,我通过调用/dev/urandom
命令改变了你的yes
示例,因为它是所有unix风格的标准,并且还产生无限输出)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char *writer_program = "yes";
char *argv_writer[] = {
"yes", "yes", NULL,
};
char *reader_program = "head";
char *argv_reader[] = {
"head", "-c", "10", NULL,
};
int main()
{
int pipe_fds[2];
int res;
pipe(pipe_fds);
if ((res = fork()) < 0) { /* PLEASE, CHECK FOR ERRORS. */
perror("fork");
} else if (res == 0) { /* child process, ACTING AS WRITER */
close(pipe_fds[0]); /* close writing part of pipe */
dup2(pipe_fds[1], 1); /* dup2 reading part as stdin */
execvp(writer_program, argv_writer);
perror("execvp"); /* PLEASE, CHECK FOR ERRORS. */
} else { /* parent process, ACTING AS A READER */
close(pipe_fds[1]); /* just the opposite */
dup2(pipe_fds[0], 0);
execvp(reader_program, argv_reader);
perror("execvp"); /* PLEASE, CHECK FOR ERRORS. */
}
/* BOTH, THIS IS REACHED IN CASE OF FAILURE (fork fails, or any
* of the exec(2) calls fail. */
exit(EXIT_FAILURE);
}
EDIT
下面是一个完整的示例,其管道链等同于下一个:
dd if=/dev/urandom ibs=1024 | base64 | head -n 150 | sort -r | pr
在这种情况下,第一个死的程序是head
,如你所愿,并且每个程序都在它后面死掉。父程序正在等待他的所有孩子都死了,并且每个系统调用都已被包装,因此您可以通过stderr获得执行跟踪,看看会发生什么。
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define F(fmt) "[pid=%d]:"__FILE__":%d:%s: " fmt, getpid(), __LINE__, __func__
#define SIZE(a) (sizeof a/sizeof *a)
#define ERR(fmt, ...) do{
fprintf(stderr,
F(fmt": %s (errno = %d)
"),
##__VA_ARGS__,
strerror(errno),
errno);
exit(EXIT_FAILURE);
}while(0)
#define CLOSE(expr) do{
close(expr);
fprintf(stderr,
F("close("#expr" === %d);
"),
(expr));
}while(0)
#define PIPE(var) do{
if(pipe(var)<0) ERR("pipe");
fprintf(stderr,F("PIPE ==> %d, %d
"), var[0], var[1]);
}while(0)
#define FORK() do{
if((res = fork()) < 0)ERR("fork");
fprintf(stderr,F("FORK ==> %d
"), res);
}while(0)
#define DUP(expr1, expr2) do{
if((res = dup2(expr1, expr2)) < 0) ERR("dup");
fprintf(stderr,
F("DUP("#expr1" === %d, "#expr2" === %d);
"),
(expr1), (expr2));
}while(0)
char * argv_DISK_DUMP[] = { "dd", "if=/dev/urandom", "ibs=1024", NULL };
char * argv_BASE64[] = { "base64", NULL };
char * argv_HEAD[] = { "head", "-n", "150", NULL };
char * argv_SORT[] = { "sort", "-r", NULL };
char * argv_PR[] = { "pr", NULL };
struct pipe_program {
pid_t pid;
pid_t ppid;
char *pname;
char **argv;
} pipe_programs[] = {
0, 0, "dd", argv_DISK_DUMP,
0, 0, "base64", argv_BASE64,
0, 0, "head", argv_HEAD,
0, 0, "sort", argv_SORT,
0, 0, "pr", argv_PR,
};
/* size of last array */
size_t pipe_programs_n = SIZE(pipe_programs);
static size_t printus(int ix, struct pipe_program *p);
static pid_t WAIT(int *status);
int main()
{
int res, i;
struct pipe_program *p = pipe_programs;
int input_fd = 0; /* first process is connected to standard input */
static int pipe_fds[2] = { -1, -1 };
for(i = 0; i < pipe_programs_n - 1; i++, p++) {
PIPE(pipe_fds);
FORK();
if (res == 0) { /* child process, we have redirected nothing yet. */
p->pid = getpid();
p->ppid = getppid();
/* print who we are */
printus(i, p);
/* redirect input, if needed */
if (input_fd != 0) {
DUP(input_fd, 0);
CLOSE(input_fd); /* not used after redirection */
}
CLOSE(pipe_fds[0]); /* we don't use this */
/* and output */
DUP(pipe_fds[1], 1);
CLOSE(pipe_fds[1]);
execvp(p->pname, p->argv);
ERR("execvp: %s", p->pname);
/* NOTREACHED */
}
/* parent process */
/* save pid to be used later */
p->pid = res; /* we'll use it later */
p->ppid = getpid();
/* close unused pipe descriptor */
CLOSE(pipe_fds[1]);
/* if we have an old input_fd, then close it */
if (input_fd) CLOSE(input_fd);
/* ... and save pipe read descriptor */
input_fd = pipe_fds[0];
} /* for */
/* now we have our input connected to the output of the last process */
FORK();
if (res == 0) { /* child, last process in the pipe */
p->pid = getpid();
p->ppid = getppid();
/* print who we are */
printus(i, p);
/* redirect input */
if (input_fd != 0) {
DUP(input_fd, 0);
CLOSE(input_fd); /* not used after_redirection */
}
/* this time no output redirection */
execvp(p->pname, p->argv);
ERR("execvp");
/* NOTREACHED */
}
CLOSE(pipe_fds[1]);
if (input_fd) CLOSE(input_fd);
/* parent code... we did pipe_programs_n fork()'s so we
* have to do pipe_programs_n wait()'s */
int status;
pid_t cld_pid;
/* while we can wait for a child */
while ((cld_pid = WAIT(&status)) > 0) {
for (i = 0, p = pipe_programs; i < pipe_programs_n; i++, p++) {
if (cld_pid == p->pid) {
fprintf(stderr,
F("Child finished: pid = %d
"),
cld_pid);
printus(i, p);
break;
}
}
} /* while */
exit(EXIT_SUCCESS);
}
static size_t printus(int ix, struct pipe_program *p)
{
size_t res = 0;
int j;
static char buffer[1024];
char *s = buffer;
size_t bfsz = sizeof buffer;
size_t n;
#define ACT() do{s+=n; bfsz-=n;}while(0)
n = snprintf(s, bfsz,
F("%d: pid = %d, ppid = %d, program = "%s": args = "),
ix, p->pid, p->ppid, p->pname);
ACT();
for (j = 0; p->argv[j]; j++) {
n = snprintf(s, bfsz,
"%s"%s"",
j ? ", " : "{",
p->argv[j]);
ACT();
}
n = snprintf(s, bfsz, "};
");
ACT();
fputs(buffer, stderr);
return res;
}
static pid_t WAIT(int *status)
{
pid_t res = wait(status);
fprintf(stderr, F("WAIT() ==> %d
"), res);
return res;
}
您可以使用以下命令获取整个程序:
git clone git@github.com:mojadita/pipe.git
请注意那里的程序几乎完全相同,但已经做了一些工作来促进不同管道的编写,而不必触及主源文件中的几个位置。如果从git服务器下载程序并且想要修改管道,请编辑文件pipe.i
。然后make
和voilá!!! :)该程序已在linux,freebsd和mac osx上测试过,所以我认为你可以根据自己的需要调整它。如果你从github得到它,你也会有一个Makefile
。
以上是关于C fork循环执行无限命令(urandom / tail在活动文件上)的主要内容,如果未能解决你的问题,请参考以下文章
C 语言文件操作 ( 配置文件读写 | 框架搭建 | 主函数逻辑结构 | 启动无限循环 | 接收用户操作值 | 宏定义 | 显示操作菜单 )