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

【Linux内核系列】:深入解析输出以及输入重定向

🔥 本文专栏:Linux
🌸作者主页:努力努力再努力wz

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

在这里插入图片描述
★★★ 本文前置知识:

文件系统以及文件系统调用接口
用c语言简单实现一个shell外壳程序

内容回顾

那么在此前的学习中,我们对于Linux的文件系统已经有了一个基本的概念,那么我们先来来做一个简单的回顾,那么Linux上的一切事物都可以视作文件,那么我们对于Linux上各种文件可以按照状态将其分为两类分别是打开的文件和未打开的文件,那么对于打开的文件,那么它的元数据会被加载到内存中,我们操作系统会为其定义一个file的结构体来记录其各种属性,那么管理这些文件就是管理这些文件对应的file结构体所组织而成的双链表的数据结构,那么我们要打开一个文件就得通过相应的代码来打开,那么意味着我们打开文件只能通过我们的进程来打开,那么打开文件的对象必定就是进程,而单个进程与文件之间的关系是一对多的,那么必然我们进程对应的task_struct结构体要维护一个指针数组,也就是文件描述表,其中该数组的每一个元素都指向一个打开的文件,而其中会默认打开三个标准输入输出文件,也就是stdin和stdout和stderr,那么分别对应指针数组下标0,1,2这三个位置,那么该数组的下标就是我们的文件描述符,那么我们可以用我们open系统调用接口来打开一个文件,那么open系统接口会为奇创建一个file结构体,并且在指针数组中分配一个位置的指针来指向该file结构体,并且返回该位置的数组下标也就是文件描述符来为后续比如write以及read系统接口来使用,那么这就是我们上一篇文章主要讲述的内容,如果对着部分内容还是感到有点陌生的话,那么可以去看我们的上一篇文章

引入

那么在此前我们学习Linux指令的时候,我们知道会有这样一条指令:

echo "hello Linux" > log.txt
echo "hello Linux" >>log.txt

而我们的echo指令本身是获取之后的字符串“hello Linux",然后将该字符串打印在我们的终端也就是显示器上,但是添加了之后的大于符号后,那么它会将“hello Linux”这个字符串给写入到log.txt文件中,而这就是所谓的输出重定向的现象,而如果后面是两个大于符号,那么则是在之前文件末尾继续写入,那么也就是追加重定向。

< log.txt

而小于符号则是本应该从键盘文件中读取内容,替换为读取小于符号后面的目标文件的文件内容,那么这就是输入重定向,那么输入重定向我们相对比较陌生,而输出以及追加重定向我们则比较熟悉,那么在引入了文件系统以及相关的系统调用接口之后,那么输入以及输出重定向的原理本质上就是对文件进行操作,那么我们就可以根据输入以及输出重定向的原理,结合系统调用接口来自己实现输入以及输出重定向,并且完善我们之前的shell外壳程序

输出以及输入重定向的原理以及实现

那么我们知道进程对应的task_struct结构体内部会有一个指针指向一个指针数组,那么该指针数组的每一个元素则指向该进程已经打开的文件的结构体,那么我们知道像我们的echo和pwd以及ls指令,那么这些指令在执行过后,都会在显示器终端打印信息,那么它们既然能够在显示器打印相应的内容,那么必然他们是要将它们要打印的内容给写入到显示器文件中,而我们知道我们进程会默认打开三个标准输入输出文件,那么其中下标为1的位置就是我们的显示器文件,那么他们必然就是往文件描述符下标为1的文件中做写入,而所谓的输出重定向就是原本我们要将显示器文件中写入的内容给替换写入到另一个目标文件中,所以当我们运行ehco “hello Linux” > log.txt时,我们发现我们终端不会打印“hello Linux" 字符串,而是将该字符串写入到了目标文件log.txt当中,那么这就是我们的输出重定向

那么要做到输出重定向的话,那么这里我们就得引入一个关键的系统调用接口,那么就是dup系统调用接口

  • dup
  • 头文件:<unistd.h>
  • 函数原型:
int dup(int fd)
  • 原理:

那么dup接口会接受一个文件描述符fd,那么该文件描述符fd所指向的file结构体就是要复制的对象,那么接下来dup接口会从前往后线性的扫描整个指针数组,直到找到一个空的位置也就是没有指向任何结构体的指针的位置,然后将该位置的指针复制之前文件描述符fd所指向的结构体,并返回该数组下标,而如果分配复制失败则返回-1,而由于这个接口由于要线性扫描整个指针数组,所以一般不推荐使用dup这个接口

  • dup2
  • 头文件:<unistd.h>
  • 函数原型:
int dup2(int oldfd, int newfd);
  • 原理:

