Linux文件理解和系统调用

文件理解和系统调用

本文已收录至《Linux知识与编程》专栏!
作者:ARMCSKGT
演示环境:CentOS 7

在这里插入图片描述


文件理解和系统调用

  • 前言
  • 正文
    • 文件概念
    • 文件描述符
      • 文件描述符概念
      • 文件管理
      • 关于 files_struct
      • 文件描述符的分配
      • 一切皆文件思想
    • C语言文件操作
      • 文件的打开与关闭
      • 文件读写
    • 文件操作系统调用
      • 打开文件open
      • 关闭文件close
      • 写入文件write
      • 读取文件read
      • 系统调用演示
  • 最后


前言

我们在学习C语言时可以使用fopen打开文件fclose关闭文件;那么是C语言帮我们打开的文件吗?其实并不是,语言没有这个能力,而是借助操作系统之手打开文件并进行操作,本篇将为大家介绍关于Linux下文件操作的系统调用,并介绍Linux系统如何组织和管理进程打开的文件!
结构层次


正文

在对文件操作之前,我们需要知道文件在系统中到底是什么!

文件概念


  • 文件操作本质:我们从语言层面是使用库函数操作文件,让我们以外文件操作是语言承担;但其实并非如此,文件操作是操作系统的事,是系统层面的问题,操作系统对于打开文件也会像管理进程一样通过先描述再组织进行管理
  • 几乎所有语言都有文件操作,但是操作方法不太相同,但都是对系统接口的封装,不同的语言有不同的范式,所以使用上会有区别,底层调用的是同一个系统调用
  • 操作文件的第一件事就是打开文件,而 文件=内容+属性 ,针对文件的操作有对文件内容的操作,也有对文件属性的操作;当文件没有被操作的时候,文件一般在磁盘(外存)上;当我们对文件进行操作的时候,文件需要被加载到内存中,因为冯诺依曼体系结构,CPU要读文件需要先把文件搬到内存
  • 当我们对文件进行操作的时候,文件需要提前被加载到内存,至少需要把文件属性加载到内存中;而每分每秒不止一个文件被加载到内存(不止一个文件被打开),也可能不止一个用户在加载文件到内存,内存中一定存在大量的不同文件的属性
  • 打开文件本质就是将我们需要的文件属性加载到内存中;操作系统内部一定会存在大量的被打开的文件,操作系统要管理这些被打开的文件,管理方式是:先描述再组织

    所以:每一个被打开的文件都要在操作系统内对应文件对象的struct file对象,可以将所有的struct file对象用某种数据结构链接起来,在操作系统内部对被打开的文件进行管理就被转换成为了对数据结构的增删查改

    描述: 构建在内存中的文件结构体,也就是创建 struct file 对象记录被打开的文件的属性信息
    组织: 通过struct file类型对象指针形成链表(等其他数据结构),对文件的管理就变成了对数据结构的增删查改
  • 文件其实可以被分成两大类:磁盘文件和被打开的文件(内存文件)
  • 文件被打开,是操作系统在打开,是用户让操作系统打开的,用户是以进程(bash)为代表的;我们之前的所有的文件操作,都是 进程被打开文件 的关系;

    进程被打开文件 的关系:struct task_struct 和 struct file 之间的联系

结论:真正的文件操作是需要通过系统调用实现,而我们之前的文件操作都是进程操作系统间的交互;文件被打开,操作系统要为被打开的文件,创建对应的内核数据结构struct file,其中包含各种属性和各种链接关系!


文件描述符


文件描述符概念

在C语言中,我们打开一个文件会形成一个FILE类型的指针,fopen打开文件会传递一个FILE类型的对象地址(底层创建了一个FILE对象)供我们使用,我们操作不同的文件围绕不同的FILE指针即可!

#include <stdio.h>int main()
{FILE* f1 = fopen("test1.txt", "r");FILE* f2 = fopen("test2.txt", "w");//读取写入操作fclose(f1);fclose(f2);f1 = f2 = NULL;return 0;
}

代码中,我们要操作不同的文件只需要对f1和f2指针进行操作即可,但是FILE是一个对象,而底层是对文件描述符和其他属性进行封装,在操作系统层面上,对任何文件操作只认该进程的文件描述符

FILE对象
这里分享FILE对象源码,FILE对象是_IO_FILE的重命名,_IO_FILE对象中 _fileno 就是文件描述符!

