1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 文件I/O(基础知识 打开文件 创建文件 关闭文件描述符 文件偏移 读 写 文件共享

文件I/O(基础知识 打开文件 创建文件 关闭文件描述符 文件偏移 读 写 文件共享

时间:2019-07-17 02:42:09

相关推荐

文件I/O(基础知识 打开文件 创建文件 关闭文件描述符 文件偏移 读 写 文件共享

(0)基础知识

1.文件的读写操作都是从当前文件的偏移处开始的。

2.文件偏移量保存在文件表中。

3.每个进程都有一个文件表。

4.同步IO与异步IO

在同步文件IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。

在异步文件IO中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了。

5.链接: 硬链接, 符号链接

硬链接:

1.默认情况下,ln产生硬链接。

2.建立硬链接时,链接文件和被链接文件必须位于同一个文件系统中。

3.不能建立指向目录的硬链接。

符号链接:

1.ln命令加上- s选项,则建立符号链接。

2.可以链接任意一个文件或者目录

3.可以在不同的文件系统中链接

(1)打开文件

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

·pathname :表示要打开的文件相对路径。

·mode :只在创建文件时需要,用于指定所创建文件的权限位

·flags :用于指示打开文件的选项,常用的有 O_RDONLY 、 O_WRONLY 和 O_RDWR 。

注意: O_RDWR ! =O_RDONLY|O_WRONLY // O_RDONLY 被定义为 0 , O_WRONLY 被定义为 1 ,而 O_RDWR 却被定义为 2 。

1.open 是 glibc 的一个变参函数。

·O_APPEND :每次进行写操作时,内核都会先定位到文件尾,再执行写操作。

·O_ASYNC :使用异步 I/O 模式。

·O_CLOEXEC :在打开文件的时候,就为文件描述符设置 FD_CLOEXEC 标志。

// 这里设置为FD_CLOEXEC表示当程序执行exec函数时本fd将被系统自动关闭,表示不传递给exec创建的新进程, 如果设置为fcntl(fd, F_SETFD, 0);

那么本fd将保持打开状态复制到exec创建的新进程中。

·O_CREAT :当文件不存在时,就创建文件。

·O_DIRECT :对该文件进行直接 I/O ,不使用 VFS Cache 。

·O_DIRECTORY :要求打开的路径必须是目录。

·O_EXCL :该标志用于确保是此次调用创建的文件,需要与 O_CREAT 同时使用;当文件已经存在时, open 函数会返回失败。

·O_LARGEFILE :表明文件为大文件。

·O_NOATIME :读取文件时,不更新文件最后的访问时间。

·O_NONBLOCK 、 O_NDELAY :将该文件描述符设置为非阻塞的(默认都是阻塞的)。

·O_SYNC :设置为 I/O 同步模式,每次进行写操作时都会将数据同步到磁盘,然后 write 才能返回。

·O_TRUNC :在打开文件的时候,将文件长度截断为 0 ,需要与 O_RDWR 或 O_WRONLY 同时使用。

int openat(int dirfd, const char *pathname, int flags);

int openat(int dirfd, const char *pathname, int flags, mode_t mode);

1.如果pathname是绝对路径,则dirfd参数没用。

2.如果pathname是相对路径,并且dirfd的值不是AT_FDCWD,则pathname的参照物是相对于dirfd指向的目录,而不是进程的当前工作目录;

3.如果pathname是相对路径,并且dirfd的值是AT_FDCWD,pathname则是相对于进程当前工作目录的相对路径,此时等同于open。

(2)创建文件

int creat(const char *pathname, mode_t mode);

1.若成功返回为只写打开的文件描述符; 若出错,返回-1

2.等效于open(path, O_WRONLY | O_CREATE | O_TRUNC, mode);

(3)关闭文件描述符

int close(int fd);

1.close 用于关闭文件描述符。而文件描述符可以是普通文件,也可以是设备,还可以是 socket 。

2.遗忘 close 造成的问题

· 文件描述符始终没有被释放。

· 用于文件管理的某些内存结构没有被释放。

·对于普通进程来说,即使应用忘记了关闭文件,当进程退出时, Linux 内核也会自动关闭文件,释放内存(详细过程见后文)。

·但是对于一个常驻进程来说,问题就变得严重了。

3.lsof查看打开了,但是尚未关闭的文件

(4)文件偏移

off_t lseek(int fd, off_t offset, int whence);

0.如果fd是指向一个管道,FIFO或者网络套接字, lseek返回-1,置errno为ESPIPE

1.该函数用于将 fd 的文件偏移量设置为以 whence 为起点,偏移为 offset 的位置。

2. whence 可以为三个值: SEEK_SET 、 SEEK_CUR 和 SEEK_END ,分别表示为 “ 文件的起始位置 ” 、

“ 文件的当前位置 ” 和 “ 文件的末尾 ” ,而 offset 的取值正负均可。

3. lseek 执行成功后,会返回新的文件偏移量。出错返回-1。

4.确定打开文件的当前偏移量

off_t curpos;

curpos = lseek(fd, 0, SEEK_CUR);

5.文件偏移量可以大于文件的当前长度, 在这种情况下, 对该文件的下一次写将加长该文件。并在文件中

构建一个空洞,这一点是允许的。位于文件中没有学过的直接都被设为0

6.lseek函数只修改文件表项中的当前文件偏移量, 不进行任何IO操作

7.文件空洞不占用磁盘空间

(5)读

ssize_t read(int fd, void *buf, size_t count);

1.返回读取到的字节数。 若已经到文件尾部,再次调用read时返回0; 若出错返回-1。

2.有很多情况可使实际读到的字节数少于要求读的字节数:

1.文件中的当前偏移位置到文件结尾 < 要读取的字节数, 返回实际读取的字节数

2.文件当前偏移位置已经在文件尾部, 返回0。(不阻塞)

3.从网络读时, 网络中的缓冲机制可能造成返回值小于所要求读的字节数

4.读管道或者FIFO时。 如果包含的字节数 < 要读取的字节数, 那么read将只返回实际可用的字节数。

5.某些面向记录的设备(如磁带)一次最多返回一个记录。

6.读取过程中被信号中断时, 返回实际读取的字节数

(6)写

ssize_t write(int fd, const void *buf, size_t count);

1.write 尝试从 buf 指向的地址,写入 count 个字节到文件描述符 fd 中,并返回成功写入的字节数,同时

将文件偏移向前移动相同的字节数。 write 有可能写入比指定 count 少的字节数。

2.当多个进程同时写一个文件时,即使对 write 进行了锁保护,在进行串行写操作时,文件依然不可避免地会被写乱。根本原因就在于文件偏移量是进程级别的。

(7)文件共享

1.文件指针指向文件表项

2.v节点指针指向v节点表项

3.v_data指向i节点。

1.每个进程在进程表中都有一个打开文件描述符表

2.内核为所有打开的文件维持一种文件表

3.每个打开的文件(设备)都有一个v节点

4.unix环境编程60页图

注意:

1.可能有多个文件描述符指向同一文件表项

1.dup

2.fork后, 父进程打开的所有"文件描述符"都被复制到子进程中,父子进程每个相同的打开描述符

共享一个文件表项

(8)文件的原子读写

ssize_t pread(int fd, void *buf, size_t count, off_t offset);

ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);

1.pread 不会从文件表中获取当前偏移,而是直接使用用户传递的偏移量,并且在读取完毕后,不会更改当前文件的偏移量。

2.pwrite 的实现与 pread 类似

(9)复制文件描述符

int dup(int oldfd);

1.dup()系统调用使用编号最小的文件描述符创建文件描述符oldfd的副本。

2.在成功返回之后,旧的和新的文件描述符可以互换使用。

3.两个文件描述符共用同一个文件表,但是不共享文件描述符标志

例如:如果通过在其中一个描述符上使用lseek修改文件偏移量,则另一个描述符的偏移量也会更改。

关闭重复描述符的close-on-exec标志

4.调用close关闭文件描述符时, oldfd和拷贝的newfd都需要手动关闭。

int dup2(int oldfd, int newfd);

1.dup2与dup执行同样的操作,newfd是用户传入的

2.如果newfd和已经是另外一个文件打开的描述符, 那么会先将newfd所在的文件描述符关闭,然后执行拷贝工作

3.如果newfd与oldfd相同, 那么dup2函数不执行任何操作,且返回newfd

int dup3(int oldfd, int newfd, int flags);

dup3与dup2不同点在于:

1. 调用者仅仅可以通过在flags中指定O_CLOEXEC来强制为新文件描述符设置close-on-exec标志。

2. 如果oldfd等于newfd,则dup3()将失败并显示错误EINVAL。

(10)文件数据同步

void sync(void);

1.只是将所有修改过的块缓冲区排入写队列, 然后就返回, 他并不等待实际写磁盘操作结束

2.updata系统守护进程周期性的调用sync函数(30s)

int fsync(int fd);

1.fsync 只同步 fd 指定的文件,并且等待写磁盘操作结束才返回。

2.同步更新文件数据

int fdatasync(int fd);

1.类似于fsync但他只影响文件的数据部分

2.不会同步更新文件的属性

(11)获取文件的元数据

int stat(const char *path, struct stat *buf);

int fstat(int fd, struct stat *buf);

int lstat(const char *path, struct stat *buf);

这三个函数都可用于得到文件的基本信息。

1.stat 得到路径 path 所指定的文件基本信息。

2.fstat得到文件描述符 fd 指定文件的基本信息。

3.如果path指向的是链接文件,lstat得到的是链接文件自己本身的基本信息而不是其指向文件的信息。

struct stat {

dev_t st_dev; // 设备ID

ino_t st_ino; // 节点号

mode_t st_mode; // 包含

nlink_t st_nlink; // 硬链接数

uid_t st_uid; // 所有者用户ID

gid_t st_gid; // 所有者组ID

dev_t st_rdev; // 设备ID(如果是特殊的文件)

off_t st_size; // 总大小, 以字节为单位

blksize_t st_blksize; // 文件系统I/O的块大小

blkcnt_t st_blocks; // 分配了512B的块数

time_t st_atime; // 上次访问时间

time_t st_mtime; // 最后修改时间

time_t st_ctime; // 上次状态变更时间

};

(12)改变已经打开文件的属性

int fcntl(int fd, int cmd, ... /* arg */ );

提供了以下5个功能

1.复制一个文件描述符 (F_DUPFD、F_DUPFD_CLOEXEC )

2.获取设置文件描述符标志(F_GETFD、F_SETFD)

3.获取设置文件状态标志 (F_GETFL、F_SETFL)

4.获取设置异步I/O所有权 (F_GETOWN、F_SETOWN )

5.获取设置记录锁 (F_SETLK、F_SETLKW、F_GETLK)

(13)文件截断

int truncate(const char *path, off_t length);

int ftruncate(int fd, off_t length);

1. truncate 截断的是路径 path 指定的文件, ftruncate 截断的是 fd 引用的文件。

2. length 可以大于文件本身的大小,这时文件长度将变为 length 的大小,扩充的内容均被填充为 0 。

3.尽管 ftruncate使用的是文件描述符,但是其并不会更新当前文件的偏移。

1.为什么需要文件截断?

如果某个文件已经存在有5个字节12345, 这时我打开文件(偏移量为0), 写入33, 结束后文件为33345,这个结果显然不是我们想要的

2.文件截断为0长度的方法。

1.打开文件的同时,指定 O_TRUNC标志。

2.truncate(path, 0) ftruncate(fd,0)

文件I/O(基础知识 打开文件 创建文件 关闭文件描述符 文件偏移 读 写 文件共享 文件的原子读写 复制文件描述符 文件数据同步 获取文件的元数据 改变已经打开文件的属性 文件截断)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。