【Linux】文件基础IO操作:C语言接口 | 系统调用接口 | 重定向

  • 重修C语言文件知识
  • Linux文件知识
    • 标记位传参
    • 文件的系统调用
    • 理解什么是文件
    • 文件fd的分配规则
    • 重定向
    • C语言文件层面的缓冲区知识

重修C语言文件知识

  1. 打开文件操作fopen函数:

在这里插入图片描述
我们看一段代码,以写(w)的形式来打开文件:

#include <stdio.h>#define FILE_NAME "log.txt"
int main()
{FILE* fp = fopen(FILE_NAME,"w");if(NULL==fp){perror("fopen");return 1;}fclose(fp);return 0;
}

一开始我们并没有创建文件,程序运行会自动创建一个log.txt的文件
在这里插入图片描述

  1. 打印输出到文件fprintf函数

在这里插入图片描述

#include <stdio.h>#define FILE_NAME "log.txt"
int main()
{FILE* fp = fopen(FILE_NAME,"w");if(NULL==fp){perror("fopen");return 1;}fprintf(fp,"%s\n","hello world!");fclose(fp);return 0;
}

运行程序,fprintf 会发送格式化输出到流 stream
在这里插入图片描述
w的方式对文件进行操作,文件的内容会被先清空,再进行操作
在这里插入图片描述

  1. 打开文件操作读(r)的方式,fgets函数从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内

在这里插入图片描述

#include <stdio.h>
#include <string.h>#define FILE_NAME "log.txt"
int main()
{FILE* fp = fopen(FILE_NAME,"r");if(NULL==fp){perror("fopen");return 1;}char buffer[64];while(fgets(buffer,sizeof(buffer)-1,fp) != NULL){buffer[strlen(buffer)-1]=0;puts(buffer);}fclose(fp);return 0;
}

这个while循环可写可不写,这样写的作用也就是保证buffer最后一个字符是终止符号
下面输出的结果就是将log.txt中的数据输出出来
在这里插入图片描述

4.打开文件操作追加(a)的方式

#include <stdio.h>
#include <string.h>#define FILE_NAME "log.txt"
int main()
{FILE* fp = fopen(FILE_NAME,"a");if(NULL==fp){perror("fopen");return 1;}int count=5;while(count){fprintf(fp,"%s:%d\n","hello world!!",count--);}fclose(fp);return 0;
}

在这里插入图片描述

打开文件相关方式:

  • “r” “只读”,只允许读取,不允许写入。文件必须存在,否则打开失败
  • “w” “写入”。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容
  • “a” “追加”。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)
  • “r+” “读写”。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败
  • “w+” “写入/更新”,相当于w和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容
  • “a+” “追加/更新”,相当于a和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)
  • “t” 文本文件。如果不写,默认为"t"。
  • “b” 二进制文件。

5.在linux下新建文件默认权限=0666,受到umask的影响,实际创建的出来的文件权限是: mask & ~umask
在这里插入图片描述
 C语言有文件的操作接口,那么C++、Java、python、php、GO等语言同样也有文件操作接口,但是它们的接口都不一样。
 而文件在哪?在磁盘,磁盘是硬件,而需要访问硬件都必须要操作系统OS来管理,使用OS给的文件级别的系统调用,操作系统只有一个,但是语言有很多个:库函数底层必须使用系统调用接口、库函数可以变化但是底层不变。


Linux文件知识

我们使用man 2来了解有关文件的系统调用知识,它与C语言有什么不同呢?

标记位传参

通过以下代码来解释什么是标记位传参:

#define ONE (1<<0)
#define TWO (1<<1)
#define THREE (1<<2)
#define FOUR (1<<3)void show(int flag)
{if(flag & ONE)printf("one\n");if(flag & TWO)printf("two\n");if(flag & THREE)printf("three\n");if(flag & FOUR)printf("four\n");
}
int main()
{show(ONE);printf("---------\n");show(TWO);printf("---------\n");show(ONE | TWO);printf("---------\n");show(ONE | TWO | THREE);printf("---------\n");show(ONE | TWO | THREE | FOUR);return 0;
}

