我在管理 C 程序中的文件描述符方面面临挑战,特别是在 Pipex 项目的上下文中。程序退出时,Valgrind 报告有 5 个文件描述符打开,其中 3 个是标准描述符。特别是,文件描述符 5 是使用 pipeline.c 文件中第 29 行的管道函数打开的,该函数是从我的程序中地址 0x109622 处的 here_doc 函数调用的。
我在下面添加了 Valgrind 报告中的一个片段以供参考: 代码:
pipex.c
#include "../inc/pipex.h"
char *find_path(char *cmd, char **ev)
{
char **allpaths;
char *exe;
int i;
i = 0;
while (ev[i])
{
if (ft_strnstr(ev[i], "PATH=", 5) != NULL)
break ;
i++;
}
allpaths = ft_split((ev[i] + 5), ':');
i = 0;
while (allpaths[i])
{
exe = build_executable_path(cmd, allpaths[i]);
if (exe != NULL)
{
ft_clear_tab(allpaths);
return (exe);
}
i++;
}
ft_clear_tab(allpaths);
return (NULL);
}
void exe(char **ev, char *av)
{
char **cmd;
char *cmd_exe;
cmd = ft_split(av, ' ');
cmd_exe = find_path(cmd[0], ev);
if (cmd_exe == NULL)
{
perror("pipex: command not found");
ft_putendl_fd(cmd[0], 2);
ft_clear_tab(cmd);
exit(EXIT_FAILURE);
}
if (execve(cmd_exe, cmd, ev) == -1)
{
perror("pipex: execution error");
ft_putendl_fd(cmd[0], 2);
ft_clear_tab(cmd);
free(cmd_exe);
exit(EXIT_FAILURE);
}
}
void do_pipe(char **av, char **ev)
{
pid_t pid;
int fd[2];
if (pipe(fd) == -1)
error_arg();
pid = fork();
if (pid == 0)
{
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
exe(ev, av[0]);
}
else
{
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
waitpid(pid, NULL, 0);
}
}
void here_doc(char *limiter, int ac)
{
int fd[2];
pid_t pid;
char *line;
if (ac < 6)
error_arg();
if (pipe(fd) == -1 || (pid = fork()) == -1)
{
perror("Pipe or Fork");
exit(EXIT_FAILURE);
}
if (pid == 0)
{
close(fd[0]);
while (gnl(&line) > 0)
{
if (ft_strncmp(line, limiter, ft_strlen(limiter)) == 0 || line[0] == '\0')
exit(EXIT_SUCCESS);
write(fd[1], line, ft_strlen(line));
}
free(line);
close(fd[1]);
exit(EXIT_SUCCESS);
}
else
{
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
wait(NULL);
}
}
int main(int ac, char **av, char **ev)
{
int in;
int out;
int i;
if (ac < 5)
error_arg();
if (ft_strncmp(av[1], "here_doc", 8) == 0)
{
i = 3;
out = open(av[ac - 1], O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (out == -1)
exit(EXIT_FAILURE);
here_doc(av[2], ac);
}
else
{
i = 2;
out = open(av[ac - 1], O_WRONLY | O_CREAT | O_TRUNC, 0777);
in = open(av[1], O_RDONLY, 0777);
if (out == -1 || in == -1)
exit(EXIT_FAILURE);
dup2(in, 0);
}
while (i < ac - 2)
do_pipe(av + i++, ev);
dup2(out, 1);
exe(ev, av[ac - 2]);
close(in);
close(out);
return 0;
}
utils.c
#include "../inc/pipex.h"
#define BUFFER_SIZE 100000
void error_arg(void)
{
write(2, "\033[1;31m!! we need more than 5 arguments :c !!\n\033[0m\n", 49);
write(2, "\033[1;32mExample: ./Here_doc cmd1 cmd2 outfile\033[0m\n", 50);
write(2,
"\033[1;32mExample 2: ./pipex infile cmd1 cmd2 cmd3 ... outfile\033[0m\n",
65);
exit(EXIT_FAILURE);
}
int gnl(char **line)
{
char *buffer;
int i;
int r;
char c;
i = 0;
r = 0;
buffer = (char *)malloc(10000);
if (!buffer)
return (-1);
r = read(0, &c, 1);
while (r && c != '\n' && c != '\0')
{
if (c != '\n' && c != '\0')
buffer[i] = c;
i++;
r = read(0, &c, 1);
}
buffer[i] = '\n';
buffer[++i] = '\0';
*line = buffer;
return (r);
}
void ft_clear_tab(char **tab)
{
size_t i;
i = 0;
while (tab[i])
{
free(tab[i]);
i++;
}
free(tab);
}
char *build_executable_path(char *cmd, char *path)
{
char *part_path;
char *exe;
part_path = ft_strjoin(path, "/");
exe = ft_strjoin(part_path, cmd);
free(part_path);
if (access(exe, F_OK | X_OK) == 0)
return (exe);
free(exe);
return (NULL);
}
将管道 FD 之一复制到 stdin 或 stdout 后,不再需要打开原始管道 FD。所以重定向的代码应该如下所示:
if (pid == 0)
{
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
exe(ev, av[0]);
}
else
{
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
waitpid(pid, NULL, 0);
}