typedef struct _IO_FILE FILE; //stdio库中对_IO_FILE类型重命名为FILEstruct _IO_FILE {int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags/* The following pointers correspond to the C++ streambuf protocol. *//* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr;	/* Current read pointer */char* _IO_read_end;	/* End of get area. */char* _IO_read_base;	/* Start of putback+get area. */char* _IO_write_base;	/* Start of put area. */char* _IO_write_ptr;	/* Current put pointer. */char* _IO_write_end;	/* End of put area. */char* _IO_buf_base;	/* Start of reserve area. */char* _IO_buf_end;	/* End of reserve area. *//* The following fields are used to support backing up and undo. */char *_IO_save_base; /* Pointer to start of non-current get area. */char *_IO_backup_base;  /* Pointer to first valid character of backup area */char *_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno; //文件描述符
#if 0int _blksize;
#elseint _flags2;
#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/*  char* _save_gptr;  char* _save_egptr; */_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

打印文件描述符
我们尝试打印文件描述符,首先我们知道,C语言有三个默认的流:

  • sdtin标准输入流(我们从键盘输入的文件流)
  • stdout标准输出流(打印到屏幕的流)
  • stderr标准错误输出流

    这三个流都是FILE类型我们通过以下C++代码输出标准流的类型与普通文件流做对比:
#include <iostream>
#include <cstdio>
#include <typeinfo> //typeid所需库
using namespace std;
int main()
{FILE* f1 = fopen("./test1.txt", "r");FILE* f2 = fopen("./test2.txt", "w");cout<<"类型对比:"<<endl;cout<<"f1指针类型:"<<typeid(f1).name()<<endl;cout<<"stdin指针类型:"<<typeid(stdin).name()<<endl;cout<<endl;cout<<"文件描述符"<<endl;cout<<"stdin文件描述符:"<<stdin->_fileno<<endl;cout<<"stdout文件描述符:"<<stdout->_fileno<<endl;cout<<"stderr文件描述符:"<<stderr->_fileno<<endl;cout<<"f1文件描述符:"<<f1->_fileno<<endl;cout<<"f2文件描述符:"<<f2->_fileno<<endl;fclose(f1);fclose(f2);f1 = f2 = NULL;return 0;
}

文件描述符
从这里可以看出文件描述符是真实存在的,且是一个整数从0开始,而我们自己打开一个文件的文件描述符是从3开始的!


文件管理

操作系统要对这些打开的文件进行管理,否则每次操作文件都需要去内存中查找,高效的管理可以极大的提高IO效率!

对于打开的文件,操作系统会以先描述再组织的形式管理这些被打开的文件!

操作系统将这些打开的文件视为file对象,通过他们的file指针操作,并将这些file指针存入数组中,使用数组下标进行管理,这个数组为 file* fd_array[] ,而数组的下标就是神秘的文件描述符fd

除了文件描述符外,文件的属性还有文件权限、大小、路径、引用计数、挂载数等信息,将这些文件属性信息汇集起来就构成了 struct files_struct 这个结构体,它正是进程控制块 struct task_struct 中的成员之一

而当一个程序启动时,操作系统会默认为他打开 stdin标准输入流stdout标准输出流stderr标准错误流 这三个文件流,将他们的文件file指针存入 fd_array[] 数组中,依次为 0,1,2 三个数组下标,也就是文件描述符;后续再打开文件时,默认将打开的文件file指针分配到当前数组未使用的最小下标处,所以用户打开文件一般是从3开始的,当然我们关闭所有标准流后打开文件就是从0开始分配文件描述符!


关于 files_struct

当我们打开一个文件时,在内存中会形成一个files_struct对象,files_struct对象是对该文件属性的描述!
文件描述符
针对每个进程都会打开文件,进程控制块task_struct中必然包含文件操作相关信息,也就是files_struct !

注意:当我们没有被打开时,文件在磁盘上;当文件被打开后,并不是直接将全部内容加载到内存上,而是先通过文件inode(后面介绍)找到磁盘上文件的详细信息,加载文件属性信息形成files_struct,待使用时再加载内容!


文件描述符的分配

文件描述符fd的分配规则是:分配到当前描述符数组未使用的最小下标位置处!

