File I/O
文件I/O
在Unix系统中,文件I/O大多数的使用五大函数来实现:open,read,write,lseek 和close.
术语unbuffered的意思是每个read或者write函数向内核请求一个系统调用
对于内核而言,每一个打开的文件都有相应的file descriptors(非负整数).
open 函数
#include<fcntl.h>
int open(const char* pathname,int oflag,......,/* mode_t mode */);
如果正确返回file descriptor,否则返回 -1
pathname是文件名的路径,oflag是option flag,选项标签这里的选项是宏定义
oflag宏:
O_RDONLY 只可读
O_WRONLY 只可写
O_RDWR 可读写
O_APPEND 对于每个write函数的写入点从当前文件末尾开始
O_CREAT 如果文件不存在,就创建这个文件。
当然这个对于open函数使用的选项需要和函数的第三个参数mode一起,mode用于描述新建文件的权限
O_EXCL 如果使用了O_CREAT宏,并且这个文件已经存在了,于是 产生一个错误
O_TURNC 如果文件存在了并且被每一个write或者read函数成功的打开了,这个选项将“命令“open函数去清空所有文件内容,使之文本长度为0.
O_NOCTTY 如果pathname提及的是terminal设备,不允许设备被作为controling terminal
O_DSYNC 使得write等待每一个物理I/O完成自己的工作,但是不等待那种不影响读取刚写入的数据的文件
O_RSYNC 在任何write操作对于某一文件的同一部分写入操作完成之前,让被read操作的file descriptor所指的文件保持等待
/***************
just a demo
***************/
#include"apue.h"
#include"fcntl.h"
#include"stdio.h"
int main()
{
int file_descriptor = 0;
if(file_descriptor = open("./text.t",O_RDONLY) < 0)
{
printf("open error\nprocess end\n");
return 0;
}
else
{
printf("open success\n");
close(file_descriptor);//if there is not close function ,it would be OK...
}
return 0;
}
creat 函数
#include<fcntl.h>
int creat(const char* pathname,mode_t mode);
如果函数成功就返回打开的文件的file descriptor,否则返回-1
mode :
S_IRUSR 用户可读
S_IWUSR 用户可写
S_IXUSR 用户可执行
#include<apue.h> #include<unistd.h> #include<fcntl.h> #define RWX (S_IRUSR | S_IWUSR | S_IXUSR) int main() { int file_descriptor = 0; if((file_descriptor = creat("./hello.t",RWX)) < 0) { printf("creat fail\nprocess end"); return 0; } else { printf("creat successful\n"); } close(file_descriptor); return 0; }
close函数
#include<unistd.h>
int close(int filedes)
如果成功返回0,否则返回-1
每个文件都有一个相关的“当前文件偏置”(current file offset),通常是一个非负整数。它测量表示的意义是从文件开头到结束的地方这段区域内,所含字节数。
read函数write函数的读写操作通常从这个offset开始
通常(default情况),offset被初始化为0.
lseek函数
#include"unistd.h"//unistd.h - standard symbolic constants and types
off_t lseek(int files, off_t offset, int whence)
如果成功返回file offset,否则返回-1
whence宏:
SEEK_SET 将文件的offset设置为文件开头
SEEK_CUR将文件的offset设置为当前值加上lseek的第二参数offset的值(offset必须为正数)
SEEK_END将文件的offset设置为文件的大小加上lseek的第二个参数offset的值(offset可正可负)
/*
just a demo for lseek
*/
#include"apue.h"
#include"stdio.h"
#include"unistd.h"
#include"fcntl.h"
int main()
{
off_t file_off_set = 0;
int file_descriptor = 0;
if((file_descriptor = open("./text.t",O_RDONLY)) < 0)
{
printf("open error\nProcess end\n");
return 0;
}
if((file_off_set = lseek(file_descriptor,file_off_set,SEEK_CUR)) < 0)
{
printf("lseek error\n");
return 0;
}
else
{
printf("lseek execute successful\nCurent file off-set is %d\n",file_off_set);
}
return 0;
}
atomic operation微操作
#inlcude<unistd.h>
ssize_t pread(int filedes, void * buf,size_t nbytes,off_t offset);
ssize_t pwrite(int filedes,const void*buf,size_t nbytes,off_t offset);
调用pread函数就相当于调用lseek函数之后紧接着调用read函数。这两者几乎是等价的,除了下面两点情况
1.调用pread的时候, 没有什么办法可以打断这两者(lseek和read)操作
2.文件指针没有更新
pwrite同理
简单的说,atomic operation就是指的一种操作。这种操作可能由其他多种操作组成(multiple step)。
这个atomic operation有个特点,就是这个atomic operation包含的操作步骤(step)要么都完成,要么都不
会开始进行。这个atomic operation中不会有其他的操作“夹杂着"发生。
下面是man pwrite 的结果:
pwrite() writes up to count bytes from the buffer
starting at buf to the file descriptor fd at offset
The file offset is not changed.
值得注意的是这里的offset 是个定值而不是动态获得的,也就是说可能覆盖之前的文本内容(亲测,有这个情况)
如果要动态更新offset达到,每次都在 “新的文本末尾" 写入数据的话,那么则需要lseek和SEEK_END来实现更新offset
动态更新的在文本末尾写入数据用pwrite是没有必要的,lseek+write就可以了
my source code:
#include"unistd.h" int dup(int fd); int dup2(int fd,int fd2) 对于file descriptor的复制
These system calls create a copy of the file descriptor oldfd. dup() uses the lowest-numbered unused descriptor for the new descriptor. dup2() makes newfd be the copy of oldfd, closing newfd first if necessary, but note the following:
* If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
* If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.
#include"apue.h" #include"unistd.h" #include"fcntl.h" int main() { int file_descriptor = 0; int duped_fd = 0; if((file_descriptor = open("./text.t",O_RDONLY)) < 0) { printf("open error\nprocess end\n"); return 0; } else { duped_fd = dup(file_descriptor); printf("The duped file descriptor is %d\n",duped_fd); printf("The file_descritor before dup:%d\n",file_descriptor); } return 0; }
NAME
fsync, fdatasync - synchronize a file's in-core state with storage device
#include <unistd.h> int fsync(int fd); #include<fcntl.h> int fcntl(int filedes, int cmd,.../*int arg*/);
filedes文件标志符
cmd是参数宏,总共有十个,第三章只讨论前面七个,后面三个再第14章讨论
在秒速记录锁(record locking)的时候,第三个参数是一个指向结构体的指针
fcntl被用作五种不同的路径
第一,复制已经存在的文件标志符(descriptor)cmd = F_DUP
第二,设置或者获得文件标识符cmd = F_GETFD或者 cmd = F_SETFD
第三,设置或者获得状态符(status flag) cmd = F_GETFL或则F_SETFL
status flag:
O_RDWR
O_RDONLY
O_WRONLY
O_APPEND
O_NONBLOCK
O_SYNC
O_DSYNC
O_RSYNC
O_FSYNCO_ASYNC
第四,设置或则获得异步I/O权限(asynchronous I/O ownership)cmd = F_GETOWN or cmd = F_SETOWN
第五,设置或者获得记录锁(record lock)(cmd = F_GETLK, or cmd = F_SETLK)
#include<apue.h> #include<fcntl.h> #include<stdio.h> int main() { int file_descriptor = 0; int copy_file_descriptor = 0; int file_flags = 0; int ownership = 0; int temp = 0; if((file_descriptor = open("./text.t",O_RDWR)) < 0) { printf("open error\nProcess end\n"); return 0; } printf("file_descriptor is %d\n",file_descriptor); //cmd = F_DUPFD ,dupliacte the file descriptor if((copy_file_descriptor = fcntl(file_descriptor,F_DUPFD,0)) < 0) { printf("fcntl error\nProcess end"); return 0; } else { printf("The return value is %d\n",copy_file_descriptor); } //cmd = F_GETFD, return file descriptor flass for fiedes as the vale of the fucntion if(temp = fcntl(file_descriptor,F_GETFD) < 0) { printf("fcntl error\nProcess end\n"); return 0; } else { printf("file descriptor is %d\n",temp); } //cmd = F_GETFL,return file status flags for filedes. if(file_flags = fcntl(file_descriptor,F_GETFL) < 0) { printf("fcntl error\nProcess end\n"); return 0; } else { switch(file_flags& O_ACCMODE) //& O_ACCMODE) { case O_RDWR : printf("file flags is O_RDWR\n"); break; case O_RDONLY: printf("file flags is O_RDONLY\n"); break; case O_WRONLY: printf("file flags is O_WRONLY\n"); break; } if(file_flags&O_APPEND) { printf("file flags is O_APPEND\n"); } if(file_flags&O_NONBLOCK) { printf("file flags is O_NONBLOCK\n"); } #if defined(O_SYNC) if(file_flags&O_SYNC) { printf("file flags is O_SYNC\n"); } #endif #if defined(O_DSYNC) if(file_flags&O_DSYNC) { printf("file flags is O_DSYNC\n"); } #endif #if defined(O_FSYNC) if(file_descriptor&O_FSYNC) { printf("file flags is O_FSYNC\n"); } #endif #if defined(O_ASYNC) if(file_descriptor& O_ASYNC) { printf("file flags is O_ASYNC\n"); } #endif #if defined (O_RSYNC) if(file_descriptor&O_RSYNC) { printf("file flags is O_RSYNC\n"); } #endif } //cmd = F_GETOWN if(ownership = fcntl(file_descriptor,F_GETOWN) < 0) { printf("fcntl error!\nProcess end\n"); return 0; } else { printf("file ownership is %d",ownership); } return 0; }
在 /dev/fd文件夹目录下的文件,如果被打开,相当于复制该文件标志符。