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

深入了解linux系统—— 基础IO(下)

前言

在基础IO(上)中,我们了解了文件相关的系统调用;以及文件描述符是什么,和操作系统是如何将被打开的文件管理起来的。

本篇文章来继续学习文件相关的知识

重定向

在了解重定向之前,我们先来看这样的一段代码:

#include <sys/types.h>      
#include <sys/stat.h>      
#include <fcntl.h>      
#include <unistd.h>      
#include <string.h>
int main()      
{      close(0);      close(1);      close(2);int fd1 = open("log1.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);      int fd2 = open("log2.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);      int fd3 = open("log3.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);printf("fd1 : %d\n",fd1);      printf("fd2 : %d\n",fd2);      printf("fd3 : %d\n",fd3);      return 0;                   
}

在这里插入图片描述

通过观察我们可以发现,我们关闭了012(也就是标准输入、标准输出、标准错误);

然后打开了三个文件,这三个文件的文件描述符就是012

我们看到的现象就是:printf并没有将数据输出到显示器文件中,而是将数据输出到了文件描述符1所对应的文件中。

我们大致可以理解:

在系统层面:文件描述符0所对应的文件就是标准输入、文件描述符1所对应的文件就是标准输出、文件描述符2所对应的文件就是标准错误。

重定向是什么

相信对于重定向肯定没有那么陌生;

重定向又分为输出重定向>、追加重定向>>和输入重定向<

在这里插入图片描述

对于追加重定向和输入重定向这里就不演示了;

简单来说:在这里重定向就是修改程序原来的输入/输出文件

重定向的原理

知道什么是重定向,那重定向是如何实现的呢?

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

在上述代码中我们将文件描述符1对应的文件(也就是显示器文件)关了,再打开文件log.txt,它的文件描述符就是1

此时再向标准输出中输出数据,我们就会发现数据没有输出到我们的显示器文件中,而是输出到我们文件描述符1对应的文件log.txt文件中。

那这不就是一种重定向吗!

原本要输出到显示器文件中的数据,我们让他输出到指定文件中,这不就是输出重定向吗!!!

这样我们通过代码理解了重定向,已经它是如何实现的;但是这样关闭文件再打开文件,这操作未免也有点太挫了吧。有没有其他方法

系统调用dup2

在这里插入图片描述

当然除了dup2还存在系统调用dupdup3;这里主要看dup2

通过参数我们可以发现dup2存在两个参数:oldfdnewfd

dup2是做什么的呢?

dup2的作用就是使用oldfd位置的值去覆盖newfd位置的值,这样做来达到修改file_array数组中的指针指向。

在这里插入图片描述

通过查看man手册也可以发现:让newfd变成oldfd的拷贝,有必要的话就先关闭newfd指向的文件。

int main()    
{    int fd = open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);    if(fd < 1)  return -1;    printf("hello\n");                                                 dup2(fd,1);           printf("hello\n");                                                         return 0;
}

在这里插入图片描述

实现重定向只需要修改0/1/2文件描述符的内容即可。

重定向的本质

在上述中,我们了解了通过代码去实现重定向操作,但是在系统层面,dup2做了什么呢?

又是如何支持重定向的呢?

我们知道每一个进程都有自己的文件描述符表,这个文件描述符表中存在一个数组file_array存放着进程打开的文件。

我们还知道,0指向的是标准输入、1指向标准输入、2指向标准错误;

那输出重定向的本质是什么呢?

这个就比较简单了,其本质就是将我们新的文件的地址,覆盖式的填充到文件描述符1的位置。

那这样原本要输出到显示器文件的内容,就不再输出到显示器文件了,而是输出到了新的文件中。

所以,究其根本重定向就是修改了fd_array数组中的指针指向。

在这里插入图片描述

理解一切皆文件

我们知道:Linux下一切皆文件;

windows中是文件的东西,在Linux下也是文件;一些在windows中不是文件的东西(磁盘,显示器,键盘等)这样的硬件硬件设备在Linux中也被抽象成了文件;这样可以使用访问文件的方法去访问这些硬件。

这样将一切都抽象为文件,都可以看做是文件;我们就只需一套API和开发工具就可以调取Linux中的绝大部分资源。

Linux操作系统中几乎所有(读文件,读系统状态,读PIPE)读操作都可以使用read函数来进行;几乎所有修改(修改文件,修改系统参数,写PIPE)操作都可以使用write函数进行。

