Linux内核编程实验二

来源:互联网 发布:金蝶在线软件 编辑:程序博客网 时间:2024/05/21 10:08

1、实验名称:shell命令解释系统设计实验
2、实验要求:
问题 A:
实现一个能处理前后台运行命令的 shell。
问题 B:
实现一个带有管道功能的 shell。
问题 C:
实现一个能处理 I/O 重定向的 shell。
问题 D:
实现一个能在一行上处理多条命令的 shell。
将问题 A-D 集中到一个 shell 解析程序中。

3、解决思路
第一步解决问题D。一行上多条命令使用 ; 分开,;两边可以有空格,也可以没有,或者都考虑(健壮性),本方案采用没空格。根据 ; 将其拆分成多条命令,逐条执行即可。

第二步解决问题A。让进程在后台运行,意味着父进程不需要等待子进程运行完毕。否则使用waitpid()函数让父进程等待一下即可。

第三步解决输出重定向,即问题C。根据定向符 > 获得后面的文件名。使用open()打开,使用dup2()函数将标准输出重定向到该文件。那么该进程的输出都将添加到文件中,而不是终端。通常在子进程内进行相应操作。

第四步解决输出重定向,问题C。根据输入定向符 < 获得后面文件名。使用open打开。使用dup2将标准输入重定向到该文件,那么该进程将使用该文件作为输入,而不是终端。

第五步解决管道问题。此处使用匿名管道。匿名管道使用pipe(fds)创建。fds为int类型长度为2的数组。fds[0]为读取数据端。fds[1]为写入数据端。创建两个进程一个管道。进程1关闭读端,将标准输出重定向到管道写端,将数据暂时保存在管道内。进程2关闭写端,将标准输入重定向到管道读端,将管道内数据读出,完成进程通信。

不足之处,仅供参考:

缺点一是支持格式有限。比如
普通命令:ls -a /bin //加粗部分必须为全路径
输出重定向:ls /bin > /home/me/shiyan/test.c //>两侧必须空格
输入重定向:cat < /home/me/shiyan/test.c
管道:cat /home/me/shiyan/test.c | sort //管道两端命令不支持出现重定向
//管道符号两侧必须有空格
后台运行:ls /bin > /home/me/shiyan/test.c& //&符号必须紧跟着
多条命令:ls /home;ls /bin //多条命令分号两边无空格
所有测试命令必须严格遵循上面的格式,否则将可能导致错误的运行结果。

另外一个缺点是,没有完整测试所有linux命令,只测试了部分简单常用命令,因此可能部分命令不支持。


4、实现代码

