《Apue》笔记:文件I/O

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zztingfeng/article/details/82932870

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函数可以打开或者创建一个文件;

扫描二维码关注公众号,回复: 3739116 查看本文章

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,它把文件描述符映射成指向底层物理文件的符号链接。

猜你喜欢

转载自blog.csdn.net/zztingfeng/article/details/82932870