文件IO系统IO

系统IO
一.引入
    在大学期间大家都学过51单片机,我们要利用51单片机去点LED灯,直接去操控单片机内部的寄存器(硬件),这种直接操控单片机的硬件的方式称之为"裸奔",就是说用户与芯片(硬件)之间没有中间层。我们在利用我们的电脑去做一件事的时候(播放音乐),好像并没有接触寄存器之类的硬件,这是因为我们电脑上的芯片的运行方式是"带OS",意思就是我们与硬件之间还有一层,这一层就是OS(Operating System),我们要去实现某个功能,相当于我们把需求告诉OS,那么OS就会帮我们去操作寄存器去实现功能,那么功能实现完毕之后,硬件就会将结果反馈给OS,OS就会将现象呈现给用户。
    很明显,"带OS"比"裸奔"更好。这是因为随着芯片的发展,内部的CPU中寄存器的种类和数量越来越多,我们肯定不可能去记住这些寄存器的功能和名字,那么记不住怎么办?我们只能请别人来帮我们,我们称OS是管理我们硬件资源的一种软件,那么肯定包括寄存器。
    我们只需要学会怎么去下达命令给OS,非常方便。
    那么怎么下达命令给OS?
    在linux操作系统下,Linux为了简化我们的工作,提高开发效率,它将对寄存器的操作封装成了接口函数,我们并不需要知道这些函数具体是怎么实现这些功能的,但是我们需要知道这些函数是用来干嘛的。比如,Linux下我们要去打开一个文件,我们只需要调用open函数 即可,我们如果要对文件进行读写操作,只需要调用read/write即可。
    我们后续就是要去学习一下这些接口函数的操控。
        
二.Linux是什么?
    Linux是一个开源的操作系统
    设计哲学: 
        Everything is a file,in Linux!
        在linux下,一切皆文件,普通文件是文件,设备也是文件
        也就是说,在linux下面,操作任何东西,都是操作文件。
        在操作系统中有一种软件,叫做文件系统,文件系统的作用是帮助我们管理计算机中纷杂的文件。所以文件系统的完整概念定义为:
        操作系统中负责管理和存储文件信息的软件称之为文件系统。
        文件系统有很多不同的格式,比如FAT,NTFS,EXT3格式等,格式不同,意味着文件管理,存储文件的方式不同,但是不管是哪一种
    文件系统,文件系统会将文件分成两个部分存储:
    1. 文件属性 
        文件的属性比如文件的创建的时间,文件最后一次被修改的时间等时间戳方面的信息,还有文件的大小以及文件的拥有者.....
        那么这些文件属性我们用什么东西进行存储?
        首先我们肯定不能用数组进行数据的存储,因为各个文件属性的类型不一致,最好就是利用结构体。那么实际上,文件系统就是
        用一个结构体去保存这些文件的属性,那么这个结构体名字为 struct inode,我们经常称之为inode节点(有些文献上叫vnode)
        
    2. 文件内容 
        文件中真正存储的内容。
        将图片里面的内容进行一个总结:
            硬件:inode节点  --> 文件内容
            linux的内核中:
                struct inode{}
                用来描述一个文件的物理信息,一个文件对应一个inode结点
                struct file{}
                用来描述一个已经打开的文件
                每一个打开的文件都会对应一个struct file。一个文件可以被不同的进程打开。
                Linux为了简化文件操作的细节,它会为每一个进程创建一个"进程文件表项":保存每个进程打开的文件的struct file *数组
                所以文件的操作过程: 
                0 --> struct file * --> struct file --> struct inode -->inode结点 -->文件内容
                1 --> struct file * --> struct file --> struct inode -->inode结点 -->文件内容
                .....
                所有我们实际上操作文件其实就是操作0,1....等这样的"文件描述符"
                那么文件描述符怎么得到?
                通过调用open函数得到的,它的本质上就是"进程文件表项"的下标,是一个int。"文件描述符"用来描述一个已经打开的文件
                唯一的ID。后续操作文件(read/write),只需要操作文件描述符
                那么像上面用的这种操作文件的函数:open,read,write等接口函数,我们称之为"系统IO"
                
    三. 系统IO 
        在linux系统文件操作的接口函数
        操作文件的步骤: 
            (1)打开文件
            (2)操作文件 
            (3)关闭文件
        1) 打开文件 open 
        NAME
       open - open and possibly create a file
            open在linux下,用来打开或者创建(创建后打开)一个文件 
        
        头文件

