c - 使用 winapi 在 C 语言中为 shell 实现管道

标签 c multithreading winapi process pipe

我正在用 C 语言为我正在编写的 shell 实现一个管道,该管道应该能够支持像 ls | 这样的东西查询 |查询 | grep (换句话说,它应该是递归的)。

Shell 使用 winapi 在 cygwin 上运行

我找到了两种方法: 使用进程或线程。

这是我当前的管道函数:

static void parcmd (void *arg){
    cmddata cdata = *(cmddata*) arg;
    parse_command(cdata.c, cdata.level, cdata.father, cdata.h);
}


static bool do_on_pipe(command_t *cmd1, command_t *cmd2, int level, command_t *father)
{
    bool bRes;
    SECURITY_ATTRIBUTES sa;
    HANDLE hRead, hWrite;
    DWORD dwRes1, dwRes2;
    handledata hPipeIn, hPipeOut;
    cmddata cmd1_data, cmd2_data;
    HANDLE thread1, thread2;

    ZeroMemory(&sa, sizeof(sa));
    sa.bInheritHandle = TRUE;
    bRes = CreatePipe(&hRead, &hWrite, &sa, 0);

    //printf("\n\ndo_on_pipe debug write =  %d  read = %d\n", hWrite, hRead);

    hPipeIn.pipeIn = INVALID_HANDLE_VALUE;
    hPipeIn.pipeOut = hWrite;

    cmd1_data.c = cmd1;
    cmd1_data.level = level + 1;
    cmd1_data.father = father;
    cmd1_data.h = &hPipeIn;


    thread1 = (HANDLE) _beginthreadex(NULL, 0, parcmd, &cmd1_data, 0, NULL); 
    //

    hPipeOut.pipeIn = hRead;
    hPipeOut.pipeOut = INVALID_HANDLE_VALUE;

    cmd2_data.c = cmd2;
    cmd2_data.level = level + 1;
    cmd2_data.father = father;
    cmd2_data.h = &hPipeOut;



    thread2 = (HANDLE) _beginthreadex(NULL, 0, parcmd, &cmd2_data, 0, NULL); 
    //

    //DIE(dwRes1 == WAIT_FAILED, "WaitForSingleObject - thd1");

    dwRes1 = WaitForSingleObject(thread1, INFINITE);
    dwRes2 = WaitForSingleObject(thread2, INFINITE);
    //DIE(dwRes2 == WAIT_FAILED, "WaitForSingleObject - thd2");

    CloseHandle(hWrite);
    CloseHandle(hRead); 
    return 0; 
}

我的问题是,在当前的形式中,第二个线程,即在 (cmd1 | cmd2) 中执行第二个命令的线程不会停止,基本上,我没有得到提示。

如果我在第二个线程创建之前移动dwRes1 = WaitForSingleObject(thread1, INFINITE);,我就能够成功执行类似于 (cmd1 | cmd2) 的管道命令,但是在 cmd1 | cmd1 | cmd1 上。 cmd2 | cmd3、cmd2 打印到 stdout,而 cmd3 永远不会停止。

这是简单的命令函数(它从解析器获取输入并执行它):

bool parse_simple(simple_command_t *s, int level, command_t *father, void *h)
{
    /* TODO sanity checks */


        STARTUPINFO si;
        PROCESS_INFORMATION pi;
        DWORD dwRes;
        BOOL bRes;
        HANDLE inHandle = INVALID_HANDLE_VALUE;
        HANDLE outHandle = INVALID_HANDLE_VALUE;
        HANDLE errHandle = INVALID_HANDLE_VALUE;
        SECURITY_ATTRIBUTES sa;
        handledata *hd;


        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);
        ZeroMemory(&pi, sizeof(pi));
        ZeroMemory(&sa, sizeof(sa));
        sa.bInheritHandle = TRUE;


    if (s->in != NULL) {
        inHandle = getInputHandle(get_word(s->in), sa);
        }


        if (s->out != NULL && s->err != NULL 
            && lstrcmp(get_word(s->out), get_word(s->err)) == 0) {
                outHandle = getOutErrHandle(get_word(s->out), sa, IO_REGULAR);
                errHandle = outHandle;

        } else {
        if (s->out != NULL) {
            if(s->io_flags == IO_OUT_APPEND) {

            outHandle = getOutErrHandle(get_word(s->out), sa, IO_OUT_APPEND);

            } else {

            outHandle = getOutErrHandle(get_word(s->out), sa, IO_REGULAR);

            }
        }
        if(s->err != NULL) {
            if(s->io_flags == IO_ERR_APPEND) {
            errHandle = getOutErrHandle(get_word(s->err), sa, IO_ERR_APPEND);
            } else {
            errHandle = getOutErrHandle(get_word(s->err), sa, IO_REGULAR);
            }
        }
    }
        if(h != NULL) {
        //printf("we got unnull h\n");
        fflush(stdout);
        hd = (handledata *) h;

        if (hd->pipeIn != INVALID_HANDLE_VALUE) {
            inHandle = hd->pipeIn;
        }

        if(hd->pipeOut != INVALID_HANDLE_VALUE) {
            outHandle = hd->pipeOut;
        }

        //printf("\n\nhandle debug cmd = %s write =  %d  read = %d\n",get_argv(s), hd->pipeOut, hd->pipeIn);
        fflush(stdout);

        }



        RedirectAllHandles(&si, inHandle, outHandle, errHandle);
        //printf("\n\nhandles after redirect for cmd = %s write =  %d  read = %d err = %d\n",get_argv(s), outHandle, inHandle, errHandle);
        fflush(stdout);

        if (lstrcmp(get_word(s->verb), "exit") == 0 || 
            lstrcmp(get_word(s->verb), "quit") == 0 ||
            lstrcmp(get_word(s->verb), "cd") == 0)
        {
            //internal command
            if (lstrcmp(get_word(s->verb), "exit") == 0 || 
            lstrcmp(get_word(s->verb), "quit") == 0) {
                shell_exit();
            } else if (lstrcmp(get_word(s->verb), "cd") == 0) {
            return shell_cd(s->params);
            }

        } else 
        if (strchr(get_word(s->verb), '=') != NULL) {
         // env val
        } else {

        bRes =  CreateProcess( 
        NULL,           /* No module name (use command line) */
        get_argv(s),        /* Command line */
        NULL,           /* Process handle not inheritable */
        NULL,           /* Thread handle not inheritable */
        TRUE,          /* Set handle inheritance to FALSE */
        0,              /* No creation flags */
        NULL,           /* Use parent's environment block */
        NULL,           /* Use parent's starting directory */ 
        &si,            /* Pointer to STARTUPINFO structure */
        &pi             /* Pointer to PROCESS_INFORMATION structure */
    ); 

    if (!bRes)
    {
        printf("Execution failed for '%s'\n", get_argv(s));
        return false;
    }
    //printf("cmd %s waiting for process\n", get_argv(s));
    dwRes = WaitForSingleObject(pi.hProcess, INFINITE);
    //todo add die
    //printf("cmd %s process done\n", get_argv(s));
    bRes = GetExitCodeProcess(pi.hProcess, &dwRes);
    //todo add die
    //printf("cmd %s got to handles\n", get_argv(s));
    CloseHandle(inHandle);
    CloseHandle(outHandle);
    CloseHandle(errHandle);
    //printf("cmd %s got to closed handles\n", get_argv(s));

    return dwRes;

    }

    return 0; /* TODO replace with actual exit status */
}

