当前位置: 首页 > news >正文

【Linux】解锁文件描述符奥秘,高效缓存区的实战技巧

fd和缓冲区

  • 1. 文件描述符fd
    • 1.1. 概念与本质
    • 1.2. 打开文件的管理
    • 1.3. 一切皆文件的理解
    • 1.4. 分配规则
    • 1.5. 重定向的本质
      • 1.5.1. dup2
  • 2. FILE中的缓冲区
    • 2.1. 概念
    • 2.2. 存在的原因
    • 2.3. 类型(刷新方案)
    • 2.4. 存放的位置
      • 2.4.1. 代码证明、现象解释
    • 2.5. 模拟C标准库中的方法

1. 文件描述符fd

1.1. 概念与本质

  1. 定义:是用于标识打开文件的非负整数。

  2. 文件描述符的本质,就是数组下标。

1.2. 打开文件的管理

问:为什么访问文件的系统调用接口,都必需使用文件描述符fd?

  1. 当我们打开一个文件时,OS会在内存中创建一个file结构体,用来描述被打开的文件,这个结构体包含了文件的当前读写位置、文件描述符、文件路径等相关信息。
struct file {struct path f_path;          // 文件路径struct file_operations *f_op; // 文件操作指针int _fileno;                  //文件描述符loff_t f_pos;                // 当前文件读写位置...
};struct file_operations {int (*open)(struct inode *, struct file *);int (*release)(struct inode *, struct file *);ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);loff_t (*llseek)(struct file *, loff_t, int);...
};
  1. 因为open函数是由进程来执行的,所以必须让进程和文件关联起来。在每一个task_struct结构体中都包含了一个struct file_struct*类型的指针,它指向了一个包含文件描述符表的file_struct结构体。
struct task_struct {...files_struct *files;  // 文件描述符表...
};
  1. 文件描述符表是一个file*类型的指针数组,每个元素都指向一个打开的文件,而文件描述符就是此数组的下标,所以只要拿到了文件描述符表,就可以索引到对应的文件。
struct files_struct {...struct file *fd[FD_SETSIZE];  // 文件描述符数组...
};



打开文件本质:在内核中,把文件在磁盘上找到、内容和属性加载,在内存中创建file结构体,属性、方法、缓冲区都初始化,然后把结构体链入到系统管理文件的链表中,并且在指针数组中找到一个数据下标,再把它的地址填充进来,最后把数组下标(fd)返回给上层用户,应用层得到fd值。

a. 把数据写到文件中write:

  1. 因为write是由进程通过系统调用来执行的,而系统能够识别出是哪个进程在请求服务,即:OS可以找到进程(task_struct);

  2. 因为write需要访问文件,所以通过fd,直接在数组中进行索引,从而找到文件。再把用户空间中的缓冲区buf中的数据,拷贝到内核空间中的文件结构体对象的缓冲区中,最后让OS把缓冲区的刷新到磁盘文件中。

b. 从文件中读取数据read:

  1. 因为read是由进程通过系统调用来执行的,而系统能够识别出是哪个进程在请求服务,即:OS可以找到进程(task_struct);

  2. 因为read需要访问文件,所以通过fd,直接在数组中进行索引,从而找到文件。如果文件结构体对象的缓冲区中有内容,就直接读取到用户空间的缓冲区buf中,反之,就让OS把磁盘文件中的数据导入到内存中。

c. 关闭文件close:

  1. 因为close是由进程通过系统调用来执行的,而系统能够识别出是哪个进程在请求服务,即:OS可以找到进程(task_struct);

  2. 因为close需要访问文件,所以通过fd,直接在数组中进行索引,从而找到文件。OS再将文件结构体对象进行释放。

1.3. 一切皆文件的理解

一、文件系统的抽象和VFS

  1. VFS(Virtual File System):虚拟文件系统,是Linux内核的一个软件层,它提供了一套统一的接口来访问各种类型的文件系统和硬件设备。

