1、不带缓冲的I/O
每个读写都调用内核中的一个系统调用。这些不带缓冲的I/O函数不是ISO C的组成部分。
2、文件描述符
1)对于内核而言,所有打开的文件都通过文件描述符引用;
2)为非负整数;
3)当打开或者传建一个新文件时,内核向进程返回一个文件描述符;
4)POSIX应用程序,0、1、2应替换成STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,头文件包含<unistd.h>。
3、open函数
1)调用open函数可以打开或者创建一个文件;
2)#include<fcntl.h>
Int open(const char *pathname,int flag,…../*mode_t mode*/);
3) pathname :打开或者创建的文件名字;
flag:函数的选项(O_RDONLY 只读 0 /O_WRONLY 只写 1 /O_RDWR读写 2)
4、creat函数
1)可用来创建一个新文件
2)#include<fcntl.h>
Int creat(const char *pathname,mode_t mode);
该函数等效于
Open(pathname,O_WRONLY | O_CREAT | O_TRUNC,mode);
5、close函数
1)用close函数关闭一个打开的文件:
2)#include<unistd.h>
Int close(int filedes);
3)关闭一个文件时会释放该进程加在该文件上的所有记录锁;
6、lseek函数
1)文件的读写操作都从文件的偏移处开始,并从偏移量增加所读写的字节数,系统默认偏移量为0;
2)#include<unistd.h>
Off_t lseek(int filedes,off_t offset,int whence);
whence为SEEK_SET,则该文件偏移量设置为距文件开始处offset个字节;
whence为SEEK_CUR,则该文件偏移量设置为当前值加offset,offset可为正或者为负;
whence为SEEK_END,则该文件偏移量设置为文件长度加offset,offset可为正或者为负;
7、read函数
1)从打开文件中读数据;
2)#include <unistd.h>
ssize_t read(int fileds,void *buf,size_t nbytes);
read成功,返回读到的字节数。如果已经达到文件结尾,则返回0。
实际读到的字节数少于要求读的字节数的集中情况:
A、读普通文件时,在读到要求字节数之前已经达到文件尾端;
B、从终端设备读,通常一次最多读一行;
C、从网络中读,缓冲机制的存在可能造成返回值小于所要求读的字节数;
D、从管道或者FIFO读时,管道字节数小于所需数量时;
E、从面向记录的设备读时,一次最多返回一个记录。
8、write函数
1)向打开的文件写数据;
2)#include <unistd.h>
ssize_t write(int fileds,void *buf,size_t nbytes);
9、文件共享
内核使用三种数据结构表示打开的文件。
1)每个进程在进程表中都有一个记录项,记录项中包含一张打开的文件描述符表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:
A、文件描述符标志;如图1
B、指向一个文件表项的指针。
2)内核为每个打开文件维持一张文件表。每个文件表项包含:
A、文件状态标志(读、写、添加、同步、阻塞等);
B、当前文件偏移量;
C、指向该文件V节点表项的指针。
3)每个打开文件(或者设备)都有一个V节点结构(包含了文件类型和对此文件进行各种操作的指针)注:linux没有使用V节点,使用的是i节点,但概念类似。
图1 打开文件的数据结构
如果两个独立进程各自打开了同一文件,每个进程都有自己的文件表项,使得每个进程都有它自己的对该文件的当前偏移量。
图2 两个独立进程各自打开同一文件
总结:文件描述符标志和文件状态标志在作用域方面的区别,前者只用于一个进程的一个描述符,后者适用于指向该给定文件表项的任何进程中的所有描述符。
10、原子操作
1)添加至一个文件
任何一个需要多个函数调用的操作都不可能是一个原子操作,因为在两个函数调用之间,内核可能会临时挂起该操作。unix操作系统提供了一种方式实现原子操作,该方法是在打开文件时设置O_APPEND标志,使得内核在对这种文件进行写之前,都将进程的当前偏移位置设置到该文件的尾端,于是在每次写之前就不需要调用lseek函数。
2)pread函数与pwrite函数
#include<unistd.h>
ssize_t pread (int field,void *buf,size_t nbytes,off_t offset);返回值:读到的字节数,如果已经读到文件结尾则返回0,否则返回-1
ssize_t pwrite (int field,void *buf,size_t nbytes,off_t offset);返回值:成功则返回已写的字节数,出错则返回-1
3)创建一个文件
总结:原子操作指的是有多步组成的操作。如果该操作原子的执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。
11、dup和dup2
int dup(int fd);
int dup2(int fd, int fd2);
返回值:成功,返回新的文件描述符;出错,返回-1
12、sync、fsync、fdatasync
#include <unistd.h>
int fsync(int fd);
int fdatasync(int fd);
返回值:成功,返回0;出错,返回-1
void sync(void);
功能:将内核中高速缓存写入磁盘。
sync:将修改过的块缓冲区排入写队列就返回,不等待写完成。
fsync、fdatasync:等待往磁盘上写完成;针对特定文件描述符;fdatasync只影响文件的数据部分。
13、fcntl
#include <fcntl.h>
int fcntl(int fd, int cmd, .../*arg*/);
返回值:成功,依赖于cmd;错误,返回-1
功能:改变已打开文件的属性
14、ioctl
终端I/O是使用ioctl最多的地方。
15、./dev/fd
目录项是名为0,1,2,255的文件,打开文件/dev/fd/n等效于复制描述符n。
linux中的/dev/fd,它把文件描述符映射成指向底层物理文件的符号链接。