title: 文件IO
date: 2019/11/23 10:49:52
toc: true
文件IO
文件描述符
文件描述符是非负的整数,一般是系统调用的,这个与file_struct
区别开来. STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO
被定义在 <unistd.h>
获取最大支持的描述符
新的linux
已经不支持OPEN_MAX
来直接获取这个最大描述符了,
sysconf(_SC_OPEN_MAX)
shell 下这么查看,其中的open files (-n) 1024
就是了
reallin@ubuntu:/work/pan/apue$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 31609
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 31609
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
shell下使用ulimit -n <number>
来修改,这个-n
就是上面的显示出来的
代码获取参照获取最大文件描述符
open/openat/creat
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
int openat(int dirfd, const char *pathname, int flags);
int openat(int dirfd, const char *pathname, int flags, mode_t mode);
openat
使得oepn
的路径可以是相对于xxx
目录打开的实现,其中的xxx
目录实际就是使用open
或者opendir
返回的文件目录的文件描述符,dirfd
用来转换opendir
为普通描述符.如果fd
为AT_FDCWD
则等同与open
creat
函数可以被open
取代,注意还有个选项O_EXCL
,如果文件存在则创建失败
// O_TRUNC 是文件存在的话使长度为0
open(path, O_WRONLY|O_CREAT|O_TRUNC,mode)
// 比如 open("./me.txt", O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU);
注意 使用open创建已经存在的文件,文件的权限位不变 apue 练习题4.4
close
man里面写了close不能确保文件一定被写入硬盘,最好用fsync
int fsync(int fd);
lseek
lseek
不支持fifo
和管道 和网络套接字,返回-1
,这里一定要与-1
比较,因为有些文件允许负的偏移量
$ cat < /etc/passwd | ./exe
seek at fd 0 is err
lseek
跨过文件大小,如果继续写则在后续位置写,造成文件空洞
off_t 类型说明
- 使用
sysconf
查看 - c99使用命令
getconf
查看 - 可以手动指定设置宏
_FILE_OFFSET_BITS
为32或者64
lseek与OAPPEND的区别
lseek到文件末尾只是一次性的,而使用OAPPEND
的形式打开时,write
操作时都会从内核维护的文件信息节点中获得seek偏移
内核维护的文件信息
-
文件描述符是针对进程范围的
-
file status flag 是内核维护的,针对的是所有指向这个文件的进程的
dup/dup2
// 返回最小的描述符
int dup(int fd);
// 如果new_fd2 !=old,先关闭 new_fd2,
// 再new_fd2=old_fd
//如果这个fd2是标准输出,则可以实现重定位 fd 到标准输出了
int dup2(int old_fd, int new_fd2);
如果两个fd不等,fd2的FD_CLOEXEC
标志会被清,也就是exec后依然可用
还有就是即使关闭fd,但是实际的file table 有计数,只有当计数为0才真正关闭
FD_CLOEXEC
子进程以写时复制(COW,Copy-On-Write)方式获得父进程的数据空间、堆和栈副本,这其中也包括文件描述符。刚刚fork成功时,父子进程中相同的文件描述符指向系统文件表中的同一项(这也意味着他们共享同一文件偏移量)。
在复杂系统中,有时我们fork子进程时已经不知道打开了多少个文件描述符(包括socket句柄等),这此时进行逐一清理确实有很大难度。我们期望的是能在fork子进程前打开某个文件句柄时就指定好:“这个句柄我在fork子进程后执行exec时就关闭”。其实时有这样的方法的:即所谓的 close-on-exec
int fd=open("foo.txt",O_RDONLY);
int flags = fcntl(fd, F_GETFD);
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
当fork子进程后,仍然可以使用fd。但执行exec后系统就会字段关闭子进程中的fd了
缓存同步到存储
// 等待写硬盘
int fsync(int fd);
// 只影响数据,不影响属性,比如文件的修改时间
int fdatasync(int fd);
// 告知内核的守护去写硬盘,但不会等到写完返回
void sync(void);
int syncfs(int fd);
linux下这个fsync 和sync 是一样的,在man里面有写的
According to the standard specification (e.g., POSIX.1-2001), sync() schedules the writes, but may return before the actual writing is done. However Linux waits for I/O completions, and thus
sync() or syncfs() provide the same guarantees as fsync called on every file in the system or filesystem respectively.
Linux 不支持使用fcntl
对O_SYNC
标志位的修改,只能是open
的时候确认的
标准IO流
标准io流如果要使用fsync
,需要先使用fflush
将存储从内存缓冲区输出到实际的流中,再存储.<P5.5练习题>
fcntl改变文件属性
#include <fcntl.h>
// 错误返回-1 成功返回其他值
int fcntl(int fd, int cmd, ... /* int arg */ );
1. Duplicate an existing descriptor (cmd = F_DUPFD or F_DUPFD_CLOEXEC)
2. Get/set file descriptor flags (cmd = F_GETFD or F_SETFD)
3. Get/set file status flags (cmd = F_GETFL or F_SETFL) // O_RDONLY ...等属性
4. Get/set asynchronous I/O ownership (cmd = F_GETOWN or F_SETOWN)
5. Get/set record locks (cmd = F_GETLK, F_SETLK, or F_SETLKW)
-
获得文件状态
F_GETFL
,如果要判断读写权限,需要与val & O_ACCMODE
再去与O_RDONLY
等比较 -
Linux 不支持
O_SYNC
标志位的修改,只能是open
的时候确认的
ioctl
这是一个杂类的接口,以前写驱动的时候也是会用到的这个
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
文件截断
#include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
目录遍历
// fn 是对遍历到的文件做的钩子函数
int nftw(const char *dirpath,
int (*fn) (const char *fpath, const struct stat *sb,
int typeflag, struct FTW *ftwbuf),
int nopenfd, int flags);
int ftw(const char *dirpath,
int (*fn) (const char *fpath, const struct stat *sb,
int typeflag),
int nopenfd);
也可以用另一个系列的函数遍历
fts, fts_open, fts_read, fts_children, fts_set, fts_close - traverse a file hierarchy
代码附录
获取最大文件描述符
extern "C" {
#include "apue.h"
}
#include <stdio.h>
#include <limits.h>
#include <unistd.h> //sysconf
int main(int argc ,char** argv)
{
printf("max open fd number is %ld
",sysconf(_SC_OPEN_MAX));
}
#if(0)
max open fd number is 1024
#endif
使用openat来实现一种相对路径的打开
extern "C" {
#include "apue.h"
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
/*
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
int openat(int dirfd, const char *pathname, int flags);
int openat(int dirfd, const char *pathname, int flags, mode_t mode);
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
int dirfd(DIR *dirp);
*/
int main(int argc ,char** argv)
{
int dir_fd;
int file_fd;
// 方式1,使用open打开dir,返回的文件描述为openat的dirfd
// 方式2,使用opendir,返回值用dirfd 转换为fd
dir_fd=open("../",O_RDONLY); //------this line is diff
if(dir_fd<0)
{
err_quit("open ../ dir err");
}
file_fd=openat(dir_fd,"./3-3-1/Makefile",O_RDONLY);
if(file_fd<0)
{
err_quit("open Makefile err");
}
unsigned char buf[100]={0};
read(file_fd,buf,99);
printf("read by open Makefile for 99 byte :
:%s
",buf);
DIR* dirfd_pt=opendir("../"); //------this line is diff
if(dirfd_pt==NULL)
{
err_quit("opendir ../ dir err");
}
dir_fd=dirfd(dirfd_pt); //------this line is diff
if(dir_fd<0)
{
err_quit("dirfd convert err");
}
file_fd=openat(dir_fd,"./3-3-1/Makefile",O_RDONLY);
if(file_fd<0)
{
err_quit("open Makefile err");
}
unsigned char buf2[100]={0};
read(file_fd,buf2,99);
printf("read by opendir Makefile for 99 byte :
:%s
",buf2);
}
// read by open Makefile for 99 byte :
// :SRCS = $(wildcard ./*.cpp)
// #SRCS += $(wildcard ./*.c)
// #OBJS := $(patsubst %.c, %.o, $(SRCS))
// OBJS
// read by opendir Makefile for 99 byte :
// :SRCS = $(wildcard ./*.cpp)
// #SRCS += $(wildcard ./*.c)
// #OBJS := $(patsubst %.c, %.o, $(SRCS))
// OBJS
lseek测试管道等
extern "C" {
#include "apue.h"
}
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
/*
off_t lseek(int fd, off_t offset, int whence);
*/
int main(int argc ,char** argv)
{
off_t l_seek =lseek(STDIN_FILENO, 0, SEEK_SET);
if(l_seek==-1)
{
err_quit("lseek at fd 0 is err");
}
printf("lseek success
");
}
// lseek 不支持 fifo 管道 和网络套接字
// reallin@ubuntu:~/work/pan/apue/study/3-6-1$ ./exe
// lseek at fd 0 is err
// reallin@ubuntu:~/work/pan/apue/study/3-6-1$ ./exe <Makefile
// lseek success
// reallin@ubuntu:~/work/pan/apue/study/3-6-1$ cat < Makefile |./exe
// lseek at fd 0 is err
lseek文件跨越写
extern "C" {
#include "apue.h"
}
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
/*
off_t lseek(int fd, off_t offset, int whence);
*/
int main(int argc ,char** argv)
{
int file_fd=open("me.o",O_CREAT|O_WRONLY|O_TRUNC,S_IRWXU);
if(file_fd<0)
{
err_quit("creat file err");
}
off_t l_seek =lseek(file_fd, 100, SEEK_SET);
if(l_seek==-1)
{
err_quit("lseek at fd 0 is err");
}
char* buf="hello in lseek at 100";
ssize_t len=write(file_fd, buf,(size_t)strlen(buf));
printf("lseek success
");
}
// reallin@ubuntu:~/work/pan/apue/study/3-6-2$ hexdump me.o
// 0000000 0000 0000 0000 0000 0000 0000 0000 0000
// *
// 0000060 0000 0000 6568 6c6c 206f 6e69 6c20 6573
// 0000070 6b65 6120 2074 3031 0030
// 0000079
// reallin@ubuntu:~/work/pan/apue/study/3-6-2$ ls -lh me.o
// -rwx------ 1 reallin reallin 121 Nov 23 14:55 me.o
fcntl获取文件状态
extern "C" {
#include "apue.h"
}
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h> //atoi
int main(int argc ,char** argv)
{
if(argc==1)
{
err_quit("pls input a num as file descriptor
");
}
int val= fcntl(atoi(argv[1]), F_GETFL,NULL );
if(val<0)
err_quit("get file descriptor err
");
// O_RDONLY=00,O_WRONLY= 01,O_RDWR=02
const char* attr[]={"read only","write only","read write","unknown access mode"};
printf("%d-%s",val& O_ACCMODE,attr[val& O_ACCMODE]);
if (val & O_APPEND)
printf(", append");
if (val & O_NONBLOCK)
printf(", nonblocking");
if (val & O_SYNC)
printf(", synchronous writes");
putchar('
');
exit(0);
}
// > ./exe 0 0</dev/tty
// 0-read only
// > ./exe 1 1>out.o
// > cat out.o
// 1-write only
// > ./exe 2 2>>out.o
// 1-write only, append
// > cat out.o
// 1-write only
// > bash
// $ ./exe 5 5<>out.o
// 2-read write