一直在说Linux下一切皆文件,那它到底是什么,应该如何去理解呢?

我们知道操作系统是一款管理软硬件资源的软件,那操作系统是不是也要将硬件管理起来呢?

肯定要将硬件资源管理起来的,如何管理呢?先描述,后组织

Linux操作系统内核当中,存在一个struct device,它描述了硬件设备的相关信息;然后将这些硬件设备对应的struct device管理起来即可(将所有的struct device放入到一个链表当中)。

我们再想一下,我们对应硬件设备的访问,无非就是读和写,也就是I/O,那我们只需知道啊硬件设备如何进行I/O操作不就可硬件设备统一读写操作吗

在我们的struct file中还存在着两个函数指针writeread;它们就表示了如何对设备进行读写操作。

对于不同的设备,它们有不同的读写方式;但是我们进行读写操作时,不关心你是如何读写的。只需要拿到你的读写方法writeread即可。

这样就算文件有文件的读写方法,磁盘有磁盘的读写方法,键盘有键盘的读写方法;我们不关心如何去读写操作,只需拿到其对应的writeread;然后通过调用writeread就可以统一I/O操作。

此外,我们知道我们访问资源,本质上是进程去访问资源,那进程访问资源,通过writeread就可以访问任何软硬件资源

所以Linux下一切皆文件本质上就是,进程在访问一切资源时都可以想访问文件那样去访问;这样一切资源在进程看来都是文件了。

在这里插入图片描述

缓冲区

是什么

缓冲区是内存空间的一部分;简单来说就是在内存中预留一定的存储空间,这些存储空间用来缓存输入/输出数据。

缓冲区又分为输入缓冲区和输出缓冲区(根据其对应的设备是输入设备还是输出设备决定)。

为什么

为什么要存在缓冲区呢,我们直接通过系统调用进行读写操作不行吗?

我们要知道,如果没有缓冲区,我们直接通过系统调用进行读写操作;这样我们每次读写时都要通过系统调用来处理此操作;而我们的系统调用也是有成本的

每执行一次系统调用,都要涉及到CPU状态的转换(从用户空间切换到内核空间,实现进程上下文切换),要损耗一定的时间;

如果我们频繁访问磁盘对程序的执行效率影响还是很大的。

所以为了减少使用系统调用,提高程序运行的效率,就采取缓冲区机制;

这样在对磁盘文件操作时,就会从文件中读取大量数据到文件缓冲区当中,这样我们就可以先对缓冲区的数据操作,操作完(读取完缓冲区内容)再读取/写入磁盘;这样我们就减少了磁盘的读写次数。所以我们缓冲区的存在就是为了提供计算机的运行速度。

语言级缓冲区和文件缓冲区

我们之前了解的缓冲区,它是语言层提供给我们的缓冲区;

而文件缓冲区是操作系统在打开文件时会给被打开的文件分配一块缓冲区。

int main()
{printf("hello\n");const char* msg = "abcdef";write(1,msg,strlen(msg));close(1);return 0;
}
int main()
{printf("hello");const char* msg = "abcdef";write(1,msg,strlen(msg));close(1);return 0;
}

在这里插入图片描述

上面两段代码,唯一区别就是printf在输出时一个输出了\n,一个没有输出\n

我们在程序的末尾关闭了1文件(显示器文件)可以看到,输出\n,内容就输出到了显示器文件上;而不输出\n,最终内容没有输出到显示器文件上。

而我们使用write系统调用,就算显示器文件被关闭了,内容还是可以写到显示器文件中。

我们知道,C语言库函数都是封装了系统调用,那使用write系统的调用就可以把内容写到文件中,而库函数却没有;

这也证实了语言层为我们提供了缓冲区。

而又因为标准输出是行缓冲,遇到换行就会调用系统调用,将缓冲区内容写入到显示器文件的缓冲区中;

所以printf输出带\n时,内容就会刷新到文件缓冲区当中。

而我们这里探讨的主要是语言层的缓冲区;而文件缓冲区暂时不研究

我们认为:数据写入到文件缓冲区中就相当于写入到了文件当中。(文件缓冲区刷新是操作系统做的事情)

缓冲类型

