【Linux】基础IO(文件描述符、缓冲区、重定向)
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12625432.html

目录
前言
C文件IO相关操作
系统文件I/O
open
open函数返回值
文件描述符fd
read、stat
文件描述符的分配规则
重定向
使用 dup2 系统调用
缓冲区
缓冲区的刷新策略
自主shell补充
封装简单库
stderr
前言
💬 hello! 各位铁子们大家好哇。
今日更新了Linux基础IO的内容
🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝
C文件IO相关操作
1 #include<stdio.h>2 3 int main()4 {5 FILE* fp=fopen("log.txt","w");6 if(NULL==fp)7 {8 perror("fopen");9 return 1;10 }11 fclose(fp); 12 return 0;13 }

执行上面代码后,创建了log.txt。运行代码时,进程就跑起来了,此时进程所在的路径就是当前进程的工作路径,所以在此创建文件。打开文件的本质其实是进程打开文件。
文件存在,但没有被打开时存在于磁盘中。
进程可以打开很多文件,系统中可以有很多进程。

文件=内容+属性
创建文件时, 没有内容,大小显示的是0kb,但它还是占一定空间的,因为他有各种属性。
系统文件I/O
open

pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
上面这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限 O_APPEND: 追加写
O_TRUNC: 如果文件已经存在,而且是个常规文件,以写的方式打开,传入这个选项后,他就会把文件清空。
返回值:
成功:新打开的文件描述符
失败:-1
mode_t理解:就是int类型,表示起始权限。
open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。


运行上面代码,发现创建了log.txt,但是它的权限是乱码的。


我们加上666权限后,现在权限不是乱码了,但权限依旧不是666对应的权限,而是664。这是因为权限掩码会与你的设置的权限,进行位运算,结果就不一样了。系统默认权限掩码是0002
所以我们可以在打开文件前设置默认的权限掩码为0
此时权限就对应上了。注意:权限掩码按照就近原则,如果我们有设置默认权限掩码,就用我们设置的,如果没有,就会使用系统默认的。



上面是系统调用接口close和write。fd就是open的返回值。


我们把message里的内容换成abc,然后直接运行代码。发现之前的内容还在,旧的内容没被清空。
这是因为这里的open默认不存在就创建,存在就打开,默认不清空文件。
如果我们想像C语言fopen的“w”打开方式一样, 打开就清空文件,就需要再传一个选项O_TRUNC。



它表示 如果文件已经存在,而且是个常规文件,并以写的方式打开,传入这个选项后,他就会把文件清空。


O_APPEND就是追加的意思。
open函数返回值
在认识返回值之前,先来认识一下两个概念:系统调用和库函数
fopen fclose fread fwrite都是C标准库当中的函数,我们称之为库函数(libc)。
open close read write lseek 都属于系统提供的接口,称之为系统调用接口

可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。
文件描述符fd
文件描述符就是一个小整数
open的返回值fd是从3开始的。因为C语言默认会打开三个输入输出流,
标准输入stdin、
标准输出stdout、
标准错误stderr 。



我们可以用write配合文件描述符在显示器上打印。

文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件 。
Linux中一切皆文件,所以0,1,2可以代表键盘,显示器。
在OS内,系统在访问文件的时候,只认文件描述符fd。

FILE* 是C语言提供的结构体类型,里面封装着文件fd。
所有的C语言上的文件操作函数,本质都是对系统调用的封装。


FILE* 结构体中就封装着文件描述符fd。
学了系统调用,我们可以用系统调用接口,也可以用语言提供的文件方法。但还是推荐使用语言提供的方法。因为系统不同,系统调用的接口可能不一样。
我们打开的文件是当前xshell的终端。

系统中有一个proc目录,里面有很多蓝色的文件夹,它是由进程的pid来做的。

我们查看某个进程的文件夹。cwd就是当前进程的工作路径。exe指向当前可执行程序的二进制文件。里面还有一个目录fd。

进入fd目录,可以看到默认的文件描述符0、1、2是打开的。
打开的设备是dev目录下的pts/3。



/dev/pts/3 指的就是我打开的右边的界面,也就是终端,这个终端文件就叫/dev/pts/3。


运行后如上图,会在另一个终端上打印。
云服务器下,我们看到的显示器文件一般在 /dev/pts/目录下


如果再登录一个就多了一个新的文件。
read、stat




