【Linux】学习-基础IO—下

Linux基础IO—上

重定向

通过上篇的学习,我们了解了文件描述符的分配规则是遍历指针数组,用没有被使用的最小下标作为新的文件描述符,也就是我们可以通过关闭三个标准流文件并使用他们原先所占用的0,1,2描述符。

那我们假设这样一种情况,我们向显示器中打印内容,但在此之前我们将显示器文件,也就是标准输出流关闭,然后再创建并打开一个新的文件,此时我们向显示器中打印的内容会到哪去呢?

int main()
{close(1);int fd = open("myfile", O_WRONLY | O_CREAT, 00644);if (fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);fflush(stdout);close(fd);exit(0);
}

image-20230920214947600

运行之后我们发现,无论运行多少变显示器上都不会打印出内容,但是通过查看文件内容发现本来应该输出到显示器的内容被写到了文件里

这种现象就叫输出重定向!

注意:printf默认输出的文件就是stdout,而进程默认会打开三个标准流,每次都是如此,并且标准输出流文件描述符也总是1。

也就是说,像printf,scanf,fprintf,fscanf这些函数在底层只认识文件描述符,比如stdout对应的文件描述符是1,他们只认识1,而不认识什么stdout,是通过stdout建立起联系的

因此,重定向的本质,其实是在OS内部,更改fd对应的文件指向!

接下来我们再看个追加重定向的例子,也就是先关闭文件描述符为1的文件,再以追加的方式打开一个文件,这时系统自动分配,完成重定向!

int main()
{close(1);int fd = open("myfile", O_WRONLY|O_APPEND|O_CREAT);if (fd < 0){perror("open");return 1;}printf("hello append,my fd is: %d\n", fd);printf("hello append,my fd is: %d\n", fd);printf("hello append,my fd is: %d\n", fd);fflush(stdout);close(fd);exit(0);
}

image-20230921100805532

这就是追加重定向,但我们使用的时候,我们利用的是文件描述符的分配规则来完成的,也就是在打开文件前先一步关闭对应的想重定向的文件,这样的话导致了代码使用时非常不灵活,因此能不能有一种灵活的方法能够随时重定向呢?系统就给我们一个接口:dup2函数——duplicate file description

dup2函数

其实完成重定向的本质就是把fd_array中对应的内容覆盖掉,因为数组下标是固定的,只需要将需要完成重定向的文件描述符覆盖掉即可完成,在系统中就用这样的一个接口来帮助我们完成这件事

画图:占位

没什么比直接阅读man手册实在!

image-20230921101208937

newfd是oldfd的拷贝,也就是后者原本fd_array数组对应的fd下标里的内容如今换成了前者的内容,如果前者原本对应的内容不是有效的内容,则调用失败,而后者的文件并不会被关闭。必要时可以先关闭newfd对应的文件。

例如,我们要以上面举过的例子,我们要将原本输出到显示器上的内容重定向到文件中,那么oldfd对应的应该就是我们打开的文件对应的文件描述符fd,而newfd对应的就是显示器原本对应的内容1,这样子我们可以任意时候打开文件并任意时候完成重定向而不用预先关闭,非常灵活方便!

// test:  dup2
int main()
{int fd = open("./log", O_CREAT | O_RDWR,0666);if (fd < 0){perror("open");return 1;}close(1);dup2(fd, 1);for (;;){char buf[1024] = {0};ssize_t read_size = read(0, buf, sizeof(buf) - 1);if (read_size < 0){perror("read");break;}printf("%s", buf);fflush(stdout);}return 0;
}

image-20230921104802285

运行程序后可以明显发现我在键盘上敲入的内容并没有显示到显示器上,而查看文件内容后发现:

image-20230921104813285

已经被重定向到log文件中!

注意:fflush虽然参数是stdout,但由于他只认识文件描述符,而此时1的内容已经被覆盖了,因此每一次写入缓冲区后立马刷新到文件里,实现同步过程,而不是等到缓冲区满了或者程序正常退出时才一次性刷新到文件里。

image-20230921121517002

若调用成功,会返回new description,也就是后者原本占有的文件描述符,失败则返回-1。

myshell添加重定向功能