缓冲类型,也就是缓冲区什么时候刷新

  • 全缓冲区:这种缓冲方式当整个缓冲区被写满时,才会进行I/O系统调用操作。

    一般情况下对于磁盘文件(普通文件)的操作都是使用全缓冲的方式。

  • 行缓冲:当输入输出中遇到了换行符,就会执行系统调用操作。(缓冲区满了,没有遇到换行符也会刷新;缓冲区大小默认是1024

  • 无缓冲:也就是立即刷新,不对字符进行缓存,直接调用系统调用。

    标准错误stderr通过就是无缓冲的,这样错误信息能够立即刷新出来。

缓冲区刷新

那我们知道了存在语言层的缓冲区,那这一缓冲区内容什么时候被写入到文件缓冲区中呢?

  1. 强制刷新;如fflush
  2. 当刷新条件满足(全缓冲、行缓冲、无缓冲)。
  3. 进程退出

我们先来看以下代码:

int main()    
{    const char* msg1 = "hello printf\n";    const char* msg2 = "hello fprintf\n";    const char* msg3 = "hello write\n";printf("%s",msg1);    fprintf(stdout,"%s",msg2);    write(1,msg3,strlen(msg3));fork(); return 0;    
}    

这段代码还是非常好理解的,printffprintf都向标准输出中输出内容;write1文件中输出内容。

此时都是printffprintf向显示器中写入是行缓冲,遇到\n就会刷新缓冲区;

所以在程序执行到fork时,父进程的缓冲区中是没有内容的,创建的子进程缓冲区是拷贝父进程的,也是没有内容的。

我们尝试往普通文件中写入:(也就是程序运行的结果我们重定向到其他文件中)

在这里插入图片描述

通过对比我们发现,往显示器文件写入时,writeprintffprintf都输出了一次;

而重定向输出到普通文件中write输出了一次,printffprintf输出了两次(准确来说是刷新了两次缓冲区)。

原因:

我们知道printffprintf向显示器中写入是行缓冲,所以printffprintf输出的内容会按行刷新到显示器文件缓冲区中;而write是直接写入到显示器文件的缓冲区中;

所以当往显示器文件写入时,程序运行到fork时,父进程的缓冲区是空的;(子进程的缓冲区来源于父进程)子进程退出时会刷新缓冲区,但是缓冲区是空的。

而向普通文件写入时,就是全缓冲(也就是缓冲区满才刷新);程序运行到fork时,printffprintf输出的内容还在缓冲区当中,所以子进程退出时会刷新一遍缓冲区,而父进程退出时也会刷新一遍缓冲区;这样printffprintf输出的内容就会被刷新两次,也就是输出两次。而write是将内容直接写入到文件的缓冲区当中,所以只输出了一次。

简单设计libc

我们知道在C语言层面,我们使用文件操作并不是直接使用的文件描述符,而是使用FILE* 文件指针。

FILE是什么呢?

我们知道C语言库函数底层肯定是封装了系统调用的;而系统调用对于文件的操作只认文件描述符,所以FILE中肯定要包含文件描述符。

除此之外呢,也要存在其他信息,比如:缓冲区刷新方式,缓冲区,文件大小等等。

我们这里简单设计一下FILE,模拟实现一下文件操作函数:

首先先来看一下,MYFILE中包含哪些内容,以及要实现哪些函数:

#pragma once
#define MAX 1024 //缓冲区大小
#define NONE_FLUSH  001 //0001
#define LINE_FLUSH  002 //0010
#define FULL_FLUSH  004 //0100
typedef struct IO_FILE{int fileno;//文件描述符int flag;    char outbuff[MAX]; //缓冲区    int bufflen; //缓冲区内容长度    int flush_buff;    
}MYFILE;
MYFILE* MyOpen(const char* pathname, const char* mode);    
void MyClose(MYFILE* file);
int MyWrite(MYFILE* file, void* str, int len);              
void MyFlush(MYFILE* file);    

首先看MYFILE,其中包含文件描述符fileno,文件打开方式flag,缓冲区outbuff,缓冲区数据个数bufflen,缓冲区刷新方式flush_buff

然后我们简单实现打开文件MyOpen,关闭文件MyClose,文件写入MyWrite,刷新缓冲区MyFlush

打开文件MyOpen

这个实现起来相对是比较简单的了;

不过我们要注意文件的打开方式mode,文件缓冲区的初始化。

MYFILE* BuyFile(int fd, int flag)
{MYFILE* myfile = (MYFILE*)malloc(sizeof(MYFILE));myfile->fileno = fd;myfile->flag = flag;myfile->bufflen = 0;myfile->flush_buff = LINE_FLUSH;    //初始化缓冲区    memset(myfile->outbuff, 0, sizeof(myfile->outbuff));    return myfile;    
}
MYFILE* MyOpen(const char* pathname, const char* mode)    
{    //确定文件的打开方式    int fd = -1;    int flag = 0;    if(strcmp(mode, "w") == 0)    {    flag = O_CREAT | O_WRONLY | O_TRUNC;    fd = open(pathname, flag, 0666);    }    else if(strcmp(mode, "a") == 0)    {    flag = O_CREAT | O_WRONLY | O_APPEND;    fd = open(pathname, flag, 0666);    }    else if(strcmp(mode, "r"))    {    flag = O_RDONLY;    fd = open(pathname, flag);    }    else{    //???    }    if(fd < 0) return NULL;//打开文件失败    return BuyFile(fd,flag);    
}

刷新缓冲区MyFlush

这里我们先看文件缓冲区的刷新,因为我们关闭文件时也要刷新缓冲区,写入时也要判断是否刷新缓冲区

这里实现的是缓冲区的行刷新

这里缓冲区刷新,我们不仅可以把缓冲区内容写入到文件缓冲区中,还可以使用fsync函数做一下数据同步。

fsync函数的主要作用就是将文件描述符引用的文件的所有修改后的核心数据传输到磁盘设备,这样确保即使系统崩溃或者重新启动,所有更改的信息也能保存到磁盘中。

void MyFlush(MYFILE* file)                               
{              if(file->bufflen <= 0)  return;write(file->fileno, file->outbuff, file->bufflen);file->bufflen = 0;fsync(file->fileno);    
}                            

文件写入MyWrite

文件写入实现起来也是非常简单的,我们要做的就是将要写入的内容,先拷贝到缓冲区当中;然后再判断是否需要刷新缓冲区即可。

int MyWrite(MYFILE* file, void* str, int len)    
{    //将数据拷贝到缓冲区当中    //防止缓冲区溢出if(file->bufflen + len > MAX)        MyFlush(file);       memcpy(file->outbuff + file->bufflen,str,len);    file->bufflen += len;    if(file->flush_buff & NONE_FLUSH)//无缓冲    MyFlush(file);    else if((file->flush_buff & LINE_FLUSH) && (file->outbuff[file->bufflen-1] == '\n' || file->bufflen == MAX))MyFlush(file);              else if((file->flush_buff & FULL_FLUSH) && (file->bufflen == MAX))//全缓冲    MyFlush(file);    return 0;    
}    

关闭文件MyClose

在关闭文件之前,我们需要先刷新一下缓冲区,将缓冲区内容写入到文件缓冲区当中。

void MyFlush(MYFILE* file)    
{    if(file->bufflen <= 0)  return;    write(file->fileno, file->outbuff, file->bufflen);    file->bufflen = 0;    fsync(file->fileno);    
}    

相关文章:

深入了解linux系统—— 基础IO(下)

前言 在基础IO&#xff08;上&#xff09;中&#xff0c;我们了解了文件相关的系统调用&#xff1b;以及文件描述符是什么&#xff0c;和操作系统是如何将被打开的文件管理起来的。 本篇文章来继续学习文件相关的知识 重定向 在了解重定向之前&#xff0c;我们先来看这样的…...

Flink Table SQL

Apache Flink 提供了强大的 Table API 和 SQL 接口&#xff0c;用于统一处理批数据和流数据。它们为开发者提供了类 SQL 的编程方式&#xff0c;简化了复杂的数据处理逻辑&#xff0c;并支持与外部系统集成。 &#x1f9e9; 一、Flink Table & SQL 核心概念 概念描述Table…...

【Git】基本操作

【简介】 Git是一种“版本控制器”&#xff0c; 可以用于记录每次的修改以及版本的迭代 其可以控制电脑上所有格式的文件&#xff0c;方便地查看文件的每个小修改版本都修改了什么内容&#xff0c;但前提条件是被管理的文件需要放在对应的git仓库&#xff08;又名“版本库”&…...

【八股战神篇】MySQL高频面试题

目录 专栏简介 一 什么是索引 延伸 1 索引的底层使用的是什么数据结构&#xff1f; 2 MySQL 索引分类有哪些&#xff1f; 3 什么字段适合创建索引&#xff1f; 4 索引失效的场景 5 什么是最左匹配原则&#xff1f; 二 为什么 InnoDB 存储引擎选用 B 树而不是 B 树呢&a…...

服务器防文件上传手写waf

一、waf的目录结构&#xff0c;根据自己目录情况进行修改 二、创建文件夹以及文件 sudo mkdir -p /www/server/waf-monitor sudo mkdir -p /www/server/waf-monitor/quarantine #创建文件夹 chmod 755 /www/server/waf-monitor #赋权cd /www/server/waf-monitor/touch waf-m…...

ElasticSearch-集群

本篇文章依据ElasticSearch权威指南进行实操和记录 1&#xff0c;空集群 即不包含任何节点的集群 集群大多数分为两类&#xff0c;主节点和数据节点 主节点 职责&#xff1a;主节点负责管理集群的状态&#xff0c;例如分配分片、添加和删除节点、监控节点故障等。它们不直接…...

Android开发——原生渲染方案实现 PDF 预览功能

Android开发——原生渲染方案实现 PDF 预览功能 1. 引言2. 原生渲染方案核心设计:从数据到视图3. 混合文档容器:ViewPager2 与适配器设计1. 引言 在移动应用开发中,PDF 预览是文档处理场景的核心需求之一。Android 生态提供了多元化的技术方案,从系统级简版预览到原生渲染…...

Java求职者面试:从Spring Boot到微服务的技术点解析

Java求职者面试&#xff1a;从Spring Boot到微服务的技术点解析 场景&#xff1a;互联网医疗-预约挂号系统 面试官&#xff1a; “小明&#xff0c;我们今天的场景是一个互联网医疗的预约挂号系统。我们需要支持高并发的用户预约操作&#xff0c;同时保证数据一致性和系统的高…...

操作系统听课笔记之进程的概念

引入新的概念,为什么不能叫程序 内存中进程Image实例: stack: 局部变量(函数弹出来没有了) data: 全局变量(共享) 静态变量 heap: malloc分配的内存 从数据结构和算法角度解决问题: 设计相应的数据结构和设计算法 数据结构: 进程PCB 算法:创建进程和进程通信各种操作在线内…...

【基于Spring Boot 的图书购买系统】深度讲解 用户注册的前后端交互,Mapper操作MySQL数据库进行用户持久化

引言 在现代Web应用中&#xff0c;用户注册功能是用户与应用交互的入口。一个高效、安全且用户友好的注册系统不仅能吸引用户&#xff0c;还能为后续功能&#xff08;如个性化服务&#xff09;奠定基础。本博客将通过一个实际案例&#xff0c;展示如何使用HTML、JavaScript、j…...

Spark,连接MySQL数据库,添加数据,读取数据

以下是使用 Spark/SparkSQL 连接 MySQL 数据库、添加数据和读取数据的完整示例&#xff08;需提前准备 MySQL 驱动包&#xff09;&#xff1a; 一、环境准备 1. 下载 MySQL 驱动 - 下载 mysql-connector-java-8.0.33.jar &#xff08;或对应版本&#xff09;&#xff0c;放…...

ubuntu的虚拟机上的网络图标没有了

非正常的关机导致虚拟机连接xshell连接不上&#xff0c;ping也ping不通。网络的图标也没有了。 记录一下解决步骤 1、重启服务 sudo systemctl restart NetworkManager 2、图标显示 sudo nmcli network off sudo nmcli network on 3、sudo dhclient ens33 //(网卡) …...

Linux系统:ext2文件系统的核心概念和结构

本节重点 块、块组、分区的引入块组的构成inode与inode Table路径解析与路径缓存机制目录与文件名在文件系统中的存储分区的初始化与挂载 一、ext2文件系统 1.1 “块”的引入 在前言部分我们说扇区是磁盘硬件的最小读写单位&#xff0c;通常为512字节&#xff0c;但是在操作…...

Python 装饰器详解

装饰器是 Python 中一种强大的语法特性&#xff0c;它允许在不修改原函数代码的情况下动态地扩展函数的功能。装饰器本质上是一个高阶函数&#xff0c;它接受一个函数作为参数并返回一个新的函数。 基本装饰器 1. 简单装饰器示例 def my_decorator(func):def wrapper():prin…...

Docker配置容器开机自启或服务重启后自启

要将一个 Docker 容器设置为开机自启&#xff0c;你可以使用 docker update 命令或配置 Docker 服务来实现。以下是两种常见的方法&#xff1a; 方法 1&#xff1a;使用 docker update 设置容器自动重启 使用 docker update 设置容器为开机自启 你可以使用以下命令&#xff0c…...

20250518 黎曼在三维空间中总结的一维二维的规律,推广到高维度合适吗?有没有人提出反对意见

黎曼在三维空间中总结的一维二维的规律&#xff0c;推广到高维度合适吗&#xff1f;有没有人提出反对意见 黎曼几何在数学物理中的广泛应用&#xff0c;尤其是在广义相对论和高维空间理论中&#xff0c;确实是建立在黎曼在三维空间中的推广基础上的。不过&#xff0c;这种推广…...

使用AI 生成PPT 最佳实践方案对比

文章大纲 一、专业AI生成工具(推荐新手)**1. 推荐工具详解****2. 操作流程优化****3. 优势与局限**二、代码生成方案(开发者推荐)**1. Python-pptx进阶用法****2. GitHub推荐**三、混合工作流(平衡效率与定制)**1. 工具链升级****2. 示例Markdown结构**四、网页转换方案(…...

【Docker】Docker Compose方式搭建分布式协调服务(Zookeeper)集群

开发分布式应用时,往往需要高度可靠的分布式协调,Apache ZooKeeper 致力于开发和维护开源服务器&#xff0c;以实现高度可靠的分布式协调。具体内容见zookeeper官网。现代应用往往使用云原生技术进行搭建,如何用Docker搭建Zookeeper集群,这里介绍使用Docker Compose方式搭建分布…...

R for Data Science(3)

R for Data Science以下是关于网页内容的详细笔记&#xff1a; 1. 章节概览 章节主题&#xff1a;数据转换&#xff08;Data Transformation&#xff09;核心内容&#xff1a;介绍如何使用 R 中的 dplyr 包进行数据转换&#xff0c;包括对数据框的行、列和组的操作&#xff0…...

深入浅出Hadoop:大数据时代的“瑞士军刀”

深入浅出Hadoop&#xff1a;大数据时代的“瑞士军刀” 在当今这个数据爆炸的时代&#xff0c;每天产生的数据量已经远超人类的想象。从社交媒体的互动到电商平台的交易记录&#xff0c;从物联网设备的实时监控到科学研究的实验数据&#xff0c;大数据已经成为推动各行各业变革…...

《Python星球日记》 第94天:走近自动化训练平台

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、自动化训练平台简介1. Kubeflow Pipelines2. TensorFlow Extended (TFX) 二…...

MetaMask安装及使用-使用水龙头获取测试币的坑?

常见的异常有&#xff1a; 1.unable to request drip, please try again later. 2.You must hold at least 1 LINK on Ethereum Mainnet to request native tokens. 3.The address provided does not have sufficient historical activity or balance on the Ethereum Mainne…...

软件架构之--论微服务的开发方法1

论微服务的开发方法1 摘要 2023年 2月,本人所在集团公司承接了长三角地区某省渔船图纸电子化审查系统项目开发,该项目旨在为长三角地区渔船建造设计院、以及渔船图纸审查机构提供一个便捷的渔船图纸电子化审查服务平台。在此项目中,我作为项目组成员参与项目的建设工作,并…...

SOLID 面对象设计的五大基本原则

SOLID 原则的价值 原则核心价值解决的问题SRP职责分离&#xff0c;提高内聚性代码臃肿、牵一发而动全身OCP通过扩展而非修改实现变化频繁修改现有代码导致的风险LSP确保子类行为的一致性继承滥用导致的系统不稳定ISP定制化接口&#xff0c;避免依赖冗余接口过大导致的实现负担…...

游戏引擎学习第293天:移动Familiars

回顾并为今天的内容定下基调 我们正在做一款完整的游戏&#xff0c;今天的重点是“移动模式”的正式化处理。目前虽然移动机制大致能运作&#xff0c;但写法相对粗糙&#xff0c;不够严谨&#xff0c;我们希望将其清理得更规范&#xff0c;更可靠一点。 目前脑逻辑&#xff0…...

《沙尘暴》观影记:当家庭成为人性的修罗场

起初点开《沙尘暴》&#xff0c;不过是想在碎片时间里寻个消遣&#xff0c;毕竟短剧的篇幅显得轻松无负担。未曾想&#xff0c;这看似简短的故事却如一场裹挟着砂砾的风暴&#xff0c;在心底掀起层层涟漪&#xff0c;让我忍不住在家庭教育、人性幽微处反复踱步沉思。 一、风暴眼…...

牛客网NC21989:牛牛学取余

牛客网NC21989:牛牛学取余 &#x1f4dd; 题目描述 ⏱️ 限制条件 时间限制&#xff1a;C/C/Rust/Pascal 1秒&#xff0c;其他语言2秒空间限制&#xff1a;C/C/Rust/Pascal 32 M&#xff0c;其他语言64 M输入范围&#xff1a;两个整数&#xff0c;在int范围内 &#x1f4e5;…...

王者荣耀游戏测试场景题

如何测试一个新英雄&#xff1a;方法论与实践维度 测试一个新英雄不仅仅是“打打打”&#xff0c;而是一套完整的测试流程&#xff0c;包括设计文档验证、功能验证、数值验证、性能验证、交互验证等。可以从以下多个角度展开&#xff1a; &#x1f50d; 1. 方法论维度 ✅ 测试…...

Spring Boot 与 RabbitMQ 的深度集成实践(二)

集成步骤详解 配置 RabbitMQ 连接信息 在 Spring Boot 项目中&#xff0c;通常在application.properties或application.yml文件中配置 RabbitMQ 的连接信息。以application.yml为例&#xff0c;配置如下&#xff1a; spring: rabbitmq: host: localhost port: 5672 usern…...

医疗信息系统安全防护体系的深度构建与理论实践融合

一、医疗数据访问系统的安全挑战与理论基础 1.1 系统架构安全需求分析 在医疗信息系统中&#xff0c;基于身份标识的信息查询功能通常采用分层架构设计&#xff0c;包括表现层、应用层和数据层。根据ISO/IEC 27001信息安全管理体系要求&#xff0c;此类系统需满足数据保密性…...

多模态大语言模型arxiv论文略读(八十)

## MMWorld: Towards Multi-discipline Multi-faceted World Model Evaluation in Videos ➡️ 论文标题&#xff1a;MMWorld: Towards Multi-discipline Multi-faceted World Model Evaluation in Videos ➡️ 论文作者&#xff1a;Xuehai He, Weixi Feng, Kaizhi Zheng, Yuji…...

FFmpeg:多媒体处理的终极利器

FFmpeg详细介绍 1. 定义与基本概述 FFmpeg是一套开源的跨平台多媒体处理工具集,最初由法国程序员Fabrice Bellard于2000年开发,其名称源自“Fast Forward MPEG”,体现了其高效处理MPEG格式的能力。它不仅是命令行工具,还包含多个库和开发套件,支持视频转码、剪辑、合并、…...

【Leetcode】取余/2的幂次方

给定一个非负整数 num&#xff0c;反复将各个位上的数字相加&#xff0c;直到结果为一位数。返回这个结果。 示例 1: 输入: num 38 输出: 2 解释: 各位相加的过程为&#xff1a; 38 --> 3 8 --> 11 11 --> 1 1 --> 2 由于 2 是一位数&#xff0c;所以返回 2。 …...

程序代码篇---ESP32的数据采集

文章目录 前言 前言 本文简单介绍了ESP32可以怎样采集数据。...

系统架构设计(十三):虚拟机体系结构风格

概念 虚拟机&#xff08;Virtual Machine&#xff09;体系结构风格&#xff0c;是指将整个系统抽象为一台“虚拟机”&#xff0c;通过解释或模拟的方式运行应用程序。 它本质上提供了一种“平台中立”的运行环境&#xff0c;典型代表就是 Java 虚拟机&#xff08;JVM&#xf…...

lvs-dr部署

实验准备&#xff1a; 准备4台设备&#xff0c;1台作为客户机&#xff0c;3台作为服务器&#xff0c;服务器中1台作为调度器&#xff0c;2台作为后端真实访问服务器。并关闭所有防火墙与核心防护。 systemctl stop firewalld setenforce 0 实验开始 调度器配置 yum -y ins…...

数据库blog2_数据结构与效率

&#x1f33f;计算机中的数据————存储结构与逻辑结构 &#x1f342;存储结构&#xff08;物理结构&#xff09; 定义&#xff1a;存储结构是指数据在计算机存储器中的实际存储方式&#xff0c;由计算机硬件特性决定。它涉及到数据的物理位置和存储顺序。存储结构直接影响数…...

聊天室项目总结

已实现的功能点&#xff1a; 存在的问题&#xff1a; 1.没有实现有含金量的创新功能点 2.太过于依赖工具&#xff0c;不喜欢自己看文章总结对知其然而不知其所以然&#xff0c;自己的理解比较少&#xff0c;懒于去思考 3.太过于依赖他人&#xff0c;自己的想法有点少&#x…...

数据结构:二叉树一文详解

数据结构:二叉树一文详解 前言一、二叉树的基本概念与结构特性1.1 二叉树的定义1.2 二叉树的特殊类型1.3 二叉树的性质 二、二叉树的遍历方式2.1 前序遍历&#xff08;Pre-order Traversal&#xff09;2.2 中序遍历&#xff08;In-order Traversal&#xff09;2.3 后序遍历&…...

2025年- H28-Lc136- 24.两两交换链表中的节点(链表)---java版

1.题目描述 2.思路 cur指针要先放在虚拟头节点&#xff0c;才能去操作第一个数和第二个数 先判断偶数个节点&#xff0c;再判断奇数个节点&#xff0c;否则会犯空指针异常。 &#xff08;1&#xff09;如果节点是偶数个节点&#xff0c;只要满足curr.nextnull&#xff0c;就说…...

ubuntu18.04通过cuda_11.3_xxx.run安装失败,电脑黑屏解决办法

项目场景&#xff1a; ubuntu18.04跑DG-SLAM相关代码&#xff0c;安装lietorch包报错&#xff0c;需要用到GPU。 问题描述 跑代码需要cuda11.3&#xff0c;系统里面有另外一个版本&#xff0c;运行cuda_11.3_xxx.run&#xff0c;同时也选择了driver&#xff0c;安装成功后&am…...

Linux之基础IO

目录 一、理解 "文件" 1.1、狭义理解 1.2、广义理解 1.3、文件操作的归类认知 1.4、系统角度 二、回顾C语言接口 2.1、打开文件 2.2、写文件 2.3、读文件 2.4、stdin & stdout & stderr 2.6、打开文件的方式 三、系统文件I/O 3.1、一种传递标志…...

上位机知识篇---涂鸦智能云平台

文章目录 前言 前言 本文简单介绍了涂鸦智能云平台。...

InfluxDB 3 Core + Java 11 + Spring Boot:打造高效物联网数据平台

一、 引言&#xff1a;为什么选择InfluxDB 3&#xff1f; 项目背景&#xff1a; 在我们的隧道风机监控系统中&#xff0c;实时数据的采集、存储和高效查询是至关重要的核心需求。风机运行产生的振动、倾角、电流、温度等参数是典型的时序数据&#xff0c;具有高并发写入、数据…...

Kubernetes控制平面组件:Kubelet详解(七):容器网络接口 CNI

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…...

Pandas 构建并评价聚类模型② 第六章

构建并评价聚类模型 构建并评价聚类模型一、数据读取与准备&#xff08;代码6 - 6部分&#xff09;结果代码解析 二、Kmeans聚类&#xff08;代码6 - 6部分&#xff09;结果代码解析 三、数据降维可视化&#xff08;代码6 - 6部分&#xff09;结果代码解析 四、FMI评价&#xf…...

【simulink】IEEE33节点系统潮流分析模型

目录 主要内容 程序内容 2.1 33节点simulink模型一览 2.2 节点模型图 下载链接 主要内容 该仿真采用simulink模型对33节点网络进行模拟仿真&#xff0c;在simulink模型中定义了33节点系统的电阻、电抗、节点连接关系等参数&#xff0c;通过控制块来实现信号连接关系&…...

彻底解决docker代理配置与无法拉取镜像问题

为什么会有这篇文章? 博主在去年为部署dify研究了docker,最后也是成功部署,但是因为众所周知的原因,卡ziji脖子 ,所以期间遇到各种网络问题的报错,好在最后解决了. 但时隔一年,博主最近因为学习原因又一次使用docker,原本解决的问题却又没来由的出现,且和之前有很多不同(有时…...

Linux 安装 Unreal Engine

需要对在unreal engine官网进行绑定github账号&#xff0c;然后到unreal engine github仓库中进行下载对应的版本&#xff0c;并进行安装unreal engine官网 github地址...

tensorflow图像分类预测

tensorflow图像分类预测 CPU版本和GPU版本二选一 CPU版本 pip -m install --upgrade pippip install matplotlib pillow scikit-learnpip install tensorflow-intel2.18.0GPU版本 工具 miniconda 升级依赖库 conda update --all创建目录 mkdir gpu-tf进入目录 cd gpu-tf创建虚…...