说明:

  • 当我们打开文件时,因为默认的三个标准文件流已经打开,所以当前的最小下标一定是3
  • 如果我们关闭了标准文件流,例如stdin(文件描述符为0),则新打开的文件会分配文件描述符0(未使用的最小下标处)
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{cout<<"stdin文件描述符:"<<stdin->_fileno<<endl;cout<<"stdout文件描述符:"<<stdout->_fileno<<endl;cout<<"stderr文件描述符:"<<stderr->_fileno<<endl;FILE* f1 = fopen("./test1.txt", "r"); //先打开test1文件cout<<"f1文件描述符:"<<f1->_fileno<<endl;cout<<"关闭stdin标准文件流"<<endl; //关闭stdinfclose(stdin);FILE* f2 = fopen("./test2.txt", "w"); //再打开test2文件cout<<"f2文件描述符:"<<f2->_fileno<<endl;fclose(f1);fclose(f2);f1 = f2 = NULL;return 0;
}

文件描述符分配


一切皆文件思想

我们知道在Linux系统下一切皆文件,我们如何理解这个概念?

对于Linux系统来说,无论是键盘还是显示器等设备,在他开来都是文件,是一个file对象

无论是硬件外设还是软件,对于操作系统来说无非就是输入和输出两个操作,所以操作系统对于这些硬件只需要提供读方法和写方法即可驱动该硬件(对于只读或只写的设备屏蔽其中一个方法即可),所以这些硬件设备被当成文件打开,在程序启动时将他们的file写入fd_array中管理即可,所以Linux下一切皆文件!
一切皆文件


C语言文件操作


在讲解系统调用前,我们简单了解一下C库的文件操作方式!
如果想要详细的了解,请阅读官方文档:C文件操作库

文件的打开与关闭

文件的打开使用fopen,关闭使用fclose!

打开文件:

FILE * fopen ( const char * filename, const char * mode ); //打开文件
  • 参数:
    – filename:被打开文件的本地路径
    – mode:打开方式(以字符串的方式传递)
  • 返回值:
    –如果文件不存在则返回空指针
文件打开方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

关闭文件:

int fclose ( FILE * stream );
  • 参数:
    –stream:FILE文件指针
  • 返回值:
    –成功返回0,失败返回EOF(-1)

演示:

#include <stdio.h>
int main()
{FILE* f1 = fopen("./test1.txt", "r");FILE* f2 = fopen("./test2.txt", "w");if(f1 && f2) printf("文件打开成功!\n"); //不是空指针则打开成功int m = fclose(f1);int n = fclose(f2);if(!m && !n) printf("文件关闭成功!\n"); //返回0则关闭成功f1 = f2 = NULL;return 0;
}

打开与关闭演示


文件读写

文件读写结果配套出现,有读就有写!

文件读取接口:

//读取文件中的一个字符
int fgetc ( FILE * stream ); //读取文件中一行字符
char * fgets ( char * str, int num, FILE * stream ); //从给定流 stream 读取数据到 ptr所指向的数组中
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );//从一个流中执行格式化输入,遇到空格和换行时结束
int fscanf ( FILE * stream, const char * format, ... );//从字符串读取格式化输入,遇到空格和换行时结束
int sscanf ( const char * s, const char * format, ...);

文件写入接口:

//将字符character写入文件中
int fputc ( int character, FILE * stream );//将字符串str写入文件中
int fputs ( const char * str, FILE * stream );//把 ptr 所指向的数组中的数据写入到给定流 stream 中
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );//将字符串格式化输出到流 stream 中
int fprintf(FILE *stream, const char *format, ...);

其他写入接口:

//将字符串格式化写入str中
int sprintf(char *str, const char *format, ...);//向s字符串格式化写入n个字符
int snprintf ( char * s, size_t n, const char * format, ... );

snprintf是sprintf的升级版,可以控制写入字符数量,更加安全;在文件操作中,这两个接口常用于向缓冲区中写入数据,然后整体写入到文件中(为了更加方便合理的向文件中写入数据,一般预先定义缓冲区存储数据然后整体写入)!

示例:
这里我们使用fscanf格式化读取文件,snprintf格式化写入缓冲区,fprintf格式化写入到文件!

