代码改变世界

简单的外壳(shell)

2012-02-23 22:23  捣乱小子  阅读(3961)  评论(7编辑  收藏  举报

写在前面的

很多人在csdn上分享了自己的面试经验,其中笔试值得一提。公司笔试的题目有些比较基础但是又是比较核心的概念。就比如,什么是线程,什么是多线程,什么是进程,什么是多进程,线程和进程有什么区别!!

这题目一上眼,脑子中一晃就一个印象:看过很多次了,但是就是说不明白他是什么东西了。

线程是进程中的一个单一控制流;一个进程可能完成很大的任务,多线程即一个进程中有多个线程,每个线程完成不同的工作,这样就提高了程序运行的速度。进程是运行中的程序,它是与线程的区别是进程有独立的代码,数据和存储空间,但是线程可以共享数据空间,而每个线程有独立的执行堆栈和程序计数器。

概念是很清楚的,非得理解并记住不可,不然以后应聘跟头有的栽。

废话不多说了。

外壳简介

1212979936272_000

外壳是什么,在刚上大学的时候就有听到这个形象的

在linux或者unix,可能很多人都接触过ubuntu或者red hat之类的有良好图形界面的linux,在字符界面下ls,cd,或者grep等等来完成想要完成的操作,这些都是一个个程词汇,说形像,可能它不可能简单到就是一个蜗牛贝壳的外壳吧… 

序,而接受输入的命令(命令后可能会有参数列表)就是外壳,但是外壳也不是很神秘。

 为了方便与操作系统交互,外壳诞生了。外壳是一个交互型的应用级程序,所以外壳也是一个程序,它隐藏了很多操作系统底层的细节操作,它代表用户运行了其他的程序。

有了“外壳”的称谓,那外壳里面也应该有名副其实的东西,不可能是个空壳吧。没错,在外壳之下就是kernel(内核),这才是操作系统实实在

在的东西,与底层硬件交互着。 

(插入外壳解释图) 

 images (1)

 images

linux下是这样,win下也有类似的东西,此时它的外壳就是我们熟悉的dos界面啦,cmd.exe。把重点放在linux上,win上的东西也是大同小异的。

linux下的fork和execve

在linux下的c编程,fork,execve函数很常见的。fork函数一式两份复制了父进程也就是当前进程,创建了子进程,新建的子进程与父进程几乎完全一样,唯一的不同是他们的标识进程id,即PID。
(插入fork的描述图)image

execve函数加载并运行可执行目标文件,注意它是在当前进程的上下文当中加载并欲行一个新的进程,当前进程的地址空间会被新建的进程覆盖,与fork不同,它并没有创建新的进程,新加载和运行的进程与此前的进程有着相同的进程id。
(插入execve的描述图)image

   
linux中采用了这些函数,集成了父子进程这样一种关系,一个简单的外壳就是这样的,为什么说是简单的外壳?因为里面还涉及了很多复杂的设计,但是核心的东西是这些而已。下面是简单的外壳,参照了《深入理解计算机系统》。
 

简单外壳设计

环境:linux,vim编辑器,gcc编译器。
除了main函数,还有其他的三个函数,eval函数调用praseline函数来解释命令行参数,并会将结果传递给execve来执行。第一个参数可以是内置的命令或者可执行目标文件。如果是前者,那么就直接解释执行,为了简化外壳,就只内置了“q”命令,退出;如果是后者,那么调用fork创建子进程,execve在该子进程的上下文中加载并执行这个文件。

来测试一下,方便测试,在这个外壳所属目录下放了一个简单的hello可执行目标文件:
测试1:

1
测试2:

2
测试3:

3

简单的外壳还可以添加Tab键补全命令或者目录的功能。最大的缺陷就是父进程不对子进程进行回收,在unix中。当子进程结束的时候,其所属的代码数据空间都仍然存留在内存当中,等待父进程对其进行回收。

下面是代码:

View Code
#include <stdlib.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <string.h> 

#define MAXLINE 128 
#define MAXARGVS 6 

void eval(char * cmdline) 

    char * argv[MAXARGVS]; 
    char buf[MAXLINE]; 
    int bg; 
    pid_t pid; 

    strcpy(buf,cmdline); 
    bg = parseline(buf,argv); 

    if(argv[0]==NULL) 
        return

    if(!builtin_command(argv)) 
    { 
        if((pid=fork()==0)) 
        { 
            if(execve(argv[0],argv,0)<0
            { 
                printf("%s:command not found .\n",argv[0]); 
                exit(0); 
            } 
        } 

        if(!bg) 
        { 
            int status;        //status for waitpid 
            if(waitpid(pid,&status,0)<0
                printf("waitpid error"); 
        } 
        else 
            printf("%d %s",pid,cmdline); 
    } 
    return


int parseline(char * buf,char ** argv) 

    char * delimiter; 
    int argc; //argv counter; 
    int bg; 

    buf[strlen(buf)-1] = ' '
    while(*buf && (*buf==' '))    //check the cmd 
        buf++; 

    argc = 0
    while((delimiter = strchr(buf,' '))) 
    { 
        argv[argc++] = buf; 
        *delimiter = '\0'
        buf = delimiter + 1
        while(*buf && (*buf==' '))    //ignore blanks 
            buf++; 
    } 
    argv[argc] = NULL; 

    if(argc==0
        return 1

    if((bg=(*argv[argc-1]=='&')) != 0
        argv[--argc] = NULL; 

    return bg; 



int builtin_command(char ** argv) 

    if(!strcmp(argv[0],"q")) 
        exit(0); 
    else if(!strcmp(argv[0],"&")) 
        return 1
    return 0


int main() 

    char cmdline[MAXLINE]; 
    while(1
    { 
        printf("shell> :"); 
        fgets(cmdline,MAXLINE,stdin); 

        if(feof(stdin)) 
            exit(0); 

        eval(cmdline); 
    } 
    return 1
}

 

题外话

题外话:如何在linux中挂载U盘,方便虚拟机和PC机之间的数据传输?
首先linux要对u盘等外设做出必要的中断或者反应
fdisk -l 可以看到相关的信息
21
如果是fat16的u盘就使用下面的命令,/mnt/usb可能不存在,可以创建。
mount -t msdos /dev/sdc1 /mnt/usb
如果是fat32的盘
mount -t vfat /dev/sdc1 /mnt/usb
如果是ext2格式,就用命令:
mount -t ext2 /dev/sdc1 /mnt/usb

如果无出错提示,那么挂载成功,可以对u盘内容进行管理(cp,rm等)。

 

 

本文完。

捣乱小子 2012年2月23日

原文链接:http://www.cnblogs.com/daoluanxiaozi/archive/2012/02/23/2365513.html