文件IO
打开文件
#include
open
openat:fd是打开的目录的文件描述符,所以可以写相对路径
掌握:O_RDONLY、O_WRONLY、O_RDWR、O_APPEND、O_CREAT、
O_EXCL:和O_CREAT一起用,测试文件是否存在,存在则出错,否则创建,使得测试和创建成为原子操作。
实验:
第一次执行,创建了hello文件,第二次执行打印:no:File exists
1 #include
2 #include"apue.h"
3
4 int main()
5 {
6 int fd = open("hello",O_CREAT | O_EXCL,0777);
7 if(fd>0)
8 {
9 printf("yes");
10 }
11 else
12 {
13 perror("no");
14 }
15 }
O_NONBLOCK:非阻塞方式,用于FIFO,快特殊文件等。
O_TRUNC:文件存在,并且以写的方式打开,则文件截断为0。
文件名最大长度
#include
实验:
打印出:NAME_MAX=255
#include
2 #include
3
4 int main()
5 {
6 printf("NAME_MAX=%d",NAME_MAX);
7 }
创建文件
#include
creat
相当于:open(path,O_WRONLY | O_CREAT | O_TRUNC,mode)
关闭文件
close
lseek(int fd,off_t offset,int whence);
wence:SEEK_SET(0)文件开始位置计算偏移 SEEK_CUR(1)当前位置 SEEK_END(2)文件末尾
FIFO、管道、网络套接字不可以设置偏移量,调用此函数返回-1,error设为ESPIPE。
实验:测试标准输入能否lseek
打印出:cannot seek: Illegal seek
执行:./a.out < /etc/passwd 打印seek_ok
1 #include
2 #include "apue.h"
3
4 int main()
5 {
6 if(lseek(STDIN_FILENO,0,SEEK_SET)==-1)
7 {
8 perror("cannot seek");
9 }
10 else
11 {
12 printf("seek ok");
13 }
14 }
实验:创建空洞文件
lseek之后必须写入才能创建空洞,空洞不分配磁盘块。
结果:ls -l之后16394字节,因为后面又写入10个字节。
od -c a.out:结果如下
0000000 a b c d e f g h i j \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0040000 A B C D E F G H I J
0040012
1 #include
2 #include"apue.h"
3
4 char buf1[]="abcdefghij";
5 char buf2[]="ABCDEFGHIJ";
6
7 int main()
8 {
9 int fd;
10 if((fd=creat("file.hole",0777))<0)
11 {
12 perror("creat error");
13 }
14 if(write(fd,buf1,10)==-1)
15 {
16 perror("write error");
17 }
18 if(lseek(fd,16384,SEEK_SET)==-1)
19 {
20 perror("lseek error");
21 }
22 if(write(fd,buf2,10)==-1)
23 {
24 perror("write2 error");
25 }
26 exit(0);
27
28 }
#include
read:到达文件末尾,返回0
write
实验:文件复制
1 #include
2 #include"apue.h"
3 #define BUFFSIZE 4096
4 int main(int argc,char**argv)
5 {
6
7 int fd_src,fd_dec;
8 char buf[BUFFSIZE]={0};
9 int n;
10 if(argc!=3)
11 {
12 printf("error\n");
13 }
14 fd_src = open(argv[1],O_RDONLY);
15 if(fd_src<0)
16 {
17 perror("fd_src");
18 }
19 fd_dec=open(argv[2],O_CREAT | O_WRONLY,0777);
20 if(fd_dec<0)
21 {
22 perror("fd_dec");
23 }
24
25 while((n=read(fd_src,buf,BUFFSIZE))>0)
26 {
27 if(write(fd_dec,buf,n)<0)
28 {
29 perror("write");
30 }
31 }
32 exit(0);
33
34 }
文件共享
Linux没有V节点,采用了与文件系统相关的i节点和与文件系统无关的i节点。
两个进程打开一个文件,拥有各自的文件表项,每个进程有自己的文件表项,维护自己的文件偏移量。V节点是共享的。
原子操作
1、追加写的步骤:先定位到尾端,然后写。会出问题:两个进程打开同一个文件,A定位到1500,此时进程切换到B,B定位到1500,写了100字节,偏移量为1600,再切换到A进程,A从1500开始写,就把B的数据覆盖掉了。(问题在于采用了分开的函数调用)O_APPEND标志打开文件,实现了原子操作,不需要先lseek再写。
2、O_EXCL | O_CREAT标志,检查文件不存在则创建,也是原子操作。假设分为两个函数,如果检测不存在,此时被另一个进程抢占,并创建文件file,再切换回去,原来的进程创建file,就会擦掉另一进程写到file的数据。
dup和dup2
int dup(int fd)返回的文件描述符和fd指向同一个文件。
int dup2(int fd,inty fd2):关闭fd2指向文件,返回fd2,fd2和fd指向同一个文件,这是原子操作。
延迟写:
写文件时,数据先到缓冲区,排入队列,晚些时候再写。内核要用缓冲区存放其他数据,就把延迟写的内容写到磁盘。
sync没有参数,update守护进程周期调用(一般是30s),定时冲洗缓冲区。
int fsync(int fd)只对fd指定的文件有用。
fdatasync(int fd)只影响文件数据,fsync会同步更新文件属性。
改变打开文件属性
fcntl:命令太多,先掌握三个,复制文件描述符、获取文件状态标志、设置文件状态标志。
O_ACCMODE值为3可以获取打开标志的低三位,代表只读,只写,读写。
实验:open创建文件,测试文件的状态
结果:
read only
write: Bad file descriptor(没有写权限导致的)
换成第10行的,就可以写了。
下面这几行不能修改使得文件可写,F_SETFL允许更改的状态标志有O_APPEND, O_NONBLOCK, O_NOATIME, O_ASYNC和O_DIRECT。系统将忽略对其他标志的修改操作。
val |= O_RDWR;
if(fcntl(fd,F_SETFL,O_RDWR)<0)
{
perror("F_SETFL");
}
1 #include
2 #include"apue.h"
3
4
5 int main()
6 {
7 int fd;
8 int val;
9 fd = open("hello",O_CREAT | O_EXCL ,0777);
10 //fd = open("hello",O_CREAT | O_EXCL | O_RDWR ,0777);
11 if(fd<0)
12 {
13 perror("open");
14 }
15 else
16 {
17 val = fcntl(fd,F_GETFL,0);
18 if(val<0)
19 {
20 perror("fcntl");
21 }
22 else
23 {
24 switch(val&O_ACCMODE)
25 {
26 case O_RDONLY:
27 printf("read only\n");
28 break;
29 case O_WRONLY:
30 printf("write only\n");
31 break;
32 case O_RDWR:
33 printf("write read\n");
34 break;
35 default:
36 printf("unknow node");
37 break;
38 }
39 }
可以设置O_SYNC同步写标志,每次写要等待,知道写入到磁盘再返回。不允许用fcntl设置。(APUE P69)
ioctl函数:先不管。
/dev/fd 目录下看到了0、1、2、255 打开相应文件,相当于复制文件描述符。这些实际是符号链接。(APUE P71)