以及根据情况决定调用哪个函数的函数:

int parse_command(command_t *c, int level, command_t *father, void *h)
{
    /* TODO sanity checks */
    int status;
    if (c->op == OP_NONE) {
        /* TODO execute a simple command */
        return parse_simple(c->scmd, level, c, h);
        return 0; /* TODO replace with actual exit code of command */
    }

    switch (c->op) {
    case OP_SEQUENTIAL:
        /* TODO execute the commands one after the other */
            parse_command(c->cmd1, level + 1, c, h);
            return parse_command(c->cmd2, level + 1, c, h);
        break;

    case OP_PARALLEL:
        /* TODO execute the commands simultaneously */
        return do_in_parallel(c->cmd1, c->cmd2, level + 1, c);
        break;

    case OP_CONDITIONAL_NZERO:
        /* TODO execute the second command only if the first one
         * returns non zero */
        status = parse_command(c->cmd1, level + 1, c, h);
        if (!status)
            return status;
        return parse_command(c->cmd2, level + 1, c, h);

        break;

    case OP_CONDITIONAL_ZERO:
        status = parse_command(c->cmd1, level + 1, c, h);
        if (status)
            return status;
        return parse_command(c->cmd2, level + 1, c, h);
        /* TODO execute the second command only if the first one
         * returns zero */
        break;

    case OP_PIPE:
            do_on_pipe(c->cmd1, c->cmd2,level, c);
        /* TODO redirect the output of the first command to the
         * input of the second */
        break;

    default:
        return SHELL_EXIT;
    }

    return 0; /* TODO replace with actual exit code of command */
}

我知道这是一个很大的代码库,不太可能得到任何回应,但我已经连续处理这个问题两到三天了,我根本看不到解决方案,我怎样才能实现像这样的功能管道( cmd1 | cmd2 ... | cmdN) 使用此代码?谢谢!

最佳答案

创建函数

Execute(c, stdin, stdout, stderr, wait)

如果 wait 为 true,则执行 c 并等待其完成,否则执行 c 并返回一个可以等待完成的句柄

在伪代码中,实现类似于:

if c is an external command:
    launch process
    if (wait)
        wait for it to complete
    else
        return process handle

In all other cases:

if (!wait) create thread and return thread handle

Then, on the new thread (if created) or the existing thread:

if c is an internal command:
    Call the internal code for the command

if c is `a | b`:
    Create pipe
    waita = Execute(a, stdin, pipein, stderr, false)
    Execute(b, pipeout, stdout, stderr, true)
    Wait on waita

if c is `a & b`:
    Execute(a, stdin, stdout, stderr, true)
    Execute(b, stdin, stdout, stderr, true)

请注意,使用此设计,您需要在尝试执行任何操作之前完成解析。

关于c - 使用 winapi 在 C 语言中为 shell 实现管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15727704/

相关文章:

javascript - 从 Node-JS 将焦点设置到 Windows 应用程序

c++ - 如何检查 HANDLE 是否有效?

python - 在python中停止线程

.net - 需要非常快速的线程安全收集或内存数据库

c++ - Nodejs C/C++ 会使用多个Core吗?

c++ - 任务计划程序 : How to schedule a task to execute at login

c - 在多个数组中执行多重搜索的最佳方法

我们可以在一台 PC 上构建 linux 内核并将其安装在另一台 PC 上吗?

第一次循环迭代后将字符串更改为 NULL。内存分配

C++ VisualStudio 获取文件版本信息