我们在Linux进程控制一章中,自模拟实现过一个简易的命令行解释器myshell,下面我们可以像bash命令行一样实现重定向 > < 以及 >> 的功能!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <assert.h>#define NUM 1024
#define SIZE 32
#define SEP " "char *g_argv[SIZE];
char sub[SIZE];
char cmd_line[NUM];#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3int redir_status = NONE_REDIR;
//新增重定向功能,这里进行命令行解析
char *Checkredir(char *start)
{// ls -a>log.txtassert(start);char *end = start + strlen(start) - 1;while (end >= start){if (*end == '>'){// ls -a>>log.txtif (*(end - 1) == '>'){*(end - 1) = '\0';++end;redir_status = APPEND_REDIR;break;}// ls -a>log.txt*end = '\0';++end;redir_status = OUTPUT_REDIR;break;}else if (*end == '<'){// cat<log.txtredir_status = INPUT_REDIR;*end = '\0';++end;break;}else--end;}if (end >= start){return end;}elsereturn NULL;
}int main()
{while (1){printf("[root@localhost myshell]# ");fflush(stdout);memset(cmd_line, '\0', sizeof cmd_line);if (fgets(cmd_line, sizeof cmd_line, stdin) == NULL)continue;// ls -l -a\n\0 去除\ncmd_line[strlen(cmd_line) - 1] = '\0';char *ret = Checkredir(cmd_line);// 命令行解析: "ls -l -a\0\0" -> "ls" "-l" "-a"// strtok能做到此功能// 将解析出来的命令以及参数一个一个的放进指针数组中while (g_argv[index++] = strtok(NULL, SEP)); // 放入NULL表示还要继续解析上一个解析的字符串// 开始让子进程用进程替换执行用户输入的命令:pid_t id = fork();//先判断是否有重定向if (id == 0){if (ret){// 不为空说明是需要重定向int fd = -1;switch (redir_status){case INPUT_REDIR:fd = open(ret, O_RDONLY);assert(fd != -1);dup2(fd, 0);break;case OUTPUT_REDIR:fd = open(ret, O_WRONLY | O_TRUNC | O_CREAT, 0666);assert(fd != -1);dup2(fd, 1);break;case APPEND_REDIR:fd = open(ret, O_WRONLY | O_APPEND | O_CREAT, 0666);assert(fd != -1);dup2(fd, 1);break;default:perror("redirect error");break;}}// childprintf("以下功能由子进程进行进程替换所实现!\n");// 由于需要调系统程序,因此需要自动搜索环境变量PATHexecvp(g_argv[0], g_argv);exit(1);// 执行失败就退出,不会到下面去,和父进程互不干扰}// father// 父进程进行阻塞等待:int status = 0;pid_t res = waitpid(id, &status, 0);if (res > 0){printf("wait successfully!! exit code:%d\n", WEXITSTATUS(status));}}return 0;
}

效果展示:

test_red1

stdout和stderr

在Linux基础IO—上中我们学习了默认打开的三个流,分别是stdout,stderr和stdin,这三个流本质上是文件指针,指向的文件分别是外设:显示器与键盘,其中stdout和stderr都是显示器,stdin是键盘,这也就说明,显示器这个文件是被打开了两次的,因为有两个文件指针都指向了他,他们都能向显示器文件做对应的io操作,那他们用法上有什么区别呢?

我们来看代码:

//test: stdout stderr
int main()
{//stdout->1printf("hello printf 1\n");fprintf(stdout,"hello fprintf 1\n");//stderr->2perror("hello printf 1\n");//write->1const char*s1="hello write 1\n";write(1,s1,strlen(s1));//write->2const char*s2="hello write 2\n";write(2,s2,strlen(s2));//cout->1std::cout<<"hello cout 1"<<std::endl;//cerr->2std::cerr<<"hello cerr 2"<<std::endl;return 0;
}

编译运行后输出:

image-20230925104102167

不管是1还是2都能在我们的显示器看到,也证明了stdout和stderr对应的都是显示器

下面我们对程序进行重定向,看看会有什么变化:

image-20230925104502898

重定向后我们发现2号文件仍然向显示器打印了,而1号文件就正常的重定向进了文件里

结论:重定向默认是对1号文件描述符进行重定向

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

也就是1号和2号分别打开的显示器是不会影响彼此的,其实就是显示器文件是被打开了两次!

若就想让1号和2号的都往同一个文件重定向:

image-20230925105711518

这样就成功让1号和2号都重定向进了文件

如何使用?

一般而言如果程序运行有可能有问题的话,建议使用strerr或者cerr来打印,如果是常规的内容文本,建议使用stdout,cout来打印。

为什么?

因为在库中,有这样一个函数:image-20230925110612471

errno,被称为错误码,当某个函数调用时失败或者程序出现一些问题时,错误码就会被设置,而使用对应的perror就能向2号stderr中输出被设置的错误码所对应的错误信息,若你不想他直接运行后会在显示器上打印出来,就可以指定2号并重定向进日志文件中,方便管理者查看错误信息。image-20230925110751151

而perror的底层正是调用了strerror函数,这个函数在上篇有介绍过,不再细说。

void my_perror(const char* msg)
{fprintf(stderr,"%s",msg,strerror(errno));
}

FILE

我们在Linux基础io—上提到过,C语言所提供的文件操作函数,类似于fopen之类的函数,他们对文件的操作是需要用到文件指针FILE的,而我们现在已经学习了系统调用接口是通过文件描述符实现进程与打开文件建立联系的,而FILE文件指针底层正是封装了文件描述符才得以使用,本质上都是对系统接口进行二次封装提高可用性

Linux的设计哲学正是一切皆文件,体现在操作系统的设计软件层面,但其实Linux的底层也是C语言写的。

而我们学习过,文件正是=文件内容+文件属性,想用C语言实现这种具有面向对象思想的模块,只有一个东西能实现,那就是结构体struct。

而面向对象思想中,除了封装有有成员的属性,还有成员方法,但结构体如何实现成员方法呢?可以用函数指针来建立成员方法与成员的联系。

现在我们可以来看一下我们一直所说的文件指针FILE*,其指向的FILE底层究竟是如何实现的:

image-20230921162643605

我们查文档后发现,FILE其实是结构体struct _IO_FILE的别名:

image-20230921162845534

再转到_IO_FILE文件下,我们发现了有一个名为 _fileno的整型,这个整型其实就是我们熟悉的文件描述符fd,而不仅包含了fd,它还包含了该文件fd对应的语言层面的缓冲区结构!!

既然这样,由于底层各式各样的硬件,他们对应的操作方式都不同,但是他们的核心方式都是系统接口,也就是外设想要访问系统亦或是系统想要访问外设,在底层都是通过read,write,open,close来完成的,而这些硬件又被描述组织成文件被封装进了FILE中,这样一来,系统看待文件的方式都统一成为了管理结构体

缓冲区

什么是缓冲区

缓冲区其实就是用户自己提供的一块内存空间——char buffer[SIZE]

他的作用就是用来提高整机效率,提高用户的响应速度

我们知道文件是存在磁盘上的,若每次我们进行文件操作时都要直接往磁盘里加载,他的速度是非常慢的且效率不高,因此我们通过内存来过度,等到必要的时候再统一写入磁盘,这样减少频繁的访问磁盘,明显加速系统速度,这种模式叫做写回模式(write-back)

那缓冲区写入磁盘的时机又是什么时候呢?也就是缓冲区的刷新策略是什么?

缓冲区刷新策略:

  • 立即刷新
  • 行刷新(行缓冲)-‘\n’
  • 满刷新(全缓冲)

特殊情况:

  • 用户强制刷新(fflush)
  • 进程退出

我们来看一个简单的例子:

int main()
{ printf("hello Linux!");sleep(5); return 0;
}

按照代码逻辑来看,执行情况应该是先打印出来,再停留五秒后退出

但实际执行并不是这样,而是停留了五秒后再打印出来然后退出

其实这就是C语言库提供的缓冲区,若没有达到缓冲区的刷新要求,字符串会暂时保存起来,最后进程要退出了才会达到要求然后刷出来,若需要先刷出来再停留五秒的效果,则需要加上‘\n’告诉缓冲区需要刷新了或者fflush强制刷新

了解完基本概念,我们来看一段奇怪的代码:

// test:buffer:缓冲区
int main()
{const char *msg0 = "hello printf\n";const char *msg1 = "hello fwrite\n";const char *msg2 = "hello write\n";printf("%s", msg0);fwrite(msg1, strlen(msg0), 1, stdout);write(1, msg2, strlen(msg2));fork();return 0;
}

运行结果为:

image-20230921172237920

正常按照顺序打印一次,没毛病,符合预期。但我们稍微改动一下,我们运行程序后,将其重定向到文件中:

image-20230921173110863

这就奇了怪了,同一份代码,只是改变了输出方向,怎么就有不同的结果呢?

我们还观察到,fwrite和printf还出现了两次,而write只出现了一次,怎么就这么巧了?

初步推断:

出现两次可以初步推断这个结果跟程序最后的fork()有关,而write系统调用接口只有一次,其他接口都是两次可以推断罪魁祸首并不会影响系统接口!!!

两个问题:

  1. 为什么会打印两次?
  2. 为什么write系统接口只打印了一次?

为什么会打印两次?

我们注意到,我们在打印时,是已经使用了缓冲区的刷新策略—行刷新策略,那么当执行到fork的时候,这时候打印函数已经执行完毕了,按照正常来说,已经是将数据从缓冲区刷出来的,但实际上确并没有刷新!也就是说,执行到fork函数的时候,数据仍然存在C语言库管理的缓冲区中,并且这部分数据是属于父进程的数据。

  • 那么为什么会没有刷新呢??这不已经达到刷新策略的要求了吗?

我们需要注意到,我们这次运行时作了不同的动作,也就是进行了输出重定向!

而我们的输出重定向,本质就是将向显示器上打印的东西,转而写入了存储在磁盘上的文件!这时候隐形中的缓冲区刷新策略就改变了,变成了全缓冲,那么对应的行刷新也就没有作用了!

让我们接着来解释为什么会打印两次的问题:

存在缓冲区中的父进程的数据,在执行到fork()函数时,子进程采用写时拷贝的方法来保持进程间的独立性,父子进程共享一份代码和数据,但一旦父子进程的代码和数据需要被修改时,子进程会在物理内存中拷贝一份父进程的数据,并修改自己所持有的数据。真相渐渐水落石出了:

  • 父进程刷新缓冲区,本质上是把数据写入系统的过程,也就是写的过程。会发生写时拷贝

  • 父进程在刷新缓冲区前,由于子进程的存在,为了不让父进程的刷新影响到子进程,子进程也要拷贝一份一模一样的父进程缓冲区中的数据,因此有两份数据出现了,若提前强制刷新,缓冲区没数据了,子进程自然也就拷贝不到了

为什么write系统接口只打印了一次?

  • 因为我们所谈的缓冲区,是C标准库维给我们提供的用户及缓冲区,是属于语言层面的,跟系统层面的不是一个东西,当然不会影响系统接口!但是write在系统内核中有内核对应的缓冲区,也并非直接写到磁盘上的!

那么我们所说的用户级缓冲区他又在哪里呢??

  • 在我们前面提到过的FILE结构体,也就是_IO_FILE中,其内部不仅封装了文件描述符,还封装了缓冲区的结构!

我们调用fflush接口时,我们只需要传文件指针,那么他又是怎么知道缓冲区在哪里的,要去哪里刷新呢??

  • *fflush中传的参数是FILE ,FILE 指向的FILE结构体内封装有缓冲区结构!

缓冲区总结

  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。

  • printf fwrite 库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。

  • 而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后

  • 但是进程退出之后,会统一刷新,写入文件当中。

  • 但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。

  • write 没有变化,说明没有所谓的缓冲

综上:printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围之内。那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供。

模拟实现缓冲区

关于我们对缓冲区的现理解,我们简单的模拟一下打开文件后对应分配到的缓冲区的结构,增加理解:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>typedef struct MYFILE_ MYFILE;struct MYFILE_
{int _fd;char buffer[1024];int end; // 缓冲区结尾
};MYFILE *fopen_(const char *pathname, const char *mode)
{assert(pathname && mode);MYFILE *fp = NULL;if (strcmp(mode, "r") == 0){}else if (strcmp(mode, "r+") == 0){}else if (strcmp(mode, "w") == 0){int fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd >= 0){//创建文件时会分配缓冲区fp = (MYFILE *)malloc(sizeof(MYFILE));memset(fp, 0, sizeof(MYFILE));fp->_fd = fd;}}else if (strcmp(mode, "w+") == 0){}else if (strcmp(mode, "a") == 0){}else if (strcmp(mode, "a+") == 0){}else{// nothing}
}
void fputs_(const char *message, MYFILE *fp)
{assert(message);assert(fp);// hellofputs\0// fputs\0strcpy(fp->buffer + fp->end, message);fp->end += strlen(message);// 刷新策略if (fp->_fd == 0){// stdin}else if (fp->_fd == 1){// stdoutif (fp->buffer[fp->end - 1] == '\n'){ write(fp->_fd, fp->buffer, fp->end);fp->end = 0;}}else if (fp->_fd == 2){// stderror}
}
void fflush_(MYFILE *fp)
{assert(fp);if(fp->end!=0){write(fp->_fd,fp->buffer,fp->end);//向磁盘中写入syncfs(fp->_fd);fp->end=0;}
}void fclose_(MYFILE *fp)
{assert(fp);fflush_(fp);close(fp->_fd);free(fp);
}int main()
{// close(1):debugMYFILE *fp = fopen_("test.txt", "w");assert(fp);fputs_("one\n", fp);sleep(1);fputs_("two", fp);sleep(1);fputs_("three", fp);sleep(1);fclose_(fp);return 0;
}

syncfs函数能够帮助我们将文件磁盘中正式写入

image-20230924211336264

只是简单模拟这个思路,底层真正想做到没有这么简单只为加深理解

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

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

相关文章

【蓝桥杯Python】试题 算法训练 藏匿的刺客

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 强大的kAc建立了强大的帝国&#xff0c;但人民深受其学霸及23文化的压迫&#xff0c;于是勇敢的鹏决心反抗。   kAc帝国防守…

元素显示模式

1.块级元素 显示特点&#xff1a; 1.独占一行&#xff08;一行只能显示一个&#xff09; 宽度默认是父元素的宽度可以设置宽高 代表标签&#xff1a;div、p、h系列、ul、li、dl、dd、from、header、nav、footer...... 2.行内元素 显示特点&#xff1a; 一行可以显示多个宽…

中国判决生效,诺基亚全面与中国手机签署授权协议,降低专利费

日前媒体报道指诺基亚与中国两家手机企业都签署了专利授权协议&#xff0c;全面结束诉讼&#xff0c;而这一切正是在OPPO于去年底在重庆法院就OPPO与诺基亚的专利费诉讼问题&#xff0c;做出裁决之后&#xff0c;要求诺基亚按公平、公正等合理收费原则收取专利费。 这几年诺基亚…

HiveSQL——连续增长问题

注&#xff1a;参考文章&#xff1a; SQL连续增长问题--HQL面试题35_sql判断一个列是否连续增长-CSDN博客文章浏览阅读2.6k次&#xff0c;点赞6次&#xff0c;收藏30次。目录0 需求分析1 数据准备3 小结0 需求分析假设我们有一张订单表shop_order shop_id,order_id,order_time…

动态水印怎么加 怎么去除动态水印 视频剪辑软件 会声会影安激活序列号 会声会影怎么剪辑视频

为了防止白嫖或者增加美观效果&#xff0c;视频制作者可能会采用动态水印的方式&#xff0c;让其他人难以盗取视频使用。动态水印的添加&#xff0c;需要应用到运动路径功能。接下来&#xff0c;本文会教大家动态水印怎么加&#xff0c;怎么去除动态水印的相关内容。感兴趣的小…

第67讲自定义icon实现

element-plus内置有一些常用的icon供我们使用&#xff0c;但是我们假如需要用自己的icon时候&#xff0c;我们可以搞一个icon自定义组件&#xff1b; 先把icons文件放到src下&#xff1b; 再新建一个SvgIcon组件&#xff1b; index.vue <template><svg class"…

【Linux】指令提权-sudo

Hello everybody&#xff0c;新年快乐&#xff01;哈哈&#xff01;今天打算给大家讲讲指令提权的相关知识&#xff0c;虽然内容不多&#xff0c;但有时却很有用。在我们学习过权限&#xff0c;vim后就可以学习指令提权啦&#xff0c;没看过的宝子们建议先去看一看我之前的文章…

【数据存储+多任务爬虫】

数据存储 peewee模块 第三方模块&#xff0c;也需要在cmd中安装。 from peewee import *db MySQLDatabase("spider",host"127.0.0.1",port3306,userroot,password123456 )# 类》表 class Person(Model):name CharField(max_length20) # 类型/约束bi…

C# WinForm开发系列 - DataGridView

原文地址&#xff1a;https://www.cnblogs.com/peterzb/archive/2009/05/29/1491891.html 1.DataGridView实现课程表 testcontrol.rar 2.DataGridView二维表头及单元格合并 DataGridView单元格合并和二维表头.rar myMultiColHeaderDgv.rar 3.DataGridView单元格显示GIF图片 …

第70讲axios后端请求工具类封装

axios工具类封装&#xff1a; // 引入axios import axios from axios;// 创建axios实例 const httpService axios.create({// url前缀-http:xxx.xxx// baseURL: process.env.BASE_API, // 需自定义baseURL:http://localhost:80/,// 请求超时时间timeout: 3000 // 需自定义 })…

Linux中常用的工具

软件安装 yum 软件包 在Linux中&#xff0c;软件包是一种预编译的程序集合&#xff0c;通常包含了用户需要的应用程序、库、文档和其他依赖项。 软件包管理工具是用于安装、更新和删除这些软件包的软件。常见的Linux软件包管理工具包括APT&#xff08;Advanced Packaging To…

Linux第45步_通过搭建“DNS服务器”学习图形化配置工具

学习的意义&#xff1a;通过搭建“DNS服务器”&#xff0c;来学习“图形化配置工具”。“DNS服务器”&#xff0c;我们用不到&#xff0c;但为后期移植linux系统服务&#xff0c;因为在移植系统时&#xff0c;需要用到这个“图形化配置工具”。 1、“menuconfig图形化配置工具…

CodeWave学习笔记--博物馆预约管理系统

场馆信息管理页面搭建&#xff08;PC&#xff09; 首先是场馆实体的创建 页面的搭建 在总览界面下创建子界面venueManage界面 现在总览页中实现跳转场馆管理子界面 设计场馆管理界面 效果 访客预约申请页面搭建&#xff08;H5&#xff09; 添加H5端&#xff0c;点击确认即可 …

Dubbo源码一:【Dubbo与Spring整合】

正常在项目中&#xff0c;我们都是在Spring环境下使用Dubbo&#xff0c;所以我们这里就在Spring的环境下看看Dubbo是如何运作的 入口 在源码下载下来之后&#xff0c;有一个dubbo-demo目录&#xff0c;里面有一个基于spring注解的子目录dubbo-demo-annotation, 里面有一个生产…

第三百一十六回

[tod] 我们在上一章回中介绍了"如何在输入框中处理光标"相关的内容&#xff0c;本章回中将介绍如何添加输入框默认值.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 在项目中经常使用输入框获取用户输入的内容&#xff0c;有时候在输入框中反复输入相…

【数学建模】【2024年】【第40届】【MCM/ICM】【E题 财产保险的可持续性】【解题思路】

一、题目 &#xff08;一&#xff09; 赛题原文 2024 ICM Problem E: Sustainability of Property Insurance Extreme-weather events are becoming a crisis for property owners and insurers. The world has endured “more than $1 trillion in damages from more than …

appears to be hung in Auto SQL Tuning task

appears to be hung in Auto SQL Tuning task Oracle 自动定时优化任务执行失败分析 错误现象&#xff1a; Sat Feb 10 03:10:57 2024 Process 0x0x00007FFB81BE44A8 appears to be hung in Auto SQL Tuning task Current time 1707505857, process death time 1707505803 …

Redisson分布式锁 原理 + 运用 记录

Redisson 分布式锁 简单入门 pom <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version></dependency>配置类 package com.hmdp.config;import org.redisson.Redisson;…

【Spring MVC篇】参数的传递及json数据传参

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Spring MVC】 本专栏旨在分享学习Spring MVC的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、普通参数的传…

CVE-2012-1823 漏洞复现

CVE-2012-1823 PHP SAPI 与运行模式 首先&#xff0c;介绍一下PHP的运行模式。 下载PHP源码&#xff0c;可以看到其中有个目录叫sapi。sapi在PHP中的作用&#xff0c;类似于一个消息的“传递者”&#xff0c;比如在《Fastcgi协议分析 && PHP-FPM未授权访问漏洞 &…