说明:
百度得shell定义 在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器),相似的还有Windows下的cmd。
正文:
shell主体框架如下:主要完成对命令的解析以及调用相应的操作下的可执行文件
命令解析器:
1 运行方式:利用while死循环支撑解析器的常驻直至用户主动exit退出
2 运行过程:
1) 用户运行解析器后的提示符 主要包括三个方面
a: 当前用户名:使用getpwuid()函数获取(作用根据用户uid获取用户passwd数据并存放在struct passwd 中获取用户uid使用getuid()函数
passwd结构体(声明在头文件sys/types.h中)含有以下数据struct passwd {
char *pw_name; /*user name */
char *pw_passwd; /*user password */
uid_t pw_uid; /*user id */
gid_t pw_gid; /*group id */
char *pw_gecos; /*user real name */
char *pw_dir; /*home directory */
char *pw_shell; /*shell program */
};
我们需要使用的为pw_name参数
b:一个字符的隔断 @
c:本地主机名称的缩写:头文件<sys/utsname.h>在 struct utsname结构体中 使用uname()函数获取
struct utsname
{ char sysname[_UTSNAME_SYSNAME_LENGTH];//当前操作系统名
char nodename[_UTSNAME_NODENAME_LENGTH];//网络上的名称
char release[_UTSNAME_RELEASE_LENGTH];//当前发布级别
char version[_UTSNAME_VERSION_LENGTH];//当前发布版本
char machine[_UTSNAME_MACHINE_LENGTH];//当前硬件体系类型
#if _UTSNAME_DOMAIN_LENGTH - 0
/* Name of the domain of this node on the network. */
# ifdef __USE_GNU
char domainname[_UTSNAME_DOMAIN_LENGTH]; //当前域名(感觉这一条有 很大用处,未使用过)
# else
char __domainname[_UTSNAME_DOMAIN_LENGTH];
# endif
#endif
};
d:当前目录 getcwd()函数获取当前工作位置的绝对路径 再通过/字符来截断路径以获得当前路径即可
e:一个字符隔断(一般root用户为# 其他用户为$)
2)对于输入命令的解析:主要是将命令分解开进行识别
此处可用到strtok()函数利用输入命令时加的空格将各指令分开以实现分割 strtok函数源码另记
3)利用系统函数fork()创建子进程,使用exec族函数完成子进程的替换(即将子进程的执行文件替换成其他的可执行文件 实现指令的转换)注意:当子进程创建失败时即未找到相应指令或用户指令输入错误应结束子进程(exit) 否则会形成僵死进程 累积后对系统造成负担 在父进程中也要实现前台执行时子进程运行完毕才能继续进行下一步的操作 故父进程调用wait函数
整体简单框架实现如下
int main(int argc, char *argv[])
{
PrintfFlag()//输出提示符
{
内含上述提示符需要使用的结构体及函数
}
while(1)
{
1 完成对指令的分解(strtok()函数)
2进程替换(fork()函数以及exec族函数) 父进程使用wait函数 子进程注意添加exit防止exec调用失败从而形成僵死进程
}
}
简单框架完成后需要考虑的一点是 是否所有的指令都由子进程完成 父进程需要完成的指令有哪些?
事实上指令集中有两个需要在父进程中完成 1 cd指令 因为如果将此指令放在子进程中进行 子进程中调用相关可执行文件退出后 父进程还是位于原来的位置未更改 所以 应该将cd指令放于父进程实现 cd指令及其他各指令见其他记录过程个人笔记中
2 exit指令 即退出shell 这个好理解为什么要放在父进程中执行