Linux基础-Linux系统函数

linux下应用层的系统I/O函数

  1. man文档使用

     man man :打开man文档目录
     man 3 printf: 查找man文档第3章中的printf函数
     man 2 open:查看man文档第2张的open函数
    
    1. 查看man文档时会看到很多的陌生数据类型,比如:size_t
    2. 因为man文档是nuix下面的,unix早于C语言,unix用C语言重写之前就有自己的数据类型。所以那些陌生类型就是Unix的数据类型,但是他跟C语言的基本数据类型本质一样。比如size_t就是int
  2. open函数

     //文件已经存在
     int open(const char *pathname, int flags);
     //文件不存在
     int open(const char *pathname, int flags, mode_t mode);
    
    1. pathname :要打开的文件路径
    2. flags:要打开的方式

       The  argument  flags  must  include  one of the following access modes:
       O_RDONLY, O_WRONLY, or O_RDWR.  
       The  file  creation  flags  are O_CLOEXEC,O_CREAT,O_DIRECTORY,O_EXCL,O_NOCTTY,  O_NOFOLLOW,  O_TMPFILE, and O_TRUNC. 
      
    3. mode:这个调用方式是用于文件不存在调用方式
      1. 给创建文件制定访问权限
    4. 返回值:

       //返回一个新的文件描述符。或者-1,如果为-1就是错误发生(在这种情况下,有个全局变量errno就会被赋值。可以通过errno的值来判别错误的具体原因)
       open(), openat(), and creat() return the new file descriptor, or -1  if
       an error occurred (in which case, errno is set appropriately).
      
    5. errno是linux系统的全局变量,专门用于标记错误类型。
      1. 通过void perror(const char *s)函数来获取到errno的具体错误内容
      2. 使用perror函数时,会先读取errno的值,然后就会把错误原因输出到标准设备(stderr)
      3. 参数s所指的字符串会先打印出,后面再加上错误原因字符串
      4. 错误原因依照全局变量errno的值来决定要输出的字符串
      5. errno的声明在errno.h头文件中:vi /usr/include/errno.h
    6. open函数的使用

       #include<sys/types.h>
       #include<sys/stat.h>
       #include<fcntl.h>
       #include<unistd.h>
       #include<stdlib.h>
       #include<stdio.h>
                 
       int main()
       {
            int fd;
            //打开一个已经存在的文件
            //2K(shift+k)跳转到man文档中open函数
            fd = open("add.c",O_RDWR);
            if(fd==-1)
            {
                    perror("open file");
                    exit(1);
            }
            //打开一个不存在的文件
            fd = open("hello",O_RDWR | O_CREAT,0777);
            if(fd==-1)
            {
                    perror("open file");
                    exit(1);
            }
            printf("fd=%d",fd);
            //
            int ret = close(fd);
            printf("ret = %d",ret);
            if(ret=-1)
            {
                    perror("close file");
            }
            return 0;
       }
      
  3. read和write函数
    1. 查看man文档:man 2 read

       //锁需要包含的头文件
       #include <unistd.h>
       //ssize_t 有符号整型
       ssize_t read(int fd, void *buf, size_t count);
      
      1. fd:文件描述符
      2. buf:需要用户提供的缓冲区
      3. 每次读数据用多大的buf
      4. 返回值:

         -1:读取失败了
         0: 文件读完了
         >0: 读取的字节数
        
      5. 同理文件读取的错误信息,会赋值给errno
    2. 查看man文档:man 2 write

       #include <unistd.h>
       ssize_t write(int fd, const void *buf, size_t count);
      
      1. 将buf中的内容写到磁盘上
    3. 查看man文档:man 2 lseek

       #include <sys/types.h>
       #include <unistd.h>
       off_t lseek(int fd, off_t offset, int whence);
      
      1. 作用:移动文件指针,获取文件长度
      2. offset:文件指针的偏移量
    4. 读取代码示例:(将一个文件的内容读取出来,然后写到一个新的文件中)

       #include<stdio.h>
       #include<stdlib.h>
       #include<sys/types.h>
       #include<sys/stat.h>
       #include<fcntl.h>
       #include<unistd.h>
               
       int main()
       {
        // 打开一个已经存在的文件
        int fd = open("english.txt",O_RDONLY);
        if(fd == -1)
        {
                perror("openfile");
                exit(1);
        }
        //创建一个新的文件 ----写操作
       int fd1 = open("newenglisht.txt",O_CREAT | O_WRONLY,0664);
       if(fd1 == -1)
       {
              perror("opennewfile");
               exit(1);
       }
        //读文件
       //手动定义缓冲区,缓冲区读完,刷新
       char buf[2048] = {0};
       //count= 0;文件读取完了,cont = -1 读取错误,count>0文件读取的字节数
       int count = read(fd,buf,sizeof(buf));
                  
       if(count  == -1)
       {
               perror("read error");
               exit(1);
       }
                  
       while(count)
       {
               //将读出的数据写入新的文件中
               int ret = write(fd1,buf,count);
              printf("write bytes %d \n",ret);
               count = read(fd,buf,sizeof(buf));
        }
         //关闭文件
         close(fd);
         close(fd1);
         return 0;
       }
      
  4. lseek函数
    1. 作用:
      1. 获取文件大小
      2. 移动文件指针
      3. 文件拓展
    2. 使用:

       #include<stdio.h>
       #include<stdlib.h>
       #include<sys/types.h>
       #include<sys/stat.h>
       #include<fcntl.h>
       #include<unistd.h>
              
       int main()
       {
           int fd = open("aa",O_RDWR);
           if(fd == -1)
           {
               perror("open");
               exit(1);
           }
           //获取文件长度
           int ret = lseek(fd,0,SEEK_END);
           printf("file len = %d \n",ret);
                  
           //文件拓展:把一个文件拓展成更大的文件。
           //从文件最后拓展2000byte
           int ret2 = lseek(fd,2000,SEEK_END);
           printf("return value %d \n",ret2);
           //实现文件拓展,必须在最后作一次写的操作
                   
           write(fd,"a",1);
                   
           close(fd);
           return 0;
       }
      