#include <stdio.h>
#include <stdlib.h>
#define FNAME "log" //操作文件名int main()
{//写操作FILE* wfp = fopen(FNAME,"w+"); //以只写的方式打开log文件,如果没有则创建if(!wfp) //打开失败就退出{perror("fopen error!\n");exit(EOF);}char buf1[64] = {0}; //写入缓冲区snprintf ( buf1, sizeof(buf1), "%s:%d", "向文件写入",668 ); //先格式化写入缓冲区fprintf(wfp,"%s",buf1); //将缓冲区中的字符串整体写入文件中fclose(wfp); //写操作完成后关闭文件wfp = NULL;//读操作FILE* rfp = fopen(FNAME,"r"); //以只读的方式打开log文件if(!rfp) //打开失败就退出{perror("fopen error!\n");exit(EOF);}char buf2[64] = {0}; //读取缓冲区fscanf(rfp,"%s",buf2);printf("%s\n",buf2);fclose(rfp); //读操作完成后关闭文件rfp=NULL;return 0;
}

运行结果
注意:我们在写入字符串时没有加入 \n 换行,因为 \n 是C语言定义的换行符,其他语言和软件可能无法识别,所以我们写入时建议不要带有一些仅语言定义的格式符;当我们使用cat打印出log文件中的信息时,输出了与我们文件操作一模一样的字符只不过没有换行!


文件操作系统调用


前面简单介绍了C语言的文件操作,现在我们来介绍Linux文件操作系统调用!

打开文件open

打开文件使用open接口,关于open接口:

#include <sys/types.h> //open接口所需库
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags); 
int open(const char *pathname, int flags, mode_t mode); //打开文件可以修改权限
  • 返回值:
    –如果文件打开成功则返回对应的文件描述符,如果打开失败则返回-1
  • 参数:
    –pathname:被打开文件的路径(与C语言保持一致)
    –flags:打开方式,使用的标记位的方式传递选项信号(标记位以位图方式设置)
    –mode:如果文件不存在,创建文件时的权限设置,文件起始权限为0666
  • 注意:
    这两个open函数类似于重载,如果我们打开的文件可能不存在,则一定要手动设置mode权限,否则文件创建出来权限是随机值组成的,这样可以保证文件安全;继承环境变量表后,umask 默认为 0002,当然也可以使用umask函数自定义!

关于设置标志位flags的理解:
我们知道一个int有四字节,一共32个比特位,每一个比特位可以表示1/0
标志位位图
而在open函数的flags函数中,我们可以想想为有32个开关,进行不同的组合,不能有相同的组合和包含某一个组合,这样某一个标志位都是独立的,表示一个指令信息!
如果我们要验证某一个标志位是否满足使用按位与即可,如果我们要融合多个标志位指令一起传递给函数使用按位或即可

利用这个特性,我们可以实现一个小的位图deom:

#include <stdio.h>#define ONE 0x1 //定义位图标志位(比特位不能包含和相同)
#define TWO 0x2
#define THREE 0x4void directives(int flags)
{//模拟实现三种选项指令传递if(flags & ONE)printf("ONE指令\n");if(flags & TWO)printf("TWO指令\n");if(flags & THREE)printf("THREE指令\n");
}int main()
{//使用按位或传递多个标志位参数directives(ONE);printf("**************************\n");directives(ONE | TWO);printf("**************************\n");directives(ONE | TWO | THREE);return 0;
}

标志位传参

关于flags常用的标志位:

O_RDONLY	//只读
O_WRONLY	//只写
O_APPEND	//追加
O_CREAT		//新建
O_TRUNC		//清空
O_RDRW		//可读可写
O_EXCL		//文件必须是被创建的,如果文件已存在则报错-1

这些标志位可以通过按位或进行组合!

基于C语言打开方式的常用组合:

  • w:O_WRONLY | O_CREAT | O_TRUNC (如果文件不存在则创建并清空文件内容,只写)
  • a:O_WRONLY | O_CREAT | O_APPEND (如果文件不存在则创建,只追加写入)
  • r:O_RDONLY (以只读的方式打开文件)
  • …还有一些其他功能,根据标志位进行自由组合即可!

所以只要我们想使用open做到只写方式打开不存在的文件,也不会报错,加个 O_CREAT 参数即可实现自动创建!


关闭文件close

系统调用关闭文件使用close函数,与fclose相似!

#include <unistd.h>
int close(int fildes); //fildes(fd)是文件描述符