标记对应比特位,每一个宏对应的数值,只有一个比特位是1,彼此不会重叠,如果想要互相结合就或(|),函数里面通过与(&)来判断,这样就相当于可以传入多个参数
在这里插入图片描述

文件的系统调用

1.系统调用打开文件open
在这里插入图片描述在这里插入图片描述
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数: 前三个常量,必须指定一个且只能指定一个

  • O_RDONLY: 只读打开

  • O_WRONLY: 只写打开

  • O_RDWR : 读写打开

  • O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限

  • O_APPEND: 追加写

使用系统调用来打开文件O_WRONLY

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME "log.txt"
int main()
{int fp = open(FILE_NAME,O_WRONLY);if(fp < 0){perror("open");return 1;}close(fp);return 0;
}

在C语言中我们使用读的形式打开文件可以直接成功,但是在系统调用中我们使用只使用读的形式访问文件,是会失败的。
在这里插入图片描述
我们必须要更改代码为:

    int fp = open(FILE_NAME,O_WRONLY | O_CREAT);

更改后创建出来的文件是乱码:
在这里插入图片描述
凭什么认为Linux一创建文件就按照比如666、777去创建?我们在C语言中使用的是已经封装过的系统调用,他会自动生成权限,而系统调用没有这些东西,他需要自己去传参,所有我们最后还需要传入作为权限的参数:

    int fp = open(FILE_NAME,O_WRONLY | O_CREAT, 0666);

在这里插入图片描述
在这里插入图片描述

更改umask的值
在这里插入图片描述

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME "log.txt"
int main()
{umask(0);int fp = open(FILE_NAME,O_WRONLY | O_CREAT,0666);if(fp < 0){perror("open");return 1;}close(fp);return 0;
}

我们在创建访问之前将umask的值更改为0,使用我们传入的权限值去初始化,最后这个log.txt的权限值就是666
在这里插入图片描述

我们再访问shell中umask的值发现它还是0002,这是为什么,我们不是刚刚已经修改了,而且创建出来的文件权限也是按照更改后的umask初始的?这是因为我们程序里面的umask是这个子进程在执行,而与shell这个父进程无关系,子进程只能改变自己的文件权限,所以我们更改的时候不会影响shell。
在这里插入图片描述

  1. 向文件写入write

在这里插入图片描述

ssize_t是有符号整型,在32位机器上等同与int,在64位机器上等同与long int.

open函数返回值:
我们以读的形式打开(或创建)文件,并打印出open函数的返回值

int main()
{umask(0);int fp = open(FILE_NAME,O_WRONLY | O_CREAT,0666);if(fp < 0){perror("open");return 1;}printf("fp:%d\n",fp);close(fp);return 0;
}

为什么这个打印的值是3呢?且看目录中的理解什么是文件详细讲述
在这里插入图片描述

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

 读写文件有两种读写方案:文本类、二进制类(而这些文件读取的分类是语言本身提供的)
而操作系统就很简单粗暴,直接以void*方式返回,在操作系统看来都是二进制,操作系统只会管你要写几个字节,而不会管你具体内容(不管你是图片还是字符串什么的,只认二进制)

 假如我们也想像C语言那样写入hello word!! 并加上数字。一个是字符串、一个是数字,那么我们如何使用系统调用来实现呢?
在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FILE_NAME "log.txt"int main()
{umask(0);int fp = open(FILE_NAME,O_WRONLY | O_CREAT,0666);if(fp < 0){perror("open");return 1;}int count = 3;char arr[64];while(count){sprintf(arr,"%s:%d\n","hello world!!",count--);write(fp,arr,strlen(arr));}close(fp);return 0;
}