linux下的文件操作函数

linux的文件操作系统函数都有对应的命令,比如:stat函数对应stat命令‘

stat函数

  1. 作用:获取文件的属性信息,对应命令:stat
  2. 举例:命令使用stat english.txt

     文件:english.txt
     大小:95        	块:8          IO 块:4096   普通文件
     设备:802h/2050d	Inode:921171      硬链接:1
     权限:(0644/-rw-r--r--)  Uid:( 1000/coderzhong)   Gid:( 1000/coderzhong)
     最近访问:2019-11-04 11:04:19.947951850 +0800
     最近更改:2019-11-04 10:37:57.979086559 +0800
     最近改动:2019-11-04 10:37:57.979086559 +0800
     创建时间:-
    
    1. 列出文件的属性信息
    2. Inode:对于操作系统来说,它对文件名不感兴趣,只对Inode感兴趣,每个文件都对应一个Inode号,操作系统通过这个Inode号找到对应的磁盘位置
  3. 查看man 2 stat

     //依赖的头文件
     #include <sys/types.h>
     #include <sys/stat.h>
     #include <unistd.h>
        
     //函数声明
     int stat(const char *pathname, struct stat *statbuf);
     int fstat(int fd, struct stat *statbuf);
     int lstat(const char *pathname, struct stat *statbuf);
    
    1. stat参数说明:
      1. pathname:文件的路径
      2. statbuf:stat类型的结构体指针
    2. stat结构体:

       struct stat {
           dev_t         st_dev;       //文件的设备编号
           ino_t         st_ino;       //节点
           mode_t        st_mode;      //文件的类型和存取的权限(重点!!!)
           nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1
           uid_t         st_uid;       //用户ID
           gid_t         st_gid;       //组ID
           dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号
           off_t         st_size;      //文件字节数(文件大小)
           blksize_t     st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
           blkcnt_t      st_blocks;    //块数
           time_t        st_atime;     //最后一次访问时间
           time_t        st_mtime;     //最后一次修改时间
           time_t        st_ctime;     //最后一次改变时间(指属性)
       };
      
    3. st_mode
      1. 改变量占2个字节,16位
      2. 采用位域存储技术来存储信息

        图4

        1. 其他人权限(0-2 bit)

           S_IROTH 00004 读权限   二进制:100
           S_IWOTH 00002 写权限   二进制: 010
           S_IXOTH 00001 执行权限  二进制:001
           掩码:S_IRWXO    00007  过滤 st_mode中除其他人权限以外的信息
           本质就是让st_mode与111相与,获取后3位的值
          
        2. 所属组权限(3-5 bit)

           S_IRGRP 00040 读权限  二进制;100000
           S_IWGRP 00020 写权限  二进制:010000
           S_IXGRP 00010 执行权限 二进制:001000
           掩码:S_IRWXG 00070 过滤 st_mode中除所属组权限以外的信息
           对应二进制:111000
          
        3. 文件所有者权限(6-8 bit)

          S_IRUSR 00400 读权限
          S_IWUSR 00200 写权限
          S_IXUSR 00100 执行权限
          掩码:S_IRWXU 00700 过滤 st_mode中除文件所有者权限以外的信息
          
        4. 特殊权限位(9-11 bit)

           S_ISUID 0004000 设置用户ID
           S_ISGID 0002000 设置组ID
           S_ISVTX 0001000 黏住位
                          
           很少用
          
        5. 文件类型(12-15 bit)

           S_IFSOCK 0140000 套接字
           S_IFLNK 0120000 符号链接(软链接)
           S_IFREG 0100000 普通文件
           S_IFBLK 0060000 块设备
           S_IFDIR 0040000 目录
           S_IFCHR 0020000 字符设备
           S_IFIFO 0010000 管道
           掩码:S_IFMT 0170000 过滤 st_mode中除文件类型以外的信息
           if(S_IFMT & st_mode == S_IFSOCK)
          
  4. 使用:

     #include<sys/types.h>
     #include<sys/stat.h>
     #include<unistd.h>
     #include<stdio.h>
     #include<stdlib.h>
         
     int main(int argc,char *argv[])
     {
          struct stat st;
          int ret = stat("english.txt",&st);
          if(ret == -1)
          {
                  perror("stat");
                  exit(1);
          }
          //获取文件的大小
          int size =(int) st.st_size;
          printf("file size = %d \n",size);
         
          //查看文件的类型
          if((st.st_mode & S_IFMT) == S_IFREG)
          {
                  printf("这个文件是一个普通文件\n");
           }
         //所有者对文件的操作权限
         if(st.st_mode & S_IRUSR)
          {
                   printf("----r\n");
          }
            
         if(st.st_mode & S_IWUSR)
          {
                   printf("----w\n");
          }
         if(st.st_mode & S_IXUSR)
          {
                   printf("----x\n");
          }
            
         return 0;
     }
    
    1. 编译: gcc stat.c -o stat
    2. 运行: ./stat
    3. 打印:

       file size = 95 
       这个文件是一个普通文件
       ----r
       ----w
      
  5. lstat与stat的区别
    1. 区别在于读链接文件(快捷方式,ln-s创建出来的)时候
    2. 比如:终端输入ln -s english.txt s.s
    3. 这样会创建一个english.txt文件的快捷方式s.s
    4. 将上面的代码读取文件english.txt换成s.s,然后执行

       file size = 95 
       这个文件是一个普通文件
       ----r
       ----w
      
      1. 这应该是一个链接文件
      2. 文件大小也不对
    5. 将上面的读取方式由stat换成lstat则

       file size = 11 
       ----r
       ----w
       ----x
      
      1. 这里没有打印“普通文件”,说明不是普通文件
      2. 大小也变化了
    6. 总结:
      1. stat 在读链接文件时,实际上读的是链接文件指向的实际文件的属性
      2. lstat 读取链接文件时,读取的就是链接文件的属性。
  6. fstat函数
    1. 第一个参数是文件描述符fd
    2. 用于获取有文件描述符fd的文件属性
    3. 比如用open函数打开一个文件,那么就可以用这个返回的文件描述符fd,来查看文件的属性

