UNIX高级编程总结-----文件I/O (一)

一、文件描述符

        1、对于内核而言,所有打开的文件都通过文件描述符来表示。文件描述符是一个非负整数,当打开或者创建一个文件时,内核会向进程返回一个文件描述符。当对文件进行操作时,会使用到文件描述符。UNIX系统shell把0与标准输入,1与标准输出,2与标准错误相关联。所以在unix中文件描述符0,1,2被标准化,为提高可读性,定义了三个宏STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。在头文件<unistd.h>中定义。在早期文件描述符的最大范围定义为0~19,允许每个进程同时打开20个文件,现在很多系统将其上限增加至0~63.

 二、open和openat

#include<fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
 
int openat(int fd, const char *pathname, int flags);
int openat(int fd, const char *pathname, int flags, mode_t mode);
                                   函数的返回值:若成功,返回文件描述符; 若出错,返回-1

         openat打开的的是,相对于参数 fd 的相对地址pathname,以下是从网上借鉴了一个小例子,帮助理解:

#include <stdio.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <stdlib.h>  
#include <unistd.h>  
  
void creat_at(char *dir_path, char *relative_path)  
{  
    int dir_fd;  
    int fd;  
    int flags;  
    mode_t mode;  
  
    dir_fd = open(dir_path, O_RDONLY);  //fd参数是通过打开相对路径名所在的目录来获取。
    if (dir_fd < 0)   
    {  
        perror("open");  
        exit(EXIT_FAILURE);  
    }  
  
    flags = O_CREAT | O_TRUNC | O_RDWR;  
    mode = 0640;  //-rw-r-----
    fd = openat(dir_fd, relative_path, flags, mode);  
    if (fd < 0)   
    {  
        perror("openat");  
        exit(EXIT_FAILURE);  
    }  
  
    write(fd, "HELLO", 5);  
  
    close(fd);  
    close(dir_fd);  
}  
  
int main()  
{  
    creat_at("../openat", "log.txt");  
    return 0;  
}

运行结果:

 @debian69:~/algoAndSturct/UnixProgramTest/openat$ ./a.out 
@debian69:~/algoAndSturct/UnixProgramTest/openat$ ls
a.out  log.txt  openatTest.c
liubowen@debian69:~/algoAndSturct/UnixProgramTest/openat$ ls -l
总用量 16
-rwxrwx--x+ 1 liubowen liubowen 5636 4月  24 11:35 a.out
-rw-r-----+ 1 liubowen liubowen    5 4月  24 11:35 log.txt
-rw-rwx-w-+ 1 liubowen liubowen  844 4月  24 11:34 openatTest.c
@debian69:~/algoAndSturct/UnixProgramTest/openat$ cat log.txt 
HELLO@debian69:~/algoAndSturct/UnixProgramTest/openat$ 

通过上述结果,可以看到创建文件信息等 。

        openat平时用的不多,主要的用途运用unix高级编程中的一段原文解释:

        open与openat的权限以及flag参数解释,不详细整理。可自行百度

三、creat函数

该函数能被open完全替代,故只需要了解下,曾经被用过

#include<fcntl.h>

 int creat(const char *pathname, mode_t mode);
                                   函数的返回值:若成功,返回文件描述符; 若出错,返回-1

其等效与:

open(path, O_WRONLY | O_CREAT | O_TRUNC)

四、close函数

      #include<unistd.h>

      int close(int fd);
                                   函数的返回值:若成功,返回0; 若出错,返回-1

        关闭一个文件时还会释放该进程加在该文件上的所有记录锁。当一个进程终结时,内核会自动关闭所有打开的文件,很多程序利用这一点,而不显式关闭文件。 好的习惯有开就有关,特殊情况可以考虑让内核去关。

 

五、lseek函数

        1、文件偏移量:通常是一个非负整数,用以度量从文件开始处计算的字节数。通常来讲,文件的偏移量都是非负整数,但某些设备会允许返回负数,但对于普通文件,文件偏移量必须是非负数。故在判断返回值时,不要用 <0,而是用是否 == -1.

        2、如果文件描述符指向一个管道、FIFO或者网络套接字,则lseek返回-1,并将errno设置为ESPIPE

        3、lseek仅将当前的文件偏移量记录在内核中,他并没有引起任何的I/O操作。该偏移量只用于下一次的I/O操作。文件偏移量可以大于文件的当前长度,在这种情况下,对该文件下一次写将加长该文件,并造成文件中的空洞。这些空洞都被读为0。文件中的空洞在磁盘上不占用存储区。当定位到超出文件尾端之后开始写时,对于新写的数据需要分配磁盘块,但是对于源文件尾端和新开始写的数据之间的空洞部分,不需要分配磁盘块。
 

#include<unistd.h>

int lseek(int fd, off_t offset, int whence);
                                   函数的返回值:若成功,返回新文件偏移量; 若出错,返回-1

 

 

六、read函数

#include<unistd.h>

ssize_t read(int fd, void* buf, size_t nbytes);
                                   函数的返回值:读到的字节数,若读到文件尾部则返回 0; 若出错,返回-1

以下有几种情况实际读到的字节数少于要求读到的字节数(注意): 

七、write函数

#include<unistd.h>

ssize_t write(int fd, const void* buf, size_t nbytes);
                                   函数的返回值:以写的字节数; 若出错,返回-1

 

八、I/O的读写效率
​​​​​​​​​​​​​​
       书中用了一个读写文件的小例子,对比了调整buffer大小之后,复制同样大小文件的时间。书中代码如下:

#include "apue.h"

#define    BUFFSIZE    4096

int
main(void)
{
    int        n;
    char    buf[BUFFSIZE];

    while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
        if (write(STDOUT_FILENO, buf, n) != n)
            err_sys("write error");

    if (n < 0)
        err_sys("read error");

    exit(0);
}

可以使用:

        time ./mycat < tmp.txt > file1.txt

进行测试时间(期间不多修改 BUFFSIZE 的大小)

这里要补充一点:

        我们如果想测试复制1G大小的文件,又找不到正好1G大小的文件,可以用一下命令去创建一个1G大小的文件。

 dd if=/dev/zero of=tmp.txt bs=1G count=1

具体的命令解释如下:

 if=FILE      : 指定输入文件,若不指定则从标注输入读取。这里指定为/dev/zero是Linux的一个伪文件,它可以产生连续不断的null流(二进制的0)  
of=FILE      : 指定输出文件,若不指定则输出到标准输出  
bs=BYTES     : 每次读写的字节数,可以使用单位K、M、G等等。另外输入输出可以分别用ibs、obs指定,若使用bs,则表示是ibs和obs都是用该参数  
count=BLOCKS : 读取的block数,block的大小由ibs指定(只针对输入参数)  

相信说明,可见该链接 

        书中用了20种不同的缓冲区做了尝试,其结果如下图:

下一篇​​​​​​​

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值