#include <stdio.h>#include <string.h> //strlen()#include <unistd.h> //fork()#include <sys/types.h> //pid_t#include <sys/wait.h> //waitpid()#include <stdlib.h> //exit()#include <fcntl.h> //open()#define MAXLEN 80#define MAXPARA 8int checkpipe(char *command,int comlen)//判断是否包含|{    int i;    for(i=0; i<comlen; i++)    {        if(command[i] == '|')            return i;    }    return -1;}int checkout(char *command,int comlen)//判断是否包含>{    int i;    for(i=0; i<comlen; i++)    {        if(command[i] == '>')            return 1;    }    return 0;}int checkin(char *command,int comlen) //判断是否包含<{    int i;    for(i=0; i<comlen; i++)    {        if(command[i] == '<')            return 1;    }    return 0;}void runcommand(char *command){    int comlen = strlen(command);    int backrun = 0;    char *argv1[MAXPARA],*argv2[MAXPARA];    int index1,index2;    int i,fd,pipei,fds[2];    char *filename;    pid_t p1,p2;    if(command[comlen-1] == '&')//是否后台运行    {        backrun = 1;        command[comlen-1] = 0;        comlen--;    }    pipei = checkpipe(command,comlen);     if(pipei > -1)//有管道    {        command[pipei-1] = 0;        command[pipei] = 0;        command[pipei+1] = 0;        index1 = 0;        argv1[index1++] = command;        for(i=0; i<pipei-1; i++)        {            if(command[i] == ' ')            {                command[i] = 0;                argv1[index1++] = &command[i+1];            }        }        argv1[index1] = NULL;        i = pipei + 2;        index2 = 0;        argv2[index2++] = &command[i];        for(; i<comlen; i++)        {            if(command[i] == ' ')            {                command[i] = 0;                argv2[index2++] = &command[i+1];            }        }        argv2[index2] = NULL;        //命令处理完毕,建立管道,进程        pipe(fds);        p1 = fork();        if(p1 == 0)        {            close(fds[0]);//关闭读端            close(STDOUT_FILENO);            dup2(fds[1],STDOUT_FILENO);//重定向            close(fds[1]);            execvp(argv1[0],argv1);//该函数执行后进程结束        }        else        {            if(backrun == 0)                waitpid(p1,NULL,0);            p2 = fork();            if(p2 == 0)            {                close(fds[1]);                close(STDIN_FILENO);                dup2(fds[0],STDIN_FILENO);                close(fds[0]);                execvp(argv2[0],argv2);            }            else            {                //父进程关闭管道,让两个子进程进行通信                close(fds[0]);                close(fds[1]);                waitpid(p2,NULL,0);            }        }    }    else    {        if(checkout(command,comlen) == 1)//输出重定向        {            index1 = 0;            argv1[index1++] = command;            for(i=0; i<comlen; i++)            {                if(command[i] == ' ')                {                    command[i] = 0;                    if(command[i+1] == '>')                        break;                    argv1[index1++] = &command[i+1];                }            }            argv1[index1] = NULL;            i = i + 3;            filename = &command[i];            p1 = fork();            if(p1 == 0)            {                fd = open(filename,O_WRONLY);                close(STDOUT_FILENO);                dup2(fd,STDOUT_FILENO);                close(fd);                execvp(argv1[0],argv1);            }            else            {                if(backrun == 0)                {                    waitpid(p1,NULL,0);                }            }        }        else if(checkin(command,comlen) == 1)//输入重定向        {            index1 = 0;            argv1[index1++] = command;            for(i=0; i<comlen; i++)            {                if(command[i] == ' ')                {                    command[i] = 0;                    if(command[i+1] == '<')                        break;                    argv1[index1++] = &command[i+1];                }            }            argv1[index1] = NULL;            i = i + 3;            filename = &command[i];            p1 = fork();            if(p1 == 0)            {                fd = open(filename,O_RDONLY);                close(STDIN_FILENO);                dup2(fd,STDIN_FILENO);                close(fd);                execvp(argv1[0],argv1);            }            else            {                if(backrun == 0)                {                    waitpid(p1,NULL,0);                }            }        }        else//普通命令        {            index1 = 0;            argv1[index1++] = command;            for(i=0; i<comlen; i++)             {                if(command[i] == ' ')                {                    command[i] = 0;                    argv1[index1++] = &command[i+1];                }            }            argv1[index1] = NULL;            p1 = fork();            if(p1 == 0)            {                execvp(argv1[0],argv1);            }            else            {                if(backrun == 0)                {                    waitpid(p1,NULL,0);                }            }        }    }}int main(){    char linebuf[MAXLEN], *command;    int buflen,i;    while(1)    {        write(STDOUT_FILENO,"$",1);        fgets(linebuf,MAXLEN,stdin);        buflen = strlen(linebuf);        if(linebuf[buflen-1] == '\n')        {            linebuf[buflen-1] = 0;            buflen--;        }        if(strcmp(linebuf,"exit") == 0)            exit(0);        else        {            command = linebuf;            for(i=0; i<buflen; i++)            {                if(linebuf[i] == ';')                {                    linebuf[i] = 0;                    runcommand(command); //循环执行多条命令                    command = &linebuf[i+1];                }            }            runcommand(command); //多条命令最后一条        }    }    return 0;}

5、程序的健壮性优化请参阅:
http://www.powerxing.com/unix-simple-shell-with-argv/

0 0