理解struct file内核对象
一个structfile里面有内容+属性
如何理解文件读写:
进程文件描述符表的第三步骤时才会对文件进行读写操作.
本质:对文件数据的读写====将内核中缓冲区的数据进行来回拷贝
fd的分配规则
返回值:实际返回读到的文件字符的长度
第一个参数:文件fd(文件描述符)
第二个参数:将读到的内容放入buf字符串
第三个参数:要读取的文件内容中的字符个数
向0号文件(stdin—键盘)读取---->read函数会以读的方式打开这个文件,
然后,等待键盘输入(这是stdin的特性)
打开0号文件,从0号文件向buf内读入1024个字节(以回车符结束)
1号文件的写入
打开1号文件,向1号文件写入内容为buf,长度为strlen(buf)
结论1
当关闭1号文件时,那么打印信息不会输出,打开显示器的文件被关闭了
继续实验
关闭stdout文件,新文件创建文件描述符为stdout的文件描述符1
同时输出重定向,打印信息给了log.txt
结论2
结论3
新建文件的fd为1
printf()函数向文件名描述符为1的文件打印,只认1号文件,上层用户来说并不关心这个1是什么,所以只要修改1号位对应的文件,就能重定向
原先是显示器,现在是log.txt文件
所以完成重定向.
只需要修改文件描述符表中数组的下标所指向的文件即可
用fprintf()函数来验证(他可以指向文件进行输入)
按特定格式向指定文件输入:(不关闭1号文件演示)
现在关闭1号文件,再看fprintf()指向stdout输出的操作:
可以看到结果在log.txt’文件中,并不会在显示器输出
追加重定向只要open追加即可实现
重定向让读取数据从文件读取
上层:不变
下层:将文件描述符从0指向的stdin改为指定的文件
文件打开时的文件重定向为0
然后根据c语言的fread()函数进行内容写入,到stdin.
stdin的文件描述符0指向的是一个文件,所以写入实现
会写入到这个文件
补充
必选项(三种方式之间是互斥的)
O_RDONLY:只读打开--------rdonly
O_WRONLY:只写打开-------wrondly
O_RDWR:可读可写打开
可选项
O_APPEND:表示追加,加附到文件末尾,不覆盖原数据
O_CREAT:若此文件不存在则创建它,需提供第三个参数mode:表示该文件的访问权限
文件权限由open的mode参数和当前进程的umask掩码共同决定: mode&~umask
O_EXCL:如果同时指定了O_CREAT,并且文件已经存在,则出错返回
O_TRUNC:如果文件已经存在,则将其长度截断(Truncate)为0字节
O_NINBLOCK:设置文件为非阻塞状态
重点:
从stream中读取数据到buffer
读取的大小为nmeb,每次读取的字节大小为size
这边是:从stdin读取buffer个数据大小长度的数据,存到buffer中
对于C语言函数来说,确实是从stdin中读取,但是系统底层
是从stdini->fd:0文件中读取,现在这个0文件是log.txt
所以执行代码,会从这个log.txt文件读取
这个操作就是输入重定向
原理都是上层都不变
下层fd执行的文件进行改变
dup2的使用
以输出重定向为例:
dup2会保留oldfd
eg: dup2(fd,1);将fd和1 都指向fd所指的文件
原先1指向的文件OS会进行关闭
重定向的实现
写入重定向
读取重定向
输入重定向本质:
command < input.txt
将文件的内容作为输入传递给命令
命令将从名为"input.txt"的文件中读取输入数据,而不是从键盘读取。
完善shell程序的重定向功能
往期手写shell
重定向检查
//1.1检查重定向
checkRedir(usercommand,strlen(usercommand));
//2.分割字符串
函数实现:
#define SkipSpace(pos) do{while(isspace(*pos)) pos++;}while(0)
void checkRedir(char * command,int len)
{
char* end = command + len - 1;
char* begin = command;
while(end > begin)
{
if(*(end) =='>')
{
if(*(end-1)=='>')
{
*(end-1)='\0';
filename = end+1;
SkipSpace(filename);
redir = AppendRedir;
break;
}
else{
*(end)='\0';
filename = end+1;
SkipSpace(filename);
redir = OutputRedir;
break;
}
}
else if(*(end)=='<')
{
*(end) = '\0';
filename = end+1;
SkipSpace(filename);
redir = InputRedir;
break;
}
else{
end--;
}
}
}
在进行分割字符串之前就对这个字符串进行是否是重定向的检查
声明两个变量redir和filename
在cheakRedir函数中判断< >> >的个数
将判断的情况放在redir中,同时还会将这个用户输入的命令进行前后分割
即:会在文件和指令中间加上\0
然后,文件指令分给filename
指令继续是指令
后续工作交给执行指令的函数
检查完之后在执行指令的时候对重定向的文件进行test
int fd = 0;
if(redir==InputRedir)
{
fd = open(filename,O_RDONLY);
if(fd<0)
{
perror("open");
return 1;
}
dup2(fd,0);
}
else if(redir==OutputRedir)
{
fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC, 0666);
if(fd<0)
{
perror("open");
return 1;
}
dup2(fd,1);
}
else if(redir==AppendRedir)
{
fd = open(filename,O_WRONLY | O_CREAT | O_APPEND, 0666);
if(fd<0)
{
perror("open");
return 1;
}
dup2(fd,1);
}
else{
//....
}
execvp(argv[0],argv);
exit(1);
}
在execute中,执行调用指令时,普通指令不进if条件
含有< >> > 的指令特殊处理
其中就包括存好的filename和不同的文件操作
然后在每次循环结束后,将这个redir和filename
进行初始化
filename = NULL;
redir = 0;
char usercommand[NUM];
char *argv[SIZE];
//int argc = 0;
//1.印提示符&&获取用户命令字符串
探究stderr
#include<stdio.h>
int main()
{
fprintf(stdout,"output stdout\n");
fprintf(stderr,"output stderr\n");
return 0;
}
stdout 和 stderr 都能向屏幕打印数据
区别:
输出重定向时:会改变1号文件的文件描述符指向的文件
欢迎三连支持,有问题可评论或者私信~