那么dup2会接受两个参数分别是oldfd以及newfd,那么我们的oldfd就会是被复制的对象,而我们的newfd则是被替换的对象,那么dup2首先会先close掉我们的指针数组newfd下标所对应的位置,那么其中就会让其指向的结构体的引用计数减一,如果该结构体引用计数为0,那么操作系统则会回收清理该文件对应的结构体以及在内存中的元数据,那么引用计数不为0,那么则是只是将位置的指针给置空,那么close完指针数组中的newfd位置后,那么下一步就是将oldfd指针所指向的结构体给拷贝到newfd,那么此时newfd和oldfd就指向了同一个file结构体,该接口调用成功的返回值就是newfd,失败则返回-1


所以我们输出重定向就是调用该dup2接口,那么我们假设输出重定向的目标文件是log.txt,那么我们首先就是先调用open接口打开我们的log.txt,然后获得了log.txt的文件描述符,那么接着我们在调用dup2,那么将我们原本要向显示器文件也就是文件描述符为1的位置中写入的内容给写入到log.txt中,所以接下来我们就调用dup2接口,那么oldfd就是目标文件log.txt的文件描述符,而newfd则是显示器文件的文件描述符也就是1,那么这样就是实现了所谓的输出重定向,而我们知道我们open的时候可以指定我们打开该文件要进行的行为或者说模式,也就是通过第二个参数来指定,那么如果是输出重定向,那么我们每次输出都是会清空之前的文本内容,从文本开始处写入,所以我们需要或上O_TRUNC的宏定义,而追加重定向则是从之前的文本末尾处接着写入,所以追加重定向打开目标文件的open的第二个参数不能或上O_TRUNC,而是或上O_APPPEND,那么这就是我们输出以及追加重定向的原理实现

而输入重定向的话则是我们打开的模式要设置为只读打开,第二个参数要或上O_RDONLY,那么我们用open打开我们的目标文件假设为log.txt,然后得到其描述符,那么这里我们接着调用dup2替换的文件也就是键盘文件,那么对应的文件描述符就是0,所以第二个参数newfd就是0,那么这样就实现了我们的输入重定向

完善shell外壳程序

那么之前我们的shell外壳程序只是简单实现了获取用户输入的指令,那么判断指令是内置指令还是外部命令,如果是内置指令的话就交给父进程也就是shell外壳程序来执行,而如果是外部指令的话,那么则交给子进程来执行,那么子进程执行流中就会被进程替换为要执行的指令的进程的上下文,而父进程的执行流则是等待子进程的退出,获取子进程的退出码,那么这就是之前我们shell外壳程序所实现的功能

那么在此基础上,我们shell外壳程序无法进行输出以及输入重定向,那么在本文学习了输出以及输入重定向之后,我们就可以完善我们的shell外壳程序这部分功能

1.获取重定向内容

那么我们知道我们的shell外壳程序的第一个环节就是获取用户输入的指令,本质上就是获取用户输入的字符串然后将其保存在临时字符数组temp中,而用户输入指令时会手动用空格分割隔开指令部分和各个参数部分,所以下一个环节就是解析指令部分与参数部分,利用strtok函数利用空格作为分隔符将其分割的各个参数部分的字符串保存在argv字符指针数组的各个位置中,那么在解析的这一步骤中,我们用户可能有输入或者输出重定向的情况出现,那么在解析完字符串的各个参数部分后,那么我们就得从后往前线性扫描每一个元素所指向的字符串中是否出现重定向的符号也就是"<",">",">>",我们可以定义一个int类型的全局变量check_redir来追踪重定向的情况,如果出现了重定向,那么该变量则不为0,那么如果是输入重定向我们则设置为1,追加重定向设置为2,输入重定向则设置为3,然后再定义一个全局属性字符指针,因为如果出现重定向之后,那么该重定向符号的下一个位置就一定还是目标文件的文件名,所以我们得用指针来保存
实现细节:

细节1:.由于这里我们的重定向的符号以及目标文件名并不是有用的指令部分以及参数部分,所以我们接得保存完该文件名之后,就得将其分别设置为NULL,因为在进程替换的时候,会扫描我们该字符指针数组argv,直到遇到NULL结束,而重定向符号如“>”以及文件名如log.txt不是有命令行参数,所以得设置为空

细节2:这里由于我们字符指针数组argv的最后一个元素是NULL,而我们每一个位置都要调用strcmp函数来匹配,所以这里匹配的开始一定是从字符指针数组的倒数第二个位置开始匹配

细节3:这里由于我们的重定向符号以及之后的文件名不是命令行参数,而我们之前解析命令行参数argc是将其计入了的,所以我们就得重新设置我们的返回的命令行参数的个数,这是一个细节