这种设计使得用户和应用程序能够通过调用相同的系统调用(如open、write、read等)来操作不同的文件系统,而无需关心底层文件系统的具体实现细节。

  1. 文件操作结构体file_operations:在linux内核中,每个打开的文件都有一个指向file_operations结构体的指针,它包含一系列函数指针,即:它定义了文件的各种操作(如:读、写、打开、关闭),其内包含的函数指针指向具体的实现方法。

不同的文件系统或硬件的驱动程序会提供这些函数的具体实现,但这些函数的参数类、返回值类型、函数名,必须与定义在file_operations结构体中的函数指针相匹配。

struct file {struct inode *f_inode;  // 文件的inodestruct file_operations *f_op;  // 文件操作函数指针unsigned long f_flags;  // 文件标志loff_t f_pos;  // 当前文件位置// 其他信息...
};struct file_operations {  //文件操作函数int (*open)(struct inode *, struct file *);int (*release)(struct inode *, struct file *);ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);loff_t (*llseek)(struct file *, loff_t, int);// 其他操作...
};
  1. VFS的核心思想是通过抽象和封装来屏蔽底层硬件的差异。

二、面向对象编程的类比

  1. 多态性:在面向对象编程中,多态性允许我们使用统一的接口来调用不同的实现。在VFS中,是通过函数指针来实现多态性,不同的文件系统的具体实现方法不同,但上层应用程序只使用统一的函数指针接口。

  2. 封装:VFS屏蔽了文件系统和硬件设备的差异,即:隐藏了底层的细节;使得上层应用程序可以使用统一的函数指针接口来访问文件。

上层代码无需关心底层操作的实现,只需按照统一接口的规范进行操作。


不能从显示器读数据,平时在显示器输入的东西,显示器也显示了,不是通过显示器把数据交给了你的程序,而是从键盘中输入数据,你的程序先从键盘中读到的,为了让用户看到你输入的数据,程序就把数据同步的给显示器拷贝了一份。

1.4. 分配规则

  1. fd分配规则:最小未被使用的数组下标,会被分配给最新打开的文件。
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/stat.h>
#include<string.h>int main()
{close(1);//关闭标准输出流int fd1 =  open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);printf("fd1: %d\n", fd1); //printf默认向stdin—>fd = 1打印printf("hello world\n");  //输出重定向:本来应该把内容向显示器文件进行写入,更改为向磁盘文件进行写入return 0;
}

#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/stat.h>
#include<string.h>int main()
{close(0);  //关闭标入输出流                                           int fd2 = open("data.txt", O_RDWR);printf("fd2: %d\n", fd2); char buff[64]; fgets(buff, 64, stdin);   //输入重定向:本来应该从键盘文件中读取的内容,更改为从磁盘文件中读取printf("%s\n", buff); return 0;}

1.5. 重定向的本质

  1. 重定向的本质:更改文件描述符表的内容,即:更改文件描述符(stdin、stdou、stderr)的指向,使得原本要写入到标准输出的数据,被重定向到其他文件、或者原本要从标准输入中读取的数据,重定向到来自于其他文件。

1.5.1. dup2

int dup2(int oldfd,int newfd);

  1. 功能:将stdin、stdout、stderr重定向到文件或其他设备。

  2. 参数:oldfd:要被复制的文件描述符;newfd:目标文件的描述符。

  3. 返回值:成功,返回新的文件描述符(即:newfd)。出错时,返回 -1,并设置 errno以指示错误。

#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/stat.h>int main()
{int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);dup2(fd, 1);printf("hello zzx\n");return 0;
}

2. FILE中的缓冲区

2.1. 概念

  1. 概念:本质是一块内存区域,用于暂时存放数据,以便更高效地处理输入、输出操作。

💡此处的缓冲区(如:进度条中的缓冲区等),不是内存中的缓冲区,它是语言层面的缓冲区,即:C语言自带的缓冲区,由C语言标准库提供。

  1. 缓冲区也会为格式化输入、输出操作提高场所。

printf函数工作原理:它会将其他类型的数据(如整数、浮点数等)转换为字符数据(即字符串),转化后的数据会被写入到FILE结构体维护的缓冲区中,根据条件刷新缓冲区。

