Linux:关于FILE那些事

流和FILE对象
对于标准I/O库,它们的操作是围绕流进行的,当用标准I/O库打开或者创建一个文件时,我们已使一个流与一个文件相关联。流的定向决定了所读,写的字符是单字节还是多字节(宽)。freopen函数清除一个流的定向;fwide函数可用于设置流的定向。
对于每个ANSI C程序,运行时系统必须提供至少三个流——标准输入(stdin),标准输出(stdout),标准错误流(stderr),它饿们都是一个指向FILE结构的指针。

#include<stdio.h>
#include<wchar.h>
int fwide(FILE *fp,int mode);

若流是宽定向返回正值,若流是字节定向返回负值,若流是未定向的返回0

fd与FILE结构体

同样,linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0,标准输出1,标准错误2。
默认的标准输入指的是键盘,默认的标准输出与标准错误输出指的是屏幕或者终端。
这里写图片描述
当我们打开文件时,操作系统在内存中要创建相应的数据结构,于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来,每一个进程都有一个指针*files,指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每个元素都是一个指向打开文件的指针!所以本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。
分配规则:

[a@localhost ~]$ vim fd.c
[a@localhost ~]$ cat fd.c
#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<string.h>
int main()
{
  int fd = open("myfile",O_RDONLY);
  if(fd<0){
     perror("open");
     return 1;
    }
    printf("fd:%d\n",fd);
    close(fd);
    return 0;
}
[a@localhost ~]$ gcc fd.c
[a@localhost ~]$ ./a.out
fd:3

稍作改动:关闭0

[a@localhost ~]$ vim fd.c
[a@localhost ~]$ cat fd.c
#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<string.h>
int main()
{
  close(0);
 //close(2);
  int fd = open("myfile",O_RDONLY);
  if(fd<0){
     perror("open");
     return 1;
    }
    printf("fd:%d\n",fd);
    close(fd);
    return 0;
}
[a@localhost ~]$ gcc fd.c
[a@localhost ~]$ ./a.out
fd:0

由此可见,文件描述符的分配规则:在file_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

重定向
本质:
这里写图片描述

凡是往1号文件描述符写的内容都写到了myfile中,而不在写到标准输出中。
输入重定向:输入不从键盘读入,而是从文件输入或其它。
“<”表示输入重定向运算符 “<<”当前标准输入来自命令行的一对分隔号的中间内容。

eg:

[root@localhost a]# wc</etc/inittab
 26 149 884
 wc统计行数,单词数,字符数
[root@localhost a]# wc<<aa

> 1
> 11
> 111
> 1111
> aa
 4  4 14

输出重定向:不输出到终端,而是输出到文件中去或其它。
“>”表示输出重定向运算符,如果文件不存在会自动建立一个文件,如果是第二次输出会覆盖前一次输入内容
“>>”表示把第二次的输出内容追加到文件中去,而不是覆盖。

[root@localhost a]# ps -ef>a.txt
[root@localhost a]# ps -ef>>a.txt//把进程信息追加到a.txt的文件中去

典例分析:

(1)a.out > outfile 2 > &1

首先把a.out重定向到outfile文件中去。然后再把2 > &1标准错误输出重定向到标准输出中去,此时标准输出已经重定向了,相当于把标准输出与标准错误输出一起重定向到outfile中去。

(2)a.out2 > &1 > outfile

首先把标准错误输出重定向到标准输出中,此时标准输出还在终端,。然后标准输出到outfile中去,而标准错误还在终端。

缓冲
标准I/O库提供缓冲的目的是尽可能减少使用read和write调用次数。
(1)全缓冲:在填满标准I/O缓冲区后才进行的实际I/O操作。
flash:在标准I/O方面,flush意味着将缓冲区中的内容写到磁盘上。在终端驱动程序方面,flush丢弃已存储在缓冲区中的数据。
任何时候,我们都可强制冲洗一个流。

#include<stdio.h>
int fflush(FILE *fp);

