写在最前面:其实这是一个上课作业,我觉得我不能就这么简单的实现了而不给后人留下点什么糟粕。所以下面有兴趣的同学就跟着我误入歧途吧。我没想按着APUE这本书一章一章来,我认为既然是我自己读完了这本书并要实现东西,就需要有我自己的节奏,这种节奏不仅仅让我自己,也要让各位看客感到轻松,并偶有所获~我就十分开心了。

Preface

这学期报了一门课叫《Unix环境高级编程》,本来打算水水过去的,没想到老师需要我们每个人完成一份大作业,作业内容就是艰深复杂的各种调用底层API实现的shell。

怎么说呢?从严格意义上,操作系统可以定义为一种软件,它控制计算机硬件资源,提供程序运行环境,我们将这种软件成为内核(也就是kernel),内核的接口被称为系统调用,公用函数库构建在系统调用接口之上,应用程序既可以使用公用函数库,也可以使用系统调用。shell是一个特殊的应用程序,为运行其他程序提供了一个接口。

我们要实现的shell就是这么一个东西。

这么大段的定义还不如说【oh-my-zsh ,zsh ,bash】 来的实在,以上三种就是我们常见的shell,我现在最喜欢的就是 oh-my-zsh 了,其优点太多了,tabtab可以自动提示参数,配色好看几百倍等等等等。

废话不多说,要开车了哦。

准备工作

首先

  1. 一台Linux系统的计算机(比较方便,在windows下需要配置很多的环境,因为windows没有Unix的很多库函数,我使用的是 Ubuntu 16.04)

  2. 下载apue源码 传送门

  3. 解压源码文件 tar -zxvf *.tar.gz

  4. cd apue.3e/ & make
    在这个过程中,你会看到最后由于can,t find-lbsd而不能make成功,解决办法是添加libbsd.a的静态链接库
    sudo apt-get install libbsd-dev

5. 重新 make 然后再

     sudocp  ./include/apue.h   /usr/include/
     sudocp  ./lib/libapue.a   /usr/local/lib/
     

有时候以上步骤完成后就可以随便找书上的例子开始装逼了,但是我的电脑不行,因为里面有很多err_sys之类的报错函数未定义,那么还需要再执行一步。

复制下面的代码,并保存成 /usr/include/myerr.h
以后你写的例子里需要用到err_sys之类的函数,你需要 #include "myerr.h"

#include "apue.h"
#include <errno.h> /* for definition of errno */
#include <stdarg.h> /* ISO C variable aruments */
static void err_doit(int, int, const char *, va_list);
/*
* Nonfatal error related to a system call.
* Print a message and return.
*/
void
err_ret(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
}
/*
* Fatal error related to a system call.
* Print a message and terminate.
*/
void
err_sys(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    exit(1);
}
/*
* Fatal error unrelated to a system call.
* Error code passed as explict parameter.
* Print a message and terminate.
*/
void
err_exit(int error, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, error, fmt, ap);
    va_end(ap);
    exit(1);
}
/*
* Fatal error related to a system call.
* Print a message, dump core, and terminate.
*/
void
err_dump(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    abort(); /* dump core and terminate */
    exit(1); /* shouldn't get here */
}
/*
* Nonfatal error unrelated to a system call.
* Print a message and return.
*/
void
err_msg(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
}
/*
* Fatal error unrelated to a system call.
* Print a message and terminate.
*/
void
err_quit(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
    exit(1);
}
/*
* Print a message and return to caller.
* Caller specifies "errnoflag".
*/
static void
err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
    char buf[MAXLINE];
   vsnprintf(buf, MAXLINE, fmt, ap);
   if (errnoflag)
       snprintf(buf+strlen(buf), MAXLINE-strlen(buf), ": %s",
         strerror(error));
   strcat(buf, " ");
   fflush(stdout); /* in case stdout and stderr are the same */
   fputs(buf, stderr);
   fflush(NULL); /* flushes all stdio output streams */
}

尾声

既然是第一章,当然我们轻松点,那么就来一个最最开始的1-7程序吧,这个程序搭建了最基本的功能,也就是输入,连命令也不是去解析调API而是直接用原始bash的命令~

//
// Created by jasperyang on 17-6-6.
//
#include "apue.h"
#include <sys/wait.h>
#include "myerr.h"

int main(void) {
    char buf[MAXLINE];  /* from apue.h */
    pid_t pid;
    int status;

    printf("%% ");  /* print prompt (printf requires %% to print %) */
    while(fgets(buf,MAXLINE,stdin) != NULL) {
        if(buf[strlen(buf) -1] == '\n'){
            buf[strlen(buf)-1]=0;   /* replace newline with null */
        }
        if((pid = fork()) < 0) {
            err_sys("fork error");
        } else if (pid == 0){   /* child */
            execlp(buf,buf,(char*)0);
            err_ret("couldn't execute: %s",buf);
            exit(127);
        }

        /* parent */
        if((pid = waitpid(pid,&status,0)) < 0)
            err_sys("waitpid error");
        printf("%% ");
    }
    exit(0);
}

简单易懂老少皆宜,这段程序输入命令,并fork一个child去执行命令,执行命令用的是execlp这个函数,执行失败与否子程序都exit(127),127倒没什么特殊的含义。


jasperyang
203 声望58 粉丝

Highest purpose is Hacking...