【Linux】第二十二站:文件(二)深入理解重定向

文章目录

  • 一、重定向
    • 1.文件描述符对应的分配规则
    • 2.重定向的接口
  • 二、再次实现myshell
    • 1.实现细节
    • 2.盘点文件与进程替换的一个细节
    • 3.代码
  • 三、1号文件和2号文件的区别
  • 四、如何理解“一切皆文件?”

一、重定向

1.文件描述符对应的分配规则

我们先看如下代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>#define filename "log.txt"int main()
{int fd = open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);if(fd < 0){perror("open");return 1;}printf("fd : %d\n",fd);const char* msg = "hello linux\n";int cnt = 5;while(cnt){write(fd,msg,strlen(msg));cnt--;}close(fd);return 0;
}

运行结果为,一切都符合我们的预期

image-20231127162842345

紧接着,我们将代码改为如下

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>#define filename "log.txt"int main()
{close(0);int fd = open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);if(fd < 0){perror("open");return 1;}printf("fd : %d\n",fd);const char* msg = "hello linux\n";int cnt = 5;while(cnt){write(fd,msg,strlen(msg));cnt--;}close(fd);return 0;
}

运行结果为

image-20231127165617384

如果我们将代码改为如下,即关闭一号文件

image-20231127165926548

那么运行结果为,将没有任何东西可以打印出来

image-20231127170010669

这是因为1号文件对应的是stout输出流,而printf里面是用到了这个流的。当我们关闭了以后,自然就出现问题了

如果我们关闭的是二号文件

image-20231127172002501

那么结果为

image-20231127172026414

我们可以发现如下现象

当我们关0的时候,为这个新文件分配的文件描述符是0

当我们关1的时候,为这个新文件分配的文件描述符是1

当我们关2的时候,为这个新文件分配的文件描述符是2

这就说明,文件描述符的分配规则很简单,从0下标开始,寻找最小的没有使用的数组位置,它的下标就是新文件的文件描述符

我们来看下面的代码

image-20231127172615148

最终运行结果为

image-20231127172650731

这是因为我们的东西并没有写到这个文件中,而是写入到了显示器文件中。所以才会打印出来。

我们再来看以下代码

image-20231127172836589

运行结果为

image-20231127172917076

这是因为,我们关闭了一号文件,而由于我们又打开了一个新文件,那么最终这个新文件的文件描述符变为了1。所以最终变为了向该文件写入

而这里,我们会发现,本来应该写入到显示器上的内容写入到了文件中,这不就是输出重定向吗?

如下图所示,是我们一开始的状态

image-20231127173921374

后来我们关闭了1号文件,然后打开了一个新的文件。就会将原来的引用计数减减,然后将该指针置空。随后我们创建新文件的时候,会让1号下标的位置指向log.txt这个文件中

image-20231127174302710

而在我们前面的代码中,上层并不知道我们已经将1号文件给改掉了。它只知道要向一号文件写,所以最终变为了向log.txt文件中去写

而上面所说的就是重定向的原理。

所以重定向的本质就是对文件描述符表里面的数组的内容进行修改

2.重定向的接口

我们会发现上面的方法其实有点麻烦,因为我们还需要关闭文件之后,才去打开一个新的文件。

所以操作系统本身就提供了系统调用

image-20231127175304336

int dup2(int oldfd, int newfd);

它的作用是直接将新的文件描述符表数组中的oldfd下标的内容直接拷贝到newfd处。即newfd是要被oldfd所覆盖的

如下图所示,fd代表的是oldfd,1代表的是newfd。

fd的内容最终被拷贝到1号的内容当中。最终保留的就是fd的内容

image-20231127181112181

所以我们就可以写出这个代码了

image-20231127181702696

运行结果为

image-20231127181723935

这样就同样实现了重定向的效果

如果我们将打开的方式换为了O_APPEND

image-20231127181908347

image-20231127181948257

我们会发现这个其实就是追加重定向

我们再来看下面的这段代码

注意这个接口的意思是:从fd中读取count字节个数据到buf中,count是期望读取的数量,返回值是实际读取的数量。

