UNIX环境高级编程-文件I/O

相关api列表

#include   
int open(const char *path, int oflag, /* mode_t mode */ );  
int openat(int fd, const char *path, int oflag, /* mode_t mode */ );  
//返回文件描述符  
  
STDIN_FILENO      //标准输入  
STDOUT_FILENO   //标准输出  
  
#include  
int creat(const char *path, mode_t moe);  
//返回文件描述符,负数表示错误  
  
#include   
int close(int fd);  
//关闭一个文件还会释放该进程在该文件上的所有锁记录  
  
#include   
off_t  lseek(int fd, off_t offset, int whence);  
//1.若whence是 SEEK_SET,则将该文件的偏移量设置为距离文件开始出offset个  
   //字节  
//2.若whence是 SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,  
  //offset可为正或负  
//3.若whence是 SEEK_END,则将该文件的偏移量设置为文件长度加offset,  
  //offset可正可负  
  
  
#include   
ssize_t read(int fd, void *buf, size_t nbytes);  
ssize_t write(int fd, const void *buf, size_t nbytes);  
  
//read成功,返回读到的字节数,如果已达到文件尾端则返回0  
//读普通文件时,读要求的字节数之前已经达到文件尾端则返回0  
/从终端设备读一次最多读一行  
//从网络读,网络中的缓冲机制可能造成返回值小于所要求读的字节数  
//当从管道或FIFO读时,若管道包含的字节少于所需的数量,那么read将返回实际  
   //可用的字节数  
//当从某些面向记录的设备(如磁带)读时,一次最多返回一个记录  
//当一信号造成中断,而已经读了部分数据时,在成功返回之前,该偏移量将增加  
   //实际读到的字节数  
//write成功,返回实际写入的字节数  
  
  
//原子性的执行读写操作  
#include   
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);  
ssize_t pwrite(int fd, void *buf, size_t nbytes, off_t offset);  
  
  
//复制文件描述符  
#include   
int dup(int fd);  
int dup2(int fd, int fd2);  
  
  
//同步刷新  
#include   
int fsync(int fd);  
int fdatasync(int fd);  
void sync(void);  
//sync只是将所有修改过的块缓冲区排入写队列,然后就返回,并不等待实际写  
//磁盘操作结束  
//一般系统守护进程30秒调用sync函数  
//fsync函数只对文件描述符fd指定的一个文件起作用,并且等待写磁盘操作结束  
//fdatasync函数类似fsync,但是只影响文件的数据部分,fsync还会同步更新  
  //文件的属性  
  
  
  
//更改已经打开文件的属性  
#include   
int fcntl(int fd, int cmd, /* int arg */);  
//fcntl函数有5种功能  
//1.复制一个已经的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)  
//2.获取/设置文件描述符标志(cmd=F_GETFD或F_SETFD)  
//3.获取/设置文件状态标志(cmd=F_GETFL或F_SETFL)  
//4.获取/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)  
//5.获取/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW)  
  
  
//杂物箱函数  
#include   
#include    //BSD and linux  
int ioctl(int fd, int request);  

 

 

 

 

open函数的oflag参数

参数 含义
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读,写打开
O_EXEC 只执行打开
O_SEARCH 只搜索打开
O_APPEND 每次写时都追加到文件尾端
O_CLOEXEC

把FD_CLOEXEC常量设置为文件描述符

O_CREAT

若文件不存在则创建它,同时需要第三个参数指明

新文件的访问权限

O_DIRECTORY 如果path不是目录则出错
O_EXCL

如果同时指定了O_CREAT,而文件已存在则出错

次功能可测试一个文件是否存在若不存在则创建

O_NOCTTY

如果path引用的是终端设备,则不将该设备分配作为

进程的控制终端

O_NOFOLLOW

如果path引用的是一个符号链接则出错

O_NONBLOCK

如果path引用的是一个FIFO,一个块特文件或一个字符特殊文件,则

此选项为文件的本次打开操作和后续的I/O操作设置为非阻塞方式

O_SYNC

每次write等待物理I/O操作完成,包括由该write操作引起的文件属性更新

所需的I/O

O_TRUNC

如果此文件存在,而且为只写或读-写成功打开,则将其长度截断为0

O_TTY_INIT

如果打开一个还未打开的终端设备,设置非标准termios参数值,使其符合Single

UNIX Specificationi

O_DSYNC

每次write要等待物理I/O操作完成,但如果该写操作并不影响读取写入的数据,

则不需要等待文件属性被更新

O_RSYNC

使每一个以文件描述符作为参数进行的read操作等待,直至所有对文件同一部分挂起

的写操作都完成

 

 

 

写空洞的例子

#include   
#include   
#include   
  
char buf1[] = "abcdefghij";  
char buf2[] = "ABCDEFGHIJ";  
  
int main(int argc, char *argv[]) {  
  
        if(argc < 2) {  
                printf("input create file path!\r\n");  
                return 1;  
        }  
  
        int fd;  
        if( (fd=creat(argv[1],0755)) < 0 ) {  
                printf("create error");  
                printf("\r\n");  
        }  
  
        if( write(fd,buf1,10) != 10 ) {  
                printf("buf write error");  
                printf("\r\n");  
        }  
  
        if( lseek(fd,16384, SEEK_SET) == -1) {  
                printf("lseek error");  
                printf("\r\n");  
        }  
  
        if( write(fd,buf2,10) != 10 ) {  
                printf("buf2 write error");  
                printf("\r\n");  
        }  
        return 0;  
}  

 

 

读写文件的例子,将一个文件读取,写入到另一个文件中

