简单的外壳(shell)
2012-02-23 22:23 捣乱小子 阅读(3961) 评论(7) 编辑 收藏 举报写在前面的
很多人在csdn上分享了自己的面试经验,其中笔试值得一提。公司笔试的题目有些比较基础但是又是比较核心的概念。就比如,什么是线程,什么是多线程,什么是进程,什么是多进程,线程和进程有什么区别!!
这题目一上眼,脑子中一晃就一个印象:看过很多次了,但是就是说不明白他是什么东西了。
线程是进程中的一个单一控制流;一个进程可能完成很大的任务,多线程即一个进程中有多个线程,每个线程完成不同的工作,这样就提高了程序运行的速度。进程是运行中的程序,它是与线程的区别是进程有独立的代码,数据和存储空间,但是线程可以共享数据空间,而每个线程有独立的执行堆栈和程序计数器。
概念是很清楚的,非得理解并记住不可,不然以后应聘跟头有的栽。
废话不多说了。
外壳简介
外壳是什么,在刚上大学的时候就有听到这个形象的
在linux或者unix,可能很多人都接触过ubuntu或者red hat之类的有良好图形界面的linux,在字符界面下ls,cd,或者grep等等来完成想要完成的操作,这些都是一个个程词汇,说形像,可能它不可能简单到就是一个蜗牛贝壳的外壳吧…
序,而接受输入的命令(命令后可能会有参数列表)就是外壳,但是外壳也不是很神秘。
为了方便与操作系统交互,外壳诞生了。外壳是一个交互型的应用级程序,所以外壳也是一个程序,它隐藏了很多操作系统底层的细节操作,它代表用户运行了其他的程序。
有了“外壳”的称谓,那外壳里面也应该有名副其实的东西,不可能是个空壳吧。没错,在外壳之下就是kernel(内核),这才是操作系统实实在
在的东西,与底层硬件交互着。
(插入外壳解释图)
linux下是这样,win下也有类似的东西,此时它的外壳就是我们熟悉的dos界面啦,cmd.exe。把重点放在linux上,win上的东西也是大同小异的。
linux下的fork和execve
在linux下的c编程,fork,execve函数很常见的。fork函数一式两份复制了父进程也就是当前进程,创建了子进程,新建的子进程与父进程几乎完全一样,唯一的不同是他们的标识进程id,即PID。
(插入fork的描述图)
execve函数加载并运行可执行目标文件,注意它是在当前进程的上下文当中加载并欲行一个新的进程,当前进程的地址空间会被新建的进程覆盖,与fork不同,它并没有创建新的进程,新加载和运行的进程与此前的进程有着相同的进程id。
(插入execve的描述图)
linux中采用了这些函数,集成了父子进程这样一种关系,一个简单的外壳就是这样的,为什么说是简单的外壳?因为里面还涉及了很多复杂的设计,但是核心的东西是这些而已。下面是简单的外壳,参照了《深入理解计算机系统》。
简单外壳设计
环境:linux,vim编辑器,gcc编译器。
除了main函数,还有其他的三个函数,eval函数调用praseline函数来解释命令行参数,并会将结果传递给execve来执行。第一个参数可以是内置的命令或者可执行目标文件。如果是前者,那么就直接解释执行,为了简化外壳,就只内置了“q”命令,退出;如果是后者,那么调用fork创建子进程,execve在该子进程的上下文中加载并执行这个文件。
来测试一下,方便测试,在这个外壳所属目录下放了一个简单的hello可执行目标文件:
测试1:
简单的外壳还可以添加Tab键补全命令或者目录的功能。最大的缺陷就是父进程不对子进程进行回收,在unix中。当子进程结束的时候,其所属的代码数据空间都仍然存留在内存当中,等待父进程对其进行回收。
下面是代码:
#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 可以看到相关的信息
如果是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