赞
踩
流和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)添加文件名到目录
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。