scanf函数工作原理:scanf会从输入流中读取字符数据,将读取的数据转化为相应的格式化数据,格式化的数据会被存放到FILE结构体维护的缓冲区中,最终被存放到变量中。

2.2. 存在的原因

  1. 提高使用者的效率

减少了C接口的使用时间,从而减少了用户的等待时间,提高了使用者的效率:调用C接口时,只要将数据交给了缓冲区,就可立即返回,无需等待实际的写入操作完成,意味这用户可以更快地继续执行其他任务。

  1. 提高计算机整体的拷贝效率。

调用系统调用接口,都是有成本的,有时间和空间的开销。

减少调用系统调用的次数,提高了计算机整体的拷贝效率:缓冲区可以聚集大量数据,直到缓冲区满了,再调用一次系统调用进行实际的数据写入,即:进行一次拷贝。

故事理解:张三给李四送生日礼物,只需要将礼物交给附近的菜鸟驿站,就可立即其他活动,无需亲自送到的李四那,即:提高了使用者的效率。菜鸟驿站不是每次只处理一个包裹,而是收集多个包裹,直到它们填满整个运输车辆,然后再一次性运送到目的地,即:聚集数据,一次拷贝,提高了计算机的整体效率。—— 菜鸟驿站就相当于缓冲区。

2.3. 类型(刷新方案)

一、无缓冲、无刷新

  1. 无缓冲:无刷新,意味着数据不会暂存在缓冲区中,而是立即被写入到目标设备中。

  2. 适用场景:需要立即看到结果、实时性要求很高的场景,如:实时系统、设备驱动程序。

  3. 优点:保证了数据的即时可见性。

  4. 缺点:性能下降,频繁的使用系统调用会增加开销。

二、全缓冲、全刷新

  1. 全缓冲:全刷新,缓冲区满了或者关闭文件时,缓冲区的数据才会被刷新到目的设备中。

  2. 适用场景:文件的读写操作,尤其是大文件。

  3. 优点:减少了系统调用的次数,提高了性能。

  4. 缺点:可能会丢失数据,如:在缓冲区的数据未被刷新前,发生崩溃,则这部分的数据就会丢失。

三、行缓冲、行刷新

  1. 行缓冲:行刷新,意味着遇到换行符\n,缓冲区的数据就会被立即刷新到目的设备中。

  2. 适用场景:标准输入输出(显示器)。

💡当调用c语言接口fflush(),进行强制刷新; 进程退出时,或文件关闭时,自动刷新。

2.4. 存放的位置

  1. 缓冲区存放在FILE结构体中,即:缓冲区是被FILE结构来维护的。

  2. 每个通过标准C库函数打开的文件,都拥有自己的缓冲区。

fwrite等标准库函数,会先将数据拷贝到缓冲区中,然后根据一定的条件,调用系统调用接口进行刷新。

文件操作的系统调用接口,其实是个拷贝函数,它将数据从语言层的缓冲区拷贝到内存的缓冲区。

typedef struct _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 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
};

2.4.1. 代码证明、现象解释

#include<stdio.h>
#include<unistd.h>
#include<string.h>int main()
{const char* s1 = "hello write\n";write(1, s1, strlen(s1));  //调用系统调用,直接将数据写入到内核中//fprintf、fwrite为库函数,向显示器进行写入,行刷新(遇到换行符)const char* s2 = "hello fprintf\n";fprintf(stdout, "%s", s2); const char* s3 = "hello fwrite\n";fwrite(s3, strlen(s3), 1, stdout);                     fork(); //在创建子进程之前,缓冲区中的数据全部被刷新到内核中了return 0;
}

现象1解释:write()为系统调用接口,直接将数据写入到内核中;fprintf、fwrite为库函数,先将数据写入到缓冲区中,因为它们都是向显示器进行写入,而写入显示器是行刷新(遇到换行符\n,进行刷新),所以fork创建子进程前缓冲区中的数据全部被刷新到内核中了。

Tips:刷新到内核的数据,不属于进程的数据;存放在缓冲区中的数据,属于进程的数据。