我们使用sprintf将字符串写入数组中,然后使用write将数组写入文件
在这里插入图片描述
我们的write(fp,arr,strlen(arr));这个代码strlen不需要加1来存储\0,如果加了会出现乱码,因为以\0作为字符串的结束符是C语言规定的,和系统调用层面的文件操作没有关系

在C语言中,以写的方式打开文件会直接删除掉原数据,但是在系统调用的时候则是覆盖式的比如:
更改之前代码为:

sprintf(arr,"%s:%d\n","aaaaaa!!",count--);

在这里插入图片描述
我们在系统调用还需要传入一个O_TRUNC,才能实现出在C语言中w的效果:

    int fp = open(FILE_NAME,O_WRONLY | O_CREAT | O_TRUNC,0666);

在这里插入图片描述
在这里插入图片描述
追加就是将O_TRUNC跟换为O_APPEND

    int fp = open(FILE_NAME,O_WRONLY | O_CREAT | O_APPEND,0666);

在这里插入图片描述

  1. 读文件read函数

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FILE_NAME "log.txt"int main()
{umask(0);int fp = open(FILE_NAME,O_RDONLY);if(fp < 0){perror("open");return 1;}char buffer[1024];ssize_t num = read(fp,buffer,sizeof(buffer)-1);if(num>0)buffer[num]=0;printf("%s",buffer);close(fp);return 0;
}

sizeof(buffer)-1这段作用是为填写终止符留出空间
buffer[num]=0;这个语句的作用是添加结束符(0、\0、NULL),因为在C语言中的函数会自己写,而系统调用需要我们自己去写
在这里插入图片描述
在这里插入图片描述

理解什么是文件

文件操作的本质:进程 + 被打开文件 的关系

进程可以打开多个文件么?答案是肯定的,进程很多,同样系统一定会存在大量的被打开文件,那么这些被打开文件肯定也需要被操作系统OS管理起来,先描述再组织 -> OS为了管理对应的打开文件,必定会为文件创建对应的内核数据结构标识文件:struct file{}(包含了大部分属性)

