Linux基础IO之文件操作符

(。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~icon-default.png?t=N7T8https://blog.csdn.net/ky233?type=blog

点个关注不迷路⌯'▾'⌯

目录

一、文件操作符

1.什么是文件操作符

2.FILE类型 

3.fd的分配规则

二、重定向

1.重定向的原理

2.使用dup2的系统调用

3.如何理解Linux下的一切皆文件

三、缓冲区

1.什么是缓冲区

2.缓冲区的刷新策略

 3.用户级缓冲区


一、文件操作符

1.什么是文件操作符

在我们调用系统接口的时候一般会这样写

int fd = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);

我们在这里可以看到fd的返回值是int,说明是一个整数,其中如果程序错误就会返回-1,打开一个文件是从3开始的,那么0、1、2呢?

  • 答案是Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2。
  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器

所以fd其实就是文件操作符!

2.FILE类型 

我们知道一个进程是可以打开多个文件的,如果多个进程都打开自己的文件呢?那么要怎么管理呢?所以就需要先描述在组织了!

所以FILE是一个结构体,里面包含着一个被打开文件的几乎所有的内容!我们每打开一个文件就会创建一个这样的结构体,其中缓冲区也在这里面

那么我们要怎么找呢?这就需要fd了!

文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来 描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进 程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数 组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件!

3.fd的分配规则

int main()
{
 int fd = open("myfile", O_RDONLY);
 if(fd < 0){
 perror("open");
 return 1;
 }
 printf("fd: %d\n", fd);
 close(fd);
 return 0;
}

首先默认是fd是3,那么我们关掉0呢?

int main()
{
 close(0);
 int fd = open("myfile", O_RDONLY);
 if(fd < 0){
 perror("open");
 return 1;
 }
 printf("fd: %d\n", fd);
 close(fd);
 return 0;
}

发现是结果是:fd: 0,可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的 最小的一个下标,作为新的文件描述符。

二、重定向

1.重定向的原理

int main()
{
 close(1);
 int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
 if(fd < 0){
 perror("open");
 return 1;
 }
 printf("fd: %d\n", fd);
 fflush(stdout);
 
 //close(fd);
 exit(0);
}

可是如果我们先关闭1呢?这个时候在把最后的close屏蔽,那么我们就会发现,打印的内容居然到了myfile里面!

这是因为1对应的是标准输出,但是我们把标准输出关掉了,那么,根据fd的规则,上面代码的fd就是1了,本来1是对应的标准输出,结果现在对应的是myfile里面!

这种现象叫做输出重定向!所以重定向的本质就是:在OS内部更改fd对应的内容指向!

2.使用dup2的系统调用

int dup2(int oldfd, int newfd);

这个函数的作用是把oldfd拷贝给newfd

所以是把3拷贝给1。

那么我们就可这样完成一个重定向

 int mian(int argc,char* argv[])
    9 {
   10     if(argc != 2)
   11     {
   12         return 2;
   13     }
   14     int fd = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC);
   15     if(fd<0)
   16     {
   17         perror("open");
   18         return 1;
   19     }
   20     dup2(fd,1);
   21     fprintf(stdout,"%s\n",argv[1]);
   22 }

3.如何理解Linux下的一切皆文件

  • 虽然底层不同的硬件,对应的是不同的操作方法
  • 但是每一个设备的核心访问函数,都可以是read、write
  • 所有的设备都有自己的read、write,但是代码的实现是不一样的

所以在OS看来一切皆是文件!

三、缓冲区

1.什么是缓冲区

缓冲区就是一个buffer数组,是为了提高我们整体的效率而存在

2.缓冲区的刷新策略

1.立刻刷新

2.行刷新

如printf的刷新位行刷新,遇到\n才会刷新,如磁盘文件

3.满刷新

缓冲区被写满的时候才会刷新,即便有\n也不会刷新

4.特殊情况

  • 用户强制刷新fflush
  • 进程退出

但是所有的设备都倾向于满缓冲因为这样可以减少IO的次数,可以减少访问外设的次数

 3.用户级缓冲区

首先我们来看这段代码

int main()
{
    fprintf(stdout, "hello world\n");
    char* s = "hello Linux\n";
    write(1, s, strlen(s));

    fork();
    return 0;
}

如果我们正常运行程序,只会打印hello world和hello Linux这两个字符串。

但是如果我们进行重定向那么就会发现hello world被打印了两份!!

这是因为,我们在显示器上打印是行刷新,当执行到fork的时候已经打印完成了,缓冲区内已经没有东西了,但是如果我们在磁盘上打印就会变成全缓冲,这个时候fork,缓冲区内还有东西,这个时候就发生了写时拷贝!在缓冲区内的数据就变成了父子进程各有一份,那么在进程结束后,父子进程就直接各自刷新了自己的缓冲区,而write则不进入用户级缓冲区,所以则打印一份!

我们可以在前面加上fflush就可以避免这样了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值