Linux文件理解和系统调用

news2024/11/24 14:50:52

文件理解和系统调用

本文已收录至《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类型重命名为FILE

struct _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 0
 int _blksize;
#else
 int _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; //关闭stdin
   fclose(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 0x4

void 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.coloradmin.cn/o/624407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IMX6ULL裸机篇之SPI原理图

一. IMX6ULL的 SPI 实验 I.MX6ULL 有4 个 SPI 接口&#xff0c;可以通过这 4 个 SPI 接口来连接一些 SPI 外设。 I.MX6U-ALPHA 使用 SPI3 接口 连接了一个六轴传感器 ICM-20608 &#xff0c;本章我们就来学习如何使用 I.MX6U 的 SPI 接口来驱动 ICM-20608…

【vue3】09-vue组件化额外知识补充(上)-生命周期-ref引用

组件化-额外知识补充&#xff08;上&#xff09; 生命周期认识生命周期生命周期函数的演练 refs引用 生命周期 认识生命周期 什么是生命周期呢? 生物学上&#xff0c;生物生命周期指得是一个生物体在生命开始到结束周而复始所历经的一系列变化过程;每个组件都可能会经历从创…

适应新时代的FTP已经出现?这种产品有何过人之处?

大家都知道&#xff0c;FTP是用于在网络上进行文件传输的一套标准协议&#xff0c;它作为互联网最经典的协议之一&#xff0c;至今已经存在了50年。而随着时代发展&#xff0c;越来越多的用户与企业开始觉得FTP不够满足大家的需求&#xff0c;出现的问题与漏洞越来越多&#xf…

好用工具第4期:全能播放器PotPlayer

好用工具第4期:全能播放器PotPlayer Global Potplayer 是一款 Windows 平台的全能播放器。支持几乎所有的视频格式&#xff0c;音频格式&#xff0c;以及在线播放全世界的电视直播。 其官网是&#xff1a; https://potplayer.daum.net/?langzh_CN 特点 支持强劲引擎加速支持3…

【微服务】SpringCloudAlibaba

一 微服务架构 1.1 微服务 微服务其实是一种架构风格&#xff0c;我们在开发一个应用的时候这个应用应该是由一组小型服务组成&#xff0c;每个小型服务都运行在自己的进程内&#xff1b;小服务之间通过HTTP的方式进行互联互通。 1.2 微服务架构的常见问题 一旦采用微服务系…

【论文阅读】(2013)Exact algorithms for the bin packing problem with fragile objects

文章目录 一、摘要二、介绍三、之前在这个问题上的工作四、易碎物品背包问题的求解4.1 ILP模型4.2 基于KP01的方法4.3 动态规划 五、二元分支方案5.1 分支方案1&#xff08;基于决策变量的分支&#xff09;5.2 分支方案2&#xff08;基于yj和xji的分支&#xff09;5.3 将L2嵌入…

精进嵌入式系统设计

当涉及到嵌入式系统设计时&#xff0c;以下是一些建议和关键点&#xff0c;可以帮助您进行有效的设计&#xff1a; 确定需求&#xff1a;明确系统设计的功能需求和性能指标。了解系统的预期用途、功能要求、资源限制和实时性需求等。 硬件选择&#xff1a;根据需求选择合适的硬…

Visio matlab 图像取消边框

visio 图像取消边框 1.菜单栏→文件→选项→自定义功能区→勾选“开发工具”→确定 2.菜单栏→开发工具→显示ShapeSheet→页→Print Properties→将“PageLeftMargin”、“PageRightMargin”、“PageTopMargin”和“PageBottomMargin”均修改为0 &#xff08;双击单元格来编辑…

报表生成器FastReport .Net用户指南:“Text“对象、文本编辑

FastReport .Net是一款全功能的Windows Forms、ASP.NET和MVC报表分析解决方案&#xff0c;使用FastReport .NET可以创建独立于应用程序的.NET报表&#xff0c;同时FastReport .Net支持中文、英语等14种语言&#xff0c;可以让你的产品保证真正的国际性。 FastReport.NET官方版…

微软MFC程序运行的正确顺序

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天来看一下微软MFC程序运行的正确顺序。这真的只是一个小众话题。但是对理解MFC很重要。 很多人写了一堆MFC程序&#xff0c;却不知道MFC程序是怎么运行顺序的。我们就来看一看这个问题。 我在之前的帖子中…

Java IO模型图解(BIO NIO AIO)

一、冯诺伊曼模型 如图&#xff1a; 图片来源&#xff1a;百度百科 输入设备向计算机输入数据&#xff0c;输出设备接收计算机输出的数据。 所有的计算机程序&#xff0c;也都可以抽象为从输入设备读取输入信息&#xff0c;通过运算器和控制器来执行存储在存储器里的程序&am…

热修复/热更新

热修复/热更新 一.Android热修复二.热修复框架三.类加载器0.BootClassLoader1.PathClassLoader2.DexClassLoader 四.实现思路五.代码1.FixManager2.App3.更加标准的代码 五.制作补丁包1.写段有bug的工具类&#xff0c;并写个点击按钮调用2.运行项目到模拟器上3.修复ToastUtils工…

node_modules插件代码修改,如何快速修改防止npm install安装覆盖代码

一、背景 有些组件不满足业务时咱们只能修改插件的源码&#xff1a; 直接在项目的node_modules下找到插件的源码直接修改&#xff1b; 优点&#xff1a;简单直接、快速见效&#xff1b;缺点&#xff1a;不能持久化&#xff0c;一旦重新npm install安装就失效&#xff1b;不方…

(五)ArcGIS空间数据的转换与处理——数据处理

ArcGIS空间数据的转换与处理——数据处理 目录 ArcGIS空间数据的转换与处理——数据处理 1.数据裁剪1.1矢量数据裁剪1.1.1图解1.1.2用法1.1.3操作步骤 1.2栅格数据裁剪1.2.1矩形裁剪1.2.2利用已有数据裁剪 2.数据拼接2.1矢量数据的拼接2.1.1图解2.1.2用法2.1.3 操作步骤 2.2栅格…

区块链学习三——比特币的数据结构

区块链学习三——比特币的数据结构 文章内容来源于北京大学肖臻老师《区块链技术与应用》公开课 文章目录 区块链学习三——比特币的数据结构 一、哈希指针&#xff08;hash pointers&#xff09;二、区块链三、Merkle tree1.Merkle tree的作用&#xff1a;Merkle Proof2.Proo…

2023最新版本Activiti7系列-任务分配

任务分配 1.固定分配 在指派用户任务的审批人时。我们是直接指派的固定账号。但是为了保证流程设计审批的灵活性。我们需要各种不同的分配方式&#xff0c;所以这节我们就详细的来介绍先在Activiti7中我们可以使用的相关的分配方式. 固定分配就是我们前面介绍的&#xff0c;在绘…

vr沉浸式仿真实训展厅中控系统提高课堂纪律

为解决实训教学过程中“看不到、进不去、成本高、危险大”的问题&#xff0c;VR智能中控系统为职业教育及高等教育老师提供一个数字化、沉浸式、集中管控的实训教学工具。 VR智能中控系统通过对VR教学课堂的实时监控、数据的收集和分析&#xff0c;为气象学院的教学提供更多帮助…

STM32 HAL库 使用 USB HID 配置

STM32 HAL库 HID 配置 STM32 CubeMax 配置修改USB描述符修改HID设备PID VID修改HID报告间隔修改USB报告长度发送和接收发送接收 STM32 CubeMax 配置 我这里使用的是HS接口但使用的是内部FS核。 修改USB描述符 在 usbd_custom_hid_if.c 的 CUSTOM_HID_ReportDesc_HS 或 CUSTO…

python基础----08-----json、pyecharts模块介绍以及折线图、地图、柱状图的绘制

一 python变量和json数据的相互转化 json就是 一种在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言中的数据传递和交互、类似于: 国际通用语言 -英语。 import json if __name__ __main__:# 1. 将python变量转成json(列表->json)# 准备列表&#xff0c;列表内…

css background-position属性

定义 background-position 属性用于设置或获取元素背景图像相对于原点的初始位置。 background-position 基本语法 background-position&#xff1a;背景图片水平位置参数 背景图片垂直位置参数 &#xff1b; background-position 通常使用水平方向以及垂直方向的组合来定义背…