运行上面代码,结果如下图:
struct stat是一个内核结构体,可以直接用。stat的参数2是一个输出型参数,我们把参数传进去后,它会把参数填满然后再传出来。


运行后,我们就可以读取文件里的内容了,如下图:
read的参数1指读取的文件fd,参数2是将读取到的内容放到该缓冲区中,参数3是要读取的字节数。
read的返回值:>0 :读取到的字节数 =0:已经读取到文件末尾。
文件描述符的分配规则


因为文件描述符的0、1、2默认是打开的,所以这里结果是3。如果我们先把描述符0关了再打开新文件会怎样呢?
结果是0。
如果我们把2先关了 ,这时候结果就是2了。
从上面的结果可以得出结论,
文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
重定向


如果我们先把1关闭,发现结果什么也不打印。这是因为文件描述符1是标准输出流,关闭后,就不会在显示器打印了。


运行上面代码,发现什么也没打印,但确实创建了新的文件log.txt。打印该文件,发现内容写在了文件里面。


如果我们把fflush注释掉,发现log.txt创建出来了,但里面什么东西也没有。

log.txt存在磁盘中,当进程启动打开时,就会被加载到内存中。由于我们先关闭了文件描述符1,所以此时log.txt的文件描述符就是1。上层的printf和fprintf都是向stdout打印,而stdout的描述符是1,OS只认文件描述符,所以最终就向log,txt打印了内容。
重定向的本质:是在内核中改变文件描述符表特定下标的内容,与上层无关!

每个文件对象都有对应的内核文件缓冲区,我们写数据都是从上层通过文件描述符1,写到对应的文件缓冲区,然后OS再把内容刷新到磁盘的文件中。
stdin、stdout、stderr都是FILE*结构体,里面除了封装着fd,还有语言级别的文件缓冲区。所以我们通过printf/fprintf不是直接写到OS的内部的缓冲区,而是直接写到语言级别的缓冲区中,然后C语言再通过1号文件描述符把内容刷新到OS的内核文件缓冲区中。
所以fflush()里面是stdout,这是因为我们是刷新语言级别缓冲区的内容到OS的内核缓冲区中,内核缓冲区的内容由OS进行刷新。
由上可知,之所以注释掉fflush后,log.txt里面啥也没有,是因为内容在语言级别的缓冲区中,还没执行到return语句,冲刷内容到内核缓冲区中,log.txt就被关闭了。

如果我们把close也注释掉,结果如下:
return的时候,语言级别缓冲区的内容就被冲刷到内核文件缓冲区中,此时log.txt就有内容了。
使用 dup2 系统调用


dup2可以在底层帮我们做两个文件描述符对应的数组内容之间的值拷贝 。
本质是文件描述符下标对应内容的拷贝。

原本1号文件的内容指向显示器,3号文件内容指向log.txt。重定向的本质是将3号的内容拷贝给1号。所以1号就不会再指向显示器了,而是变成指向log.txt,所以后来往1号里写的内容都会变成往log.txt里写。
struct file里还存在一个引用计数,有几个指针指向就是几。如log.txt由1号和3号指向就是2,显示器就是0。
如果我们要对标准输出进行重定向,把往显示器打印的内容变成往log,txt打印,根据上面的参数解释,参数的填法应该是dup2(fd,1)。也就是把oldfd留下来,拷贝给newfd。


运行上面代码,发现不在显示器上打印,而是在log.txt里打印。


我们把选项换成O_APPEND,它就会进行追加了。所以>和>>的区别就是选项不同而已。
缓冲区
缓冲区就是一段内存空间。
缓冲区由C语言维护就叫语言级缓冲区,由OS维护就叫内核缓冲区。
缓冲区存在的意义:OS为语言考虑,语言为用户考虑。给上层提供高效的IO体验,间接提高整体效率。
缓冲区的刷新策略
- 立即刷新。(无缓存) fflush(stdout)、fsync(int fd)
- 行刷新。显示器
- 全缓冲。缓冲区写满才刷新。普通文件。
- 特殊情况:
- 进程退出,系统自动刷新。
- 强制刷新。
这个刷新策略在内核和用户级别的缓冲区都能用。这里介绍用户级别的。


