linux文件IO底层读写操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013224233/article/details/82018104

1.底层文件访问

    

    运行中的程序成为进程,每个进程都有与之关联的文件描述符。

 

    文件描述符 - 一些小值整数,通过他们访问打开的文件或设备。开始运行会有三个文件描述符:

  1. 0:   标准输入   STDIN_FILENO
  2. 1:   标准输出   STDOUT_FILENO
  3. 2:   标准错误   STDERR_FILENO

 

    文件描述符的变化范围是:0~OPEN_MAX-1 (可通过ulmit -a 查看)

 

write 系统调用

作用:把缓冲区buf 的前count 个字节写入与文件描述符 fd 相关联的文件中。

#include <unistd.h>

size_t write(int fd,const void *buf, size_t count);

 

描述符出错,文件达到进程限制最大值或设备驱动程序对数据块 长度比较敏感,该返回值可能会小于count,这并不是一个错误。 0 表示未写入数据; -1 表示出错,错误代号在全局变量 errno里。

代码演示

    

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(){
    const char * output = "Hello world\n";
    const char * errstr = "A write error has occurred on file descriptor 1\n";

    //if(write(STDOUT_FILENO,output,strlen(output))!=strlen(output))
    if(write(1,output,strlen(output))!=strlen(output))
        write(2,errstr,strlen(errstr));
    exit(0);

read 系统调用

作用:从与文件描述符 fd 相关联的文件中读取前count 个字节到缓冲区buf 中。

#include <unistd.h>

size_t read(int fd,const void *buf, size_t count);

 

它返回实际读入的字节数,这可能会小于请求的字节数。 0 表示未读入任何数据,已到达了文件尾部。 -1 表示出错,错误代号在全局变量 errno里。

#include <unistd.h>
#include <stdlib.h>

int main(){
    char buffer[128];
    int nread;
    nread = read(0,buffer,128);

    if(nread == -1)
        write(2,"A read error has occurred\n",26);

    if((write(1,buffer,nread)) != nread)
        write(2,"A write error has occurred\n",27);

    exit(0);
}
 

open 系统调用

作用:创建一个新的文件描述符(文件或设备)。

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

 

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

 

open 建立一条到文件或设备的访问路径。成功后可获得供 read、write和其他系统调用使用的唯一的文件描述符。此文件描述符进程唯一;如果两个程序打开同一个文件,那么,他们分别得到不同的文件描述符,并可以单独对文件进行独立的操作。我们可以通过文件锁(O_EXCLFCNTL)功能来解决这个问题。

 

参数说明:

pathname - 指示准备打开的文件或设备的名字;

flags    - 用于指定打开文件所采取的动作;

mode    - 用于指定创建文件的权限,O_CREATE 才使用。

 

flags 参数通过必需文件访问模式 与 其他可选模式相结合的方式来指定。 首先必须指定如下文件访问模式之一:

模     式

说    明

O_RDONLY

以只读方式打开

O_WRONLY

以只写方式打开

O_RDWR

以读写方式打开

 

 

可选模式组合

  1. O_APPEND: 把写入数据最佳在文件的末尾。
  2. O_TRUNC:  打开文件时把文件长度设置为零,丢弃已有的内容。
  3. O_CREAT:   如果需要,就按参数mode 中给出的访问模式创建文件。
  4. O_EXCL:   与O_CREAT一起使用,确保创建文件的 原子操作。如果文件存在,创建

               将失败

 

访问权限的初始值

单个权限设置:   S_I  RWX     USR GRP OTH  

读写执行全权限:  S_I  RWX            U G O

 

如:S_IRUSR   读权限    文件属主

    S_IRWXO  读写执行  其他用户

最终权限生成还和进程设置的 umask 权限掩码有关,执行umask 命令或函数可改变权限.

新文件描述符总使用未用文件描述符的最小值。如果一个文件符被关闭再次调用open ,其马上会被重用。

 

Posix 规定了一个 creat 调用:  等同于  O_CREAT|O_WRONLY|O_TRUNC

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>

#define TOTAL  10

int main(int argc, char ** argv)
{
    const char * TEXT = "This is a test.\n";
    const char * filename = "./write.txt";
    int fd = -1;
    int i = 0;

    printf("file limit: %d\n",OPEN_MAX);

    fd = open(filename, O_RDWR|O_TRUNC|O_CREAT,S_IRWXU|S_IRWXG|S_IRWXO);
    if(fd<0)
    {
        fprintf(stderr, "fopen %s failed, reason: %s. \nexit.\n",filename,strerror(errno));
    return -1;;
    }
    
    
    printf("Start to sleep 10 seconds\n");
    for( i=0; i<TOTAL; i++)
    {
        if(writeToFile(fd,TEXT,strlen(TEXT)) < 0)
        {
            fprintf(stderr, "write to %s failed, reason: %s. \nexit.\n", filename, strerror(errno));
        }

        printf(" %d\n",i+1);
    }
    printf(" finished.\n");

    close(fd);
    return 0;
}

int writeToFile(int fd, char * buf, int len)
{
    int wlen=0;
    if((wlen = write(fd,buf,len)) < 0)
    {
        fprintf(stderr, "write to %d failed, reason: %s. \n", fd, strerror(errno));
        return -1;
    }
    
    return wlen;
}
 

close 系统调用

作用:终止文件描述符fd 和对应文件(文件或设备)的关联。文件描述符被释放并能够重新使用。close 调用成功返回0,出错返回 -1。

 

#include <unistd.h>

int close(int fd);

 

返回值: 检查 close 调用的返回值很重要。可以检测某些写操作错误!

 

ioctl 系统调用

ioctl 提供了一个用于控制设备及其描述行为和配置底层服务的接口。终端、文件描述符、套接字都可以定义他们的ioctl,具体需要参考特定设备的手册。

 

#include <sys/ioctl.h>

 

int ioctl(int d, int request, ...);

 

dup和dup2 的系统调用

作用:提供了一种复制文件描述符的方法,是我们通过两个或者更多个不同的描述符来访问同一个文件,主要用于多个进程间进行管道通信。

      #include <unistd.h>

 

       int dup(int oldfd);

       int dup2(int oldfd, int newfd);

lseek系统调用

作用:lseek 对文件描述符 fd 的读写指针进行设置。也就是说,设置文件的下一个读写位置。可根据绝对位置和相对位置(当前位置或文件尾部)进行设置。

#include <sys/types.h>

#include <unistd.h>

 

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

 

offset 参数用来指定位置,而whence 参数定义该偏移值的用法。Whence 可取值如下:

 

  1. SEEK_SET:     offset 是一个绝对位置。
  2. SEEK_CUR:    offset 是相对于当前位置的一个相对位置。
  3. SEEK_END:    offset 是相对于文件尾的一个相对位置。

 

lseek 返回从文件头到文件指针被设置处的字节偏移值,失败时返回-1

 

fstat、stat和lstat系统调用

作用:获取文件的状态信息,该信息将会写入一个buf 结构中,buf 的地址会以参数形式传递给fstat。

 

   #include <sys/types.h>

       #include <sys/stat.h>

       #include <unistd.h>

 

       int stat(const char *path, struct stat *buf);

       int fstat(int fd, struct stat *buf);

       int lstat(const char *path, struct stat *buf);

 

stat 和 lstat 均通过文件名查询状态信息,当文件名是符号链接时,lstat返回的时符号链接本身的信息,而stat 返回的时改链接指向的文件的信息。

 

           struct stat {                                            

               dev_t     st_dev;     /* ID of device containing file */   

               ino_t     st_ino;     /* inode number */              

               mode_t    st_mode;    /* protection */               

               nlink_t   st_nlink;   /* number of hard links */          

               uid_t     st_uid;     /* user ID of owner */            

               gid_t     st_gid;     /* group ID of owner */           

               dev_t     st_rdev;    /* device ID (if special file) */   

               off_t     st_size;    /* total size, in bytes */          

               blksize_t st_blksize; /* blocksize for filesystem I/O */     

               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */

               time_t    st_atime;   /* time of last access */          

               time_t    st_mtime;   /* time of last modification */    

               time_t    st_ctime;   /* time of last status change */    

           };                                                  

 

这里要特别提到的是,以上 st_mode 标志有一系列相关的宏,定义见 sys/stat.h 中

,可用来测试文件类型

 

错误处理

许多系统调用和函数都会因为各种各样的原因失败。他们失败时设置外部变量errno 来知名失败原因。许多不同函数库都把这个变量用做报告错误的标准方法。

注意: 程序必须在函数报告出错 之后立刻检查errno 变量,因为它可能马上就被下一个函数调用所覆盖,即使下一个函数没有出错,也可能会覆盖这个变量。

 

常用错误代码的取值和含义如下:

  1.  EPERM:     操作不允许
  2.  ENOENT:   文件或目录不存在。
  3.  EINTR:     系统调用被中断。
  4.  EAGAIN:    重试,下次有可能成功!
  5.  EBADF:     文件描述符失效或本身无效
  6.  EIO:        I/O错误。
  7.  EBUSY:     设备或资源忙。
  8.  EEXIST:     文件存在。
  9.  EINVL:      无效参数。
  10.  EMFILE:     打开的文件过多。
  11.  ENODEV:    设备不存在。
  12.  EISDIR:      是一个目录。
  13. ENOTDIR:     不是一个目录。

 

两个有效函数可报告出现的错误: strerror  和 perror。

 

strerror 函数

  作用:把错误代号映射成一个字符串,该字符串对发生的错误类型进行说明。

   #include <string.h>                                                    

   char *strerror(int errnum);                                               

   int strerror_r(int errnum, char *buf, size_t buflen);                            

 

perror函数

  作用:perror 函数也把errno 变量中报告的当前错误映射成一个字符串,并把它输出到标准错误输出流。

 

perror(“test”);                                                            

结果:

Test: Too many open files

 

 

 

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h> 
#include <stdlib.h>


#define  BUFSIZE   1024
#define  SUCCESS   0
#define  FAILURE   -1

int write_to_file (int fd, const char * buf, const size_t nbytes){
    size_t left =  nbytes;
    size_t written_len = 0;
    const char * p_tmp = buf;


    while(left > 0){
        written_len = write(fd, p_tmp,left);
        
        if(written_len<0){
        written_len = -errno;
            
            if(written_len == EINTR || written_len == EAGAIN ){
                continue;
            }
            if(written_len == EBADF){
                //重新open 这个文件,对它进行重写
                break;
            }
            //
            fprintf(stderr,"write failed. reason:%s\n",strerror(errno));
            return FAILURE;

    }else if( 0 == written_len ){
            break;
        }

        left -= written_len;
        p_tmp += written_len;
    }

    if( 0 != left) return FAILURE;
    return SUCCESS;

}


int main(int argc,char **argv){

    int fd = 0;

    fd = open("./demo.txt",O_RDWR|O_CREAT,S_IRWXU|S_IRGRP|S_IROTH);

    if(fd == -1){
        fprintf(stderr,"open failed.reason: %s\n",strerror(errno));
        exit(-1);
    }

    off_t size = lseek(fd,0,SEEK_END);
    char buf[BUFSIZE];
    int ret = 0;
    int i = 0;

    memset(buf,'8',BUFSIZE);

    for(i=0;i<10;i++){
        ret = write_to_file(fd,buf,BUFSIZE);
        if(ret == FAILURE) {
            //log
            break;
        }
    }

    close(fd);
    return 0;

}

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/u013224233/article/details/82018104