我们通过下面代码来理解文件:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(number) "log.txt"#number//把参数转化为字符串,然后合起来
int main()
{int fd0 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd1 = open(FILE_NAME(2), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd2 = open(FILE_NAME(3), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd3 = open(FILE_NAME(4), O_WRONLY | O_CREAT | O_APPEND, 0666);int fd4 = open(FILE_NAME(5), O_WRONLY | O_CREAT | O_APPEND, 0666);printf("fd: %d\n", fd0);printf("fd: %d\n", fd1);printf("fd: %d\n", fd2);printf("fd: %d\n", fd3);printf("fd: %d\n", fd4);close(fd0);close(fd1);close(fd2);close(fd3);close(fd4);return 0;
}

通过下面的输出结果我们发现:为什么open返回值从3开始?012又去了哪里?连续的小整数,一般情况我们只在数组下标才有所对应
在这里插入图片描述
三个标准输入输出流:

  • stdin—键盘
  • stdout—显示器
  • stderr—显示器

FILE* fp = fopen();这个FILE是结构体,它里面有一个字段是文件描述符

在这里插入图片描述

我们增加下面代码:

	printf("stdin->fd: %d\n", stdin->_fileno);printf("stdout->fd: %d\n", stdout->_fileno);printf("stderr->fd: %d\n", stderr->_fileno);

从输出结果我们可以看出,012在哪是什么了:三个标准输入输出提前占用了012
在这里插入图片描述

为什么这些输出的数字是为数组下标?且看下图
在这里插入图片描述
通过上面的学习,我们知道了文件描述符就是一个小整数:

  • Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.
  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器

上图可知:文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

文件描述符的本质,就是数组的下标!

文件fd的分配规则

我们首先做一个结果分析:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define FILE_NAME "log.txt"
int main()
{//close(0);umask(0);int fd = open(FILE_NAME,O_WRONLY | O_CREAT,0666);if(fd < 0){perror("open");return 1;}printf("fd:%d\n",fd);close(fd);return 0;
}

打印的结果是3,这是我们上面解释过的
在这里插入图片描述

我们在main函数刚开始的地方先close(0);看看输出结果:
在这里插入图片描述

我们在main函数刚开始的地方先close(2);看看输出结果:
在这里插入图片描述

我们在main函数刚开始的地方先close(0);close(2);看看输出结果:
在这里插入图片描述

正常情况我们会自动打开三个标准输入输出,我们加载一个文件就会从3开始:
在这里插入图片描述
但是,如果我们关闭掉一个会怎么样呢?
在这里插入图片描述

如果我们关闭close(1);会出现什么情况呢?
在这里插入图片描述
我们发现,显示器上并没有打印的结果,这是为什么呢?且看下面解释:
在这里插入图片描述

    printf("fd:%d\n",fd);fprintf(stdout,"fd:%d\n",fd);

这两个printf的结果都是一样:
在这里插入图片描述
在这里插入图片描述

上面结果可以说明printf本质上就是打印输出到stdout中,现在我们将stdout先关闭了,然后创建的新struct_file占据了原本的stdout的位置,所以我们可以认为现在的printf输出应该输出到新创建的文件log.txt中,但是我们cat log.txt发现里面并没有数据,难道我们的结论是错误的?不是的,这里是因为缓冲区的缘故。

我们在最后刷新一下fflush(stdout);,我们这里刷新的是stdout,且最后输出的结果就是fd:1,证明确实是分配的1号位
在这里插入图片描述

本来输出打印应该打印到stdout显示器上,但是我们现在关闭close(1);却将打印的结果打印到了新创建的文件中,这种特性叫做重定向

重定向

如果我们也想实现跟刚才一样的显示效果,将原本打印到显示器上的数据,输出到新建文件中去:
在这里插入图片描述
在这里插入图片描述
我们需要将1号位中的数据替换为新建文件fd,那么最终留下来的肯定是fddup2调用是将newfd中的内容替换为oldfd,留下来的是oldfd,所以我们使用dup2调用需要:dup2(fd,1);

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FILE_NAME "log.txt"
int main()
{umask(0);int fd = open(FILE_NAME,O_WRONLY | O_CREAT,0666);if(fd < 0){perror("open");return 1;}dup2(fd,1);printf("fd:%d\n",fd);fprintf(stdout,"fd:%d\n",fd);fflush(stdout);close(fd);return 0;
}

这样我们就完成了一个重定向功能
在这里插入图片描述

追加重定向O_APPEND + dup2(fd,1);

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FILE_NAME "log.txt"
int main()
{umask(0);int fd = open(FILE_NAME,O_WRONLY | O_CREAT | O_APPEND,0666);if(fd < 0){perror("open");return 1;} dup2(fd,1);printf("fd:%d\n",fd);fprintf(stdout,"fd:%d\n",fd);const char* msg = "hello world";write(1, msg, strlen(msg));fflush(stdout);close(fd);return 0;
}

持续运行./Test,将输出内容追加到log.txt
在这里插入图片描述

输入重定向dup2(fd,0);

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FILE_NAME "log.txt"
int main()
{umask(0);int fd = open(FILE_NAME,O_RDONLY);if(fd < 0){perror("open");return 1;}dup2(fd,0);char arr[64];while(1){printf("输入> ");if(fgets(arr, sizeof(arr), stdin) == NULL)break;printf("%s",arr);}close(fd);return 0;
}

通过dup2(fd,0);log.txt里面的数据用做标准输入,while循环的作用是将标准输入中的数据拿出来存放入数组中,然后将其打印出来
在这里插入图片描述

如果我们在父进程中创建子进程,然后这个子进程做重定向操作会影响父进程吗?
在这里插入图片描述
程序替换,同样不会影响曾经进程打开过的重定向文件,重定向的各种操作属于内核数据结果,而程序替换则是磁盘与内存的代码数据替换,二者不会影响到。

常见的重定向有:> >> <
 我们分别使用一下:

  1. <输出重定向
    在这里插入图片描述
  2. >输入重定向
    在这里插入图片描述
  3. >>追加重定向
    在这里插入图片描述

C语言文件层面的缓冲区知识

我们先运行下面代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{// C语言接口printf("hello printf\n");fprintf(stdout, "hello fprintf\n");fputs("hello fputs\n", stdout);// 系统调用const char *msg = "hello write\n";write(1, msg, strlen(msg));return 0;
}

在这里插入图片描述
我们将输出的结果重定向到log.txt文件中,可以发现打印的结果是一样的
在这里插入图片描述
但是如果我们将代码更改一下,在结尾创建子进程:

int main()
{// C语言接口printf("hello printf\n");fprintf(stdout, "hello fprintf\n");fputs("hello fputs\n", stdout);// 系统调用const char *msg = "hello write\n";write(1, msg, strlen(msg));fork();//do nothing -> return quitreturn 0;
}

在这里插入图片描述
还是一样的操作,但是在我们创建子进程后的,将输出重定向到log.txt,我们发现C语言接口打印了两遍,而系统调用只打印了一次
在这里插入图片描述

缓冲区刷线策略问题:
 缓冲区一定会结合具体的设备,定制自己的刷新策略

  • 立即刷新 – 无缓冲
  • 行刷新 – 行缓冲 – 显示器
  • 缓冲区满 – 全缓冲 – 磁盘文件
  • 用户强制刷新,比如fflush
  • 进程退出 – 一般都要进行缓冲区刷新

上面的出现的现象,一定与缓冲区有关,且缓冲区一定不在内核中,因为如果在内核中,write也会打印两次。我们之前谈论过的缓存区,都是指的用户级语言层面给我们提供的缓冲区,我们之前的输出输出操作都要传入 -> stdout stdin stderr 它们都是FILE*类型的,而FILE是一个结构体,这个结构体里面封装了fd还有一个缓冲区,所以我们刷新缓冲区都是fflush(文件指针)fclose(文件指针)

解释上面现象:
代码结束之前,进行创建子进程
1.如果我们没有进行重定向>,看到了4条消息,stdout 默认使用的是行刷新,在进程fork之前,三条C函数已经将数据进行打印输出到显示器上(外设),我们的FILE内部,进程内部不存在对应的数据了
2.如果我们进行了重定向>,写入文件不再是显示器,而是普通文件,采用的刷新策略是全缓冲,之前的3条c显示函数,虽然带了\n,但是不足以让stdout缓冲区写满,数据并没有被刷新!!!
执行fork的时候,stdout属于父进程,创建子进程时,紧接着就是进程退出,谁先退出,一定要进行缓冲区刷新(就是修改),修改就会发生写时拷贝,数据最终会显示两份
3. write为什么没有呢?上面的过程都和wirte无关,wirte没有FILE,而用的是fd,就没有C提供的缓冲区


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/177.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在Mac上安装Aspectj1.9.8(用于Java17)+IDEA

1. 确定所使用的Java版本和AspectJ的对应关系 2. 下载AspectJ包 3. 安装AspectJ 4. 添加AspectJ对应的环境变量 5. AspectJ测试-简单终端测试 6. AspectJ测试-通过IDEA敲代码测试 ---------------------------------------详细教程-------------------------------------…

css基础(二)

目录 1. CSS 的复合选择器 1.1 什么是复合选择器 1.2 后代选择器(重要&#xff09; 1.3 子选择器(重要&#xff09; 1.4 并集选择器(重要&#xff09; 1.5 伪类选择器 1.6 链接伪类选择器 1.7 :focus伪类选择器 1.8 复合选择器总结 二、 CSS 的元素显示模式 2.1什么是元素显示模…

组合逻辑电路设计---多路选择器

目录 1、多路选择器简介 2、硬件设计 3、实验任务 4、程序设计 4.1、模块设计 4.2、绘制波形图 4.3、编写代码 &#xff08;1&#xff09;assign 中条件运算符&#xff08;三目运算符&#xff09;实现方法&#xff1a; &#xff08;2&#xff09;always 语句块中使用 …

微信小程序editor富文本编辑器 有关图片的属性

微信小程序editor富文本编辑器 有关图片的属性 属性类型默认值必填说明show-img-sizebooleanfalse否点击图片时显示图片大小控件show-img-toolbarbooleanfalse否点击图片时显示工具栏控件show-img-resizebooleanfalse否点击图片时显示修改尺寸控件 详细请看&#xff1a;edito…

CPU、内存、缓存的关系

术语解释 &#xff08;1&#xff09;CPU&#xff08;Central Processing Unit&#xff09; 中央处理器 &#xff08;2&#xff09;内存 内存用于暂时存放CPU中的运算数据&#xff0c;以及与硬盘等外部存储器交换的数据。它是外存与CPU进行沟通的桥梁&#xff0c;内存的运行决定…

AI 绘画(1):生成一个图片的标准流程

文章目录 文章回顾感谢人员生成一个图片的标准流程前期准备&#xff0c;以文生图为例去C站下载你需要的绘画模型导入参数导入生成结果&#xff1f;可能是BUG事后处理 图生图如何高度贴合原图火柴人转角色 涂鸦局部重绘 Ai绘画公约 文章回顾 AI 绘画&#xff08;0&#xff09;&…

Angular 与 PDF之五 实现方式的选择与扩展

在纯web的前提下&#xff08;不考虑移动端native&#xff09;&#xff0c;PDF的功能基本包括&#xff1a; 客户端PDF&#xff1a;最简单的场景&#xff0c;实现方式也很多&#xff0c;基本不需要有什么顾虑的地方&#xff0c;简单的实现可以参考系列第一篇文章。客户端PDF预览&…

RabbitMQ 2023面试5题(四)

一、RabbitMQ有哪些作用 RabbitMQ是一个消息队列中间件&#xff0c;它的作用是利用高效可靠的消息传递机制进行与平台无关的数据交流&#xff0c;并基于数据通信来进行的分布式系统的集成&#xff0c;主要作用有以下方面&#xff1a; 实现应用程序之间的异步和解耦&#xff1a…

Nginx Rewrite的应用

目录 一、Nginx Rewrite 二、Rewrite的功能 1.Rewrite 跳转场景 2.Rewrite 跳转实现 3.Rewrite 实际场景 4.Rewrite 正则表达式 5.Rewrite 命令/语法格式 6.location 分类 7.location 优先级 8.Rewrite和location比较 9.根据以上了解&#xff0c;小案例来操…

vtk绘制折线

vtk绘制折线 引言示例一开发环境项目结构示例代码运行结果 示例二概况示例代码运行结果 引言 本文通过两种方式来绘制折线。示例一中通过vtkPolyLine来绘制折线;示例二中通过vtkLineSource来绘制折线&#xff0c;只不过示例二中折线是由一段一段的直线绘制&#xff0c;最后构成…

Prometheus+Grafana普罗米修斯搭建+监控MySQL

PrometheusGrafana普罗米修斯搭建监控MySQL 一&#xff0c;Prometheus 1.什么是Prometheus&#xff1f; ​ Prometheus 是 Cloud Native Computing Foundation 的一个监控系统项目, 集采集、监控、报警等特点于一体。 ​ Prometheus主要受启发于Google的Brogmon监控系统&…

h5手写签名示例

前言 业务中需要用户进行签字&#xff0c;如何让用户在手机端进行签字&#xff1f; 示例如下 代码已分享至Gitee: https://gitee.com/lengcz/qianming 原示例&#xff1a; https://www.jq22.com/jquery-info13488 H5实现手写签字 创建一个html页面 <!DOCTYPE html> …