zoukankan      html  css  js  c++  java
  • UNIX环境高级编程(5):文件I/O(1)

    UNIX系统中的大多数文件I/O仅仅须要用到5个函数:open、read、write、lseek以及close。本章说明的函数常常称为“不带缓冲的I/0”,术语不带缓冲指的是每一个read和write都调用内核中的一个系统调用。

    这些不带缓冲的I/O函数不是ISO C的组成部分,可是它们是POSIX.1和Single UNIX Specification的组成部分。

    文件描写叙述符:

    对内核而言。全部打开的文件都通过文件描写叙述符引用。

    文件描写叙述符是个非负整数。依照惯例,UNIX系统shell使用文件描写叙述符0与进程的标准输入相关联。文件描写叙述符1与标准输出相关联,文件描写叙述符2与标准出错相关联。这是各种shell以及很多应用程序使用的惯例。而与UNIX内核无关。在依从POSIX的应用程序中。幻数0、1、2应当替换成符号常量STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,这些常量都定义在<unistd.h>中。

    文件描写叙述符的变化范围是0-OPEN_MAX。

    open函数:

    调用open函数能够打开或创建一个文件:

    #include <fcntl.h>

    int open(const char *pathname,  int flag,  .../* mode_t mode */);

    返回值,若成功则返回文件描写叙述符,若出错则返回-1

    pathname:是要打开或创建文件的名字,flag參数能够说明此函数的多个选项,用下列一个或多个常量进行或运算,构成flag參数。

    • O_RDONLY:仅仅读打开;
    • O_WRONLY:仅仅写打开。
    • O_RDWR:读写打开;
    上面三个常量必须指定一个且仅仅能指定一个,下列常量则是可选择的:
    • O_APPEND:每次写时都追加到文件的尾端;
    • O_CREAT:若文件不存在,则创建它。使用此选项时。须要第三个參数mode,用于指定新文件的訪问权限位。
    • O_EXCL:若同一时候指定了O_CREAT,而文件已经存在,则会出错。用此能够測试一个文件是否存在,假设不存在。则创建此文件。

      这使得測试和创建成为一个原子操作。

    • O_TRUNC:假设文件存在,并且为仅仅写或读写成功打开,则将其长度截短为0;
    • O_NOCTTY:假设pathname指定的是终端设备。则不将该设备分配作为此进程的控制终端。
    • O_NONBLOCK:假设pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本次打开操作和兴许I/O操作设置为非堵塞模式;
    以下三个标志也是可选的。他们是SUS中同步输入和输出选项的一部分:
    • O_DSYNC:使每次write等物理I/O操作完毕,可是假设写操作并不影响读取刚写入的数据,则不等待文件属性被更新;
    • O_SYNC:使每次write都等到物理I/O操作完毕。包含由write操作引起的文件属性更新所需的I/O(数据和属性总是同步更新);
    • O_RSYNC:使每个以文件描写叙述符作为參数的read操作等待,直至不论什么对文件同一部分的未决写操作都完毕。

    open函数返回的文件描写叙述符一定是最小的未用描写叙述符数值。这一点被某些应用程序用来在标准输入、标准输出或标准错误上打开新的文件。

    文件名称与路径名截短:

    在POSIX.1中,常量_POSIX_NO_TRRUNC决定了是否要截短过长的文件名称或路径名。还是返回一个出错。依据文件系统类型。此值可变。若_POSIX_NO_TRUNC有效。则在整个路径名超过PATH_MAX,或路径名中的随意文件名称超过NAME_MAX时。返回出错状态,并将errno设置为ENAMETOOLONG。

    creat函数:

    也能够调用creat函数创建一个新文件:

    #include <fcntl.h>

    int creat(const char *pathname, mode_t mode);

    返回值:若成功,则返回仅仅写打开的文件描写叙述符,若出错则返回-1。

    此函数等效于:open(pathname,O_WRONLY | O_CREAT | O_TRUNC, mode)。creat函数的一个不足之处是它以仅仅写方式打开所创建的文件。

    close函数:

    可调用close函数关闭一个打开的文件:

    #include <unistd.h>

    int close(int fieldes)

    返回值:若成功则返回0,若出错则返回-1。

    关闭一个文件时。还会释放该进程加在该文件上的全部记录锁。当一个进程终止时。内核自己主动关闭它全部打开的文件。非常多程序都利用这一功能而不显示地用close关闭打开文件。

    lseek函数:

    每一个打开的文件都有一个与其相关联的当前文件偏移量。

    它一般是个非负整数,用以度量从文件開始处计算的字节数。通常,读写操作都从当前文件偏移量处開始,并使偏移量添加所读写的字节数。

    依照系统默认情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量设置为0。可调用lseek函数显式地为一个打开的文件设置其偏移量:

    #include <unistd.h>

    off_t lseek(int fieldes, off_t offset, int whence);

    返回值:若成功,则返回新的文件偏移量,若出错则返回-1。

    对參数offset的解释与參数whence的值有关:

    • 若whence是SEEK_SET,则将该文件的偏移量设置为距文件開始处offset个字节;
    • 若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可为正或负。
    • 若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset。offset可为正或负。

    因此可用例如以下方式确定打开文件的当前偏移量:

    offset currpos

    currpos = lseek(fd, 0, SEEK_CUR);

    用上述方法还能够确定所涉及的文件能否够设置偏移量。假设文件描写叙述符引用的是一个管道,FIFO或网络套接字,则leek返回-1,并将errno设置为ESPIPE。

    下列程序用于測试是否能对其标准输入设置偏移值:

    /*
     * Copyright (C) fuchencong@163.com
     */
    
    
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    
    int
    main(void)
    {
    	if (lseek(STDIN_FILENO, 0, SEEK_CUR) < 0) {
    		printf("can't seek
    ");
    	} else {
    		printf("seek ok
    ");
    	}
    	exit(0);
    }
    

    通常文件的当前偏移量应当是个非负整数。可是某些设备也可能同意负的偏移量。

    可是对于普通文件,其偏移量必须是非负值。lseek仅将当前的文件偏移量记录在内核中,它并不引起不论什么I/O操作。

    文件偏移量能够大于文件的当前长度,这样的情况下,对该文件的下一次写将加长该文件。并在文件里构成一个空洞。位于文件里但没有写过的字节都被读为0。文件里的空洞并不要求在磁盘上占用存储区,详细处理方式与文件系统的实现有关。

    下列程序用于创建一个具有空洞的文件:

    /*
     * Copyright (C) fuchencong@163.com
     */
    
    
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    
    #define FILE_NAME "file.hole"
    
    
    int
    main(void)
    {
    	char buf_one[] = "abcdefghij";
    	char buf_two[] = "ABCDEFGHIJ";
    	int fd;
    
    	if ( (fd = creat(FILE_NAME, S_IRUSR | S_IWUSR)) < 0) {
    		printf("create file %s error: %s
    ", FILE_NAME, strerror(errno));
    		exit(1);
    	} 
    	
    	if (write(fd, buf_one, 10) != 10) {
    		printf("write error: %s
    ", strerror(errno));
    		exit(2);
    	}
    	
    	if (lseek(fd, 16384, SEEK_SET) == -1) {
    		printf("lseek error: %s
    ", strerror(errno));
    		exit(3);
    	}
    
    	if (write(fd, buf_two, 10) != 10) {
    		printf("write error: %s
    ", strerror(errno));
    		exit(4);
    	}
    
    	exit(0);
    }
    

    read函数:

    调用read函数从打开文件里读取数据:

    #include <unistd.h>

    ssize_t read(int fields, void *buf, size_t nbytes);

    返回值:若成功则返回读到的字节数。若已到文件结尾则返回0,若出错返回-1。

    ssize_t和size_t都是基本系统数据类型,ssize_t为带符号的整数,size_t为不带符号的整数。

    有多种情况可使实际读到的字节数少于要求读的字节数:

    • 在读普通文件时。在读到要求的字节数之前已经到达了文件尾端;
    • 从终端设备读时,通常一次最多读一行;
    • 当从网络读时。网络中的缓冲机构可能造成返回值小于所要求读的字节数;
    • 当从管道或FIFO读时。假设管道包括的字节少于所需的数量,那么read将仅仅返回实际可用的字节数。
    • 当从某些面向记录的设备(比如磁带)读时,一次最多返回一个记录;
    • 当某一信号造成中断。而已经读了部分数据量时;

    write函数:

    调用write函数向打开的文件写数据:

    #include <unistd.h>

    sszie_t write(int fields。const void *buf, size_t nbytes)

    若成功则返回已写的字节数,若出错则返回-1。

    其返回值通常与參数nbytes的值同样,否则表示出错。write出错的一个常见原因是:磁盘已写满,或者超过了一个给定进程的文件长度限制。

    I/O的效率:

    下列程序使用read和write函数复制一个文件:

    /*
     * Copyright (C) fuchencong@163.com
     */
    
    
    #include <unistd.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    
    #define BUFSIZE 4096
    
    
    int
    main(void)
    {
    	int n;
    	char buf[BUFSIZE];
    
    	while ( (n = read(STDIN_FILENO, buf, BUFSIZE)) > 0) {
    		if (write(STDOUT_FILENO, buf, n) != n) {
    			printf("write error: %s
    ", strerror(errno));
    			exit(1);
    		}
    	}
    
    	if (n < 0) {
    		printf("read error: %s
    ", strerror(errno));
    		exit(2);
    	}
    
    	exit(0);
    }
    

    不同的BUFSIZE对程序的执行时间有很大的影响。

    系统CPU时间的最小值出如今BUFFSIZE为4096处(一个block的大小),继续增大缓冲区长度对此时间差点儿没有影响。



  • 相关阅读:
    设计模式之工厂模式-抽象工厂(02)
    1036 跟奥巴马一起编程 (15 分)
    1034 有理数四则运算 (20 分)
    1033 旧键盘打字 (20 分)
    1031 查验身份证 (15 分)
    大学排名定向爬虫
    1030 完美数列 (25 分)二分
    1029 旧键盘 (20 分)
    1028 人口普查 (20 分)
    1026 程序运行时间 (15 分)四舍五入
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7093157.html
Copyright © 2011-2022 走看看