注意它的读取之后,最终不会加上’\0’字符,而fread是C语言的接口,它会自己加上的。所以我们最终需要自己加上这个’\0’字符。

ssize_t read(int fd, void *buf, size_t count);

代码为

image-20231127183759201

运行结果为

image-20231127183823732

然后我们让log.txt的内容如下

image-20231127183946696

代码如下

image-20231127184109563

运行结果为

image-20231127184155076

我们会发现直接读取了,因为我们当前的文件内本身就有内容,所以就默认从文件中读取了

我们会发现它就相当于输入重定向

image-20231127184425755

所以重定向的本质就是对进程的指定文件描述符表中内容拷贝的问题

如果我们的代码是这样子的

image-20231127195455195

运行结果为,符合我们的预期

image-20231127195516197

如果我们将代码改为这样的

image-20231127195817659

那么结果为

image-20231127195804453

他是符合我们的预期的

所以C语言的printf,和fprintf都是往1号文件里写的,不过我们已经提前改了一号文件了。所以就会显示如上的结果

如果我们改为O_APPEND

image-20231127200057332

那么结果也是一样的,符合我们的预期

image-20231127200123154

二、再次实现myshell

1.实现细节

我们知道,像我们平时在命令行中写的重定向是这样的

image-20231127200448554

那么它与我们前面所演示的重定向有什么关系呢?