细节4:我们这里定义了两个全局属性的变量分别追踪重定向的情况以及保存文件名,所以在每一次外部的while循环之后,我们都得将其给重新设置

代码实现:

int getString(char temp[],char* argv[])
{int len=strlen(temp);if(len>0&&temp[len-1]=='\n'){temp[len-1]='\0';len--;}int argc=0;char* toke=strtok(temp," ");while(toke!=NULL&&argc<length-1){argv[argc++]=toke;toke=strtok(NULL," ");}argv[argc]=NULL;for(int i=argc-1;i>=0;i--){if(strcmp(argv[i],">")==0){check_redir=1;filename=argv[i+1];argc=i;argv[i]=NULL;argv[i+1]=NULL;break;}if(strcmp(argv[i],">>")==0){check_redir=2;filename=argv[i+1];argc=i;argv[i]=NULL;argv[i+1]=NULL;break;}if(strcmp(argv[i],"<")==0){check_redir=3;filename=argv[i+1];argc=i;argv[i]=NULL;argv[i+1]=NULL;break;}}return argc;
}

2.内置命令的重定向

那么这里我们注意对于内置命令来说,那么由于我们对于一些要往显示器写入的内置命令要做重定向,所以这里我们注意由于内置命令的执行是在父进程中执行的,那么我们关闭比如显示器文件的话就一定会影响父进程,意味着之后我们还得再重新打开,因为下一个命令的执行有可能需要向显示器文件写入,所以这里我个人在实现的时候,我是的思路首先是先open打开目标文件,得到该文件的文件描述符,然后我直接将该指令要向显示器文件中写入的内容用一个数组来保存,然后利用write函数将数组的内容写入该文件,这样就避免了关闭显示器文件,这里由于我自己在实现shell外壳程序的时候,只有cd和pwd两个内置指令,所以这里就只实现了pwd内置命令的输出重定向

代码实现:

