相信接触过linux过,听过的人都知道linux的源代码是公开的,那么我们是不是可以看看我们平时最常用的命令是怎么实现的吧!看其实没那重要,重要的是你要裂解,然后根据自己的理解自己实现,然后和源代码做对比,学习借鉴,取长补短,那应该是一种比较好的学习方式了吧!
那我今天就来把我自己的想法写在这里!
在写命令之前,我们都知道,命令必须在命令解释器下,才能完美的运行起来,脱离这个平台,那么它不过就是一堆字母吧!
第一步(很重要的一步):命令解释器的实现,简单来说呢,就是是怎么把命令调起来然后实现的,也许你一会第一反应exec,exec是完全替换自己的代码,把你要调的完全复制过来,成功可是不反悔,失败就返回。对他是我们命令解释器少不了的函数。
不妨将这个函数列举一下:
1. execl(const char *pathnaem,const char *arg,....(char *)0); 以参数形式列出
举例:当前文件下的main 想调起来,使用execl("./main","./main",(char *)0);
2. execv(const char *pathname,char *const argv[]);
举例:当前文件下的main 想调起来,使用
char *argv[] = {"./main","./main",NULL};
execv("./main",argv);
3.execve(const char * pathname, char *const argv[], char *const envp[]);
4.execvp(const char *filename,char *const argv[]); 将系统的环境变量给保护起来。
简单就是这几个,还有很多,还待查。
剩下的就是简单的函数使用了。
看看代码,具体函数具体解释:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<signal.h>
#include<pwd.h>
#define MAXSIZE 10
#define MYPATH "/home/yu/Desktop/mbash/mybin/"
void printfl()
{
int id = getuid();
char *s = "$";
if(id == 0)
{
s = "#";
}
struct passwd *p = getpwuid(id);
char hostname[128] ={0};
gethostname(hostname,128);
char *m = strtok(hostname,".");
char buff[256] = {0};
char *ps = "/";
getcwd(buff,128);
char *ql = strtok(buff,"/");
while(ql!= NULL)
{
ps = ql ;
ql = strtok(NULL,"/");
}
printf("[**%s@ %s %s]%s ",p->pw_name,m,ps,s);
fflush(stdout);
}
int main()
{
while(1)
{
//printf("[yu@localhost %s]$ ");
//fflush(stdout);
printfl();
char buff[128] = {0};
fgets(buff,128,stdin);
buff[strlen(buff)-1] = 0;
char *myargv[MAXSIZE] = {0};
int i = 1;
char*s = strtok(buff," ");
if(s == NULL)
{
continue;
}
myargv[0] = s;
while((s = strtok(NULL," ")) != NULL)
{
myargv[i++] = s;
}
if(strcmp(myargv[0], "cd") == 0)
{
if(chdir(myargv[1]) == -1)
{
printf("mybash is error!\n");
}
continue;
}
if(strcmp(myargv[0],"exit") == 0)
{
exit(0);
}
pid_t pid = fork();
if(pid == 0)
{
//execlp(buff,buff,(char *)0);
//execvp(myargv[0],myargv);
char path[512] = {0};
if(strncmp(myargv[0],"/",1) != 0 && strncmp(myargv[0],"./",2)!=0)
{ //以绝对路径或者相对路径执行可执行文件。
strcpy(path,MYPATH);
}
strcat(path,myargv[0]);
execv(path,myargv);
printf("mybashis error:\n");
exit(0);子进程结束,那也就是exec失败,那么就会退出子进程。
}
wait(NULL);
}
return 0;
}
1.getuid() 获取当前用户的uid 如果结果 == 0 那么是root,否则是普通用用户。
2.struct passwd *p = getpwuid(id); 根据当前用户的uid 获取当前用户的名字,名字存在p这个结构体中,pw_name 当前用户的名字。
3.gethostname(hostname,128); 获取当前主机名,
4.strtok(hostname,".");分割字符串,遇到.分割,分割后就将那个地方置换成\0,下次要想再次置换那就从NULL开始,,再次分割。
5.getcwd(buff,128); 获取当前的路径,根据路径可以将终端提示里面的[yu@local. 当前路径]。
这就是简单的命令解释器的实现,那么我们具体可以根据这些函数,对这个命令解释器的实现有一个很好理解。