(2)行缓冲:当在输入和输出中遇到换行符,标准I/O库执行I/O操作。
这允许我们一次输出一个字符,但只有在写了一行之后才进行实际I/O操作。
(3)不带缓冲:标准I/O库不对字符进行缓冲存储。(标准错误流不带缓冲,以便错误信息尽快显示出来)。

相关调用接口
(1)open
调用open函数可以打开或者创建一个文件。

int open(const char *path,int flags);
int open(const char *path,int flags,mode_t mde);

path:要打开或创建的目标文件
flags:打开文件的时候,可以传入多个参数选项,用下面的一个或多个常量进行“或”运算,构成flags。
参数:
O_RDONLY:只读打开
O_WRONLY:只写打开
O_RDWR:读,写打开
以上三个常量,必须指定一个且只能指定一个。
O_CREAT:若文件不存在,则创建它
O_APPEND:追加写
返回值:
成功:新打开的文件描述符
失败:-1
(2)write
调用write函数向打开文件写数据。

#include<unistd.h>
size_t write(int fildes,const void *buf,size_t nbytes);

参数:
(1)fd:文件描述符
(2)buf:指定写入数据的数据缓冲区
(3)nbytes:指定写入的字节数
返回值:
若成功,返回已写的字节数
若出错,返回-1
若未写入任何数据,返回0
示例:

[a@localhost ~]$ cat write.c
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<fcntl.h>
int main()
{
        int fd = open("./file.txt",O_WRONLY|O_CREAT,0600);
        assert(fd != -1);
        printf("fd = %d\n",fd);
        write(fd,"xjhzjx",5);//写入
        close(fd);
}
[a@localhost ~]$ gcc write.c
[a@localhost ~]$ ./a.out
fd = 3

(3)read
调用read函数从打开文件中读数据

#include<unistd.h>
size_t read(int fd,void *buf,size_t nbytes);

参数:
(1)fd:文件描述符
(2)buf:指定读入数据的数据缓冲区
(3)nbytes:指定读入的字节数
返回值:
若成功,返回已读的字节数
若出错,返回-1
若未读入任何数据,返回0

[a@localhost ~]$ cat read.c
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<fcntl.h>

int main()
{
        int fd = open("file.txt",O_RDONLY);
    assert(fd != -1);
    printf("fd = %d\n",fd);
    char buff[128] = {0};
    read(fd,buff,127);
    printf("read:%s\n",buff);
    close(fd);
}
[a@localhost ~]$ gcc read.c
[a@localhost ~]$ ./a.out
fd = 3
read:xjhzjx
[a@localhost ~]$ 

(4)close
可用close函数关闭一个打开的文件

#include<unistd.h>
int close(int fd);

close并没有做什么实质工作,它没有刷新任何内核缓冲区,而仅仅是使文件描述符可以重用。

文件系统
使用ls -l的时候看到的除了文件名外,还看到了文件元数据。

[a@localhost ~]$ ls -l
total 140
-rw-r--r--. 1 root root   160 Dec  8 16:41 \
-rw-r--r--. 1 root root   304 Nov 27 05:57 01.c

每行包含:模式,硬链接数,文件所有者,组,大小,最后修改时间,文件名

读取信息过程图:
这里写图片描述
除此之外,还可以通过stat命令看到更多信息

[root@localhost a]# stat 01.c
  File: `01.c'
  Size: 304         Blocks: 8          IO Block: 4096   regular file
Device: 802h/2050d  Inode: 269872      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2017-11-27 05:57:07.140031048 -0500
Modify: 2017-11-27 05:57:07.140031048 -0500
Change: 2017-11-27 05:57:07.169030849 -0500

为清楚上述inode所示信息,先简单了解文件系统
这里写图片描述

超级块:存放文件系统本身的结构信息。
i节点表:存放文件属性如文件大小,所有者,最近修改时间等
数据区:存放文件内容

属性和数据分开存放如上图看起来很简单,实际如何工作?

[root@localhost a]# touch abc 
[root@localhost a]# ls -i abc
270124 abc

这里写图片描述

创建一个新文件主要有以下4个操作:
(1)存储属性
(2)存储数据
(3)记录分配情况
(4)添加文件名到目录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值