void ordercomplete(int argc,char* argv[])
{if(strcmp(argv[0],"cd")==0){if(argc==2){if(chdir(argv[1])==0){char cwd[1024];getcwd(cwd,sizeof(cwd));setenv("pwd",cwd,1);}else{perror("chdir");}}else{printf("error: expected argument for 'cd'\n");}}if(strcmp(argv[0],"pwd")==0){char cwd[length];  // 定义一个足够大的缓冲区来存储路径if (getcwd(cwd, sizeof(cwd)) != NULL) {if(check_redir!=0){int fd;int flag=O_CREAT;if(check_redir==1){flag|=O_WRONLY|O_TRUNC;}if(check_redir==2){flag|=O_APPEND|O_WRONLY;}fd=open(filename,flag,0666);if(fd<0){perror("open");return;}int m=write(fd,cwd,strlen(cwd));if(m<0){perror("write");close(fd);return;}close(fd);}else{printf("Current working directory: %s\n", cwd);}
}else {perror("getcwd failed");  // 输出错误信息}
}

3.外部命令的重定向

而对于外部命令的重定向,那么我们知道外部命令的执行则是创建一个子进程来执行,而我们知道创建一个子进程的本质就是拷贝父进程的task_struct结构体得到子进程的一份task_struct结构体以及父子进程共享同一个物理内存页,而其中拷贝父进程的task_struct结构体意味着就会拷贝一份父进程所对应的文件描述表,所以父子进程各自有一份独立的文件描述表,那么这里我们对于子进程的重定向,那么就无需关心关闭显示器文件所带来的影响,因为子进程对应的文件描述表中的显示器文件也就是下标为1的位置被关闭了,那么它不会影响父进程的文件描述表,所以我们可以在子进程中直接调用我们的dup2系统接口,至于进程替换,由于进程替换只会影响进程的地址空间以及页表的映射,不会影响文件描述符表,所以我们就得在进程替换之前,调用dup2系统接口来完成我们外部命令的重定向,而其中的实现就是我们上文介绍输出以及输入重定向的原理实现

代码实现:

int id=fork();if(id==0){if(check_redir!=0){int fd;int flag=O_CREAT;int exchange;if(check_redir==1){flag|=O_WRONLY|O_TRUNC;exchange=1;}if(check_redir==2){flag|=O_WRONLY|O_APPEND;exchange=1;}if(check_redir==3){flag|=O_RDONLY;exchange=0;}fd=open(filename,flag,0664);if(fd<0){perror("open");exit(1);}int m=dup2(fd,exchange);if(m<0){perror("dup2");close(fd);exit(2);}close(fd);}execvp(argv[0],argv);perror("execvp");exit(EXIT_FAIL);}

4.完整实现

完整代码:

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdbool.h>
#define length 1000
#define EXIT_FAIL 40
const char* order[]={"cd","pwd",NULL};
int check_redir=0;
char* filename;
int getString(char temp[],char* argv[])
{int len=strlen(temp);if(len>0&&temp[len-1]=='\n'){temp[len-1]='\0';len--;}int argc=0;char* toke=strtok(temp," ");while(toke!=NULL&&argc<length-1){argv[argc++]=toke;toke=strtok(NULL," ");}argv[argc]=NULL;for(int i=argc-1;i>=0;i--){if(strcmp(argv[i],">")==0){check_redir=1;filename=argv[i+1];argc=i;argv[i]=NULL;argv[i+1]=NULL;break;}if(strcmp(argv[i],">>")==0){check_redir=2;filename=argv[i+1];argc=i;argv[i]=NULL;argv[i+1]=NULL;break;}if(strcmp(argv[i],"<")==0){check_redir=3;filename=argv[i+1];argc=i;argv[i]=NULL;argv[i+1]=NULL;break;}}return argc;
}
bool check(char* argv[])
{for(int i=0;order[i]!=NULL;i++){if(strcmp(argv[0],order[i])==0){return true;}}return false;
}
void ordercomplete(int argc,char* argv[])
{if(strcmp(argv[0],"cd")==0){if(argc==2){if(chdir(argv[1])==0){char cwd[1024];getcwd(cwd,sizeof(cwd));setenv("pwd",cwd,1);}else{perror("chdir");}}else{printf("error: expected argument for 'cd'\n");}}if(strcmp(argv[0],"pwd")==0){char cwd[length];  // 定义一个足够大的缓冲区来存储路径if (getcwd(cwd, sizeof(cwd)) != NULL) {if(check_redir!=0){int fd;int flag=O_CREAT;if(check_redir==1){flag|=O_WRONLY|O_TRUNC;}if(check_redir==2){flag|=O_APPEND|O_WRONLY;}fd=open(filename,flag,0666);if(fd<0){perror("open");return;}int m=write(fd,cwd,strlen(cwd));if(m<0){perror("write");close(fd);return;}close(fd);}else{printf("Current working directory: %s\n", cwd);}
}else {perror("getcwd failed");  // 输出错误信息}
}}
int main()
{int argc;char* argv[length];char temp[length];while(1){printf("[%s@%s %s]$",getenv("USER"),getenv("HOSTNAME"),getenv("PWD"));check_redir=0;filename=NULL;if(fgets(temp,sizeof(temp),stdin)==NULL){perror("fgets");continue;}argc=getString(temp,argv);if(argc==0){continue;}if(check(argv)){ordercomplete(argc,argv);continue;}int id=fork();if(id==0){if(check_redir!=0){int fd;int flag=O_CREAT;int exchange;if(check_redir==1){flag|=O_WRONLY|O_TRUNC;exchange=1;}if(check_redir==2){flag|=O_WRONLY|O_APPEND;exchange=1;}if(check_redir==3){flag|=O_RDONLY;exchange=0;}fd=open(filename,flag,0664);if(fd<0){perror("open");exit(1);}int m=dup2(fd,exchange);if(m<0){perror("dup2");close(fd);exit(2);}close(fd);}execvp(argv[0],argv);perror("execvp");exit(EXIT_FAIL);}else{int status;int m=waitpid(id,&status,0);if(m<0){perror("waitpid");}else{if(WIFEXITED(status)){if(WEXITSTATUS(status)==40){printf("子进程替换失败\n");}if(WEXITSTATUS(status)==1){printf("open调用失败\n");}if(WEXITSTATUS(status)==2){printf("dup2系统调用失败\n");}}}}}return 0;
}

Linux上的运行截图:
在这里插入图片描述

结语

那么本篇文章就详细解析了我们Linux下的输入以及输出重定向,那么先介绍了输入以及输出重定向的原理以及实现,然后再完善了我们的shell外壳程序,那么下一篇文章我将会解析缓冲区的内容
我会持续更新,希望你能多多关照,那么如果本文对你有所帮组的话,还请多多三连加关注哦,你的支持就是我创作的最大的动力!
在这里插入图片描述

相关文章:

【Linux内核系列】:深入解析输出以及输入重定向

&#x1f525; 本文专栏&#xff1a;Linux &#x1f338;作者主页&#xff1a;努力努力再努力wz ★★★ 本文前置知识&#xff1a; 文件系统以及文件系统调用接口 用c语言简单实现一个shell外壳程序 内容回顾 那么在此前的学习中&#xff0c;我们对于Linux的文件系统已经有了…...

Adam 优化器与动量法:二阶矩与 ODE 的联系

Adam 优化器与动量法&#xff1a;二阶矩与 ODE 的联系 作为深度学习研究者&#xff0c;你一定对 Adam&#xff08;Adaptive Moment Estimation&#xff09;优化器非常熟悉。它因自适应学习率和高效率而成为训练神经网络的标配算法。Adam 使用了一阶动量&#xff08;梯度的指数…...

嵌入式学习第二十三天--网络及TCP

进程通信的方式: 同一主机 传统 system V 不同主机 网络 --- 解决不同主机间 的进程间通信 网络 (通信) //1.物理层面 --- 联通(通路) //卫星 2G 3G 4G 5G 星链 (千帆) //2.逻辑层面 --- 通路(软件) MAC os LINUX …...

前端表单提交与后端处理全解析:从HTML到Axios与SpringBoot实战

前端表单提交与后端处理全解析:从HTML到Axios与SpringBoot实战 一、GET与POST请求的两种面孔 1. HTML表单基础实现 <!-- GET请求示例:搜索表单 --> <form action="/api/search" method="GET"><input type="text" name="…...

python django orm websocket html 实现deepseek持续聊天对话页面

最终效果&#xff1a; 技术栈&#xff1a; python django orm websocket html 项目结构&#xff1a; 这里只展示关键代码&#xff1a; File: consumers.py # -*- coding:utf-8 -*- # Author: 喵酱 # time: 2025 - 03 -02 # File: consumers.py # desc: import json from asg…...

大白话html语义化标签优势与应用场景

大白话html语义化标签优势与应用场景 大白话解释 语义化标签就是那些名字能让人一看就大概知道它是用来做什么的标签。以前我们经常用<div>来做各种布局&#xff0c;但是<div>本身没有什么实际的含义&#xff0c;就像一个没有名字的盒子。而语义化标签就像是有名…...

考研英语语法全攻略:从基础到长难句剖析​

引言 在考研英语的备考之旅中,语法犹如一座灯塔,为我们在浩瀚的英语知识海洋中指引方向。无论是阅读理解中复杂长难句的解读,还是写作时准确流畅表达的需求,扎实的语法基础都起着至关重要的作用。本文将结合有道考研语法基础入门课的相关内容,为大家全面梳理考研英语语法…...

Vue3 生命周期

回顾Vue2的生命周期 创建&#xff08;创建前&#xff0c;创建完毕&#xff09;beforeCreate、created挂载&#xff08;挂载前&#xff0c;挂载完毕&#xff09;beforeMount、mounted更新&#xff08;更新前&#xff0c;更新完毕&#xff09;beforeUpdate、updated销毁&#xf…...

hbase-05 namespace、数据的确界TTL

要点 掌握HBase的命名空间namespace概念 掌握HBase数据版本确界 掌握HBase数据TTL 1. HBase的namespace 1.1 namespace基本介绍 在HBase中&#xff0c;namespace命名空间指对一组表的逻辑分组&#xff0c;类似RDBMS中的database&#xff0c;方便对表在业务上划分。Apache…...

线程的常见使用方法

Java中的线程并不是真正意义的线程,我们使用的是Thread类来表示线程,而这个类是 JVM 用来管理线程的一个类,也就是说,每个线程都有一个唯一的 Thread对象 与之关联 每一个执行流都需要有一个对象来进行描述,那么一个Thread对象就是用来表述一个线程执行流的,JVM会将这些对象统…...

架构师面试(十一):消息收发

问题 IM 是互联网中非常典型的独立的系统&#xff0c;麻雀虽小但五脏俱全&#xff0c;非常值得深入研究和探讨&#xff0c;继上次IM相关题目之后&#xff0c;我们继续讨论IM相关话题。 关于IM系统【消息收发模型】的相关描述&#xff0c;下面说法错误的有哪几项&#xff1f; …...

MoonSharp 文档一

目录 1.Getting Started 步骤1&#xff1a;在 IDE 中引入 MoonSharp 步骤2&#xff1a;引入命名空间 步骤3&#xff1a;调用脚本 步骤4&#xff1a;运行代码 2.Keeping a Script around 步骤1&#xff1a;复现前教程所有操作 步骤2&#xff1a;改为创建Script对象 步骤…...

【linux网络编程】端口

一、端口&#xff08;Port&#xff09;概述 在计算机网络中&#xff0c;端口&#xff08;Port&#xff09; 是用来标识不同进程或服务的逻辑通信端点。它类似于一座大楼的房间号&#xff0c;帮助操作系统和网络协议区分不同的应用程序&#xff0c;以便正确地传输数据。 1. 端口…...

Vulnhub-Node

目录标题 一、靶机搭建二、信息收集靶机信息扫ip扫开放端口和版本服务信息指纹探测目录扫描 三、Web渗透信息收集zip爆破ssh连接 四、提权内核版本提权 利用信息收集拿到路径得到账户密码&#xff0c;下载备份文件&#xff0c;base64解密后&#xff0c;利用fcrackzip爆破zip压缩…...

RK3568平台(camera篇)camera3_profiles_rk3588.xml解析

camera3_profiles_rk3588.xml 是一个与 Android 相机 HAL(硬件抽象层)相关的配置文件,通常用于定义 Rockchip RK3588 平台上的相机设备及其功能。该文件基于 Android 的 Camera3 HAL 框架,用于描述相机的配置、流配置、分辨率、帧率、格式等信息。 以下是对 camera3_profi…...

高阶哈希算法

SHA-256简介 SHA-256 是 **SHA-2&#xff08;Secure Hash Algorithm 2&#xff09;**家族中的一种哈希算法&#xff0c;由美国国家安全局设计&#xff0c;并于 2001 年发布。它能够将任意长度的数据映射为一个固定长度256 位&#xff0c;即 32 字节的哈希值&#xff0c;通常以…...

Spark数据倾斜深度解析与实战解决方案

Spark数据倾斜深度解析与实战解决方案 一、数据倾斜的本质与影响 数据倾斜是分布式计算中因数据分布不均导致的性能瓶颈现象。当某些Key对应的数据量远超其他Key时,这些"热点Key"所在的Task会消耗80%以上的计算时间,成为整个作业的木桶短板。具体表现为: Task执…...

Kubernetes滚动更新实践

前言 在我之前的项目中&#xff0c;对微服务升级采用的做法是删除整个namespace&#xff0c; 再重新应用所有yaml。 这种方式简单粗暴&#xff0c;但是不可避免的导致服务中断&#xff0c;影响了用户体验 为了解决更新服务导致的服务中断问题&#xff0c; Kubernetes提供了一种…...

Broken pipe

比较常见的一个问题。 但是并不是每个人都能说清楚。 首先注意下写法&#xff1a; Broken pipe # B大写 p小写 主要是grep的时候别写错了 常见的原因 1、客户端关闭连接。 在服务器端处理请求的过程中&#xff0c;客户端突然关闭了连接&#xff0c;例如浏览器关闭、网络断开…...

doris:ClickHouse

Doris JDBC Catalog 支持通过标准 JDBC 接口连接 ClickHouse 数据库。本文档介绍如何配置 ClickHouse 数据库连接。 使用须知​ 要连接到 ClickHouse 数据库&#xff0c;您需要 ClickHouse 23.x 或更高版本 (低于此版本未经充分测试)。 ClickHouse 数据库的 JDBC 驱动程序&a…...

前K个高频单词

692. 前K个高频单词 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给定一个单词列表 words 和一个整数 k &#xff0c;返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率&#xff0c; 按字典顺序 排序…...

恢复IDEA的Load Maven Changes按钮

写代码的时候不知道点到什么东西了&#xff0c;pom文件上的这个弹窗就是不出来了&#xff0c;重启IDEA&#xff0c;reset windos都没用&#xff0c;网上搜也没收到解决方案 然后开打开其他项目窗口时&#xff0c;看到那个的功能名叫 Hide This Notification 于是跑到Setting里…...

【五.LangChain技术与应用】【31.LangChain ReAct Agent:反应式智能代理的实现】

一、ReAct Agent是啥?为什么说它比「普通AI」聪明? 想象一下,你让ChatGPT查快递物流,它可能直接编个假单号糊弄你。但换成ReAct Agent,它会先推理(Reasoning)需要调用哪个接口,再行动(Action)查询真实数据——这就是ReAct的核心:让AI学会「动脑子」再动手。 举个真…...

Leetcode 62: 不同路径

Leetcode 62: 不同路径 问题描述&#xff1a; 一个机器人位于一个 (m \times n) 网格的左上角&#xff08;起始点位于 ((0, 0))&#xff09;。 机器人每次只能向下或向右移动一步。网格的右下角为终点&#xff08;位于 ((m-1, n-1))&#xff09;。 计算机器人从左上角到右下角…...

计算机毕业设计SpringBoot+Vue.js火锅店管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

Docker Desktop 4.38 安装与配置全流程指南(Windows平台)

一、软件定位与特性 Docker Desktop 是容器化应用开发与部署的一体化工具&#xff0c;支持在本地环境创建、管理和运行Docker容器。4.38版本新增GPU加速支持、WSL 2性能优化和Kubernetes 1.28集群管理功能&#xff0c;适用于微服务开发、CI/CD流水线搭建等场景。 二、安装环境…...

算法系列之广度优先搜索解决妖怪和尚过河问题

在算法学习中&#xff0c;广度优先搜索&#xff08;BFS&#xff09;是一种常用的图搜索算法&#xff0c;适用于解决最短路径问题、状态转换问题等。本文将介绍如何利用广度优先搜索解决经典的“妖怪和尚过河问题”。 问题描述 有三个妖怪和三个和尚需要过河。他们只有一条小船…...

【技术白皮书】内功心法 | 第一部分 | IP协议的目的与工作原理(IP地址)

目录 IP协议的介绍IP协议的目的与工作原理IP协议处理过程与信件传递的相似IP协议处理过程与信件传递的区别IP协议中的概念IP数据包IP地址IP地址组成IP地址分类和组成A、B、C三类地址的格式设计特殊类型的IP地址与传统通信地址进行类比IP地址的表示五类IP地址的地址范围IP地址的…...

【Linux】外接硬盘管理

查看外接硬盘信息 连接外接硬盘后&#xff0c;使用以下命令识别设备&#xff1a; lsblk&#xff1a;列出块设备及其挂载点 lsblk示例输出可能显示设备名称如 /dev/sdb。 通过 lsblk -f 可同时显示文件系统类型和 UUID。 fdisk -l&#xff1a;列出所有磁盘的分区信息&#xff…...

【Hadoop】详解Zookeeper选主流程

1. ZooKeeper 的工作原理 Zookeeper 的核心是Zab协议。Zab协议有两种模式&#xff0c;它们分别是恢复模式&#xff08;选主&#xff09;和广播模式&#xff08;同步&#xff09;。 为了保证事务的顺序一致性&#xff0c;Zookeeper采用了递增的事务id号(zxid)来标识事务。所有…...

C语言-语法

数据类型 字符串 C中字符串拼接不用+号,直接使用空格。 char* str = "hello" "world"; 换行链接,加上\就不会报错 char* longStr = "00000000000000000000000000000\ 00000000000000000000000000000"; typedef C 语言提供了 typedef …...

Flink Forward Asia 2024 大会 内容整理

一、Flink 2.0&#xff1a;十年磨一剑&#xff0c;开启流计算新纪元 作为 Flink 诞生十周年的重磅更新&#xff0c;Flink 2.0 被定位为“面向未来十年的流计算引擎”&#xff0c;核心目标是解决云原生、AI 融合、存算效率等新时代挑战。以下是它的几大杀手锏&#xff1a; 1. …...

golang进阶知识专项-理解值传递

在 Go 语言中&#xff0c;所有函数的参数传递都是值传递&#xff08;Pass by Value&#xff09;。当你将一个变量作为参数传递给函数时&#xff0c;实际上传递的是该变量的副本&#xff0c;而不是变量本身。理解这一点对于避免常见的编程错误至关重要。根据不同的类型&#xff…...

2020年联考《申论》第三题(河北县级卷)

材料&#xff1a; 总面积不过八平方米的店铺&#xff0c;摆满货品之后&#xff0c;两人在店内走动都有些困难&#xff0c;但这家小店在当地却是小有名气的老字号。69岁的店主老林是资深木雕艺人&#xff0c;更被称为“最后的手工酸枝筷子传人”。手工木筷取材自缅甸坤甸木、老挝…...

Kali WebDAV 客户端工具——Cadaver 与 Davtest

1. 工具简介 在 WebDAV 服务器管理和安全测试过程中&#xff0c;Cadaver 和 Davtest 是两款常用的命令行工具。 Cadaver 是一个 Unix/Linux 命令行 WebDAV 客户端&#xff0c;主要用于远程文件管理&#xff0c;支持文件上传、下载、移动、复制、删除等操作。Davtest 则是一款…...

八点八数字科技:开启数字人应用的无限可能

在数字科技飞速发展的时代&#xff0c;八点八数字科技凭借卓越实力&#xff0c;成为行业的领军者。自 2014 年成立&#xff0c;公司汇聚近百位顶尖人才&#xff0c;手握 30 新型发明专利与 80 软件著作权&#xff0c;完成数千万融资&#xff0c;实力备受认可。其自主研发的全…...

Vue 使用 vue-router 时,多级嵌套路由缓存问题处理

Vue 使用 vue-router 时&#xff0c;多级嵌套路由缓存问题处理 对于三级菜单&#xff08;或多级嵌套路由&#xff09;&#xff0c;vue 都是 通过 keep-alive 组件来实现路由组件的缓存。 有时候三级或者多级路由时&#xff0c;会出现失效情况。以下是三级菜单缓存的例子。 最…...

《实战AI智能体》Deepseek可以做什么?自然语言理解与分析

在人工智能技术快速迭代的今天,Deepseek凭借其先进的自然语言处理能力,正在重塑人机交互的边界。本文将从技术实现维度,深入解析该平台在自然语言理解、知识推理与文本分类三大核心领域的技术突破与应用实践。 一、深度语义理解引擎 Deepseek构建了多层级的语义解析架构,实…...

虚拟主机认证功能

一.认证功能 类型&#xff1a; 1.基于客户端地址的认证 2.基于用户的认证 1.基于客户端地址的认证 nginx是一款模块化软件&#xff0c;功能都是基于模块实现的。 如上图所示&#xff0c;访问认证是使用的这个access_module模块。 这个模块也给我们提供了一些指令&#xf…...

BGP协议深度解析:从背景到术语的全面梳理

引言 在复杂的网络架构中&#xff0c;不同自治系统&#xff08;AS&#xff09;之间的路由交互至关重要。BGP&#xff08;Border Gateway Protocol&#xff0c;边界网关协议&#xff09;作为自治系统间的动态路由协议&#xff0c;承担着跨 AS 路由传递的关键任务。本文将深入探讨…...

初步认识线程

概念 一个线程就是一个 "执行流",每一个线程之间都可以按照顺序执行自己的代码,多个线程之间可以 "同步" 执行多份代码 比如说,原本一个人做的事情,现在交给三个人一起做,那么这三个人就是线程 使用原因 那么为什么要有线程呢?我们直接使用进程不可以…...

【从零开始学习计算机科学】数字逻辑(一)绪论

【从零开始学习计算机科学】数字逻辑(一)绪论 概论信息与数字数字系统中常用的概念数字信号的描述方法概论 从数字(集成)电路的出现到计算机到网络到今天的移动互联网,数字电路是所有现代信息技术的基础。那么数字电路的基础又是什么呢?就是数字逻辑。所有数字系统都是基…...

Tomcat与Jetty的选择

Tomcat与Jetty的对比分析&#xff0c;分核心区别、性能表现及选型建议三部分&#xff1a; 一、核心区别对比 对比维度TomcatJetty架构设计多层级容器结构&#xff08;Server→Service→Engine等&#xff09;&#xff0c;复杂度高基于Handler链的轻量级设计&#xff0c;扩展性强…...

用AI学编程2——python学习1

一个py文件&#xff0c;学会所有python所有语法和特性&#xff0c;给出注释&#xff0c;给出这样的文件 Python 学习整合文件 """ Python 学习整合文件 包含 Python 的基础语法、数据结构、函数定义、面向对象编程、异常处理、文件操作、高级特性等内容 每个部…...

【教程】宝塔提示请不要将网站根目录设置到以下关键目录中

【教程】宝塔提示请不要将网站根目录设置到以下关键目录中 【教程】宝塔提示请不要将网站根目录设置到以下关键目录中更换目录解决问题 先在宝塔下载一个 【教程】宝塔提示请不要将网站根目录设置到以下关键目录中更换目录解决问题_起尔网【教程】宝塔提示请不要将网站根目录设…...

html常用的文本标签以及属性

HTML标签 HTML通过一系列的标签&#xff08;也成为元素&#xff09;&#xff0c;来定义文本&#xff0c;图像&#xff0c;链接等等&#xff0c;HTML标签是由尖括号包围的关键字。 标签通常成对出现&#xff0c;包括开始标签和结束标签&#xff08;也成为双标签&#xff09;&a…...

城市霓虹灯夜景拍照后期Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 在城市霓虹灯夜景拍摄中&#xff0c;由于现场光线复杂等因素&#xff0c;照片可能无法完全呈现出当时的视觉感受。通过 Lr 调色&#xff0c;可以弥补拍摄时的不足。例如&#xff0c;运用基本调整面板中的曝光、对比度、阴影等工具&#xff0c;可以处理出画面的整体明暗…...

c#面试题整理

1.如何保持数据库的完整性&#xff0c;一致性 最好的方法&#xff1a;数据库约束&#xff08;check,unique,主键&#xff0c;外键&#xff0c;默认&#xff0c;非空&#xff09; 其次是&#xff1a;用触发器 最后&#xff1a;才是自己些业务逻辑&#xff0c;这个效率低 2.事…...

数据库基础以及基本建库建表的简单操作

文章目录 一、数据库是啥1.1、数据库的概念1.1、关系型数据库、非关系型数据库1.1、数据库服务器&#xff0c;数据库与表之间的关系 二、为啥要使用数据库2.1&#xff1a;传统数据文件存储2.2&#xff1a;数据库存储数据2.3、结论 三、使用数据库了会咋样四、应该咋用数据库&am…...

基于spring boot使用@Sl4j的日志功能,注解引入后爆红未生效

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 问题描述问题分析解决方案 &#x1f4c3;文章…...