#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);


        函数参数: 
            @pathname: 要打开或者创建的文件名,记得带路径。
                如果不带路径,就是当前路径
                "./1.txt"
            @flags:打开文件时的标志。
                O_RDONLY  只读   
                O_WRONLY  只写 
                O_RDWR    可读可写
                以上的三个选项必须选择一个并且只能选择一个
                O_APPEND :追加,打开文件后,文件光标在文件末尾
                O_CREAT  :创建,如果文件不存在的话,则就会先创建再打开
                O_EXCL :这个标志跟上述的O_CREAT配合使用,用来测试文件是否存在的,如果参数二指定O_CREAT|O_EXCL,如果文件
                        存在,就会失败
                O_TRUNC :截短标志,假如文件存在,并且是一个普通文件,而且打开的方式是O_WRONLY或者O_RDWR,则会先清空文件的
                        内容。如果是O_RDONLY|O_TRUNC,不会起作用
                O_NONBLOCK :非阻塞方式打开文件 
                    非阻塞:"不等待"
                    如果文件没有内容,read不会阻塞,直接返回一个错误,如果文件没有空间,write不会阻塞,直接返回一个错误
                    阻塞: "等待"
                    如果文件没有内容,read会阻塞(直到有数据或出错)
                    如果文件没有空间, write会阻塞(直到可写或出错)
                如果上述想用多个选项,则选项之间要用 | 进行连接
            @mode : 权限  
                这个参数只有当创建文件的时候,也就是上面第二个参数有O_CREAT的时候才需要第三个参数。如果不是创建文件
                第三个参数不需要,第三个参数的意思是指定新创建文件的权限,有两种方式可以指定:
                a. 宏
                      S_IRWXU  用户可读可写可执行
                      S_IRUSR  用户可读
                      S_IWUSR  用户可写
                      S_IXUSR  用户可执行
                      S_IRWXG  组用户可读可写可执行 
                      S_IRGRP  组用户可读
                      S_IWGRP  组用户可写
                      S_IXGRP  组用户可执行
                      S_IRWXO  其它用户可读可写可执行
                      S_IROTH  其它用户可读
                      S_IWOTH  其它用户可写
                      S_IXOTH  其它用户可执行
                      比如:S_IRUSR|S_IRGRP|S_IROTH ==> r--r--r--
                b. 用八进制来表示 
                    0666 ==> 110110110 ==> rw-rw-rw-
        返回值: 
            成功返回一个int整数-->文件描述符(>2)
            因为操作系统会为你每个进程都会自动打开三个文件:
                标准输入文件 文件描述符为STDIN_FILENO (0)
                标准输出文件 文件描述符为STDOUT_FILENO (1)
                标准出错文件 文件描述符为STDERR_FILENO  (2)
            失败返回-1,同时errno将被设置
        例子: 
      

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc,char *argv[])
{
    int fd = open(argv[1],O_RDWR);
    if(-1 == fd)
    {
       printf("open %s failed!!\n",argv[1]);
       printf("errno == %d\n",errno);
       perror("open failed");
       return -1;
    }
    printf("OK!!\n");
    printf("fd == %d\n",fd);
    return 0;
}


        以上例子中一旦open出错,将会返回-1,但是我们能知道是因为什么导致出错的吗?如果我们想知道出错的原因,我们就需要利用
    errno。errno就是一个全局变量,我们可以man一下errno,它的作用其实就是用来保存最后一个出错的错误码。
        errno定义在#include <errno.h>
        但是知道错误码还是不知道是什么错误。
        perror把错误码对应的提示打印出来
            perror("open failed");//它是无缓冲,所以不需要换行符
                ==> open failed : 提示信息
    (2)操作文件(read/write/lseek.....)
        read 
    NAME
       read - read from a file descriptor
            从文件描述符内读取内容
    SYNOPSIS
       #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);
           @fd:文件描述符,表示你想从哪一个文件里面读取 -->open函数的返回值
           @buf:指向一块内存的首地址,表示你要把文件里面读取到的内容保存到哪里去 -->数组
           @count: 表示你想读取多少个字节的数据
    返回值:  
        返回大于0或者等于0都表示读取成功:
            >0 :返回你实际上读取到的字节数(<=count)
            =0 :表示已经读取到了文件末尾或者文件为空
        失败返回-1,同时errno将被设置
    
    ----------------------------------------------------------        
    write 
    NAME
       write - write to a file descriptor
        向一个文件描述符内写入数据
    SYNOPSIS
       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);
            @fd:文件描述符
            @buf:指向一块内存,这个内存里面存放的是你即将要写入的数据
            @count: 想写入多少个字节的数据
       返回值:  
            >0  表示实际上写入的字节数
            ==0 表示什么也没写
            ==-1 表示出错,同时errno被设置
        例子: 

