1.内建命令的实现
先前基础版中没有对cd这些内建命令的设置,这些内建命令是在父进程中实行的,说白了就是在父进程中调用对应的OS接口
//移动到path路径
int chdir(const char*path);
if(argv[0] == "cd")
{
if(argc == 2)
{
chdir(argv[1]);
//lastcode是错误码
lastcode = 0;
}
else
{
//应该要分类讨论的
lastcode = 1;
}
return true;
补充一点知识:
(1)char* getcwd(char*buf,int size);返回值就是buf的地址,buf存当前路径,size是buf的大小后最大存储空间
(2)putenv("xx=xxx");xx已存在时就覆盖原有的环境变量,没有就创建一个新的,=号是一定要存在的。
(3)有些指令即是内置指令,也是外带指令,也就是一个指令即可能只是shell中的一个函数,也可能在/usr/bin/目录下留有可执行文件。
2.echo
(1)$? ,打印lastcode,其他情况打印对应环境变量名的值
(2)直接打印
if(gargv[0] =="echo")
{
if(gargc == 2)
{
if(gargv[1][0] == '$')
{
if(gargv[1][1] == '?')
{
printf("%d\n", lastcode);
lastcode = 0;
}
else
{
//此处还要处理$环境变量名的情况
}
}
else
{
printf("%s\n", gargv[1]);
lastcode = 0;
}
}
else
{
lastcode = 3;
}
3.生成环境变量表
此处我们简化处理使用父进程的环境变量表即可(只是模拟,并没有实际使用)
cpp
void InitEnv()
{
extern char **environ;
int index = 0;
while(environ[index])
{
genv[index] = (char*)malloc(strlen(environ[index])+1);
strncpy(genv[index], environ[index], strlen(environ[index])+1);
index++;
}
genv[index] = nullptr;
}
2.基础IO(才是核心)
1.复习c语言与一些之前的知识
操作文件就是操作其的内容与属性。文件由进程打开,因此对文件的操作就是进程对文件的操作。
磁盘的管理者只有OS,我们调用库函数的本质就是间接使用OS提供的接口。
同一时间下被打开的文件可能有很多个,因此OS是采用先描述再组织的方式管理当前所有被打开的文件。
文件分两种:
内存级(已经被打开)文件2.磁盘(未被打开)级文件,后面先介绍内存级文件
函数:
fwrite(const void *ptr,size_t size,size_t nmemb,FILE*stream);
//将ptr中size*nmemmb大小的数据存入stream中
feof(FILE*stream);
//判断stream指向的文件是否走到结尾
size_t fread(const void *ptr,size_t size,size_t nmemb,FILE*stream);
返回值是返回nmemb的值
w,a,r,的本质:
w:文件会先被清空
//如: > 重定向就是将右边的文件用类似于w的形式打开
a:往先前的文件数据中追加
//如: >>重定向就是将右边的文件用类似于a的形式打开
r+:以既能写有能读的方式打开文件,但读写共用同一个光标
3.系统的IO调用
int open(const char* pathname,int flags);
int open(const char* pathname,int flags,mode_t mode);
flags是一种标识符,例:
采用&来区分是否使用了这个标识符,使用|来同时使用多种标识符。
//二进制位0001
int a = 1;
//二进制位0010
int b = 2;
//二进制位0100
int c = 4;
//....
void func(int flags)
{
if(flags&(1 << 0))cout << "1号标识符";
if(flags&(1 << 1))cout << "2号标识符";
if(flags&(1 << 2))cout << "3号标识符";
//.....
}
int main()
{
func(a | b | c);
return 0
}
当filename是一个新文件时,要给其加上初始化的权限设置(会被权限掩码(umask)操作),也就是插入mode参数(例:0666值)来初始化文件权限。
标识符类型:
O_CREAT 打开文件
O_WRONLY 往文件中写入
O_TRONC 清空文件
O_APPEND 追加内容
O_RDONLY 以读的方式打开文件(二进制形式)
int close(int fd);
fd就是open的返回值,用于关闭对应的文件
ssize_t write(int fd,const void* buf,size_t count);
把buf前count个字符写入fd对应的文件中,返回值为成功输入的字符数
使用strlen统计buf大小时不用+1,即不用写入/0,因为文件写入时不认识c语言的转义字符。
buf的类型为void*,说明其可以传入任意类型的数据,根本上来讲就是OS其实不管心我们传进来的数据类型,所有的数据本身都是以二进制的方式写入的,而显示屏又叫字符显示屏,因此传入的数据中只有才能正常显示,其余的都是乱码。
因此语言层面上的文本写入或二进制写入的本质都是调用write的封装罢了,实现文本写入的方式为:
int func(int a)
{
char buf[16];
//提前写成字符的形式了
sprintf(buf,siseof(buf),a);
write(fd,buf,strlen(buf));
}
read
ssize_t read(int fd,void * buf,size_t count);
将fd对应文件的内容写到buf中,最大写入大小为count,返回值>0时表示写入的字符个数,=0表示已经读取到文件末尾,<0代表读取失败。
read和write调用的对应文件,必须要有对应对应的标识符支持