fd泄漏,自定义外壳 [英] fd leak, custom Shell

查看:87
本文介绍了fd泄漏,自定义外壳的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理可处理多个管道的自定义外壳.但是每次执行新管道并使用ls -l /proc/pid/fd检查过程时,都会得到下图所示的内容,并且列表随着执行的每个新管道而不断扩展:

I'm working on a custom shell that can handle multiple pipes. But every time I execute a new pipeline and check the process with ls -l /proc/pid/fd I get something like in the picture below and the list keeps expanding with every new pipeline executed:

问题:这是否被视为FD泄漏?以及我该如何解决?

Question: Is this considered as a fd leak? And how do I fix it?

以下是我执行管道的代码段:

Here's a code snippet for my pipeline execution:

enum PIPES {READ, WRITE};

void execute_pipeline(char*** pipeline)
{
    int fd[2];
    int fd_backup = 0;
    pid_t child_pid;

    while (*pipeline != '\0')
    {
        pipe(fd);
        child_pid = fork();

        if(child_pid == -1)
        {
            perror("fork");
            exit(1);
        }
        else if(child_pid == 0)
        {
            dup2(fd_backup, 0);// (old, new)
            close(fd[READ]);

            if(*(pipeline + 1) != '\0')
            {
                dup2(fd[WRITE], 1);
            }
            execvp((*pipeline)[0], *pipeline);
            exit(1);
        }
        else// Parent process
        {
            wait(NULL);
            close(fd[WRITE]);
            fd_backup = fd[READ];
            pipeline++;
        }
    }
}

编辑

如何调用execute_pipeline的示例:

An example how to call execute_pipeline:

char *ls[] = {"ls", "-l", NULL};
char *sort[] = {"sort", "-r", NULL};
char *head[] = {"head", "-n", "3", NULL};
char **pipeline[] = {ls, sort, head, NULL};

execute_pipeline(pipeline);

推荐答案

正如塔德曼(Tadman)指出的那样,使用命令结构来传递信息会更容易.

As tadman pointed out, it is easier to use a command struct to pass things around.

我们不能[嗯,我们可以,但是不应该]在管道构造wait [在父级中] >.以后必须是一个单独的循环.创建第一个孩子后,我们将吊起父项.

We can't [well, we could but shouldn't] do a wait [in the parent] during pipe construction. That has to be a separate loop later. We would hang the parent after the first child is created.

如果第一个孩子有大量输出,则 kernel 管道缓冲区可能已满,并且第一个孩子会阻塞.但是,由于没有创建第二个子对象,因此没有任何内容可以读取/清空第一个子对象的输出并将其取消阻止.

If the first child had a large amount of output, the kernel pipe buffers might fill up and the first child would block. But, since the second child has not been created, there is nothing to read/drain the first child's output and unblock it.

此外,重要的是在执行dup2之后关闭管道单元,并确保在父级中关闭了先前的管道级单元.

Also, it is important to close the pipe units after doing dup2 and ensure the previous pipe stage units are closed in the parent.

这是所有功能的重构版本.

Here's a refactored version that does all that.

关于文件描述符泄漏的原始问题,我认为我通过添加更多的close调用来解决此问题.该程序对此具有一些自我验证代码:

As to your original issue with file descriptor leakage, I think I fixed that by adding some more close calls. The program has some self verification code on this:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/wait.h>

#define FREEME(ptr_) \
    do { \
        if (ptr_ == NULL) \
            break; \
        free(ptr_); \
        ptr_ = NULL; \
    } while (0)

#define CLOSEME(fd_) \
    do { \
        if (fd_ < 0) \
            break; \
        close(fd_); \
        fd_ = -1; \
    } while (0)

// command control
typedef struct {
    unsigned int cmd_opt;               // options
    int cmd_cldno;                      // child number

    char *cmd_buf;                      // command buffer
    int cmd_argc;                       // argument count
    char **cmd_argv;                    // arguments

    int cmd_pipe[2];                    // pipe units
    pid_t cmd_pid;                      // child pid number
    int cmd_status;                     // child status
} cmd_t;

#define CMD_FIRST       (1u << 0)
#define CMD_LAST        (1u << 1)

char linebuf[1000];
int cmdcount;
cmd_t *cmdlist;

int opt_d;
int opt_l;

#define dbg(fmt_...) \
    do { \
        if (opt_d) \
            printf(fmt_); \
    } while (0)

// show open fd's
void
fdshow1(int cldid)
{
    char buf[100];

    fprintf(stderr,"CLD: %d\n",cldid);
    sprintf(buf,"ls -l /proc/%d/fd 1>&2",getpid());
    system(buf);
}

// show open fd's
void
fdshow2(int cldid)
{
    char dir[100];
    char lnkfm[1000];
    char lnkto[1000];
    int len;
    DIR *xf;
    struct dirent *ent;
    char *bp;
    char obuf[1000];

    sprintf(dir,"/proc/%d/fd",getpid());
    xf = opendir(dir);

    bp = obuf;
    bp += sprintf(bp,"%d:",cldid);

    while (1) {
        ent = readdir(xf);
        if (ent == NULL)
            break;

        if (strcmp(ent->d_name,".") == 0)
            continue;
        if (strcmp(ent->d_name,"..") == 0)
            continue;

        sprintf(lnkfm,"%s/%s",dir,ent->d_name);
        len = readlink(lnkfm,lnkto,sizeof(lnkto));
        lnkto[len] = 0;

        if (strstr(lnkto,"pipe") != 0)
            bp += sprintf(bp," %s-->%s",ent->d_name,lnkto);

        switch (ent->d_type) {
        case DT_FIFO:
            break;
        }
    }

    bp += sprintf(bp,"\n");
    fputs(obuf,stderr);
    fflush(stderr);

    closedir(xf);
}

