zoukankan      html  css  js  c++  java
  • Linux文件IO(简易)

    Linux中文件分为以下几类:

    普通文件,即一般意义上的文件、磁盘文件;
    设备文件,代表的是系统中一个具体的设备;
    管道文件、FIFO 文件,一种特殊文件,常用于进程间通信;
    套接字(socket)文件,主要用在网络通信方面。

    跟C/C++类似,Linux对文件IO的操作也无非就是打开 关闭 读 写 

    我们这里复习一下C语言的文件操作Qt的文件操作

    大致是类似的

    我开始确实是这样以为的。

    学了一下才发现,原来open read之流,也只不过九牛一毛 沧海一粟 。。

    先说一下文件描述符

    是一个非负的整数,它是一个索引值,并指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,
    内核就向进程返回一个文件描述符;当需要读写文件时, 也需要把文件描述符作为参数传递给相应的函数。

    或者叫句柄

    类似于你号码牌,通过这个号,就可以找到指定的文件

    通过这个操作可以查看文件描述符的取值范围,也就是说一个进程最多只能打开1024个文件

    然后看一下基本的操作:

    首先惯例加入头文件

    #include <sys/types.h> /* 定义数据类型,如 ssize_t,off_t 等 */
    #include <fcntl.h> /* 定义 open,creat 等函数原型,创建文件权限的符号常量 S_IRUSR 等 */
    #include <unistd.h> /* 定义 read,write,close,lseek 等函数原型 */
    #include <errno.h> /* 与全局变量 errno 相关的定义 */
    #include <sys/ioctl.h> /* 定义 ioctl 函数原型 */

    打开文件

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

    参数:

     

     返回值:成功返回文件描述符,失败返回 -1

    关闭文件

    int close(int fd);

    直接传入刚才获得的文件描述符,则可关闭文件

    当一个进程终止时,内核会自动关闭该进程所打开的文件

    我们看个例子

    int main(void) 
    { 
        //文件描述符
        int fd; 
    /*调用 open 函数,以可读写的方式打开,注意选项可以用“|”符号连接*/ 
        if((fd = open("/tmp/hello.c", O_CREAT | O_TRUNC | O_WRONLY , 0600 ))<0){ 
            perror("open:"); 
            exit(1); 
        } 
        else{ 
            printf("Open file: hello.c %d
    ",fd); 
        } 
        //关闭该文件
        if( close(fd) < 0 ){ 
            perror("close:"); 
            exit(1); 
        } 
        else 
            printf("Close hello.c
    "); 
        
        exit(0); 
    }

    例子中使用 

    O_CREAT | O_TRUNC | O_WRONLY

    对照参数表,可知表示:只写方式打开文件,打开时擦除文件内容,如果文件不存在则创建。

    这里说一下 return exit(0)  和 exit(1)

    exit(0):正常运行程序并退出程序;

    exit(1):非正常运行导致退出程序;

    return():返回函数,若在主函数中,则会退出函数并返回一值。

    详细说:

    1. return返回函数值,是关键字; exit 是一个函数。

    2. return是语言级别的,它表示了调用堆栈的返回;而exit是系统调用级别的,它表示了一个进程的结束。
    3. return是函数的退出(返回);exit是进程的退出。

    4. return是C语言提供的,exit是操作系统提供的(或者函数库中给出的)。

    5. return用于结束一个函数的执行,将函数的执行信息传出个其他调用函数使用;exit函数是退出应用程序,删除进程使用的内存空间,并将应用程序的一个状态返回给OS,这个状态标识了应用程序的一些运行信息,这个信息和机器和操作系统有关,一般是 0 为正常退出,非0 为非正常退出。

    6. 非主函数中调用return和exit效果很明显,但是在main函数中调用return和exit的现象就很模糊,多数情况下现象都是一致的。

    读取数据

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

    先看一下这个返回值类型  ssize_t

    /* 在 32 位系统中 */
    typedef int ssize_t; /* 32 位有符号整型值 */
    typedef unsigned int size_t; /* 32 位无符号整型值*/
    /* 在 64 位系统中 */
    typedef long int ssize_t; /* 64 位有符号整型值 */
    typedef unsigned long int size_t; /* 64 位无符号整型值 */

    也就是说,这个东西就是 int

    几个参数很好理解

    fd        文件描述符

    buf      存放数据的地址

    count  读出数据的大小

    返回值:如果成功,返回读出的数据字数;如果失败返回 -1 ;读至文件尾 ,返回 0.

    写入数据

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

    和read相同,buf是要写入的数据的地址。

    返回值: 成功返回写入数据数,失败返回-1

    看个例子:

    int main(void) 
    { 
        int i,fd,size,len; 
        char *buf="Hello! I'm writing to this file!"; 
        char buf_r[10]; 
        len = strlen(buf); 
    
    /*首先调用 open 函数,并指定相应的权限*/ 
        if((fd = open("/tmp/hello.c", O_CREAT | O_TRUNC | O_RDWR,0666 ))<0){ 
            perror("open:"); 
            exit(1); 
        } 
        else 
            printf("open file:hello.c %d
    ",fd); 
    
    /*调用 write 函数,将 buf 中的内容写入到打开的文件中*/ 
        if((size = write( fd, buf, len)) < 0){ 
            perror("write:"); 
            exit(1); 
        } 
        else 
            printf("Write:%s
    ",buf); 
    
    /*调用 lsseek 函数将文件指针移到文件起始,并读出文件中的 10 个字节*/ 
        lseek( fd, 0, SEEK_SET ); 
        if((size = read( fd, buf_r, 10))<0){ 
            perror("read:"); 
            exit(1); 
        } 
        else 
            printf("read form file:%s
    ",buf_r); 
    
    /*关闭文件*/
        if( close(fd) < 0 ){ 
            perror("close:"); 
            exit(1); 
        } 
        else 
            printf("Close hello.c
    "); 
    
    
        exit(0); 
    }

    这里有个问题,你使用write函数,写入一串数据进文件,不一定真的写入了磁盘等储存设备中

    为了确保文件保存了你修改的数据,我们这里使用一个函数用于强制文件数据同步

    int fsync(int fd);

    成功返回 0 ;失败返回 -1 .

    刚才我们的例子中,是直接从文件头读了数据

    那如果你要从随机或者指定的一个地方读数据,就需要重新定位读写位置

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

    这里 off_t 也是有符号整型,可以理解为32位的 long  int

    fd            文件描述符

    offset      偏移量  可正可负 分别表示向前向后移动

    whence   基点 有三种: 

    SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小 
    SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量 
    SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小

    返回值:成功返回当前偏移量,失败返回  -1。

    看个例子:

    #include <sys/types.h> /* 定义数据类型,如 ssize_t,off_t 等 */
    #include <fcntl.h> /* 定义 open,creat 等函数原型,创建文件权限的符号常量 S_IRUSR 等 */
    #include <unistd.h> /* 定义 read,write,close,lseek 等函数原型 */
    #include <errno.h> /* 与全局变量 errno 相关的定义 */
    #include <sys/ioctl.h> /* 定义 ioctl 函数原型 */
    #include <string.h> 
    #include <stdio.h>
    #include <stdlib.h>
    
    
    
    int main()
    {
        int fd;
        char *readbuf;
        char *writebuf = "111111111111111111";
        char *buf = "000000000";
    
        if((fd = open("/mnt/hgfs/share/test.c", O_CREAT | O_RDWR,0777 ))<0){ 
            //打开失败
            exit(1); 
        } 
        //写入数据
        write(fd, writebuf, strlen(writebuf));
        //文件数据同步
        fsync(fd);
        system("cat test.c");
    
        lseek(fd, 6, SEEK_SET);
    
        //再次写入数据
        write(fd, buf, strlen(buf));
        fsync(fd);
    
        system("cat test.c");
    
        
        close(fd);
        return 0;
    }

     执行hello之后,出现了2次cat test.c

    第一次全是111111111111

    第二次中间加入了00000000

    还有一些IO操作归不到read write中,所以加入了一个函数 ioctl()

    一些硬件操作 寄存器操作也都在该函数中

    int ioctl(int fd, int cmd, …);

    参数比较简单,分别是文件描述符 和 操作指令

    这里的操作指令肯定都是宏定义。


    后记:

    我在后面试用的时候出现了问题,记录一下。

    linux环境下,我代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h> /* 定义数据类型,如 ssize_t,off_t 等 */
    #include <fcntl.h> /* 定义 open,creat 等函数原型,创建文件权限的符号常量 S_IRUSR 等 */
    #include <unistd.h> /* 定义 read,write,close,lseek 等函数原型 */
    #include <errno.h> /* 与全局变量 errno 相关的定义 */
    #include <sys/ioctl.h> /* 定义 ioctl 函数原型 */
    
    int main(int argc, char const *argv[])
    {
        int fd;    
        int ret;
        char *buf = "hello hello hello";
        fd = open("/mnt/hgfs/share/txt",O_RDWR|O_CREAT,0x777);
        if(0 > fd)
        {
            perror("open file failed");
        }
      lseek(fd,0,SEEK_END); write(fd,buf,strlen(buf)); fsync(fd); close(fd); exit(
    0); }

    运行如下:

     也就是说,再没有txt这个文件的时候,可以正常创建。

    已经创建完成后,在运行该代码就会失败提示错误:拒绝访问

    然后我使用  ls -la   查看了一下文件的权限

     没有写入权限

    那么我用 chmod 给他加满权限

    则可以正常运行了

    那么问题来了,为什么我创建的 txt 文件会没有写入权限

    所以我将

     改成了

     这样就可以运行很多次不会报错了

    中途我 试过 0x700 也不可以

    不知道为什么,记住以后这样做,用宏定义。

    接下来我又:

    char *readbuf = NULL;
    
    .....
    .....
    
    
    lseek(fd,5,SEEK_SET);
    ret = read(fd,readbuf,5);
    printf("%d
    ",ret);

    想要读出距离文件头5个字节处的数据

    结果被报错

    为了解决错误,我尝试了2种方案

    1、

    char readbuf[5];

    2、

    char *readbuf = (char *)malloc(sizeof(char*));

    运行结果显示,这两种方法都可以正常读取文件。

    经过排查发现如果我注释掉

    fsync(fd);

    那么即便在栈区申请空间也不会报错了。

    目前这个函数对我来说仍是个迷 - -

    解决再更新

    lseek(fd,5,SEEK_SET);ret = read(fd,readbuf,5);printf("%d ",ret);

  • 相关阅读:
    curl post请求
    c++/c字符串操作汇集
    CCClippingNode bug
    处理Google Play的相关方法
    libcurl断点下载
    c pvr转存pvr.ccz格式
    ios 设备震动
    cocos2d-x android项目引用so库编译
    cocos2d-x CCEditBox 字符不能显示完全的bug
    Android增量更新
  • 原文地址:https://www.cnblogs.com/qifeng1024/p/12925841.html
Copyright © 2011-2022 走看看