运行上面代码,第一次在显示器上打印,第二次重定向到文件打印。发现打印的顺序不同。这是因为在显示器上打印是行刷新策略,write系统调用没有带缓冲区,就按语句顺序打印,所以第一次打印按顺序。库函数print和fprintf最后都是通过write系统调用刷新。
第二次是重定向到普通文件,此时刷新策略变成全缓冲,执行printf和fprintf语句时,内容都在缓冲区中,write直接输出,然后程序结束自动把缓冲区刷新,才打印出printf和fprintf。


直接运行跟上面的第一次一样,因为是行刷新,所以执行到fork()时,缓冲区没内容了。
如果重定向到普通文件,此时是全缓冲,printf和fprintf的内容都在语言级缓冲区中,write是直接写到内核缓冲区中,所以write打印在最前面且只打印一次。到了fork时,父子进程都有了语言及缓冲区的内容,所以程序结束时,父子进程的缓冲区的内容都被刷新,就打印两次printf和fprintf。
- 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
- printf fprintf 库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。
- 而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后
- 但是进程退出之后,会统一刷新,写入文件当中。
- 但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的 一份数据,随即产生两份数据。
- write 没有变化,说明没有所谓的缓冲。
printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区, 都是用户级缓冲区。
自主shell补充
补充了重定向的功能
1 #include <stdio.h> 2 #include <stdlib.h>3 #include <string.h>4 #include <errno.h>5 #include <unistd.h>6 #include <sys/types.h>7 #include <sys/wait.h>8 #include <ctype.h>9 #include <sys/stat.h>10 #include <fcntl.h>11 12 #define SIZE 51213 #define ZERO '\0'14 #define SEP " "15 #define NUM 3216 #define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0)17 #define SkipSpace(cmd, pos) do{\18 while(1){\19 if(isspace(cmd[pos]))\20 pos++;\21 else break;\22 }\23 }while(0)24 25 26 // "ls -a -l -n > myfile.txt"27 #define None_Redir 028 #define In_Redir 129 #define Out_Redir 230 #define App_Redir 331 32 int redir_type=None_Redir;33 char* filename=NULL;34 35 // 为了方便,我就直接定义了36 char cwd[SIZE*2];37 char *gArgv[NUM];38 int lastcode = 0;39 40 void Die()41 {42 exit(1);43 }44 45 const char *GetHome()46 {47 const char *home = getenv("HOME");48 if(home == NULL) return "/"; 49 return home;50 }51 52 const char *GetUserName()53 {54 const char *name = getenv("USER");55 if(name == NULL) return "None";56 return name;57 }58 const char *GetHostName()59 {60 const char *hostname = getenv("HOSTNAME");61 if(hostname == NULL) return "None";62 return hostname;63 }64 // 临时65 const char *GetCwd()66 {67 const char *cwd = getenv("PWD");68 if(cwd == NULL) return "None";69 return cwd;70 }71 72 // commandline : output73 void MakeCommandLineAndPrint()74 {75 char line[SIZE];76 const char *username = GetUserName();77 const char *hostname = GetHostName();78 const char *cwd = GetCwd();79 80 SkipPath(cwd);81 snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd+1);82 printf("%s", line);83 fflush(stdout);84 }85 86 int GetUserCommand(char command[], size_t n)87 {88 char *s = fgets(command, n, stdin);89 if(s == NULL) return -1;90 command[strlen(command)-1] = ZERO;91 return strlen(command); 92 }93 94 95 void SplitCommand(char command[], size_t n)96 {97 (void)n;98 // "ls -a -l -n" -> "ls" "-a" "-l" "-n"99 gArgv[0] = strtok(command, SEP);
100 int index = 1;
101 while((gArgv[index++] = strtok(NULL, SEP))); // done, 故意写成=,表示先赋值,在判断. 分割之后,strtok会返回NULL,刚好让gArgv最后一个元素是NULL, 并且while判断结束
102 }
103
104 void ExecuteCommand()
105 {
106 pid_t id = fork();
107 if(id < 0) Die();
108 else if(id == 0)
109 {
110 //重定向设置
111 if(filename != NULL){
112 if(redir_type == In_Redir)
113 {
114 int fd = open(filename, O_RDONLY);
115 dup2(fd, 0);
116 }
117 else if(redir_type == Out_Redir)
118 {
119 int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
120 dup2(fd, 1);
121 }
122 else if(redir_type == App_Redir)
123 {
124 int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
125 dup2(fd, 1);
126 }
127 else
128 {}
129 }
130
131
132 // child
133 execvp(gArgv[0], gArgv);
134 exit(errno);
135 }
136 else
137 {
138 // fahter
139 int status = 0;
140 pid_t rid = waitpid(id, &status, 0);
141 if(rid > 0)
142 {
143 lastcode = WEXITSTATUS(status);
144 if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
145 }
146 }
147 }
148
149 void Cd()
150 {
151 const char *path = gArgv[1];
152 if(path == NULL) path = GetHome();
153 // path 一定存在
154 chdir(path); //更改当前的工作路径
155
156 // 刷新环境变量
157 char temp[SIZE*2];//临时缓冲区
158 getcwd(temp, sizeof(temp)); //得到当前进程的绝对路径
159 snprintf(cwd, sizeof(cwd), "PWD=%s", temp);//
160 putenv(cwd); // 导入新的环境变量
161 }
162
163 int CheckBuildin()
164 {
165 int yes = 0;
166 const char *enter_cmd = gArgv[0];
167 if(strcmp(enter_cmd, "cd") == 0)
168 {
169 yes = 1;
170 Cd();
171 }
172 else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0)
173 {
174 yes = 1;
175 printf("%d\n", lastcode);
176 lastcode = 0;
177 }
178 return yes;
179 }
180
181
182 void CheckRedir(char cmd[])
183 {
184 // > >> <
185 // "ls -a -l -n > myfile.txt"
186 int pos = 0;
187 int end = strlen(cmd);
188
189 while(pos < end)
190 {
191 if(cmd[pos] == '>')
192 {
193 if(cmd[pos+1] == '>')
194 {
195 cmd[pos++] = 0;
196 pos++;
197 redir_type = App_Redir;
198 SkipSpace(cmd, pos);
199 filename = cmd + pos;
200 }
201 else
202 {
203 cmd[pos++] = 0;
204 redir_type = Out_Redir;
205 SkipSpace(cmd, pos);
206 filename = cmd + pos;
207 }
208 }
209 else if(cmd[pos] == '<')
210 {
211 cmd[pos++] = 0;
212 redir_type = In_Redir;
213 SkipSpace(cmd, pos);
214 filename = cmd + pos;
215 }
216 else
217 {
218 pos++;
219 }
220 }
221 }
222
223 int main()
224 {
225 int quit = 0;
226 while(!quit)
227 {
228 //0.重置
229 redir_type=None_Redir;
230 filename=NULL;
231 // 1. 我们需要自己输出一个命令行
232 MakeCommandLineAndPrint();
233
234 // 2. 获取用户命令字符串
235 char usercommand[SIZE];
236 int n = GetUserCommand(usercommand, sizeof(usercommand));
237 if(n <= 0) return 1;
238
239 //2.1checkredir
240 CheckRedir(usercommand);
241
242 // 3. 命令行字符串分割.
243 SplitCommand(usercommand, sizeof(usercommand));
244
245 // 4. 检测命令是否是内建命令
246 n = CheckBuildin();
247 if(n) continue;
248 // 5. 执行命令
249 ExecuteCommand();
250 }
251 return 0;
252 }
封装简单库
mystdio.h
#pragma once#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define LINE_SIZE 1024
#define FLUSH_NOW 1
#define FLUSH_LINE 2
#define FLUSH_FULL 4struct _myFILE
{unsigned int flags;int fileno;// 缓冲区char cache[LINE_SIZE];int cap;int pos; // 下次写入的位置
};typedef struct _myFILE myFILE;myFILE* my_fopen(const char *path, const char *flag);
void my_fflush(myFILE *fp);
ssize_t my_fwrite(myFILE *fp, const char *data, int len);
void my_fclose(myFILE *fp);
mystdio.c
#include "mystdio.h"myFILE* my_fopen(const char *path, const char *flag)
{int flag1 = 0;int iscreate = 0;mode_t mode = 0666;if(strcmp(flag, "r") == 0){flag1 = (O_RDONLY);}else if(strcmp(flag, "w") == 0){flag1 = (O_WRONLY | O_CREAT | O_TRUNC);iscreate = 1;}else if(strcmp(flag, "a") == 0){flag1 = (O_WRONLY | O_CREAT | O_APPEND);iscreate = 1;}else{}int fd = 0;if(iscreate)fd = open(path, flag1, mode);elsefd = open(path, flag1);if(fd < 0) return NULL;myFILE *fp = (myFILE*)malloc(sizeof(myFILE));if(!fp) return NULL;fp->fileno = fd;fp->flags = FLUSH_LINE;fp->cap = LINE_SIZE;fp->pos = 0;return fp;
}void my_fflush(myFILE *fp)
{write(fp->fileno, fp->cache, fp->pos);fp->pos = 0;
}ssize_t my_fwrite(myFILE *fp, const char *data, int len)
{// 写入操作本质是拷贝, 如果条件允许,就刷新,否则不做刷新memcpy(fp->cache+fp->pos, data, len); //肯定要考虑越界, 自动扩容fp->pos += len;if((fp->flags&FLUSH_LINE) && fp->cache[fp->pos-1] == '\n'){my_fflush(fp);}return len;
}void my_fclose(myFILE *fp)
{my_fflush(fp);close(fp->fileno);free(fp);
}
stderr