// show open fd's
void
fdshow(int cldid)
{

    fdshow2(cldid);
}

// pipeadd -- add single command to pipe
void
pipeadd(char *buf)
{
    char *cp;
    char *bp;
    char *sv;
    cmd_t *cmd;

    dbg("pipeadd: buf='%s'\n",buf);

    cmdlist = realloc(cmdlist,(cmdcount + 1) * sizeof(cmd_t));

    cmd = &cmdlist[cmdcount];
    memset(cmd,0,sizeof(cmd_t));
    cmd->cmd_pipe[0] = -1;
    cmd->cmd_pipe[1] = -1;

    cmd->cmd_cldno = cmdcount;
    ++cmdcount;

    bp = buf;
    while (1) {
        cp = strtok_r(bp," \t",&sv);
        bp = NULL;
        if (cp == NULL)
            break;

        cmd->cmd_argv = realloc(cmd->cmd_argv,
            (cmd->cmd_argc + 2) * sizeof(char **));

        cmd->cmd_argv[cmd->cmd_argc + 0] = cp;
        cmd->cmd_argv[cmd->cmd_argc + 1] = NULL;

        cmd->cmd_argc += 1;
    }
}

// pipesplit -- read in and split up command
void
pipesplit(void)
{
    char *cp;
    char *bp;
    char *sv;
    cmd_t *cmd;

    printf("> ");
    fflush(stdout);

    fgets(linebuf,sizeof(linebuf),stdin);

    cp = strchr(linebuf,'\n');
    if (cp != NULL)
        *cp = 0;

    bp = linebuf;
    while (1) {
        cp = strtok_r(bp,"|",&sv);
        bp = NULL;

        if (cp == NULL)
            break;

        pipeadd(cp);
    }

    cmd = &cmdlist[0];
    cmd->cmd_opt |= CMD_FIRST;

    cmd = &cmdlist[cmdcount - 1];
    cmd->cmd_opt |= CMD_LAST;

    if (opt_d) {
        for (cmd_t *cmd = cmdlist;  cmd < &cmdlist[cmdcount];  ++cmd) {
            dbg("%d:",cmd->cmd_cldno);
            for (int argc = 0;  argc < cmd->cmd_argc;  ++argc)
                dbg(" '%s'",cmd->cmd_argv[argc]);
            dbg("\n");
        }
    }
}

// pipefork -- fork elements of pipe
void
pipefork(void)
{
    cmd_t *cmd;
    int fdprev = -1;
    int fdpipe[2] = { -1, -1 };

    for (cmd = cmdlist;  cmd < &cmdlist[cmdcount];  ++cmd) {
        // both parent and child should close output side of previous pipe
        CLOSEME(fdpipe[1]);

        // create a new pipe for the output of the current child
        if (cmd->cmd_opt & CMD_LAST) {
            fdpipe[0] = -1;
            fdpipe[1] = -1;
        }
        else
            pipe(fdpipe);

        cmd->cmd_pid = fork();
        if (cmd->cmd_pid < 0) {
            printf("pipefork: fork fail -- %s\n",strerror(errno));
            exit(1);
        }

        // parent the input side for the next pipe stage
        if (cmd->cmd_pid != 0) {
            CLOSEME(fdprev);
            fdprev = fdpipe[0];
            continue;
        }

        // connect up our input to previous pipe stage's output
        if (fdprev >= 0) {
            dup2(fdprev,0);
            CLOSEME(fdprev);
        }

        // connect output side of our pipe to stdout
        if (fdpipe[1] >= 0) {
            dup2(fdpipe[1],1);
            CLOSEME(fdpipe[1]);
        }

        // child doesn't care about reading its own output
        CLOSEME(fdpipe[0]);

        if (opt_l)
            fdshow(cmd->cmd_cldno);

        // off we go ...
        execvp(cmd->cmd_argv[0],cmd->cmd_argv);
    }

    CLOSEME(fdpipe[0]);
    CLOSEME(fdpipe[1]);

    if (opt_l)
        fdshow(-1);
}

// pipewait -- wait for pipe stages to complete
void
pipewait(void)
{
    pid_t pid;
    int status;
    int donecnt = 0;

    while (donecnt < cmdcount) {
        pid = waitpid(0,&status,0);
        if (pid < 0)
            break;

        for (cmd_t *cmd = cmdlist;  cmd < &cmdlist[cmdcount];  ++cmd) {
            if (pid == cmd->cmd_pid) {
                cmd->cmd_status = status;
                ++donecnt;
                break;
            }
        }
    }
}

// pipeclean -- free all storage
void
pipeclean(void)
{

    for (cmd_t *cmd = cmdlist;  cmd < &cmdlist[cmdcount];  ++cmd)
        FREEME(cmd->cmd_argv);
    FREEME(cmdlist);
    cmdcount = 0;
}

// main -- main program
int
main(int argc,char **argv)
{
    char *cp;

    --argc;
    ++argv;

    for (;  argc > 0;  --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'd':
            opt_d = ! opt_d;
            break;

        case 'l':
            opt_l = ! opt_l;
            break;

        default:
            break;
        }
    }

    while (1) {
        pipesplit();
        pipefork();
        pipewait();
        pipeclean();
    }

    return 0;
}

这篇关于fd泄漏,自定义外壳的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