#include   
#include   
#include   
#define BUFFSIZE 4096  
int main(int argc, char *argv[]) {  
        if(argc < 3) {  
                printf("input read path, write path\r\n");  
                return 1;  
        }  
        int r_fd;  
        if( (r_fd=open(argv[1],O_RDONLY)) < 0 ) {  
                printf("open error\r\n");  
        }  
        int w_fd;  
        if( (w_fd=creat(argv[2],0755)) < 0 ) {  
                printf("create error\r\n");  
        }  
  
        int num;  
        char buf[BUFFSIZE];  
        while( (num=read(r_fd, buf, BUFFSIZE)) > 0) {  
                if( write(w_fd, buf, num) != num ) {  
                        printf("write error\r\n");  
                }  
        }  
        if( num < 0 ) {  
                printf("read error\r\n");  
        }  
        return 0;  
}  

 

读写文件时候,buf大小不同对影响的影响,12核/12G内存

buf大小 真正时间 用户时间 系统时间
512 3.269s 0.077s 2.523s
1024 1.793s 0.039s 1.723s
2048 1.682s 0.015s 1.214s
4096 2.410s 0.017s 2.184s
8192 5.404s 0.009s 1.218s
16384 5.433s 0.013s 1.175s
32768 5.396s 0.005s 1.188s
65535 5.383s 0.004s 1.205s
8M 0.878s 0.000s 0.876s

 

如果没每一次,调用fsync(fd)将内存刷新到磁盘上,性能结果如下:

buf大小 真实时间 用户时间 系统时间
1024 1m52.317s 0.386s 12.616s
2048 1m7.329s 0.086s 8.528s
4096 36.584s 0.036s 4.183s
8192 20.883s 0.085s 2.886s
16384 13.335s 0.026s 2.200s
32768 9.761s 0.020s 1.711s
65535 6.901s 0.010s 1.462s
8M 5.675s 0.001s 1.199s

 

 

 

 

UNIX支持在不同进程共享打开文件,内核使用三种数据结构表示打开文件

1.每个进程再进程表中都有一个记录项,记录项中包含一张打开文件描述符表,可将其视为一个矢量,每个

   描述符 占用一项,与每个文件描述符相关联的是:

   a)文件描述符标志(close_on_exec)

   b)指向一个文件表项的指针

2.内核为所有打开文件维持一张文件表,每个文件表项包含:

   a)文件状态标志(读,写,添写,同步和非阻塞等)

   b)当前文件偏移量

   c)指向该文件v节点表项的指针

3.每个打开文件(或设备)都有一个v节点,linux没有v节点而是用通用的 i节点结构。v节点包含了文件类型和对

  此文件进行各种操作的函数指针,对于大多数文件,v节点还包含了该文件的 i节点(i-node)。这些信息是在

  打开文件时从磁盘上读入内存的,所以文件的所有相关信息都是随时可用的。

 

打开文件的内核数据结构

UNIX环境高级编程-文件I/O_第1张图片
 

两个独立进程各自打开同一个文件

UNIX环境高级编程-文件I/O_第2张图片
 

 

原子操作

早起的unix版本不支持O_APPEND选项,所以程序被编写成下列形式

 

C代码  

if( lseek(fd,0L2) < 0 ) {  
    err_sys("lseek error");  
}  
if( write(fd, buf, 100) != 100 ) {  
    err_sys("write error");  
}  

 

 

创建一个文件

C代码 

if( (fd=open(path,O_WRONLY)) < 0 ) {  
    if(errno == ENOENT) {  
        if( (fd=creat(path,mode)) < 0 ) {  
            err_sys("create error");  
        }  
    } else {  
        err_sys("open error");  
    }  
}  

 

 

 

 

fcntl函数的cm参数有11个,先说明前8个,后3个跟锁有关

参数 含义
F_DUPFD 复制文件描述符fd
F_DUPFD_CLOEXEC

复制文件描述符,设置与新描述符关联的FD_CLOEXEC文件描述符

标志的值,返回新文件描述符

F_GETFD

对应于fd的文件描述符标志作为函数值的返回。当前只定义了一个文件

描述符标志FD_CLOEXEC

F_SETFD 对于fd设置文件描述符标志
F_GETFD 对于fd设置文件描述符标志
F_GETFL 对应于fd的文件状态标志作为函数值返回
F_SETFL

将文件状态标志设置为第三个参数的值,可以更改的几个标志是

O_APPEND,O_NONBLOCK,O_SYNC,O_DSYNC,O_RSYNC,

O_FSYNC和O_ASYNC

F_GETOWN

获取当前接收SIGIO和SIGURG信号的进程ID或进程组ID

F_SETOWN

设置接收SIGIO和SIGURG信息的进程ID或进程组ID

 

对于F_GETFL的函数返回值,在open函数中有说明,返回下列内容

文件状态标志 说明
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读写打开
O_EXEC 只执行打开
O_SEARCH 只搜索目录打开
O_APPEND 追加写
O_NONBLOCK 非阻塞模式
O_SYNC 等待写完成(数据和属性)
O_DSYNC 等待写完成(仅数据)
O_RSYNC 同步读和写
O_FSYNC 等待写完成(仅BSD和Mac)
O_ASYNC 异步I/O(仅BSD和Mac)

 

 

 

 

/dev/fd

较新的系统都提供名为 /dev/fd 的目录,其目录项是名为 0,1,2,等的文件,打开文件/dev/fd/n 等效于复制

描述符n

cat命令对其采用了一种特殊处理

  1. filter file2 | cat file1 - file3 | ipr  
  2. //首先cat读file1,然后是标准输入,然后是file3  
  3. //如果cat支持/dev/fd 也可以改成  
  4. filter file2 | cat file1 /dev/fd/0 file3 | ipr  

 

 

 

 

参考

APUE学习笔记系列

《Unix环境高级编程》学习笔记:从点到面

 

 

你可能感兴趣的:(Linux,c语言)