其他函数

  1. access函数
    1. 作用:测试文件有哪些权限
    2. 原型:int access(const char *pathname, int mode);
    3. 参数:
      1. pathname : 文件名
      2. mode: 权限类别
        1. R_OK 是否有读权限
        2. W_OK 是否有写权限
        3. X_OK 是否有执行权限
        4. F_OK 测试一个文件是否存在
    4. 返回值
      1. 0 : 所有欲查核的权限都通过了检查
      2. -1 :有权限被禁止
  2. 其他函数:chmode、chown、truncate、link、symlink、readlink、unlink

linux下的目录操作函数

rename :重命名
chdir:修改当前进程的路径
getcwd:获取当前进程工作目录
mkdir:创建目录
rmdir:删除一个目录
opendir:打开目录
readdir:读目录
closedir:关闭目录

dup和dup2函数

  1. 作用: 复制现有的文件描述符
  2. man dup

     #include <unistd.h>
        
     int dup(int oldfd); //返回的是文件描述符表中没有被占用的最小的文件描述符
     oldfd:要复制的文件描述符
     返回值:新的文件描述符
     dup调用成功:有2个文件描述符指向同一个文件
        
     int dup2(int oldfd, int newfd); 
     newfd分2种情况
         1. newfd已经指向了一个文件(文件描述符的重定向)
             1. 首先要断开close与原来文件的连接
             2. newfd指向oldfd指向的文件
             3. 注意:如果old和new指向了同一个文件,那么不需要close,直接返回
         2. newfd没有指向一个文件
             1. newfd指向oldfd指向的文件
    
  3. 代码举例:

     #include<string.h>
     #include<stdio.h>
     #include<stdlib.h>
     #include<sys/types.h>
     #include<sys/stat.h>
     #include<unistd.h>
     #include<fcntl.h>
         
     int main(void)
     {
          //打开一个文件temp,如果不存在就创建一个。
          int fd = open("temp",O_RDWR | O_CREAT,0664);
          if(fd==-1)
          {
                  perror("open");
                  exit(1);
          }
          //复制文件描述符
          int fd2 = dup(fd);
          //int fd2 = fcntl(fd,F_DUPFD);
          //写文件
          char* p = "让编程改变世界";
          write(fd2,p,strlen(p));
          close(fd2);
        
          char buf[1024];
          //将文件指针移动到文件头部
          lseek(fd,0,SEEK_SET);
          read(fd,buf,sizeof(buf));
          printf("buf = %s \n",buf);
          close(fd);
     }
    

fcntl函数

  1. 作用: 改变已经打开的文件的属性。根据文件描述符来操作文件的状态
  2. 函数原型

     #include <fcntl.h>
     int fcntl(int fd, int cmd);
     int fcntl(int fd, int cmd, long arg);
     int fcntl(int fd, int cmd, struct flock *lock);
    
  3. 使用
    1. 复制一个已有的文件描述符

       int fcntl(fd, F_DUPFD);
      
    2. 获取、设置文件状态标志

      1. open的flags参数
      2. 获取文件状态标识

         int flag = fcntl(fd,F_GETFL);
        
      3. 设置文件状态标识

         flag = flag | O_APPEND;
         fcntl(fd,F_SETFL,flag);
        
      4. 可以更改的几个标识:O_APPEND、O_NONBLOCK(常用)
Table of Contents