我们知道,我们前面的代码中,myshell并没有实现重定向功能

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 44char commandline[LINE_SIZE];
char* argv[ARGC_SIZE] = {NULL};
extern char** environ;
int last_code = 0;
int quit = 0;
char pwd[LINE_SIZE];
char myenv[LINE_SIZE];const char* getusername()
{return getenv("USER");
}
const char* gethostname()
{return getenv("HOSTNAME");
}
void getpwd()
{getcwd(pwd,sizeof(pwd));
}void Interact(char* cline,int size)
{getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getusername(),gethostname(),pwd);char* s = fgets(cline,size,stdin);(void)s;assert(s);commandline[strlen(cline) - 1] = '\0';
}
int splitstring(char cline[],char* _argv[])
{if(strcmp(cline,"") == 0) return 0;int i = 0;_argv[i++] = strtok(cline,DELIM);while(_argv[i++] = strtok(NULL,DELIM));return i - 1;
}void NormalExcute(char* _argv[])
{pid_t id = fork();if(id < 0){perror("fork");return;}else if (id == 0){//子进程执行命令execvpe(_argv[0],_argv,environ);exit(EXIT_CODE);}else{int status = 0;pid_t rid = waitpid(id,&status,0);if(rid == id){last_code = WEXITSTATUS(status);}}
}int BuildCommand(char* _argv[],int _argc)
{if(_argc == 2 && strcmp(_argv[0],"cd") == 0){chdir(_argv[1]);getpwd();sprintf(getenv("PWD"),"%s",pwd);return 1;}else if(_argc == 2 && strcmp(_argv[0],"export") == 0){strcpy(myenv,_argv[1]);putenv(myenv);return 1;}else if(_argc == 2 && strcmp(_argv[0],"echo") == 0){if(strcmp(_argv[1],"$?") == 0){printf("%d\n",last_code);last_code = 0;}else if(*_argv[1] == '$'){char* val = getenv(_argv[1] + 1);if(val) printf("%s\n",val);}else {printf("%s\n",_argv[1]);}return 1;}if(_argc > 0 && strcmp(_argv[0],"ls") == 0){_argv[_argc++] = "--color";_argv[_argc] = NULL;}return 0;
}
int main()
{while(!quit){//2.交互问题,解决命令行Interact(commandline,sizeof(commandline));//3.子串分割问题,解析命令行int argc = splitstring(commandline,argv);if(argc == 0) continue;//4.指令的判断(内建命令和普通命令)int n = BuildCommand(argv,argc);//5.普通命令的执行if(!n) NormalExcute(argv);}return 0;
}

那么我们现在可以为他添加上重定向功能,要解决重定向,我们可以交互函数函数中进行处理一下字符串,当该指令进行执行的时候,处理即可。

如下代码所示

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<ctype.h>
#include<sys/stat.h>
#include<fcntl.h>#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 44
#define NONE       -1
#define IN_RDIR     0
#define OUT_RDIR    1
#define APPEND_RDIR 2char commandline[LINE_SIZE];
char* argv[ARGC_SIZE] = {NULL};
extern char** environ;
int last_code = 0;
int quit = 0;
char pwd[LINE_SIZE];
char myenv[LINE_SIZE];
char* rdirfilename = NULL;
int rdir = NONE; const char* getusername()
{return getenv("USER");
}
const char* Gethostname()
{return getenv("HOSTNAME");
}
void getpwd()
{getcwd(pwd,sizeof(pwd));
}
void check_rdir(char* cmd)
{char* pos = cmd;while(*pos!='\0'){if(*pos == '>'){if(*(pos + 1) == '>'){*pos++ = '\0';*pos++ = '\0';while(isspace(*pos)) pos++;rdirfilename = pos;rdir=APPEND_RDIR;break;}else {*pos = '\0';pos++;while(isspace(*pos)) pos++;rdirfilename = pos;rdir = OUT_RDIR;break;}}else if(*pos == '<'){*pos = '\0';  //ls -a -l < file.txtpos++;while(isspace(*pos)) pos++;rdirfilename = pos;rdir = IN_RDIR;break;}else {// do nothing}pos++;}
}
void Interact(char* cline,int size)
{getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getusername(),Gethostname(),pwd);char* s = fgets(cline,size,stdin);(void)s;assert(s);commandline[strlen(cline) - 1] = '\0';check_rdir(cline);
}
int splitstring(char cline[],char* _argv[])
{if(strcmp(cline,"") == 0) return 0;int i = 0;_argv[i++] = strtok(cline,DELIM);while(_argv[i++] = strtok(NULL,DELIM));return i - 1;
}void NormalExcute(char* _argv[])
{pid_t id = fork();if(id < 0){perror("fork");return;}else if (id == 0){int fd = 0;if(rdir == IN_RDIR){fd = open(rdirfilename,O_RDONLY);dup2(fd,0);}else if(rdir == OUT_RDIR){fd = open(rdirfilename,O_WRONLY|O_CREAT|O_TRUNC,0666);dup2(fd,1);}else if(rdir == APPEND_RDIR) {fd = open(rdirfilename,O_WRONLY|O_CREAT|O_APPEND,0666);dup2(fd,1);}//子进程执行命令execvpe(_argv[0],_argv,environ);exit(EXIT_CODE);}else{int status = 0;pid_t rid = waitpid(id,&status,0);if(rid == id){last_code = WEXITSTATUS(status);}}
}int BuildCommand(char* _argv[],int _argc)
{if(_argc == 2 && strcmp(_argv[0],"cd") == 0){chdir(_argv[1]);getpwd();sprintf(getenv("PWD"),"%s",pwd);return 1;}else if(_argc == 2 && strcmp(_argv[0],"export") == 0){strcpy(myenv,_argv[1]);putenv(myenv);return 1;}else if(_argc == 2 && strcmp(_argv[0],"echo") == 0){if(strcmp(_argv[1],"$?") == 0){printf("%d\n",last_code);last_code = 0;}else if(*_argv[1] == '$'){char* val = getenv(_argv[1] + 1);if(val) printf("%s\n",val);}else {printf("%s\n",_argv[1]);}return 1;}if(_argc > 0 && strcmp(_argv[0],"ls") == 0){_argv[_argc++] = "--color";_argv[_argc] = NULL;}return 0;
}
int main()
{while(!quit){rdirfilename = NULL;rdir = NONE;//2.交互问题,解决命令行Interact(commandline,sizeof(commandline));//3.子串分割问题,解析命令行int argc = splitstring(commandline,argv);if(argc == 0) continue;//4.指令的判断(内建命令和普通命令)int n = BuildCommand(argv,argc);//5.普通命令的执行if(!n) NormalExcute(argv);}return 0;
}

在上面的代码中,当我们输入完指令字符串以后,然后去寻找是否存在重定向的符号,如果有,改变当前状态为输入/输出/追加重定向,然后将重定向的文件名给记录下来。最终达到分开指令与文件的目的。

分开以后,当我们进行指令的执行的时候,我们暂时只考虑子进程,如果是存在重定向的话,那么就打开对应的文件,然后将该文件的文件描述符放到对应的输入或输出位置上。就可以了。

最终的结果为如下

image-20231128164958787

image-20231128165006593

2.盘点文件与进程替换的一个细节

我们在前面的代码中

在后面我们做了重定向的工作,后面我们在进行程序替换的时候,难道不会影响吗???

在我们之前,我们已经了解了如下的东西

当一个可执行程序加载到内存的时候,会创建出对应的PCB结构体,在tash_struct这个结构体里面,有一个指针,会指向进程地址空间,然后进程地址空间根据页表找到实际的物理内存。

然后CPU就会去找到这个进程,从而进行去调度

image-20231128170345068

而现在,我们知道当我们打开一个文件的时候,会创建出它的结构体struct file

image-20231128172612430

随后为了管理起来,task_struct中有一个指针,指向文件描述符表,在这个表中的下标对应着每一个文件。

image-20231128173022187

其中左侧的这一堆,我们都把他叫做,内核数据结构

image-20231128173215796

当我们再度创建一个文件的时候

image-20231128174328772

而我们上面进程替换替换是右边的部分,并不会对左边的部分有影响

image-20231128174423713

这里就是内存管理与文件管理的解耦

进程历史打开的文件与进行的各种重定向关系都和未来进行程序替换无关

程序替换,并不影响文件访问

3.代码

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<ctype.h>
#include<sys/stat.h>
#include<fcntl.h>#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 44
#define NONE       -1
#define IN_RDIR     0
#define OUT_RDIR    1
#define APPEND_RDIR 2char commandline[LINE_SIZE];
char* argv[ARGC_SIZE] = {NULL};
extern char** environ;
int last_code = 0;
int quit = 0;
char pwd[LINE_SIZE];
char myenv[LINE_SIZE];
char* rdirfilename = NULL;
int rdir = NONE; const char* getusername()
{return getenv("USER");
}
const char* Gethostname()
{return getenv("HOSTNAME");
}
void getpwd()
{getcwd(pwd,sizeof(pwd));
}
void check_rdir(char* cmd)
{char* pos = cmd;while(*pos!='\0'){if(*pos == '>'){if(*(pos + 1) == '>'){*pos++ = '\0';*pos++ = '\0';while(isspace(*pos)) pos++;rdirfilename = pos;rdir=APPEND_RDIR;break;}else {*pos = '\0';pos++;while(isspace(*pos)) pos++;rdirfilename = pos;rdir = OUT_RDIR;break;}}else if(*pos == '<'){*pos = '\0';  //ls -a -l < file.txtpos++;while(isspace(*pos)) pos++;rdirfilename = pos;rdir = IN_RDIR;break;}else {// do nothing}pos++;}
}
void Interact(char* cline,int size)
{getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getusername(),Gethostname(),pwd);char* s = fgets(cline,size,stdin);(void)s;assert(s);commandline[strlen(cline) - 1] = '\0';check_rdir(cline);
}
int splitstring(char cline[],char* _argv[])
{if(strcmp(cline,"") == 0) return 0;int i = 0;_argv[i++] = strtok(cline,DELIM);while(_argv[i++] = strtok(NULL,DELIM));return i - 1;
}void NormalExcute(char* _argv[])
{pid_t id = fork();if(id < 0){perror("fork");return;}else if (id == 0){int fd = 0;if(rdir == IN_RDIR){fd = open(rdirfilename,O_RDONLY);dup2(fd,0);}else if(rdir == OUT_RDIR){fd = open(rdirfilename,O_WRONLY|O_CREAT|O_TRUNC,0666);dup2(fd,1);}else if(rdir == APPEND_RDIR) {fd = open(rdirfilename,O_WRONLY|O_CREAT|O_APPEND,0666);dup2(fd,1);}//子进程执行命令execvpe(_argv[0],_argv,environ);exit(EXIT_CODE);}else{int status = 0;pid_t rid = waitpid(id,&status,0);if(rid == id){last_code = WEXITSTATUS(status);}}
}int BuildCommand(char* _argv[],int _argc)
{if(_argc == 2 && strcmp(_argv[0],"cd") == 0){chdir(_argv[1]);getpwd();sprintf(getenv("PWD"),"%s",pwd);return 1;}else if(_argc == 2 && strcmp(_argv[0],"export") == 0){strcpy(myenv,_argv[1]);putenv(myenv);return 1;}else if(_argc == 2 && strcmp(_argv[0],"echo") == 0){if(strcmp(_argv[1],"$?") == 0){printf("%d\n",last_code);last_code = 0;}else if(*_argv[1] == '$'){char* val = getenv(_argv[1] + 1);if(val) printf("%s\n",val);}else {printf("%s\n",_argv[1]);}return 1;}if(_argc > 0 && strcmp(_argv[0],"ls") == 0){_argv[_argc++] = "--color";_argv[_argc] = NULL;}return 0;
}
int main()
{while(!quit){rdirfilename = NULL;rdir = NONE;//2.交互问题,解决命令行Interact(commandline,sizeof(commandline));//3.子串分割问题,解析命令行int argc = splitstring(commandline,argv);if(argc == 0) continue;//4.指令的判断(内建命令和普通命令)int n = BuildCommand(argv,argc);//5.普通命令的执行if(!n) NormalExcute(argv);}return 0;
}

三、1号文件和2号文件的区别

当我们使用如下代码的时候

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>#define filename "log.txt"int main()
{fprintf(stdout,"hello normal message\n");fprintf(stdout,"hello normal message\n");fprintf(stdout,"hello normal message\n");fprintf(stdout,"hello normal message\n");fprintf(stdout,"hello normal message\n");fprintf(stderr,"hello error message\n");fprintf(stderr,"hello error message\n");fprintf(stderr,"hello error message\n");fprintf(stderr,"hello error message\n");fprintf(stderr,"hello error message\n");return 0;
}

运行结果为,我们可以看到,这两个文件都是往显示器上打印的,似乎没有什么区别

image-20231128175412685

但是如果我们这样做

image-20231128175558749

我们会发现一部分重定向了,一部分没有重定向

在一开始的时候是这样的

image-20231128180433678

随后发生了重定向,此时这个重定向仅仅是对于1号的文件的重定向

image-20231128180524995

这样的话,凡是原来往1里面写的,就会写进这个normal.log文件了

往2里面写的就往显示屏上打印了

如果我们在命令行中的是这样的

image-20231128180848142

其实上面是一个简写

下面是完整的,代表1重定向到normal.log,2重定向到err.log

./mytest 1>normal.log 2>err.log

这样的话就可以分开了

如果我们就想要重定向到一个文件中,那么可以这样做

./mytest 1>all.log 2>&1

image-20231128181522122

同理,前面的这个1也是可以省略的

image-20231128181631739

这个命令中

2>&1的意思是把1号文件描述符里面的内容写到2号文件描述符中。

这个的前提是已经把前半部分指令的操作做完了。而1已经指向这个这个all了。

所以最终1和2都指向这个文件了。所以就全部写入到一个文件中了

四、如何理解“一切皆文件?”

我们知道系统中有很多设备,如下图所示

这些外设,几乎都要去提供他们的读写方法,只不过对于键盘而言,写方法为空,对于显示器而言,读方法为空而已。但是他们都有读写方法

image-20231128222532291

他们都可以用同一种结构体来表示。然后用类似的读写接口

image-20231128223058653

对于这些硬件,我们都可以以文件的方式用open打开。然后创建对应的struct file内核数据结构。

image-20231128223741964

然后由于我们要对这些设备进行读写

所以linux内核提供了另外一个方法表的数据结构

struct operation_func()
{int (*wirtep)();int (*readp)();
}

当我们想要访问某个设备的时候。会创建这个方法表的数据结构,然后提供一个指针指向这个方法表,方法表中的函数指针就指向对应的接口

image-20231128224429863

如此一来,task_struct就会通过文件描述符表中的数组从而去调用对应的方法数据结构,最终达到调用read函数

这样就可以实现上层都是调用一样的函数了,而根据下层的不同动态的调用不同的方法了

image-20231128225646625

所以所谓的一切皆文件,就是相当于在文件这一层封装了一个文件对象,让文件对象中的指针指向不同设备的函数方法,然后通过上层的一个方法数据结构,来对这些进行汇总。使得我们压根就不需要关心下层是如何的。只需要知道,以后要读取这个文件就调用这个方法即可

image-20231128230206588

所以从文件对象这一层,往上就有了一切皆文件,这一层我们也叫做,VFS(虚拟文件系统)

image-20231128230332248

而我们似乎就可以发现,这不就是C++中的多态吗?

上层都是基类,下层就是派生类。

所以面向对象就是历史的必然!

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

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

相关文章

《C++PrimerPlus》第9章 内存模型和名称空间

9.1 单独编译 Visual Studio中新建头文件和源代码 通过解决方案资源管理器&#xff0c;如图所示&#xff1a; 分成三部分的程序&#xff08;直角坐标转换为极坐标&#xff09; 头文件coordin.h #ifndef __COORDIN_H__ // 如果没有被定义过 #define __COORDIN_H__struct pola…

智能优化算法应用:基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于平衡优化器算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.平衡优化器算法4.实验参数设定5.算法结果…

全志XR806基于FreeRTOS下部署竞技机器人先进模糊控制器

前言 很荣幸参与到由“极术社区和全志在线联合组织”举办的XR806开发板试用活动。本人热衷于各种的开发板的开发&#xff0c;同时更愿意将其实现到具体项目中。秉承以上原则&#xff0c;发现大家的重心都放在开发中的环境构建过程&#xff0c;缺少了不少实际应用场景的运用&am…

【WebSocket】通信协议基于 node 的简单实践和心跳机制和断线重连的实现

前后端 WebSocket 连接 阮一峰大佬 WebSocket 技术博客 H5 中提供的 WebSocket 协议是基于 TCP 的全双工传输协议。它属于应用层协议&#xff0c;并复用 HTTP 的握手通道。它只需要一次握手就可以创建持久性的连接。 那么什么是全双工呢&#xff1f; 全双工是计算机网络中的…

如何计算数据泄露的成本

现在&#xff0c;几乎所有类型的组织每天都在发生企业 IT 网络遭到破坏的情况。它们是任何合规官员最担心的问题&#xff0c;并且找出更好的方法来防止它们或从中恢复是合规官员永远不会远离的想法。 但数据泄露的实际成本是多少&#xff1f;该数字从何而来&#xff1f;当您获…

考试复习

选择20道 填空10道 判断10道 简答4-5道 编程题2道 一、选择题 1.js中更改一个input框的值&#xff1a; <input ida type"text" value"123456"> 通过a.value改变他的值 方法&#xff1a; 在script标签中通过id获得该输入框对象&#xff0c;然…

Flutter应用程序的加固原理

在移动应用开发中&#xff0c;Flutter已经成为一种非常流行的技术选项&#xff0c;可以同时在Android和iOS平台上构建高性能、高质量的移动应用程序。但是&#xff0c;由于其跨平台特性&#xff0c;Flutter应用程序也面临着一些安全风险&#xff0c;例如反编译、代码泄露、数据…

numpy知识库:深入理解numpy.resize函数和数组的resize方法

前言 numpy中的resize函数顾名思义&#xff0c;可以用于调整数组的大小。但具体如何调整&#xff1f;数组形状变了&#xff0c;意味着数组中的元素个数发生了变化(增加或减少)&#xff0c;如何确定resize后的新数组中每个元素的数值呢&#xff1f;本次博文就来探讨并试图回答这…

electron调用dll问题总汇

通过一天的调试安装&#xff0c;electron调用dll成功&#xff0c;先列出当前的环境&#xff1a;node版本: 18.12.0&#xff0c;32位的&#xff08;因为dll为32位的&#xff09; VS2019 python node-gyp 1、首先要查看报错原因&#xff0c;通常在某一行会有提示&#xff0c;常…

elk+filebeat+kafka集群部署

EFK实验架构图&#xff1a; 实现高并发&#xff0c;无需指定logstash 3台esfile&#xff0c;3台kafka 20.0.0.10 esfile 20.0.0.20 esfile 20.0.0.30 esfile 20.0.0.11 kafka 20.0.0.12 kafka 20.0.0.13 kafka在es1主机上解压filebeat cd filebeat 安装nginx服务 vim /usr/loc…

无人机助力电力设备螺母缺销智能检测识别,python基于YOLOv7开发构建电力设备螺母缺销高分辨率图像小目标检测系统

传统作业场景下电力设备的运维和维护都是人工来完成的&#xff0c;随着现代技术科技手段的不断发展&#xff0c;基于无人机航拍飞行的自动智能化电力设备问题检测成为了一种可行的手段&#xff0c;本文的核心内容就是基于YOLOv7来开发构建电力设备螺母缺销检测识别系统&#xf…

软件设计师——计算机网络(一)

&#x1f4d1;前言 本文主要是【计算机网络】——软件设计师计算机网络的题目&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304…

Arduino、ESP8266、HTML相关知识点记录

C代码 const char *ssid "********"; // 这里定义将要建立的WiFi名称。 const char *password "********"; // 这里定义将要建立的WiFi密码。 多WiFi连接&#xff1a; wifiMulti.addAP("**…

mockito加junit gd 单元测试 笔记

目录 一、简介1.1 单元测试的特点1.2 mock类框架使用场景1.3 常用mock类框架1.3.1 mockito1.3.2 easymock1.3.3 powermock1.3.4 JMockit 二、mockito的单独使用2.1 mock对象与spy对象2.2 初始化mock/spy对象的方式2.3 参数匹配2.4 方法插桩2.5 InjectMocks注解的使用断言工具 三…

SQL 金额数值转换成中文大写

需求&#xff1a;将金额转换成中文大写格式填入单据合计行&#xff1a; _佰_拾_万_仟_佰_拾_元_角_分 1234567.89 壹佰贰拾叁万肆仟伍佰陆拾柒元捌角玖分 1.函数转换 drop function n2C;CREATE FUNCTION n2C (num numeric(14,2)) RETURNS VARCHAR(20) AS BEGIN …

智跃人力资源管理系统 SQL注入漏洞复现

0x01 产品简介 智跃人力资源管理系统是基于B/S网页端广域网平台&#xff0c;一套考勤系统即可对全国各地多个分公司进行统一管控&#xff0c;成本更低。信息共享更快。跨平台&#xff0c;跨电子设备 0x02 漏洞概述 智跃人力资源管理系统GenerateEntityFromTable.aspx接口处存在…

SQL Sever 基础知识 - 数据查询

SQL Sever 基础知识 - 一、查询数据 一、查询数据第1节 基本 SQL Server 语句SELECT第2节 SELECT语句示例2.1 SELECT - 检索表示例的某些列2.2 SELECT - 检索表的所有列2.3 SELECT - 对结果集进行筛选2.4 SELECT - 对结果集进行排序2.5 SELECT - 对结果集进行分组2.5 SELECT - …

正则表达式及文本三剑客grep sed awk

正则表达式 1.元字符 . //匹配任意单个字符&#xff0c;可以是个汉字 [yang] //匹配范围内的任意单个字符 [^y] //匹配处理指定范围外的任意单个字符 [:alnum:] //字母和数字 [:alpha:] //代表…

uc_12_进程间通信IPC_有名管道_无名管道

1 内存壁垒 进程间天然存在内存壁垒&#xff0c;无法通过交换虚拟地址直接进行数据交换&#xff1a; 每个进程的用户空间都是0~3G-1&#xff08;32位系统&#xff09;&#xff0c;但它们所对应的物理内存却是各自独立的。系统为每个进程的用户空间维护一张专属于该进程的内存映…

ZPLPrinter Emulator SDK for .NET 6.0.23.1123​ Crack

ZPLPrinter Emulator SDK for .NET 适用于 .NET 的 ZPLPrinter 仿真器 SDK 允许您通过编写 C# 或VB.NET 代码针对任何 .NET Framework、.NET CORE、旧版 ASP.NET MVC 和 CORE、Xamarin、Mono 和通用 Windows 平台 (UWP) 作业。 适用于 .NET 的 ZPLPrinter 仿真器 SDK 允许您将…