close函数解析:

  • 参数fildes:需要关闭文件的文件描述符
  • 返回值:关闭成功返回0,失败返回-1

我们可以通过close(0),close(1),close(2)方式关闭标准文件流stdin,stdout,stderr!


写入文件write

write函数用于写入文件,其返回值类型有点特殊,但使用方法与fwrite基本一致!

#include <unistd.h>
ssize_t write(int fildes, const void *buf, size_t count);

write函数解析:

  • 参数:
    – fildes:文件描述符
    – buf:写入的字符串目标源指针或缓冲区(简称写入源)
    – count:写入的字节数
  • 返回值:写入成功返回写入的字节数,失败返回-1

读取文件read

系统调用read用于从文件中读取指定字节的数据!

#include <unistd.h>
ssize_t read(int fildes, void *buf, size_t count);

read函数解析:

  • 参数:
    – fildes:文件描述符
    – buf:读入的缓冲区(将读入数据写入到buf缓冲区,可以是一个字符数组或开辟的空间)
    – count:读入的字节数
  • 返回值:读取成功返回读入的字节数,读取失败返回-1

系统调用演示

注意:虽然我们使用系统调用写入数据,但是为了方便,我们在写入和读取时还是借助缓冲区buf比较好!

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "log" //操作文件名int main()
{//以只写的方式打开文件,>如果不存在则创建且清空文件内容int wfd = open(FNAME,O_WRONLY | O_CREAT | O_TRUNC,0664);assert(wfd>0); //检测是否打开成功char buf1[64] = {0}; //写入缓冲区snprintf ( buf1, sizeof(buf1), "%s:%d", "向文件写入",668 ); //先格式化写入缓冲区int wsize = write(wfd,buf1,sizeof(buf1)); //写入缓冲区大小的内容printf("写入%d字节\n",wsize);close(wfd); //关闭文件//以只读的方式打开文件int rfd = open(FNAME,O_RDONLY); assert(rfd>0); //检测是否打开成功char buf2[64] = {0}; //写入缓冲区int rsize = read(rfd,buf2,sizeof(buf2)); //向缓冲区buf2读入文件中缓冲区大小的内容printf("读取%d字节\n",rsize);close(rfd); //关闭文件printf("读入内容: %s\n",buf2);return 0;
}

在这里插入图片描述
同样的,我们cat打开文件没有换行的问题我们在前面C语言已经介绍了;不过需要注意的是,通过系统级函数 write 写入字符串时,不要刻意加上 ‘\0’,因为对于系统来说,这也只是一个普通的字符(‘\0’ 作为字符串结尾也是C语言的规定)与 \n 的问题一样!


最后

Linux文件操作系统调用到这里就介绍的差不多了,本节我们介绍了Linux下关于文件操作的系统调用,了解了操作系统管理被打开文件使用文件描述符的概念,知道了语言库函数底层是对系统调用的封装,以及Linux下一切皆文件思想的依据等等,文件的学习还没结束,下一节我们继续探究通过文件描述符如何实现重定向功能!

本次 <Linux文件理解和系统调用> 就先介绍到这里啦,希望能够尽可能帮助到大家。

如果文章中有瑕疵,还请各位大佬细心点评和留言,我将立即修补错误,谢谢!
结尾

🌟其他文章阅读推荐🌟
Linux<进程控制> -CSDN博客
Linux<进程地址空间> -CSDN博客
Linux<环境变量> -CSDN博客
Linux<进程初识> -CSDN博客
Linux<进程状态及优先级> -CSDN博客
🌹欢迎读者多多浏览多多支持!🌹

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

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

相关文章

github中Mermaid的用法

这个东西是最近推出&#xff0c;首先是自己的repository中新建一个readme.md文件 需要一点前端的知识&#xff0c;就是先导入一个依赖文件&#xff0c;然后再写甘特图&#xff0c;如下&#xff1a; ### 甘特图 [<a href"https://mermaid-js.github.io/mermaid/#/gant…

RabbitMQ保证消息的可靠投递,Java实现RabbitMQ消息的可靠投递,Springboot实现RabbitMQ消息的可靠投递

文章目录 一、RabbitMQ消息可靠性概述1、引出问题2、RabbitMQ消息可靠性保证的四个环节 二、保证生产者消息发送到RabbitMQ服务器1、服务端确认&#xff1a;Transaction模式&#xff08;1&#xff09;JavaAPI&#xff08;2&#xff09;springbootAPI 2、服务端确认&#xff1a;…

