linux应用编程一:文件IO和目录操作

1.文件IO和标准IO
  文件IO: 遵照POSIX(可移植操作系统接口)规则,无缓冲,通过文件描述符操作。
  标准IO: 遵照ANSIC规则,带缓冲区,流FILE,
  Linux下,标准IO基于文件IO

2.文件描述符
  每个打开的文件都对应一个文件描述符;
  文件描述符是一个非负数。linux为程序中每个打开的文件分配一个文件描述符
  文件描述符从0开始分配,依次递增。

  每个程序分配的文件描述符都是从0开始,不同程序不影响。
  标准输入,标准输出和标准错误分别占用0,1和2。

3.打开文件
  open函数用开创建或打开一个文件

  #include "fcontl.h"
  int open(cosnt char *path, int oflag, ...);

  path: 要打开的文件的路径。
  oflag: 指定打开方式
    O_RDONLY : 只读方式打开
    O_WRONLY : 可写方式打开
    O_RDWR : 读写方式打开
  注:这三个参数互斥,只能设置一个。
    O_CREAT : 如果文件不存在,就创建一个新文件,此时必须设置第三个参数用于权限设置。
    O_EXCL : 如果使用O_CREAT是文件存在,则返回错误消息。这一参数可测试文件是否存在。
    O_TRUNC : 如果文件已经存在,那么打开文件时先删除源文件数据
    O_APPEND : 以添加的方式打开文件,所以对文件的写操作都在文件的末尾进行。

  返回值: 返回文件的文件描述符。出错时返回EOF。
  打开文件时,使用两个参数。
  创建文件是第三个参数指定新文件的权限(具体的权限与umask有关)。
  注:只能打开设备文件。

  例子:
    以只写的方式打开文件1.txt,如果文件不存在则创建,如果文件存在则清空。

        int fd;
        if((fd = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666) < 0)
        {
            perror("open");
            return -1;
        }

    以只读的方式打开文件1.txt,如果文件不存在则创建,如果文件存在则报错。

        int fd;
        if((fd = open("1.txt", O_RDONLY | O_CREAT | O_EXCL, 0666) < 0)
        {
            if(error == EEXIST)
            {
                perror("exist error");
            }
            else
            {
                perror("other error");
            }
            return -1;
        }

4.关闭文件
  close函数用开关闭一个打开的文件
  #include <unistd.h>
  int close(int fd);

  fd : 文件标识符
  成功返回0;出错是返回EOF。
  文件一旦关闭就不能在使用这个文件了。

5.读取文件
  read函数用来从文件中读取数据:
  #include <unistd.h>
  ssize_t read(int fd, void *buf, size_t count);

  fd: 文件标识符
  buf: 缓冲区首地址
  count: 读取大小,不能超过缓冲区的大小,一般指定为缓冲区的大小。

  返回值:成功是返回实际读取的字节数;出错返回EOF。
  读到文件末尾返回0;

6.写文件
  write函数用来从文件中读取数据:
  #include <unistd.h>
  ssize_t write(int fd, void *buf, size_t count);

  fd: 文件标识符
  buf: 缓冲区首地址
  count: 指定写入的个数,一般指定为缓冲区的大小或者小于buf的大小。

  返回值:成功是返回实际写入的字节数;出错返回EOF。成功返回值等于第三个参数。

7.文件定位
  lseek函数用于定位文件
  #include <unistd.h>
  off_t lseek(int id, off_t offset, intt whence);

  id : 文件描述符
  offset 指的是相对于whence的偏移量,可正可负,不能小于0。
  whence 预定义宏,
    SEEK_SET 文件开头
    SEEK_CUR 当前位置
    SEEK_END 文件结尾

  返回值:
    成功返回当前的文件读写位置,出错返回EOF。

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

        #define N 64

        int main(int argc, char const *argv[])
        {
            /* code */
            int fds, fdt, n;
            char buf[N];

            if(argc != 3)
            {
                printf("usage :%s <str_file> <dst_file> \n", argv[0]);
                return -1;
            }

            if((fds = open(argv[1], O_RDONLY)) == -1)
            {
                fprintf(stderr, "open %s : %s error \n", argv[1],strerror(error));
                return -1;
            }
            if((fdt = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
            {
                fprintf(stderr, "open %s : %s error \n", argv[2],strerror(error));
                //关闭第一个已经打开的文件
                close(fds);
                return -1;
            }

            while( (n = read(fds, buf, N)) > 0)
            {
                write(fdt,bug,n);
            }

            close(fds);
            close(fdt);

            return 0;
        }

8.文件知识补充
1、硬盘中的静态文件和inode(i节点)
  (1)文件平时都在存放在硬盘中的,硬盘中存储的文件以一种固定的形式存放的,我们叫静态文件。
  (2)一块硬盘中可以分为两大区域:一个是硬盘内容管理表项,另一个是真正存储内容的区域。操作系统访问硬盘时是先去读取硬盘内容管理表,从中找到我们要访问的那个文件的扇区级别的信息,然后再用这个信息去查询真正存储内容的区域,最后得到我们要的文件。
  (3)操作系统最初拿到的信息是文件名,最终得到的是文件内容。第一步就是去查询硬盘内容管理表,这个管理表中以文件为单位记录了各个文件的各种信息,每一个文件有一个信息列表,我们叫inode,i节点(其实质是一个结构体,这个结构体有很多元素,每个元素记录了这个文件的一些信息,其中就包括文件名、文件在硬盘上对应的扇区号、块号那些东西·····)
强调:硬盘管理的时候是以文件为单位的,每个文件一个inode,每个inode有一个数字编号,对应一个结构体,结构体中记录了各种信息。
  (4)联系实际,格式化硬盘(U盘)时发现有:快速格式化和底层格式化。
快速格式化非常快,格式化一个32GB的U盘只要1秒钟,普通格式化格式化速度慢。这两个的差异?
快速格式化只删除了U盘中的硬盘内容管理表(其实就是inode),真正存储的内容没有动。这种格式化的内容是有可能被找回的。
2、内存中被打开的文件和vnode(v节点)
  (1)在程序中打开的文件就属于某个进程。每个进程都有一个数据结构用来记录这个进程的所有信息(叫进程信息表),表中有一个指针会指向一个文件管理表,文件管理表中记录了当前进程打开的所有文件及其相关信息。文件管理表中用来索引各个打开的文件的index就是文件描述符fd,我们最终找到的就是一个已经被打开的文件的管理结构体vnode
  (2)vnode中就记录了一个被打开的文件的各种信息,我们只要知道这个文件的fd,就可以很容易的找到这个文件的vnode进而对这个文件进行各种操作。

9.文件共享

1、文件共享就是同一个文件(指的是同一个inode,同一个pathname)被多个独立的读写体(可以理解为多个文件描述符)去同时(一个打开尚未关闭的同时另一个去操作)操作。

2、3种文件共享的情况:

  第一种是同一个进程中多次使用open打开同一个文件

  第二种是在不同进程中去分别使用open打开同一个文件(这时候因为两个fd在不同的进程中,所以两个fd的数字可以相同也可以不同)

  第三种情况是后面要学的,linux系统提供了dup和dup2两个API来让进程复制文件描述符。

10 复制文件描述符

1、dup和dup2函数介绍
  #include <unistd.h>

  int dup(int oldfd);
  int dup2(int oldfd, int newfd);

  oldfd:要复制的文件描述符

  返回值:返回一个新的文件描述符

  区别:dup返回的文件描述符由系统自动分配,dup2可以指定新的文件描述符。

  使用案例:可以将close(1)与dup配合使用进行输出重定位。

  使用复制文件描述符的方式进行操作时时接续写。

 

二 目录操作

1.打开目录

  opendir函数用开打开一个目录文件
  #include <dirent.h>
  DIR *opendir(const char *name);

  DIR是用来描述一个打开的目录文件的结构体类型。
  成功返回目录流指针;失败返回NULL。

  注:

    struct __dirstream
    {
      void *__fd; /* `struct hurd_fd' pointer for descriptor. */
      char *__data; /* Directory block. */
      int __entry_data; /* Entry number `__data' corresponds to. */
      char *__ptr; /* Current pointer into the block. */
      int __entry_ptr; /* Entry number `__ptr' corresponds to. */
      size_t __allocation; /* Space allocated for the block. */
      size_t __size; /* Total valid data in the block. */
      __libc_lock_define (, __lock) /* Mutex lock for this structure. */
    };
    typedef struct __dirstream DIR;

2.读取目录内容:

  readdir函数用来读取目录流中的内容
  #include <dirent.h>
  struct dirent *readdir(DIR *dirp);

  dirent 用来描述目录流中一个目录项的结构体类型
    包含成员 char d_name[256]  文件名
  成功时返回目录流dirp中下一个目录项;
    出错或到末尾是返回NULL。

  注:

        struct dirent
        {
           long d_ino;         /* inode number 索引节点号 */
           off_t d_off;         /* offset to this dirent 在目录文件中的偏移 */
           unsigned short d_reclen;     /* length of this d_name 文件名长 */
           unsigned char d_type;         /* the type of d_name 文件类型 */
           char d_name [NAME_MAX+1];     /* file name (null-terminated) 文件名,最长255字符 */
        }

3.关闭目录

  closedir函数用于关闭一个目录文件。
  #include <dirent.h>
  int closedir(DIR *dirp);

  成功返回0;出错时返回EOF。

三 文件权限

1.修改文件访问权限

  chmod 和 fchmod 用来修改文件的访问权限。
  #include <sys/stat.h>
  int chmod(const char *path, mode_t mode);
  int fchmod(int fd, mode_t mode);

  成功返回0,出错时返回EOF。
    只有root和文件的所有者才能修改文件的权限。

2.获取文件的属性

  stat/lstat/fstat函数
  #include <sys/stat.h>
  int stat(const char *path, struct stat *buf);
  int lstat(const char *path, struct stat *buf);
  int fstat(int fd, struct stat *buf);

  成功返回0,出错时返回EOF。
  stat 和 lstat 的区别:
    当文件是一个链接文件,stat返回链接文件指向的文件的属性,而lstat指向的是链接文件本身的属性。
    其他情况功能一致。
  fstat第一个参数为文件描述符,操作要先打开文件。

    struct stat常用属性
        mode_t st_mode;            类型和访问权限
        uid_t st_uid;            所有者id
        uid_t st_gid;            用户组id
        off_t st_size;            文件大小
        time_t st_mtime;        最后修改时间

    st_mode:
        该成员描述了文件的类型和权限两个属性。
        st_mode是个32位的整型变量,不过现在的linux操作系统只用了低16位

        在<sys/stat.h>中,有如下定义。
        File type属性区域,位于bit12 ~ bit15.
        #define S_IFMT  00170000  
        #define S_IFSOCK 0140000  
        #define S_IFLNK  0120000  
        #define S_IFREG  0100000  
        #define S_IFBLK  0060000  
        #define S_IFDIR  0040000  
        #define S_IFCHR  0020000  
        #define S_IFIFO  0010000  
        #define S_ISUID  0004000  
        #define S_ISGID  0002000  
        #define S_ISVTX  0001000  
          
        #define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)      //符号链接文件(symbolic link)
        #define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)      //普通文件(regular file)
        #define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)      //目录(directory)
        #define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)      //字符设备(character device)
        #define S_ISBLK(m)      (((m) & S_IFMT) == S_IFBLK)      //块设备(block device)
        #define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)      //管道(FIFO<pipe>)
        #define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK)      //套接口文件(socket)

            
        例子:
            判断一个文件是不是文件夹
                if ((info.st_mode & S_IFMT) == S_IFDIR)  
                    printf("this is a directory");
            或
                if (S_ISDIR(info.st_mode))  
                    printf("this is a directory");


           Permission属性区域的bit0~bit8,也即st_mode字段的最低9位,代表文件的许可权限,
           它标识了文件所有者(owner)、组用户(group)、其他用户(other)的读(r)、写(w)、执行(x)权限。

           在<sys/stat.h>中,有如下定义:
               #define S_IRWXU 00700   /* mask for file owner permissions */  
            #define S_IRUSR 00400   /* owner has read permission */  
            #define S_IWUSR 00200   /* owner has write permission */  
            #define S_IXUSR 00100   /* owner has execute permission */  
              
            #define S_IRWXG 00070   /* mask for group permissions */  
            #define S_IRGRP 00040   /* group has read permission */  
            #define S_IWGRP 00020   /* group has write permission */  
            #define S_IXGRP 00010   /* group has execute permission */  
              
            #define S_IRWXO 00007   /* mask for permissions for others (not in group) */  
            #define S_IROTH 00004   /* others have read permission */  
            #define S_IWOTH 00002   /* others have write permission */  
            #define S_IXOTH 00001   /* others have execute permission */  

           例子:
               打印出文件的权限,"-rx-rx-rx"
               for(int n = 8; n >= 0; n--)
               {
                   if(buf.st_mode & (1 << n))
                   {
                       switch(n & 3):
                       {
                           case 2:
                               printf("r");
                               break;
                           case 1:
                               printf("w");
                               break;
                           case 0:
                               printf("x");
                               break;
                       }
                   }
                   else
                   {
                       printf("-");
                   }
               }

  注:目录的权限与普通文件的权限是不同的。目录的读、写、执行权限含义分别如下:
  (1)读权限。读权限允许我们通过opendir()函数读取目录,
    进而可以通过readdir()函数获得目录内容,即目录下的文件列表。
  (2)写权限。写权限代表的是可在目录内创建、删除文件,而不是指的写目录本身。
  (3)执行权限。可访问目录中的文件。

 

Ps:文件参考:man手册及《朱有鹏课程笔记》

posted @ 2020-08-05 14:57  cloos.xie  阅读(421)  评论(0编辑  收藏  举报