int main(int argc,char *argv[])
{
    printf("argc==%d\n",argc);
    printf("argv[0]:%s\n",argv[0]);
    printf("argv[1]:%s\n",argv[1]);
    printf("argv[2]:%s\n",argv[2]);
    int fd = open(argv[1],O_RDWR);
    if(-1 == fd)
    {
        perror("open failed");
        return -1;
    }
    char buf[100]="helloworld!!";
    int ret = write(fd,buf,strlen(buf));
    if(-1 == ret)
    {
        perror("write failed");
        return -1;
    }
    printf("ret == %d\n",ret);
    return 0;
}


    
    假如有一个文本4.txt : aaabbbccc 
    打开这个文件后,首先读取了3个字节的内容并打印
    然后再写入3个字节的内容: 111
    最后请猜测4.txt里面的内容: 
        aaa111ccc
    从上述的测试就能够得到文件有个"光标"属性,当我们打开文件的时候,此时光标在文件的开头,此时称之为文件的偏移量(光标距离文件开头的字节)为0,此时我们每次进行read/write成功的操作都会使光标往后移动,移动的字节跟你read/write的字节一样
    --------------------------------------------------------
    lseek 
        NAME
       lseek - reposition read/write file offset
            移动读/写的光标偏移量
    SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

    off_t lseek(int fd, off_t offset, int whence);
        @fd:文件描述符
        @offset:偏移量,就是你本次想要改变的大小
            >0  :往后偏移
            ==0 :不偏移
            <0  :向前偏移
        @whence:定位方式,有如下的三种: 
            SEEK_SET  : 基于文件的开头位置 
            SEEK_END  : 基于文件的末尾位置
            SEEK_CUR  : 基于文件的当前位置 
    例子:  
        lseek(fd,-5,SEEK_END);//基于文件末尾再往前偏移5个字节
        lseek(fd,0,SEEK_END);//新光标就在文件末尾
        
    返回值:  
        成功的话,返回新光标距离文件开头的字节数
        失败返回-1
    例子:  
        int size = lseek(fd,0,SEEK_END);//size表示就是文件的大小
    ---------------------------------------------------------------------------------
    3)关闭文件
    close
    NAME
       close - close a file descriptor
        关闭一个文件描述符
    SYNOPSIS
       #include <unistd.h>

       int close(int fd);
            @fd:就是你想要关闭的那个文件的文件描述符
            

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值