峰会来袭 | CAD模型转换工具选择的难点和关键点解答

作为世界顶尖的3D软件开发SDK和CAD模型转换工具——HOOPS Exchange已问世十多年&#xff0c;深受开发者好评&#xff0c;并在工业测量、机械加工、造船设计等领域都有广泛的应用。 本次峰会将围绕CAD软件造型技术的多样性、CAD模型数据解析的难点、3D模型转换的经典问题等&…

AI绘画结合GPT 把Ai绘画与摄影玩明白

一、绘画与摄影有什么关系&#xff1f; 绘画和摄影是两种不同的艺术形式&#xff0c;它们都以其自身独特的方式捕捉和表达现实。在某些方面&#xff0c;它们是相互联系的&#xff0c;而在其他方面&#xff0c;它们又有所不同。​ 相似之处&#xff1a;绘画和摄影都是创造性的…

低代码应用开发 高效构建业务系统

低代码是传统软件开发逐步优化和演变的产物&#xff0c;并非全新革命。传统的开发方法过于昂贵和僵化&#xff0c;无法为企业提供所需的高效和敏捷的开发流程&#xff0c;且交付周期长定制能力弱&#xff0c;难以应对不断变化的市场和客户期望&#xff0c;为提高软件开发效率&a…

linux查看内存总结

参考博客&#xff1a; https://www.atlantic.net/vps-hosting/find-top-10-running-processes-by-memory-and-cpu-usage/ 最简单的命令 free -g 查看还有多少可用的内存 但是有的时候这个提供的信息实在是太少了&#xff0c;例如我今天发现服务器上可用的内存只有50G, 我想知…

杠上了,AI绘画圈激烈开战,Midjourney 和 Stable Diffusion 双双更新!

杠上了&#xff0c;AI绘画圈激烈开战&#xff0c;Midjourney 和 Stable Diffusion 双双更新&#xff01; 文章目录 Part 1. Stable Diffusion XL 0.9图像生成重磅升级&#xff1a;35亿66亿双模型Part 2. Midjourney 5.2更新&#xff1a;相机变焦、画面扩展、自定义风格 Part 1.…

可以提升效率的时间管理APP,上班族的好帮手

在时间少&#xff0c;但是待办事项多的快节奏社会中&#xff0c;时间管理成为了很多人都需要面临的重要问题。特别是对于繁忙的上班族来说&#xff0c;高效地利用时间是提升工作效率的关键。有不少上班族都在为如何高效管理时间而烦恼&#xff0c;于是一些时间管理工具应运而生…

矢量数据库对比和选择指南

矢量数据库是为实现高维矢量数据的高效存储、检索和相似性搜索而设计的。使用一种称为嵌入的过程&#xff0c;将向量数据表示为一个连续的、有意义的高维向量。 本文将研究存储/检索向量数据和执行相似性搜索的实用方法&#xff0c;在我们深入研究之前&#xff0c;首先先介绍矢…

云贝餐饮连锁独立版 v2.6.1+公众号+小程序端+抖音端端+全插件免授权安装使用教程

云贝餐饮连锁版这系统应该持续做好多年了比较完善&#xff0c;以前的版本都需要授权一直没使用&#xff0c;整体测试下感觉从体验上还有逻辑设计上都非常不错&#xff0c;首页、会员中心支持DIY装修&#xff0c;配送支持多种平台对接&#xff0c;基本上餐饮行业需要的功能都能满…

【Python实战】Python采集大学教务系统成绩单

前言 在现代教育中,教务系统已经成为了学校管理和教学工作的重要组成部分。然而,由于各种原因,教务系统的成绩单并不能下载的,这给我们带来了很多不必要的麻烦和困扰。因此,采集教务系统成绩单的项目具有非常重要的意义。 目录 前言 环境使用 模块使用

0、技术选型

技术选型参考&#xff1a; 系统数据流程图 框架发行版本选型 1&#xff09;如何选择Apache/CDH/HDP版本&#xff1f; &#xff08;1&#xff09;Apache&#xff1a;运维麻烦&#xff0c;组件间兼容性需要自己调研。&#xff08;一般大厂使用&#xff0c;技术实力雄厚&#xf…