您的位置:首页 > 运维架构 > Linux

alin的学习之路(Linux系统编程:四)(Makefile,文件I/O)

2020-07-16 19:19 141 查看

alin的学习之路(Linux系统编程:四)(Makefile,文件I/O)

编码格式的调整:

visual stuio 中用的是GBK的编码,我们在Linux vim 里用的是 utf-8 编码。
vim 的末行模式下:

:set fileencoding=utf8  // 将文件修改为 utf-8 编码

1.Makefile

  1. 概述:Makefile是文件编译工具,方便源文件编译成可执行文件。Makefile 是一个文本文件,一个包含了编译规则的文本文件。在项目中敲击一个命令:make ,即可完成编译。

  2. 文件命名:Makefile 或 makefile

  3. 编写规则

    目标 : 依赖
    由依赖生成目标的命令
    目标:要生成的文件
  4. 依赖:制作目标文件的材料
  5. 命令:执行该命令能够通过依赖文件制作出目标文件
  6. 如果Makefile有多个规则,默认执行第一个规则
  7. 第一个规则中的依赖文件如果不存在,则从文件中向后找其他规则有没有能够生成该依赖文件的
  • 书写Makefile

    # $(wildcard 文件路径) 表示指定路径下的所有匹配文件
    # 假设要获取当前路径下的所有.c文件
    # $(wildcard ./*.c)
    #
    # $(patsubst 被替换模式,替换模式,进行操作的文件内容)
    # 假设要将所有.c 替换成.o
    # $(patsubst %.c,%.o,a.c b.c c.c)
    
    srcs=$(wildcard ./*.c)
    objs=$(patsubst %.c,%.o,$(srcs))
    target=app
    
    $(target) : $(objs)
    gcc $^ -o $@ -I include/
    #   gcc $(objs) -o $(taregt) -I include/
    
    %.o : %.c
    gcc -c $< -o $@ -I include/
    
    # .PHONY 指的该文件是一个虚拟文件,不需要创建出来这个文件,仅执行该目标文件下的命令
    .PHONY:clean
    clean:
    rm $(target) $(objs)

      srcs、objs、target都是定义变量,用来减少下面书写的代码,使用

      $(变量名)
      在后面使用

    1. 表示上面规则中的全部依赖文件,^ 表示上面规则中的全部依赖文件,表示上面规则中的全部依赖文件,< 表示上面规则中的依赖文件中的第一个,$@ 表示目标文件

    2. 规则中的%表示可以匹配任意字符,例如:%.c 表示匹配任意名字的 .c 文件

    3. 注意:在Makefile规则中%表示通配,路径中*表示通配

    4. Makefile中特有的函数(通俗理解为字符串操作函数)

      $(wildcard 文件路径) 表示指定路径下的所有匹配文件

      假设要获取当前路径下的所有.c文件

      $(wildcard ./*.c)

    5. $(patsubst 被替换模式,替换模式,进行操作的文件内容)

      假设要将所有.c 替换成.o

      $(patsubst %.c,%.o,a.c b.c c.c)

  • .PHONY:目标名
    表示虚拟文件,即不需要创建出这个文件,仅执行目标文件下的命令

  • 2.Linux I/O

    1. 标准I/O与系统I/O的区别:

      标准I/O自带缓冲区,系统I/O不带缓冲区
    2. 标准I/O通过调用系统I/O完成I/O操作
    3. 标准I/O的fopen返回的是一个FILE类型的指针,而系统I/O的open返回的是一个大小为(0-1023)的文件描述符
  • fopen与open的效率对比:

      fread 会从用户空间的缓冲区读,如果用户空间的缓冲区没有内容了,会进入到内核空间调用一次read从磁盘读数据到内核缓冲区,再把内核缓冲区的数据拷贝到用户缓冲区,fread读用户缓冲区即可
    1. read 会直接进入内核空间从内核缓冲区读数据,如果内核缓冲区没有数据了,从磁盘读数据到内核缓冲区
    2. 所以综上所述,fread不是每次都会进入内核空间,read每次都会进入内核空间,从用户空间进入内核空间会花很多时间,所以fread的效率要比read高
    3. 当调用fclose函数时释放缓冲区
  • 程序和文件:
    程序运行时,会默认打开 3 个文件:标准输入文件、标准输出文件、标准错误文件。

      对于标准IO函数而言:stdin、stdout、stderr , 这 3 个都是 FILE 类型的指针。
    1. 对于系统IO函数而言:STDIN_FILENO(0)、STDOUT_FILENO(1)、STDERR_FILENO(2), 这三个是数
      字,文件描述符。这三个定义在 unistd.h 头文件中

    3.Linux I/O 函数

    1.文件打开、关闭、读写

    1. open函数:

      int open (const char *file, int oflag, ...)

    2. 头文件:

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <fcntl.h>
    3. 当打开一个文件的时候,如果文件不存在则创建。Linux 中文件是有权限的,创建时候需要指定权
      限。由 mode 参数指定文件权限。

    4. int open(const char *pathname, int flags); 用于打开已经有的文件,不需要传递第三个参数。

    5. mode 参数

      int open(const char *pathname, int flags, mode_t mode);
      如果文件不存在,要创建,则
      需要传递第三参数 mode,实际创建文件的权限 = 指定的权限 - 掩码。
    6. mode 参数可以写数字法:0777(八进制,前面一定要加0)
    7. 指定宏写法:比如:希望文件的权限是,用户读、组用户写、其他用户执行:S_IRUSR | S_IWGRP |
      S_IXOTH
  • oflag 参数:

      O_RDONLY(只读打开文件)、O_WRONLY(只写打开文件)、O_RDWR(读写打开文
      件)、O_CREAT(创建文件)、O_APPEND(追加方式打开文件)
    1. 使用位或运算符组合多个打开模式。
    2. O_WRONLY | O_CREAT,写方式打开文件,不存在则创建。
  • open 函数的打开模式中并没有区分文本模式和二进制模式:因为Linux系统函数仅在系统中使用,没有行结尾的相异

  • 成功则返回文件描述符,失败则返回-1,此时 errno 会被设置为相应的错误码,使用 perror函数
    可打印详细的错误描述。

  • write 函数

      ssize_t write(int fd, const void *buf, size_t count);
    1. 头文件:
      include <unistd.h>
    2. 将 buf 中的 count 字节数据写入到 fd 文件中。
    3. 写入成功,则返回写入的字节数量。失败,则返回 -1,并且全局变量 errno 会被设置为相应的错
      误码。我们可以使用 perror 函数打印错误信息。
  • read 函数

      ssize_t read(int fd, void *buf, size_t count);
    1. 头文件:
      #include <unistd.h>
    2. 从 fd 文件中读取 count 字节的数据,并存储到 buf 空间中。
    3. 成功则返回读取的字节数量,失败则返回-1,并且全局变量 errno 会被设置为合适的错误码,使用
      perror 可以打印该错误码对应的详细错误描述。
  • close 函数

      头文件:
      #include <unistd.h>
    1. 关闭文件描述符,释放文件资源。
    2. 成功返回0,失败返回-1,errno 会被设置为合适的值,使用 perror 函数可打印错误信息。
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>#include <unistd.h>
    #include <string.h>
    
    void test01()
    {
    int fd;
    fd = open("demo01.txt",O_WRONLY | O_CREAT,0777);
    if(fd == -1)
    {
    perror("open");
    return;
    }
    const char* data = "hello world\n";
    int len = write(fd,data,strlen(data));
    if(len == -1)
    {
    perror("write");
    return;
    }
    int ret = close(fd);
    if(-1 == ret)
    {
    perror("close");
    return;
    }
    }
    
    void test02()
    {
    int fd;
    fd = open("demo01.txt",O_RDONLY);
    if(fd == -1)
    {
    perror("open");
    return;
    }
    char buf[128] = { 0 };
    int len = read(fd,buf,128);
    if(len == -1)
    {
    perror("write");
    return;
    }
    printf("buf = %s",buf);
    int ret = close(fd);
    if(-1 == ret)
    {
    perror("close");
    return;
    }
    }
    int main()
    {
    //test01();
    test02();
    return 0;
    }
    

    2.文件随机读写

    lseek 函数可以实现移动文件指针,从而实现随机文件读写。
    off_t lseek(int fd, off_t offset, int whence);

    头文件:

    #include <sys/types.h> #inlude <unistd.h>

    1. fd 文件描述符
    2. offset 偏移量
    3. whence 相对位置 SEEK_SET 开始位置
    4. SEEK_CUR 当前位置
    5. SEEK_END 尾部位置
  • 案例:向文件写入三个struct Person,读出第一个和第三个
  • #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>#include <unistd.h>
    
    struct Person{
    char name[32];
    int age;
    };
    
    void test01()
    {
    struct Person persons[] = {
    {"Obama",50},
    {"Smith",60},
    {"Trump",70}
    };
    int fd = open("person.txt",O_WRONLY | O_CREAT,0777);
    if(-1 == fd)
    {
    perror("open");
    return;
    }
    int len = write(fd,persons,sizeof(persons));
    if(-1 == len)
    {
    perror("write");
    return;
    }
    int ret = close(fd);
    if(-1 == ret)
    {
    perror("close");
    return;
    }
    }
    
    void test02()
    {
    int fd = open("person.txt",O_RDONLY);
    if(-1 == fd)
    {
    perror("open");
    return;
    }
    struct Person p1;
    int len = read(fd,&p1,sizeof(struct Person));
    if(-1 == len)
    {
    perror("read");
    return;
    }
    printf("Name:%s Age:%d\n",p1.name,p1.age);
    int ret = lseek(fd,sizeof(struct Person),SEEK_CUR);
    if(-1 == ret)
    {
    perror("lseek");
    return;
    }
    struct Person p2;
    len = read(fd,&p2,sizeof(struct Person));
    if(-1 == len)
    {
    perror("read");
    return;
    }
    printf("Name:%s Age:%d\n",p2.name,p2.age);
    
    ret = close(fd);
    if(-1 == ret)
    {
    perror("close");
    return;
    }
    }
    
    int main()
    {
    test01();
    test02();
    return 0;
    }
    

    3.文件信息获取

    stat 函数可以获取文件的信息。

    1. int stat(const char *pathname, struct stat *statbuf);

    2. 头文件:

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <unistd.h>
    3. pathname 指的是文件的路径。

    4. statbuf (传入传出参数)保存获取的文件信息。

    5. 成功返回0,失败返回 -1,并且 errno 会被设置为合适的值,使用 perror 函数打印。

    6. **manpage查看:**stat结构体在

      man 2 stat
      中,st_mode中一些宏的查看在
      man 7 inode

    7. struct stat 结构体:

      struct stat {
      dev_t   st_dev;     /* ID of device containing file */
      ino_t   st_ino;     /* Inode number */
      mode_t   st_mode;     /* 文件的类型、文件的权限*/
      nlink_t  st_nlink;    /* 文件的硬链接数 */
      uid_t   st_uid;     /* 文件所有者ID */
      gid_t   st_gid;     /* 文件所有者组ID */
      dev_t   st_rdev;     /* Device ID (if special file) */
      off_t   st_size;     /* 文件大小*/
      blksize_t st_blksize;   /* Block size for filesystem I/O */
      blkcnt_t  st_blocks;    /* Number of 512B blocks allocated */
      struct timespec st_atim;  /* Time of last access */
      struct timespec st_mtim;  /* Time of last modification */
      struct timespec st_ctim;  /* Time of last status change */
      };
    8. 示例代码:

      #include <stdio.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <fcntl.h>#include <unistd.h>
      
      void test01()
      {
      struct stat info;
      int ret = stat("demo01.txt",&info);
      if(-1 == ret)
      {
      perror("stat");
      return;
      }
      printf("文件大小为:%ld\n",info.st_size);
      
      if(S_ISREG(info.st_mode))
      {
      printf("普通文件\n");
      }
      if(S_ISDIR(info.st_mode))
      {
      printf("目录文件\n");
      }
      if(S_ISLNK(info.st_mode))
      {
      printf("硬链接\n");
      }
      
      if(info.st_mode & S_IRUSR)
      {
      printf("用户的读权限\n");
      }
      if(info.st_mode & S_IWUSR)
      {
      printf("用户的写权限\n");
      }
      if(info.st_mode & S_IXUSR)
      {
      printf("用户的执行权限\n");
      }
      if(info.st_mode & S_IRGRP)
      {
      printf("用户组的读权限\n");
      }
      if(info.st_mode & S_IWGRP)
      {
      printf("用户组的写权限\n");
      }
      if(info.st_mode & S_IXGRP)
      {
      printf("用户组的执行权限\n");
      }
      if(info.st_mode & S_IROTH)
      {
      printf("其他用户的读权限\n");
      }
      if(info.st_mode & S_IWOTH)
      {
      printf("其他用户的写权限\n");
      }
      if(info.st_mode & S_IXOTH)
      {
      printf("其他用户的执行权限\n");
      }
      
      }
      
      int main()
      {
      test01();
      return 0;
      }
      

    4. 文件大小扩展

    使用 truncate 函数可将指定文件扩展为指定大小。

    int truncate(const char *path, off_t length);
    int ftruncate(int fd, off_t length);

    注意:off_t 是 long 类型

    头文件:

    #include <unistd.h>	#include <sys/types.h>

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>#include <fcntl.h>
    void test()
    {
    int fd = open("download.txt",O_WRONLY | O_CREAT);
    if(-1 == fd)
    {
    perror("open");
    return;
    }
    int ret = ftruncate(fd,1024);
    if(-1 == ret)
    {
    perror("ftruncate");
    return;
    }
    
    ret = close(fd);
    if(-1 == ret)
    {
    perror("close");
    return;
    }
    }
    
    int main()
    {
    test();
    return 0;
    }
    
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: