linux中文件IO

一. linux常用文件IO接口

  1.1. 文件描述符  

    1.1.1. 文件描述符的本质是一个数字,这个数字本质上是进程表中文件描述符表的一个表项,进程通过文件描述符作为index去索引查表得到文件表指针,再间接访问得到这个文件对应的文件表。

    1.1.2. 文件描述符这个数字是open系统调用内部由操作系统自动分配的,操作系统分配这个fd时也不是随意分配,也是遵照一定的规律的:fd从0开始依次增加。fd也是有最大限制的,在linux的早期版本中(0.11)fd最大是20,所以当时一个进程最多允许打开20个文件。linux中文件描述符表是个数组(不是链表),所以这个文件描述符表其实就是一个数组,fd是index,文件表指针是value。当我们去open时,内核会从文件描述符表中挑选一个最小的未被使用的数字给我们返回。

    1.1.3. fd中0、1、2已经默认被系统占用了,因此用户进程得到的最小的fd就是3了。这三个文件对应的fd就是0、1、2。这三个文件分别叫stdin、stdout、stderr。也就是标准输入、标准输出、标准错误。

  1.2. open

    1.2.1. 在linux系统中要操作一个文件,一般是先open打开一个文件,得到一个文件描述符,然后对文件进行读写操作(或其他操作),最后close关闭文件即可

    1.2.2. 文件平时是存在块设备中的文件系统中的,我们把这种文件叫静态文件。当我们去open打开一个文件时,linux内核做的操作包括:内核在进程中建立了一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内存中特定地址管理存放(叫动态文件)。

    1.2.3. 打开文件后,以后对这个文件的读写操作,都是针对内存中这一份动态文件的,而并不是针对静态文件的。当我们对动态文件进行读写后,此时内存中的动态文件和块设备中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。这样做主要由于:块设备本身有读写限制(回忆NnadFlash、SD等块设备的读写特征),本身对块设备进行操作非常不灵活。而内存可以按字节为单位来操作,而且可以随机操作(内存就叫RAM,random),很灵活。所以内核设计文件操作时就这么设计了。      

  1.3. read

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

      a. fd表示要读取哪个文件,fd一般由前面的open返回得到
      b. buf是应用程序自己提供的一段内存缓冲区,用来存储读出的内容
      c. count是我们要读取的字节数
      d. 返回值ssize_t类型是linux内核用typedef重定义的一个类型(其实就是int),返回值表示成功读取的字节数。

  1.4. write

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

    1.4.1. 写入用write系统调用,write的原型和理解方法和read相似

  1.5. lseek

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

    1.5.1. 文件指针:当我们要对一个文件进行读写时,一定需要先打开这个文件,所以我们读写的所有文件都是动态文件。动态文件在内存中的形态就是文件流的形式。

    1.5.2. 在动态文件中,我们会通过文件指针来表征这个正在操作的位置。所谓文件指针,就是我们文件管理表这个结构体里面的一个指针。所以文件指针其实是vnode中的一个元素。这个指针表示当前我们正在操作文件流的哪个位置。这个指针不能被直接访问,linux系统用lseek函数来访问这个文件指针。
    1.5.3. 当我们打开一个空文件时,默认情况下文件指针指向文件流的开始。所以这时候去write时写入就是从文件开头开始的。write和read函数本身自带移动文件指针的功能,所以当我write了n个字节后,文件指针会自动向后移动n位。如果需要人为的随意更改文件指针,那就只能通过lseek函数了
    1.5.4. 用lseek计算文件长度(length = lseek(fd,0,SEEK_END))

  1.6. close

int close(int fd);
View Code

    1.6.1. 关闭打开的文件

  PS:实时查man手册

    (1)当我们写应用程序时,很多API原型都不可能记得,所以要实时查询,用man手册
    (2)man 1 xx查linux shell命令,man 2 xxx查API, man 3 xxx查库函数

二. open函数的flag详解

  2.1. 读写权限

    a. O_RDONLY就表示以只读方式打开,

    b. O_WRONLY表示以只写方式打开,

    c. O_RDWR表示以可读可写方式打开 

  2.2. 打开存在并有内容的文件时

  2.3. 打开不存在的文件时

  2.4. O_NONBLOCK

  2.5. O_SYNC

      

三. 文件读写的一些细节

  3.1. errno

  3.2. perror

  3.3. 文件IO效率和标准IO

四. 退出进程方式

  4.1. 在main(main函数由其父进程调用,故返回后进程就over)用return,一般原则是程序正常终止return 0,如果程序异常终止则return -1。
  4.2. 正式终止进程(程序)应该使用exit或者_exit或者_Exit之一。

五. 文件共享的实现方式

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



int main(int argc, char *argv[])
{
    int fd1 = -1, fd2 = -1;        // fd 就是file descriptor,文件描述符
    char buf[100] = {0};
    char writebuf[20] = "l love linux";
    int ret = -1;
    
    // 第一步:打开文件
    fd1 = open("a.txt", O_RDWR);
    fd2 = open("a.txt", O_RDWR);
    
    //fd = open("a.txt", O_RDONLY);
    if ((-1 == fd1) || (fd2 == -1))        // 有时候也写成: (fd < 0)
    {
        //printf("\n");
        perror("文件打开错误");
        // return -1;
        _exit(-1);
    }
    else
    {
        printf("文件打开成功,fd1 = %d. fd2 = %d.\n", fd1, fd2);
    }
    


#if 0    
    // 第二步:读写文件
    // 写文件
    ret = write(fd, writebuf, strlen(writebuf));
    if (ret < 0)
    {
        //printf("write失败.\n");
        perror("write失败");
        _exit(-1);
    }
    else
    {
        printf("write成功,写入了%d个字符\n", ret);
    }
#endif


#if 1
    while(1)
    {
        // 读文件
        memset(buf, 0, sizeof(buf));
        ret = read(fd1, buf, 2);
        if (ret < 0)
        {
            printf("read失败\n");
            _exit(-1);
        }
        else
        {
            //printf("实际读取了%d字节.\n", ret);
            printf("fd1:[%s].\n", buf);
        }
        
        sleep(1);
        
        // 读文件
        memset(buf, 0, sizeof(buf));
        ret = read(fd2, buf, 2);
        if (ret < 0)
        {
            printf("read失败\n");
            _exit(-1);
        }
        else
        {
            //printf("实际读取了%d字节.\n", ret);
            printf("fd2:[%s].\n", buf);
        }
        
    }

    
#endif    


    // 第三步:关闭文件
    close(fd1);
    close(fd2);
    
    _exit(0);
}
View Code

猜你喜欢

转载自www.cnblogs.com/linux-37ge/p/10468600.html