stdout和stderr分别对应文件描述符1和2,他们都指向显示器文件。>是标准输出重定向,只更改1号fd里面的内容,所以重定向后,1号的打印到了log,txt,而2号还是没变,依旧打印在显示器上。


直接运行代码,会全部打印在显示器上。我们可以重定向到不同文件,这样就可以将正确信息和错误信息分出来。这也是fd1,fd2的意义。上面是完整的重定向的写法。

如果我们想把1和2都重定向到同一个文件中,可以通过上面的写法实现。
相关文章:
【Linux】基础IO(文件描述符、缓冲区、重定向)
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12625432.html 目录 前言 C文件IO相关操作 系统文件I/O open open函数返回值 文件描述符fd re…...
一篇文章快速学会docker容器技术
目录 一、Docker简介及部署方法 1.1Docker简介 1.1.1什么是docker 1.1.2 docker在企业中的应用场景 1.1.3 docker与虚拟化的对比 1.1.4 docker的优势 二 、部署docker 2.1 容器工作方法 2.2 部署第一个容器 2.2.1 配置软件仓库 2.2.2 安装docker-ce并启动服务 2.2.…...
【MySQL】使用 JDBC 连接数据库
文章目录 前言1. 认识 JDBC1.1 概念1.2 好处 2. 使用 JDBC2.1 安装数据驱动包2.2 把 jar 包导入到项目中2.3 代码编写2.4 测试结果 3. 代码优化4. 源码展示结语 前言 在 MySQL 系列中,我们介绍了很多内容,包括但不限于建库建表,增删查改等等…...
数据结构与算法笔记:概念与leetcode练习题
1、数组Array 时间复杂度 数组访问:O(1) 数组搜索:O(N) 数组插入:O(N) 数组删除:O(N) 特点 适合读,不适合写 数组常用操作 # 1、创建数组 a [] # 2、尾部添加元素 a.append(1) a.append(2) a.append(3) # 3、…...
十大时间序列预测模型
目录 1. 自回归模型 原理 核心公式 推导过程: 完整案例 2. 移动平均模型 原理 核心公式 推导过程: 完整案例 3. 自回归移动平均模型 原理 核心公式 推导过程: 完整案例 4. 自回归积分移动平均模型 原理 核心公式 推导过程 完整案例 5. 季节性自回归积分…...
G2O 通过工厂函数类 OptimizationAlgorithmFactory 来生成固定搭配的优化算法
OptimizationAlgorithmFactory 类位于 optimization_algorithm_factory.h //***g2o源码 g2o/g2o/core/optimization_algorithm_factory.h ***// /*** \brief create solvers based on their short name** Factory to allocate solvers based on their short name.* The Factor…...
手机USB连接不显示内部设备,设备管理器显示“MTP”感叹号,解决方案
进入小米驱动下载界面,等小米驱动下载完成后,解压此驱动文件压缩包。 5、小米USB驱动安装方法:右击“计算机”,从弹出的右键菜单中选择“管理”项进入。 6、在打开的“计算机管理”界面中,展开“设备管理器”项&…...
SpringBootWeb快速入门!详解如何创建一个简单的SpringBoot项目?
在现代Web开发中,SpringBoot以其简化的配置和快速的开发效率而受到广大开发者的青睐。本篇文章将带领你从零开始,搭建一个基于SpringBoot的简单Web应用~ 一、前提准备 想要创建一个SpringBoot项目,需要做如下准备: idea集成开发…...
RabbitMQ 入门到精通指南
RabbitMQ 是一种开源消息代理软件,基于 AMQP(高级消息队列协议)构建,用于异步传输数据,帮助我们解耦系统、削峰流量、处理高并发。本指南将详细介绍 RabbitMQ 的架构设计、使用场景、安装步骤以及一些高级应用…...
ARM base instruction -- movz
Move wide with zero moves an optionally-shifted 16-bit immediate value to a register. 用零移动宽值将可选移位的16位即时值移动到寄存器。即把立即数移动寄存器前先把寄存器清零。 32-bit variant MOVZ <Wd>, #<imm>{, LSL #<shift>} 64-bit var…...
安装jdk安装开发环境与maven
1.下载maven 链接: https://pan.baidu.com/s/1gTmIWBFBdIQob0cqGG3E_Q 提取码: 42ck,apache-maven-3.8.4-bin.zip 2.安装java jdk yum install -y java-1.8.0-openjdk-devel 3.在/opt目录下新建目录 mkdir /opt/maven 4.将apache-maven-3.8.4-bin.zip上传到/opt/ma…...
openpnp - 图像传送方向要在高级校正之前设置好
文章目录 openpnp - 图像传送方向要在高级校正之前设置好笔记图像传送方向的确定END openpnp - 图像传送方向要在高级校正之前设置好 笔记 图像传送方向和JOG面板的移动控制和实际设备的顶部摄像头/底部摄像头要一致,这样才能和贴板子时的实际操作方向对应起来。 …...
数据库建表规范【记录】
建表规约 【强制】创建表时必须显式指定表存储引擎类型,如无特殊需求,一律为InnoDB。 【强制】必须有行数据的创建时间字段create_date和最后更新时间字段edit_date。 【强制】自增主键命名必须是id,关联表外键命名xxyyzz_id;业务…...
css的动画属性
CSS动画属性是CSS3的一个重要特性,它允许你创建平滑的过渡效果,增强用户的交互体验。CSS动画可以通过keyframes规则和animation属性来创建。 animation属性 animation属性是一个简写属性,用于设置动画的多个属性,包括动画名称、…...
【Ubuntu】PlantUML工具 | 安装 | 语法 | 使用工具画序列图
🌱 PlantUML是一个通用性很强的工具,可以快速、直接地创建各种图表。 目录 1 安装 2 使用PlantUML画序列图 ① 语法 ②示例和效果 利用简单直观的语言,用户可以毫不费力地绘制各种类型的图表。PlantUML 是一个开源项目,支持快速绘制:• 时序图• 用例图• 类图• 对...
微信步数C++
题目: 样例解释: 【样例 #1 解释】 从 (1,1) 出发将走 2 步,从 (1,2) 出发将走 4 步,从 (1,3) 出发将走 4 步。 从 (2,1) 出发将走 2 步,从 (2,2) 出发将走 3 步,从 (2,3) 出发将走 3 步。 从 (3,1) 出发将…...
AI写作工具大比拼:揭秘Claude的神秘魅力以及如何订阅Claude
AI写作困境与Claude的惊喜表现 最近有很多朋友在吐槽AI写的文章不太行,我一看他的要求写的很清楚,已经把提示词都用到位了,例如:写作背景、写作要求等,都有具体写出来。但文章阅读起来就是欠缺点啥。 你们有没有遇到…...
秋招内推2025-招联金融
【投递方式】 直接扫下方二维码,或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus,使用内推码 igcefb 投递) 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…...
GOM引擎启动后M2提示Invalid filename报错的解决办法
在架设一个GOM引擎版本的时候,启动M2就提示Invalid filename,之后的网关就没有办法再启动了,研究了半天也终于是弄好了,其实也简单,就是路径设置的不对,所以无法完成启动,很多人以为在控制台设置…...
CPU 多级缓存
在多线程并发场景下,普通的累加很可能错的 CPU 多级缓存 Main Memory : 主存Cache : 高速缓存,数据的读取存储都经过此高速缓存CPU Core : CPU 核心Bus : 系统总线 CPU Core 和 Cache 通过快速通道连接,Main menory 和 Cache 都挂载到 Bus 上…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...









结果是0。

