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

相关api列表

#include <fcntl.h>  
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<fcntl.h>  
int creat(const char *path, mode_t moe);  
//返回文件描述符,负数表示错误  
  
#include <unistd.h>  
int close(int fd);  
//关闭一个文件还会释放该进程在该文件上的所有锁记录  
  
#include <unistd.h>  
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 <unistd.h>  
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 <unistd.h>  
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 <unistd.h>  
int dup(int fd);  
int dup2(int fd, int fd2);  
  
  
//同步刷新  
#include <unistd.h>  
int fsync(int fd);  
int fdatasync(int fd);  
void sync(void);  
//sync只是将所有修改过的块缓冲区排入写队列,然后就返回,并不等待实际写  
//磁盘操作结束  
//一般系统守护进程30秒调用sync函数  
//fsync函数只对文件描述符fd指定的一个文件起作用,并且等待写磁盘操作结束  
//fdatasync函数类似fsync,但是只影响文件的数据部分,fsync还会同步更新  
  //文件的属性  
  
  
  
//更改已经打开文件的属性  
#include <fcntl.h>  
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 <unistd.h>  
#include <sys/ioctl.h>   //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 <fcntl.h>  
#include <stdio.h>  
#include <unistd.h>  
  
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 <fcntl.h>  
#include <stdio.h>  
#include <unistd.h>  
#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大小真正时间用户时间系统时间
5123.269s0.077s2.523s
10241.793s0.039s1.723s
20481.682s0.015s1.214s
40962.410s0.017s2.184s
81925.404s0.009s1.218s
163845.433s0.013s1.175s
327685.396s0.005s1.188s
655355.383s0.004s1.205s
8M0.878s0.000s0.876s

 

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

buf大小真实时间用户时间系统时间
10241m52.317s0.386s12.616s
20481m7.329s0.086s8.528s
409636.584s0.036s4.183s
819220.883s0.085s2.886s
1638413.335s0.026s2.200s
327689.761s0.020s1.711s
655356.901s0.010s1.462s
8M5.675s0.001s1.199s

 

 

 

 

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

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

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

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

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

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

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

   b)当前文件偏移量

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

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

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

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

 

打开文件的内核数据结构


 

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


 

 

原子操作

早起的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环境高级编程》学习笔记:从点到面

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值