0

这是我为自己的 shell 找到的代码。它工作正常,但我无法理解的是代码的管道部分。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

char* cmndtkn[256];
char buffer[256];
char* path=NULL;
char pwd[128];


int main(){

//setting path variable 
    char *env;
    env=getenv("PATH"); 
    putenv(env);
    system("clear");


printf("\t MY OWN SHELL !!!!!!!!!!\n ");
printf("_______________________________________\n\n");

while(1){

    fflush(stdin);
    getcwd(pwd,128);
    printf("[MOSH~%s]$",pwd);
    fgets(buffer,sizeof(buffer),stdin);
    buffer[sizeof(buffer)-1] = '\0';

    //tokenize the input command line   
    char* tkn = strtok(buffer," \t\n");
    int i=0;
    int indictr=0;



        // loop for every part of the command
        while(tkn!=NULL)
        {

            if(strcoll(tkn,"exit")==0 ){
                exit(0);                
            }

            else if(strcoll(buffer,"cd")==0){
            path = buffer;
            chdir(path+=3);
            }

            else if(strcoll(tkn,"|")==0){
            indictr=i;
            }

            cmndtkn[i++] = tkn;
            tkn = strtok(NULL," \t\n");
        }cmndtkn[i]='\0';

// execute when command has pipe. when | command is found indictr is greater than 0.
    if(indictr>0){

        char* leftcmnd[indictr+1];
        char* rightcmnd[i-indictr];
        int a,b;

        for(b=0;b<indictr;b++)
            leftcmnd[b]=cmndtkn[b];
        leftcmnd[indictr]=NULL;

        for(a=0;a<i-indictr-1;a++)
            rightcmnd[a]=cmndtkn[a+indictr+1];
        rightcmnd[i-indictr]=NULL;

        if(!fork())
        {   
            fflush(stdout);
            int pfds[2];
            pipe(pfds);

                if(!fork()){
                    close(1);
                    dup(pfds[1]);
                    close(pfds[0]);
                    execvp(leftcmnd[0],leftcmnd);
                }   
                else{
                    close(0);
                    dup(pfds[0]);
                    close(pfds[1]);
                    execvp(rightcmnd[0],rightcmnd);
                }
        }else
            wait(NULL);

//command not include pipe 

    }else{
        if(!fork()){
        fflush(stdout);
        execvp(cmndtkn[0],cmndtkn);

        }else
            wait(NULL);
    }

}

}

使用参数 0 和 1 调用 close() 的目的是什么?调用 dup() 有什么作用?

4

2 回答 2

0

在 Unix 上,dup()调用使用编号最小的未使用文件描述符。所以,close(1)调用之前dup()是强制dup()使用文件描述符1。同样for close(0).

所以,别名是让进程使用管道的写端for stdout(文件描述符1用于控制台输出),管道的读端用于stdin(文件描述符0用于控制台输入)。

代码可能已经更清楚地表达了dup2()

dup2(fd[1], 1); /* alias fd[1] to 1 */

从您关于如何ls | sort工作的问题来看,您的问题不仅限于进行dup()系统调用的原因。您的问题实际上是 Unix 中的管道如何工作,以及 shell 命令管道如何工作。

Unix 中的管道是一对文件描述符,它们在可写描述符上写入数据允许从可读描述符中读取数据。该pipe()调用在一个数组中返回该对,其中第一个数组元素是可读的,第二个数组元素是可写的。

在 Unix 中,afork()后跟某种exec()是生成新进程的唯一方法(还有其他库调用,例如创建进程的system()or popen(),但它们在后台调用fork()并执行 an exec())。Afork()产生一个子进程。子进程看到0调用的返回值,而父进程看到一个非零返回值,它要么是子进程的 PID,要么是-1表示发生错误的 a。

子进程是父进程的副本。这意味着当子修改变量时,它正在修改驻留在自己进程中的变量的副本。父级看不到修改发生,因为父级拥有原始副本)。但是,可以使用形成管道的重复文件描述符对来允许子进程与其父进程相互通信。

因此,ls | sort意味着产生了两个进程,并且由 写入的输出 ls被读取为输入sort。两个进程意味着两次调用fork()以创建两个子进程。一个子进程将exec()执行ls命令,另一个子进程将exec()执行sort命令。它们之间使用管道以允许进程相互通信。ls进程写入管道的可写端,进程从管道的sort可读端读取。

在发出 之后,该ls过程被强制写入管道的可写端。该过程被强制通过调用 after读取管道的可读端。dup()close(1)sortdup()close(0)

此外,close()关闭管道文件描述符的调用用于确保ls进程是唯一拥有对可写 fd 的开放引用的进程,该sort进程是唯一拥有对可读 fd 的开放引用的进程。这一步很重要,因为ls退出后,它将关闭 fd 的可写端,并且该sort进程将期望看到一个 EOF 作为结果。但是,如果其他进程仍然打开可写 fd,则不会发生这种情况。

于 2013-05-14T05:18:43.080 回答
0

http://en.wikipedia.org/wiki/Standard_streams#Standard_input_.28stdin.29

stdin 是文件描述符 0。

stdout 是文件描述符 1。

在 !fork 部分中,该进程关闭 stdout 然后在 pfds[1] 上调用 dup ,根据:

http://linux.die.net/man/2/dup

在最低可用位置创建指定文件描述符的副本,该位置将为 1,因为它刚刚关闭(并且标准输入尚未关闭)。这意味着发送到标准输出的所有内容都将真正进入 pfds[1]。

所以,基本上,它设置了两个新的进程来相互交谈。!fork 部分用于将数据发送到标准输出(文件描述符 1)的新子节点,父节点(else 块)关闭标准输入,因此当它尝试从标准输出读取时,它实际上是从 pfds[0] 读取的。

每个进程都必须关闭它不使用的 pfds 中的文件描述符,因为现在该进程已经分叉了文件有两个打开的句柄。每个进程现在都执行到左/右命令,但新的标准输入和标准输出映射仍然用于新进程。

这里解释了两次分叉:为什么 fork() 两次

于 2013-05-14T05:21:32.357 回答