现象2解释:重定向到普通文件时,数据刷新缓冲区的方式,由行缓存变为全缓冲,C语言接口自带缓冲区,所以它会将数据写入到缓冲区中,就不会立即刷新。fork创建子进程,父子共享缓冲区的数据,但是进程退出后,统一进行刷新。刷新缓冲区,是清空缓冲区,是修改数据的一种方式,所以父子进程的数据会发生写时拷贝,父子进程分别刷新各自的缓冲区,随即产生两份数据。write是系统调用接口,直接将数据写入到内核中,不存在所谓的缓冲区。

  1. 一般C库函数写入文件时,是全缓冲; 写入到显示器时,是行缓冲。

  2. 重定向到普通文件时,数据刷新缓冲区的方式,由行缓存变为全缓冲。

  3. 刷新缓冲区,是清空缓冲区,是修改数据的一种方式。

2.5. 模拟C标准库中的方法

#pragma once  //防止头文件重复包含#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>#define SIZE 4094//定义缓冲区的类型
#define None_Flush 1
#define Full_Flush (1<<1)
#define Line_Flush (1<<2)//自定义file结构体
typedef struct myfile{int fileno;  //文件描述符int pos;  //当前读写位置int cap;  //缓冲区容量int flush_mode;  //缓冲区类型char buff[SIZE];  //输出缓冲区
}myfile;myfile* my_fopen(const char* path, const char* mode); //打开文件
void my_fclose(myfile* fp);  //关闭文件
int my_fwrite(myfile* fp, const char* str, int size); //读文件
void my_fflush(myfile* fp);   //刷新缓冲区                                                
void print_buff(myfile* fp);  //打印file结构体的内容,便于测试
#include"mystdio.h" const char* To_string(int flush_mode) //将整形转化为字符串
{if(flush_mode & None_Flush) return "Nono Flush";else if(flush_mode & Line_Flush)return "Line_Flush";else if(flush_mode & Full_Flush)return "Full_Flush";
}void print_buff(myfile* fp) //打印file结构体的内容
{printf("fd: %d\n", fp->fileno);printf("fd: %d\n", fp->pos);printf("buff: %s\n", fp->buff);printf("flush_mode: %s\n", To_string(fp->flush_mode));
}myfile* my_fopen(const char* path, const char* mode) //打开文件
{int flag = -1; //确认它是以何种方式打开文件if(strcmp(mode, "r") == 0)flag = O_RDONLY; else if(strcmp(mode, "w") == 0)flag = O_WRONLY|O_CREAT|O_TRUNC;                                                     else if(strcmp(mode, "a") == 0)flag = O_WRONLY|O_CREAT|O_APPEND;else return NULL;//底层调用系统调用接口open打开文件int fd = -1;  if(flag & O_RDONLY) //读不需要创建新的文件fd = open(path, flag); else  //写、追加,都需要创建新的文件,并且需要设置文件权限{        umask(0);                                                                                fd = open(path, flag, 0666);}if(fd < 0) //调用open失败return NULL;//为打开的文件创建一个file类型的结构体,用来记录描述打开文件的信息myfile*  fp = (myfile*)malloc(sizeof(myfile));if(fp == NULL) //malloc调用1失败return NULL;//file结构体对象构建成功,进行初始化fp->fileno = fd;fp->pos = 0;fp->cap = SIZE;fp->flush_mode = Line_Flush;return fp;
}void my_fflush(myfile* fp) //刷新缓冲区
{if(fp->pos == 0) return ;//底层调用系统调用接口writewrite(fp->fileno, fp->buff, fp->pos);//清空缓冲区的内容fp->pos = 0;
}                                                                                                                     void my_fclose(myfile* fp) //关闭文件
{my_fflush(fp); //文件关闭,自动刷新缓冲区close(fp->fileno);free(fp);
}int my_fwrite(myfile* fp, const char* str, int size)  //向文件写内容
{//将数据先拷贝到用户层的缓冲区内memcpy(fp->buff + fp->pos, str, size); fp->pos += size; //判断是否需要刷新if(fp->flush_mode == Line_Flush && fp->buff[fp->pos - 1] == '\n') //行刷新my_fflush(fp);else if(fp->flush_mode == Full_Flush && fp->pos == fp->cap) //全刷新my_fflush(fp);return 0;
}
int main()                                                                               {   myfile* fp = my_fopen("data.txt", "w");   if(fp == NULL)           return 1;            char buf[SIZE];           int cnt = 5;   while(cnt--)   {   snprintf(buf, SIZE, "helloworld: %d  :", cnt); //字符串的拼接my_fwrite(fp, buf, strlen(buf));   print_buff(fp);   sleep(1);   }   my_fclose(fp);   return 0;   }   

相关文章:

【Linux】解锁文件描述符奥秘,高效缓存区的实战技巧

fd和缓冲区 1. 文件描述符fd1.1. 概念与本质1.2. 打开文件的管理1.3. 一切皆文件的理解1.4. 分配规则1.5. 重定向的本质1.5.1. dup2 2. FILE中的缓冲区2.1. 概念2.2. 存在的原因2.3. 类型(刷新方案)2.4. 存放的位置2.4.1. 代码证明、现象解释 2.5. 模拟C标准库中的方法 1. 文件…...

EmguCV学习笔记 VB.Net 11.9 姿势识别 OpenPose

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…...

2024.9.26 Spark学习

资料&#xff1a; Spark基础入门-第一章-1.1-Spark简单介绍_哔哩哔哩_bilibili &#xff08;1&#xff09;基础知识 Apache Spark 是用于大规模数据&#xff08;large-scale data&#xff09;处理的统一分析引擎。 分布式处理数据 PySpark模块 Spark 和 Hadoop 有区别&…...

我与Linux的爱恋:进程地址空间

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;Linux的学习 文章目录 1.来段代码2.引入最基本的理解3.尝试理解 1.来段代码 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h&g…...

C++的哲学思想

C的哲学思想 文章目录 C的哲学思想&#x1f4a1;前言&#x1f4a1;C的哲学思想☁️C底层不应该基于任何其他语言&#xff08;汇编语言除外&#xff09;☁️只为使用的东西付费&#xff08;不需要为没有使用到的语言特性付费&#xff09;☁️以低成本提供高级抽象&#xff08;更…...

IO(输入输出流)

1.IO a.介绍 i.IO是指Input和Output&#xff0c;即输入和输出&#xff0c;以内存为中心&#xff1a; 1.Input是指从外部读入数据到内存。 2.Output是指把数据从内存输出到外部。 ii.IO流是一种顺序读写数据的模式&#xff0c;它的特点是单向流动。数据类似自…...

python爬虫:从12306网站获取火车站信息

代码逻辑 初始化 (init 方法)&#xff1a; 设置请求头信息。设置车站版本号。 同步车站信息 (synchronization 方法)&#xff1a; 发送GET请求获取车站信息。返回服务器响应的文本。 提取信息 (extract 方法)&#xff1a; 从服务器响应中提取车站信息字符串。去掉字符串末尾的…...

Android个性名片界面的设计——约束布局的应用

节选自《Android应用开发项目式教程》&#xff0c;机械工业出版社&#xff0c;2024年7月出版 做最简单的安卓入门教程&#xff0c;手把手视频、代码、答疑全配齐 【任务目标】 使用约束布局、TextView控件实现一个个性名片界面的设计&#xff0c;界面如图1所示。 图1 个性名片…...

Python 课程18-SQLAlchemy

前言 SQLAlchemy 是一个功能强大的 Python SQL 工具包和对象关系映射&#xff08;ORM&#xff09;库&#xff0c;它使得开发者能够通过 Python 代码与数据库进行交互&#xff0c;而不必编写 SQL 查询。SQLAlchemy 提供了对多种数据库的支持&#xff0c;包括 MySQL、PostgreSQL…...

Module did not self-register: ‘drivelist.node‘报错解决

报错如下&#xff1a; node_modules/bindings/bindings.js:121throw e;^Error: Module did not self-register: xxxx/node_modules/drivelist/build/Release/drivelist.node.at process.func [as dlopen] (electron/js2c/asar.js:140:31)at Object.Module._extensions..node (…...

zabbix基本概念与组件

文章目录 一、zabbix简介二、​​​​​​​zabbix构成三、​​​​​​​zabbix监控对象四、​​​​​​​zabbix常用术语五、 Zabbix 6.0 新特性1.Zabbix server高可用防止硬件故障或计划维护期的停机2.Kubernetes系统从多个维度采集指标 六、zabbix 工作原理1、主动模式2、…...

Linux常用网络工具及示例

Linux系统中有许多用于网络管理、监控和故障排除的工具。以下是一些常用的网络工具及其基本用法示例&#xff1a; 1. ping - 测试主机之间的网络连接。 ping www.google.com 2. netstat - 显示网络连接、路由表、接口统计等信息。 netstat -an # 显示所有网络连接和监听…...

Go容器化微服务系统实战

1-1 本课的go微服务有什么不同&#xff1f; 聚焦于容器化可观测的购物微服务系统实战&#xff0c;通过介绍Go语言的应用趋势、容器化优势及微服务适用性&#xff0c;旨在解决学习微服务过程中遇到的难点。课程内容涵盖微服务整体架构、技术工具框架及容器平台等关键技术&#…...

研究生三年概括

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、研一1.上学期2. 下学期 二、研二1.研二上2.研二下 三、研三1.研三上2.研三下 前言 不知道是谁说的了&#xff0c;人生的路很长&#xff0c;关键的就那么几…...

MongoDB在Linux系统中的安装与配置指南

在这篇文章中&#xff0c;我们将介绍如何在CentOS 7服务器上安装MongoDB&#xff0c;并通过DataX将数据从MongoDB迁移到MySQL数据库。这将包括MongoDB的安装、配置、数据准备以及使用DataX进行数据迁移的详细步骤。 MongoDB简介 MongoDB是一个高性能、开源、无模式的文档型数据…...

Linux下如何实现不用加路径调用启动脚本

配置Systemctl启动 Linux下便于启停服务&#xff0c;可以配置systemcl,配置如下描述 说明 只有root用户可配置,文件路径为 /etc/systemd/system/XXX.service&#xff0c;本文将用nginx.service举例说明 1、创建文件 首先创建一个nginx.service文件&#xff0c;用于配置ngi…...

编程练习2 数据单元的变量替换

示例1: 1,2<A>00 示例2: 1,2<A>00,3<A>00 示例3: <B>12,1,2<B>1 示例4: <B<12,1 输出依次如下&#xff1a; #include<iostream> #include<vector> #include<string>using namespace std;/* 字符分割函数 将传入…...

mysql的查询操作

MySQL的查询操作是数据库管理和数据检索的核心。通过SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;语句&#xff0c;用户可以执行包括数据检索、数据插入、更新和删除在内的多种操作。在本文中&#xff0c;我们将重点讨论数据检索&#xff…...

0基础学前端 day2

大家好&#xff0c;欢迎来到无限大的频道。 今天继续带领大家开始0基础学前端。 一、CSS简介与基础 层叠样式表&#xff08;CSS&#xff0c;Cascading Style Sheets&#xff09;是用来进行网页样式和布局设计的语言。通过CSS&#xff0c;开发者可以控制网页中元素的颜色、字体…...

Invalid Executable The executable contains bitcode

Invalid Executable The executable contains bitcode xcode世界xcode16后&#xff0c;打包上传testflight时三方库报错&#xff1a;Invalid Executable - The executable ***.app/Frameworks/xxx.framework/xxx contains bitcode. 解决方案&#xff1a; 执行一下指令删除该f…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...

DiscuzX3.5发帖json api

参考文章&#xff1a;PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下&#xff0c;适配我自己的需求 有一个站点存在多个采集站&#xff0c;我想通过主站拿标题&#xff0c;采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

云安全与网络安全:核心区别与协同作用解析

在数字化转型的浪潮中&#xff0c;云安全与网络安全作为信息安全的两大支柱&#xff0c;常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异&#xff0c;并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全&#xff1a;聚焦于保…...

WebRTC调研

WebRTC是什么&#xff0c;为什么&#xff0c;如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...