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

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. 编写规则

    目标 : 依赖
    	由依赖生成目标的命令
    
    1. 目标:要生成的文件
    2. 依赖:制作目标文件的材料
    3. 命令:执行该命令能够通过依赖文件制作出目标文件
    4. 如果Makefile有多个规则,默认执行第一个规则
    5. 第一个规则中的依赖文件如果不存在,则从文件中向后找其他规则有没有能够生成该依赖文件的
  4. 书写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)
    
    1. srcs、objs、target都是定义变量,用来减少下面书写的代码,使用$(变量名)在后面使用

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

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

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

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

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

        假设要获取当前路径下的所有.c文件$(wildcard ./*.c)

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

        假设要将所有.c 替换成.o $(patsubst %.c,%.o,a.c b.c c.c)

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

2.Linux I/O

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

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

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

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

3.Linux I/O 函数

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

  1. open函数:

    1. 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 参数

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

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

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

  2. write 函数

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

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

    1. 头文件:#include <unistd.h>
    2. 关闭文件描述符,释放文件资源。
    3. 成功返回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 相对位置
    1. SEEK_SET 开始位置
    2. SEEK_CUR 当前位置
    3. SEEK_END 尾部位置
  4. 案例:向文件写入三个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;
}

猜你喜欢

转载自blog.csdn.net/qq_41775886/article/details/107391029