APUE 文件IO
文件 IO
记录书中的重要知识和思考实践部分
Unix 每个文件都对应一个文件描述符(file descriptor),为一个非负整数,一个文件可以有多个fd, 后面所有与文件(设备,套接字等)有关操作都是围绕这个fd来的。
在shell中 < > 都为重定向符号,前者为重定向输入,后者为输出。
文件的打开
#include <fcntl.h>
int open(const char *path, int flags, ... /* mode_t mode */);
int openat(int fd, const char *path, int flags, ... /* mode_t mode */);
O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH 这五个参数(flags)是必须的,另外可选的参数里面 O_CLOEXEC 与 FD_CLOEXEC 都是在 exec() 函数中关闭文件描述符的标志,这个后面会看到。
文件偏移量
#include <unistd.h>
int lseek(fd, off_t off, int wheren);
我们使用 lseek 函数的时候,比如lseek(fd, 10, SEEK_END); 这样会导致文件的偏移量增加而文件的大小仍然不变,
但是当再使用 write 函数向文件中写入数据时,直接给个例子更好理解, 文件 foo 中原有数据为123。
int fd;
if ((fd = open("./foo", O_RDWR)) < 0)
err_sys("open error for foo");
lseek(fd, 2, SEEK_END);
write(fd, "zxh", 3);
$ od -c foo
0000000 1 2 3 \0 \0 z x h
0000010
可以看到产生两个\0,产生了空洞文件。
使用以下的方式得到当前的文件偏移量。
off_t off;
off = lseek(fd, 0, SEEK_END); // off 为当前的文件偏移量,在上例中为 5
原子操作
操作是不可中断的,如 read write 系统调用,可能读取或者写入的数据少于我们要的数量,但是在函数调用这个事件上要么直接成功要么直接失败。
新文件的读写可以使用 open 函数的 O_CREAT 标志来创建再读写,此为原子操作;
还有一种方式是使用 creat 函数创建文件后再用 open 打开,这里有两个调用,当进行进程切换时候,其他进程对此文件进行处理,产生意向不到的错误。
上面是文件的创建操作,还有文件描述符的复制操作, 也是如此,对于单进程的效果是一样的,但是在多进程的时候就
dup2(fd, fd2);
等效于
close(fd2);
fcntl(fd, F_DUPFD, fd2);
函数 fcntl
可以改变文件的属性,算的上是个杂货箱吧。
函数原型
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */);
功能:
- 复制一个已有的描述符(cmd = F_DUPFD 或 F_DUPFD_CLOEXEC)
- 获取/设置文件描述符标志(cmd = F_GETFD 或 F_SETFD)
- 获取/设置文件状态标志(cmd = F_GETFL 或 F_SETFL)
- F_GETFL 只能用屏蔽字O_ACCMODE取得存取方式位
- F_SETFL 更改的标志只有 O_APPEND,O_NONBLOCK,O_SYNC 和 O_ASYNC
- 获取/设置异步IO所有权(cmd = F_GETOWN 或 F_SETOWN)
- 获取/设置记录锁(cmd = F_GETLK、F_SETLK 或 F_SETLKW)
函数的返回值依赖参数而定,所有失败都是返回 -1,除特定参数,如下:
- F_DUPFD、F_DUPFD_CLOEXEC,返回新的文件描述符,FD_CLOEXEC标志被清除
- F_GETFD, 返回文件描述符标志,当前只定义了一个 FD_CLOEXEC
- F_GETFL,返回文件状态标志,O_RDONLY等
- F_GETOWN,返回一个进程组ID
成功返回 0。
设置文件描述符标志(FD_CLOEXEC)和文件状态标志可用如下函数
int set_cloexec(int fd)
{
int val = fcntl(fd, F_GETFD, 0);
val |= FD_CLOEXEC;
return fcntl(fd, F_SETFD, val);
}
int set_fl(int fd, int flags)
{
int val = fcntl(fd, F_GETFL, 0);
val |= flags;
return fcntl(fd, F_SETFL, val);
}
文件描述符标志 FD_CLOEXEC
在前面提到,open 函数用使用 O_CLOEXEC 标志会是打开的文件描述符在exec打开的进程中关闭,可以达到进程间的文件隔离的效果。
#ifdef _CLOEXEC
open("./foo", O_CLOEXEC | O_RDWR);
#else
open("./file.hole", O_RDWR);
#endif
execl("./rdwr", "rdwr", "10000", NULL);
执行execl后进程是 rdwr,在编译命令里面加入 -D_CLOEXEC 选项来看变化

可以发现没有占用foo,不加入-D_CLOEXEC

这样也存在一个问题,在另外一个进程里关闭了文件描述符,就须注意当前进程后面不能再对文件进行操作了。
上面是 open 函数,我们同样可以用fcntl来改变文件的描述符标志,直接调用上面的 set_cloexec(fd) 也可以达到这个效果;
5 #include <fcntl.h>
6 #include "apue.h"
7
8 // int fcntl(int fd, int cmd, ... /* int arg */);
9
10 // F_DUPFD F_DUPFD_CLOEXEC
11 // return new fd.
12 int dupfd(int fd) {
13 printf("fcntl_dup: %d\n", fd);
14 int new_fd = fcntl(fd, F_DUPFD, 4); // new_fd 应该是 fd + 1
15 if (new_fd < 0)
16 err_sys("fcntl F_DUPFD error\n");
17 printf("F_DUPFD: %d\n", new_fd);
18 return new_fd;
19 }
20
21 // F_GETFD
22 int getfd(int fd) {
23 int val = fcntl(fd, F_GETFD);
24 if (val < 0)
25 err_sys("fcntl F_GETFD error");
26 printf("getfd %d\n", val);
27 if (val & FD_CLOEXEC) // 这里 0,所以只会走 30 行的
28 printf("getfd FD_CLOEXEC\n");
29 else
30 printf("getfd not FD_CLOEXEC\n");
31
32 close(val);
33 return val;
34 }
35
36 // F_SETFD
37 int setfd(int fd) {
38 int val = set_cloexec(fd);
39 printf("setfd %d\n", val);
40 // execl("./rdwr", "rdwr", "123", NULL);
41 return val;
42 }
43
44 void setfl(int fd, int flags) {
45 set_fl(fd, flags);
46 }
47
48 int getfl(int fd) {
49 int val;
50 if ((val = fcntl(fd, F_GETFL)) < 0)
51 err_sys("fcntl F_GETFL error");
52
53 switch (val & O_ACCMODE) {
54 case O_RDONLY:
55 printf("read only\n");
56 break;
57 case O_WRONLY:
58 printf("write only\n");
59 break;
60 case O_RDWR:
61 printf("read write\n");
62 break;
63 default:
64 err_dump("unkown access mode");
65 }
66 if (val & O_APPEND)
67 printf(", append");
68 if (val & O_NONBLOCK)
69 printf(", nonblocking");
70 if (val & O_SYNC)
71 printf(", synchronous writes");
72 return val;
73 };
74
75 int main(int argc, char *argv[]) {
76 int fd;
77
78 if ((fd = open("./foo", O_RDWR | O_CREAT)) < 0)
79 err_sys("open error");
80 dupfd(fd);
81 getfd(fd);
82 setfd(fd);
83 getfl(fd);
84 // setfl(fd, O_APPEND); // 从文件为开始操作
85 if (write(fd, "jinpi", 5) < 0)
86 err_sys("write error");
87 }
APUE 文件IO的更多相关文章
- (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- APUE学习笔记3_文件IO
APUE学习笔记3_文件IO Unix中的文件IO函数主要包括以下几个:open().read().write().lseek().close()等.这类I/O函数也被称为不带缓冲的I/O,标准I/O ...
- C3 文件IO:APUE 笔记
C3:文件IO 1 引言 本章描述的函数被成为不带缓冲的IO,涉及5个函数:open.read.write.lseek.close. 文件控制:dup.sync.fsync.fdatasync.fcn ...
- 《嵌入式linux应用程序开发标准教程》笔记——6.文件IO编程
前段时间看APUE,确实比较详细,不过过于详细了,当成工具书倒是比较合适,还是读一读这种培训机构的书籍,进度会比较快,遇到问题时再回去翻翻APUE,这样的效率可能更高一些. <嵌入式linux应 ...
- 无缓冲文件IO和目录操作
引言 在后台开发中,对于文件I/O我们通常不使用C语言封装的fopen.fread.fwrite标准I/O,而是直接使用Linux提供的系统调用函数.因为这些系统调用没有使用用户缓冲区,我们直接与内核 ...
- 标准io与文件io
A: 代码重复: 语句块1: while(判断) { 语句块2: 语句块1: } 上面可以改写为: while(1) { 语句块1: if(判断) break: 语句块2: } B: 标准IO和文件I ...
- 文件IO函数和标准IO库的区别
摘自 http://blog.chinaunix.net/uid-26565142-id-3051729.html 1,文件IO函数,在Unix中,有如下5个:open,read,write,lsee ...
- 转 漫谈linux文件IO
在Linux 开发中,有几个关系到性能的东西,技术人员非常关注:进程,CPU,MEM,网络IO,磁盘IO.本篇文件打算详细全面,深入浅出.剖析文件IO的细节.从多个角度探索如何提高IO性能.本文尽量用 ...
- Java文件IO操作应该抛弃File拥抱Paths和Files
Java7中文件IO发生了很大的变化,专门引入了很多新的类: import java.nio.file.DirectoryStream;import java.nio.file.FileSystem; ...
随机推荐
- [置顶] Silverlight之控件应用总结(一)(3)
[置顶] Silverlight之控件应用总结(一)(3) 分类: 技术2012-04-02 20:35 2442人阅读 评论(1) 收藏 举报 silverlightradiobuttondatat ...
- flash builder 4.6 下载完成后安装不成功
从网上下载了一下flash builder 4.6 下载完成后安装不成功,说是有一个安装被挂起,不成安装成功结果从注册表中删除了pendingobject,还是不行,没有办法,从网上搜了一下,发现了大 ...
- 【149】ArcGIS Desktop 10.0 & Engine 10.0 安装及破解
写在前面:可能会出现按照此方法无法破解的情况,那请确保您有将 ArcGIS 10.0 已经完全卸载干净,直接通过控制面板进行卸载的时候并不能将其卸载干净,需要进行更深层次的卸载,包括删除注册表,各种文 ...
- UVaLive 6834 Shopping (贪心)
题意:给定 n 个商店,然后有 m个限制,去 c 之前必须先去d,问你从0到n+1,最短路程是多少. 析:我们我们要到c,必须要先到d,那么举个例子,2 5, 3 7,如果我们先到5再到2,再到7再到 ...
- 引水入城 2010年NOIP全国联赛提高组(bfs+贪心)
1066 引水入城 2010年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 在一个遥远 ...
- C#+ItextSharp 查看pdf文件页面尺寸
1# Nuget下载itextSharp,下载到本地 pm>Install-Package iTextSharp -Version 5.5.10 2# 引用dll,添加命名空间 using iT ...
- SQL常用指令集(Oracle)
1. Select rownum(oracle) top(mysql): 用于规定返回指定数目的值 Where roenum < number 2. Like 用于在where子句搜索指定模式 ...
- 用Google Cloud Platform搭建***服务教程
之前FQ一直用的是***,天有不测风云,前几天发现ss服务挂了.更可怕的是ping都ping不通,多方打听,***中文社区已经炸开锅了,原因就是IP被封了.需要付费更换IP.然后到现在还是没有给我更换 ...
- 自动构造词法分析器的步骤——正规式转换为最小化DFA
正规式-->最小化DFA 1.先把正则式-->NFA(非确定有穷自动机) 涉及一系列分解规则 2.再把NFA通过"子集构造法"-->DFA 通过子集构造法将NFA ...
- 对数组名取地址&a和 数组首地址a
#include <iostream> using namespace std; ] = {,,,,}; int main() { cout<<a<<" ...