【Linux】IPC 进程间通信(一):管道(匿名管道命名管道)
✨ 无人扶我青云志,我自踏雪至山巅 🌏
📃个人主页:island1314
🔥个人专栏:Linux—登神长阶
⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞
1. 初识进程间通信 🚀
1.1 进程间通信的目的
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变
1.2 为什么要有进程间通信 ❓
为了实现两个或者多个进程实现数据层面的交互,因为进程独立性的存在,导致进程通信的成本比较高
很多场景下需要多个进程协同工作来完成要求。如下:
- 这条命令首先使用 cat 读取 log.txt 的内容,然后通过管道 (
|
) 将输出传递给 grep 命令。grep 用于搜索指定的字符串。 - grep Hello
:
这个命令搜索包含 "Hello" 的行。
1.3 进程间通信的方式
管道(通过文件系统通信)
- 匿名管道pipe
- 命名管道
- System V 消息队列
- System V 共享内存
- System V 信号量
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
注意:
- System V 标准需要重新构建操作系统代码来实现进程通信,比较繁琐。
- 在 System V 标准出现之前,而「管道通信」是直接复用现有操作系统的代码
- 现在本地通信已经被网络通信取代,所以进程间通信方式只重点介绍管道通信和共享内存通信
知识补充:
(1)进程间通信的本质:必须让不同的进程看到同一份“资源”(资源:特定形式的内存空间)
(2)这个资源谁提供?一般是操作系统
- 为什么不是我们两个进程中的一个呢?假设一个进程提供,这个资源属于谁?
- 这个进程独有,破坏进程独立性,所以要借用第三方空间
(3)我们进程访问这个空间,进行通信,本质就是访问操作系统!
- 进程代表的就是用户,资源从创建,使用(一般),释放--系统调用接口!
2. 匿名管道 🔍
2.1 什么是管道
进程可以通过 读/写 的方式打开同一个文件,操作系统会创建两个不同的文件对象 file,但是文件对象 file 中的内核级缓冲区、操作方法集合等并不会额外创建,而是一个文件的文件对象的内核级缓冲区、操作方法集合等通过指针直接指向另一个文件的内核级缓冲区、操作方法集合等。
- 这样以读方式打开的文件和以写方式打开的文件共用一个 内核级缓冲区
- 进程通信的前提是不同进程看到同一份共享资源
所以根据上述原理,父子进程可以看到同一份共享资源:被打开文件的内核级缓冲区。父进程向被打开文件的内核级缓冲区写入,子进程从被打开文件的内核级缓冲区读取,这样就实现了进程通信!
- 这里也将被打开文件的内核级缓冲区称为 「 管道文件」,而这种由文件系统提供公共资源的进程间通信,就叫做「 管道 」
注意:
此外,管道通信只支持单向通信,即只允许父进程传输数据给子进程,或者子进程传输数据给父进程。
- 当父进程要传输数据给子进程时,就可以只使用以写方式打开的文件的管道文件,关闭以读方式打开的文件,
- 同样的,子进程只是用以读方式打开的文件的管道文件,关闭掉以写方式打开的文件。
- 父进程向以写方式打开的文件的管道文件写入,子进程再从以读方式打开的文件的管道文件读取,从而实现管道通信。如果是要子进程向父进程传输数据,同理即可。
管道特点总结:
- 一个进程将同一个文件打开两次,一次以写方式打开,另一次以读方式打开。此时会创建两个struct file,而文件的属性会共用,不会额外创建
- 如果此时又创建了子进程,子进程会继承父进程的文件描述符表,指向同一个文件,把父子进程都看到的文件,叫管道文件
管道只允许单向通信
管道里的内容不需要刷新到磁盘
2.2 创建匿名管道
匿名管道:没有名字的文件(struct file)
匿名管道用于父子间通信,或者由一个父创建的兄弟进程(必须有“血缘“)之间进行通信
#include <unistd.h>
原型:int pipe(int fd[2]);功能:创建匿名管道
参数 fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码
使用如下:
-
int main() {// 1. 创建管道int fds[2] = {0};int n = pipe(fds); // fds: 输出型参数if(n != 0){std::cerr << "pipe error" << std::endl;return 1;}std::cout << "fds[0]: " << fds[0] << std::endl;std::cout << "fds[1]: " << fds[1] << std::endl;return 0; }// 运行如下: island@VM-8-10-ubuntu:~/code$ ./code fds[0]: 3 fds[1]: 4
-
输出型参数:文件的描述符数字带出来,让用户使用-->3,4,因为0,1,2分别被stdin,stdout,stderr占用。
2.3 匿名管道通信案例(父子通信)
情况一:管道为空 && 管道正常(read 会阻塞【read 是一个系统调用】)
具体代码演示如下:(子进程写入,父进程读取)
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdlib>// 父进程 -- 读取
// 子进程 -- 写入
void write(std::string &info, int cnt)
{info += std::to_string(getpid());info += ", cnt: ";info += std::to_string(cnt);info += ')';
}int main()
{// 1. 创建管道int fds[2] = {0};int n = pipe(fds); // fds: 输出型参数if (n != 0){std::cerr << "pipe error" << std::endl;return 1;}// 2. 创建子进程pid_t id = fork();if (id < 0){std::cerr << "fork error" << std::endl;return 2;}else if (id == 0){// 子进程// 3. 关闭不需要的 fd, 关闭 readint cnt = 0;while (true){close(fds[0]);std::string message = "(IsLand1314, pid: ";write(message, cnt);cnt++;sleep(2);}exit(0);}else{// 父进程// 3. 关闭不需要的 fd, 关闭 writeclose(fds[1]);char buffer[1024];while(true){ssize_t n = ::read(fds[0], buffer, 1024);if(n > 0){buffer[n] = 0;std::cout << "child->father, message: " << buffer << std::endl;}}// 记录退出信息pid_t rid = waitpid(id, nullptr, 0);std::cout << "father wait chile success" << rid << std::endl;}return 0;
}
子进程每隔 2 s 向父进程写入数据,并且打印,如下:
从上面可以知道:
- 子进程写入的信息是变化的信息
- 父进程打印信息的时间间隔和子进程一样,那么子进程没传入信息的时候,父进程处于阻塞 --> (IPC 本质:先让不同的进程,看到同一份资源,可以保护共享资源)
情况二:管道为满 && 管道正常(write 会阻塞【write 是一个系统调用】)
如下对代码做点修改(红框内的代码)
管道有上限,Ubuntu -> 64 KB
如果我们让父进程正常读取,那么结果又是怎样的呢?
运行如下:
当我们到 65536 个字节时,管道已满,父进程读取了管道数据,子进程会继续进行写入,然后进行继续读取,就有点数据溢出的感觉
情况三:管道写端关闭 && 读端继续(读端读到0,表示读到文件结尾)
代码修改如下:
else if (id == 0)
{int cnt = 0, total = 0;while (true){close(fds[0]);std::string message = "h";// fds[1]total += ::write(fds[1], message.c_str(), message.size());cnt++;std::cout << "total: " << total << std::endl; // 最后写到 65536 个字节sleep(2);break; // 写端关闭}exit(0);
}
else
{// 父进程// 3. 关闭不需要的 fd, 关闭 writeclose(fds[1]);char buffer[1024];while (true) {sleep(1);ssize_t n = ::read(fds[0], buffer, 1024);if (n > 0) {buffer[n] = 0;std::cout << "child->father, message: " << buffer << std::endl;}else if (n == 0) {std::cout << "n: " << n << std::endl;std::cout << "child quit??? me too " << std::endl;break;}std::cout << std::endl;}pid_t rid = waitpid(id, nullptr, 0);std::cout << "father wait chile success" << rid << std::endl;
}
运行如下:
结论:如果写端关闭,读端读完管道内部数据,再读取就会读取到返回值 0,表示对端关闭,也表示读到文件结尾
情况四:管道写端正常 && 读端关闭(OS 会直接杀掉写入进程)
情况二:
如何杀死呢?
a. OS 会给 目标进程发送信号:13) SIGPIPE
b. 证明如下;
else if (id == 0)
{int cnt = 0, total = 0;while (true){close(fds[0]);std::string message = "h";// fds[1]total += ::write(fds[1], message.c_str(), message.size());cnt++;std::cout << "total: " << total << std::endl; // 最后写到 65536 个字节sleep(2);}exit(0);
}
else
{close(fds[1]);char buffer[1024];while (true){sleep(1);ssize_t n = ::read(fds[0], buffer, 1024);if (n > 0){buffer[n] = 0;std::cout << "child->father, message: " << buffer << std::endl;}else if (n == 0){std::cout << "n: " << n << std::endl;std::cout << "child quit??? me too " << std::endl;break;}close(fds[0]); // 读端关闭break;std::cout << std::endl;}// 记录退出信息int status = 0;pid_t rid = waitpid(id, &status, 0);std::cout << "father wait chile success: " << rid << " exit code: " <<((status << 8) & 0xFF) << ", exit sig: " << (status & 0x7F) << std::endl;
}
运行如下:
小结
🦋 管道读写规则
- 当没有数据可读时
- read 调用阻塞,即进程暂停执行,一直阻塞等待
- read 调用返回-1,errno值为EAGAIN。
- 当管道满的时候
- write 调用阻塞,直到有进程读走数据
- 调用返回-1,errno值为 EAGAIN
- 如果所有管道写端对应的文件描述符被关闭,则read返回0
- 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号
2.4 匿名管道特性
-
匿名管道:只用来进行具有血缘关系的进程之间,进行通信,常用于父子进程之间通信
-
管道文件的生命周期是随进程的
-
管道内部,自带进程之间同步的机制(多执行流执行代码的时候,具有明显的顺序性)
-
管道文件在通信的时候,是面向字节流的。(写的次数和读取的次数不是一一匹配的)
-
管道的通信模式,是一种特殊的半双工模式,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
3. 管道通信的场景 - 进程池 💦
- 父进程创建多个子进程,并为每个子进程创建一个管道文件,父进程为写端,子进程为读端。父进程给子进程通过管道传输任务,这就是进程池。
- 如果父进程没有给子进程传输任务,即管道文件中没有数据,根据进程通信情况1,读端即子进程会阻塞等待父进程传输任务。
- 此外父进程还要给子进程平衡任务,不能让某个进程特别繁忙,其他进程没有任务可做。这就是负载均衡。
进程池 -- 源码实现
后面我会写一篇博客专门来讲这个,敬请期待
4. 命名管道 🦌
4.1 介绍
- 匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
- 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
- 命名管道是一种特殊类型的文件(命名管道 -- > mkfifo)
4.2 创建命名管道
- Linux系统中,使用 mkfifo 命令创建有名管道文件,再使用两个进程打开即可
$ mkfifo filename
如上图,当我们在终端1创建了一个命名管道后,往里面写东西,管道不会关闭,在终端2上发现,它的内存大小还是0。
- 当我们在终端2打印出内容后,管道就自动关闭了。如下图:
- Linux 系统编程中使用 mkfifo 函数创建一个管道文件,再让两个不相关的进程打开:
int mkfifo(const char *pathname, mode_t mode);
参数
- pathname:要创建的命名管道的路径名。
- mode:创建命名管道时设置的权限模式,通常以 8 进制表示,比如 0666。
返回值
- 若成功,返回值为 0;若失败,返回值为 -1,并设置errno来指示错误类型。
功能
- mkfifo() 函数的作用是在文件系统中创建一个特殊类型的文件,该文件在外观上类似于普通文件,但实际上是一个FIFO,用于进程之间的通信。
- 这种通信方式是单向的,即数据写入FIFO的一端,可以从另一端读取出来,按照先进先出的顺序。
🎢 案例:
std::string fifoPath = "/tmp/my_named_pipe"; // 命名管道的路径名
mkfifo(fifoPath.c_str(), 0666); // 创建权限为0666的命名管道
注意事项
- 路径名:确保要创建的命名管道路径名合法且没有重复。
- 权限模式:根据实际需求设置合适的权限模式,确保可被需要访问该管道的进程所访问。
- 错误处理:对 mkfifo() 函数的返回值进行适当的错误处理,根据具体的错误原因进行相应的处理和日志记录。
4.3 命名管道通信案例
先写如下的几个文件:
Comm.hpp
#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string gpipeFile = "./fifo";
const mode_t gmode = 0600;
const int gdefultfd = -1;
const int gsize = 1024;
Client.hpp
#pragma once#include <iostream>
#include "Comm.hpp" // 让不同代码看到同一份资源class Client
{
public:Client():_fd(gdefultfd){}bool OpenPipe(){_fd = ::open(gpipeFile.c_str(), O_WRONLY);if(_fd < 0) {std::cerr << "open error" << std::endl;return false;}return true;}// std::string *: 输出型参数// const std::string &: 输入型参数// std::string &: 输入输出型参数int SendPipe(const std::string &in){return ::write(_fd, in.c_str(), in.size());}void ClosePipe(){if(_fd>=0)::close(_fd);}~Client(){}
private:int _fd;
};
Server.hpp
#pragma once#include <iostream>
#include "Comm.hpp"class Init
{
public:Init(){umask(0);int n = ::mkfifo(gpipeFile.c_str(), gmode);if (n < 0){std::cerr << "mkfifo error" << std::endl;return;}std::cout << "mkfifo success" << std::endl;// sleep(10);}~Init(){int n = ::unlink(gpipeFile.c_str());if (n < 0){std::cerr << "unlink error" << std::endl;return;}std::cout << "unlink success" << std::endl;}
};Init init;class Server
{
public:Server(): _fd(gdefultfd){}bool OpenPipe(){_fd = ::open(gpipeFile.c_str(), O_RDONLY);if(_fd < 0){std::cerr << "open cerr" << std::endl;return false;}return true;}// std::string *: 输出型参数// const std::string & : 输入型参数// std::string &: 输入输出型参数int RecvPipe(std::string *out){char buffer[gsize];ssize_t n = ::read(_fd, buffer, sizeof(buffer) - 1);if(n > 0){buffer[n] = 0;*out = buffer;}return n;}void ClosePipe(){if(_fd>=0)::close(_fd);}~Server(){}private:int _fd;
};
Client.cc(客户端,写入)
#include "Client.hpp"
#include <iostream>int main()
{Client client;client.OpenPipe();std::string message;while(true){std::cout << "Please Entere# ";std::getline(std::cin, message);client.SendPipe(message);}client.ClosePipe();return 0;
}
Server.cc(服务端,读取显示)
#include "Server.hpp"
#include <iostream>int main()
{Server server;server.OpenPipe();std::string message;while(true){server.RecvPipe(&message);std::cout << "client Say# " << message << std::endl;}server.ClosePipe();return 0;
}
Makefile(通过 make 指令来生成可执行文件)
SERVER=server
CLIENT=client
CC=g++
SERVER_SRC=Server.cc
Client_SRC=Client.cc.PHONY:all
all:$(SERVER) $(CLIENT)$(SERVER):$(SERVER_SRC)$(CC) -o $@ $^ -std=c++11
$(CLIENT):$(Client_SRC)$(CC) -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f $(SERVER) $(CLIENT)
- .PHONY:all :这行声明 all 是一个伪目标。即使文件系统中存在一个名为 all 的文件,make all 命令也会执行与 all 相关的规则,而不是认为目标已经是最新的。
- all:server client :这行定义了 all 伪目标的依赖,即 server 和 client。当运行 make all 时,Makefile 会首先尝试构建 server 和 client 目标。
运行结果如下:
命名管道演示1
注意:如果客户端先退出,那么接收端就会进入死循环
命名管道演示 - 死循环
原因:
- 类似于我们上面说的匿名管道写端关闭,读端继续,读端读到0(文件结尾)
- 死循环是因为 Server.hpp 文件只处理了 n > 0 的情况,因此我们需要做出一些修改,如下对 Server.cc文件做出的修改
运行结果如下:
命名管道-死循环解决演示
为了让我们代码更加优美,并且解决一些代码重复问题,我们再进行一步完善
// Comm.hpp
#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string gpipeFile = "./fifo";
const mode_t gmode = 0600;
const int gdefultfd = -1;
const int gsize = 1024;
const int gForRead = O_RDONLY;
const int gForWrite = O_WRONLY;int OpenPipe(int flag)
{int fd = ::open(gpipeFile.c_str(), gForRead);if(fd < 0){std::cerr << "open cerr" << std::endl;return false;}return fd;
}void ClosePipeHelper(int fd)
{if(fd>=0) ::close(fd);
}// Client.hpp
#pragma once#include <iostream>
#include "Comm.hpp" // 让不同代码看到同一份资源class Client
{
public:Client():_fd(gdefultfd){}bool OpenPipeForWrite(){_fd = OpenPipe(gForWrite);if(_fd < 0) return false;return true;}int SendPipe(const std::string &in){return ::write(_fd, in.c_str(), in.size());}void ClosePipe(){ClosePipeHelper(_fd);}~Client(){}
private:int _fd;
};// Server.hpp
#pragma once
#include <iostream>
#include "Comm.hpp"class Init
{
public:Init(){umask(0);int n = ::mkfifo(gpipeFile.c_str(), gmode);if (n < 0){std::cerr << "mkfifo error" << std::endl;return;}std::cout << "mkfifo success" << std::endl;// sleep(10);}~Init(){int n = ::unlink(gpipeFile.c_str());if (n < 0){std::cerr << "unlink error" << std::endl;return;}std::cout << "unlink success" << std::endl;}
};Init init;class Server
{
public:Server(): _fd(gdefultfd){}bool OpenPipeForRead(){_fd = OpenPipe(gForRead);if(_fd < 0) return false;return true;}int RecvPipe(std::string *out){char buffer[gsize];ssize_t n = ::read(_fd, buffer, sizeof(buffer) - 1);if(n > 0){buffer[n] = 0;*out = buffer;}return n;}void ClosePipe(){ClosePipeHelper(_fd);}~Server(){}private:int _fd;
};
深入研究管道,继续对 Server.cc 文件进行修改,看看其第一次打开的时候在哪里阻塞
int main()
{Server server;std::cout << "pos 1" << std::endl;server.OpenPipeForRead();std::cout << "pos 2" << std::endl;std::string message;while (true){if (server.RecvPipe(&message) > 0){std::cout << "client Say# " << message << std::endl;}else{break;}std::cout << "pos 3" << std::endl;}std::cout << "client quit, me too!" << std::endl;server.ClosePipe();return 0;
}
运行如下:
命名管道-阻塞演示
结论:
- 读端打开文件的时候,写端还没有打开,读端对用的 open 就会阻塞
4.4 匿名管道与命名管道的区别
🎀 匿名管道与命名管道的区别
- 匿名管道由 pipe函数 创建并打开。
- 命名管道由 mkfifo函数 创建,打开用open。
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
4.5 命名管道的打开规则
🎈 如果当前打开操作是为读而打开FIFO时
- O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
- O_NONBLOCK enable:立刻返回成功
🎈 如果当前打开操作是为写而打开FIFO时
- O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
- O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
5. 小结📖
管道是一种用于进程间通信(IPC)的机制,允许一个进程将数据传递给另一个进程。在类Unix操作系统中,管道通常由内核提供,使用简单的读写接口。
管道分为两种类型:无名管道和命名管道
- 无名管道主要用于具有亲缘关系的进程(如父子进程),在创建时不需要名称,只能通过文件描述符进行访问
- 命名管道(FIFO)则可以在任何进程之间通信,使用文件系统中的路径来标识
管道的特点
- 管道是单向的:数据在一个方向上流动,从写端(写入数据的进程)到读端(读取数据的进程)
- 在写端,数据会被写入一个缓冲区,读端则从这个缓冲区读取数据
- 管道的缓冲区大小有限,因此如果写入的数据超过缓冲区容量,写入进程会被阻塞,直到有空间可用。
管道的优点在于其简单性和高效性,适用于需要实时数据传输的场景。然而,由于其单向特性和有限的缓冲区,复杂的通信需求可能需要其他IPC机制,如消息队列或共享内存。总的来说,管道是一种基础而有效的进程间通信工具。
【*★,°*:.☆( ̄▽ ̄)/$:*.°★* 】那么本篇到此就结束啦,如果我的这篇博客可以给你提供有益的参考和启示,可以三连支持一下 !!
相关文章:

【Linux】IPC 进程间通信(一):管道(匿名管道命名管道)
✨ 无人扶我青云志,我自踏雪至山巅 🌏 📃个人主页:island1314 🔥个人专栏:Linux—登神长阶 ⛺️ 欢迎关注:👍点赞 &#…...

Kotlin类与对象
类的定义与对象创建 类的创建是比较简单的,主要是看一下注意点: 1.如果主构造函数没有任何注释或可见性修饰符,则可以省略constructor关键字,如果类中没有其他内容要写,可以直接省略花括号,最后就变成下面…...

Windows版 nginx安装,启动,目录解析,常用命令
Windows版 nginx安装,启动,目录解析,常用命令 一级目录二级目录三级目录 1. 下载2. 启动方式一:方式二: 3. 验证是否启动4. 安装目录解析5. 常用命令 一级目录 二级目录 三级目录 1. 下载 官网下载:ngi…...

基于51单片机的电子隐形防盗网proteus仿真
地址: https://pan.baidu.com/s/1TSuRsB2i1tgAHjI0Miz3AQ 提取码:1234 仿真图: 芯片/模块的特点: AT89C52/AT89C51简介: AT89C52/AT89C51是一款经典的8位单片机,是意法半导体(STMicroelectro…...

Fish Agent:多语言 Voice-to-Voice 开源语音模型;Runway 推出摄像机运镜功能丨 RTE 开发者日报
开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思…...

locust压测工具环境搭建(Linux、Mac)
目录 使用anaconda搭建locust环境 1、anaconda安装python环境(推荐) 2、从conda安装locust 使用python虚拟环境搭建locust环境 1、直接安装python环境 2、从python环境安装locust 从依赖文件中安装locust环境 1、从anaconda安装locust 2、从pyt…...

欠定方程有多个真正解,超定方程可能无解所以有最小二乘解
Ax b x A\b ,但不是b/A,会报错矩阵维度不对应两个未知数,三个方程也可以是最小二乘解,因为无解满足三个方程,比如下面 A [0, 1; 1, 1;3,6]; % 一个接近奇异的矩阵 b [1; 2;3]; x A\b; % 求解线性方程组 % warning(off, MATLAB:…...

LeetCode27:移除元素
原题地址:. - 力扣(LeetCode) 题目描述 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k…...

JAVA 插入 JSON 对象到 PostgreSQL
博主主页:【南鸢1.0】 本文专栏:JAVA 目录 编辑 简介 所用: 1、 确保 PostgreSQL 数据库支持 JSON: 2、添加 PostgreSQL JDBC 驱动 3、安装和运行 PostgreSQL 4、建立数据库的连接 简介 在现代软件开发中,由于 JSON 数据…...

视图,物化视图,普通表区别简介
普通视图与物化视图的主要区别: 数据存储方式: 普通视图:不存储数据,仅包含查询定义。查询时,Oracle会根据视图的定义转换为相应的SQL语句去查询底层数据。物化视图:实际存储数据,类似于物理表。…...

C++ | Leetcode C++题解之第530题二叉搜索树的最小绝对差
题目: 题解: class Solution { public:void dfs(TreeNode* root, int& pre, int& ans) {if (root nullptr) {return;}dfs(root->left, pre, ans);if (pre -1) {pre root->val;} else {ans min(ans, root->val - pre);pre root-&g…...

使用Node.js构建实时聊天应用
使用Node.js构建实时聊天应用 Node.js简介 安装Node.js 使用npm安装依赖 创建基本的Web服务器 设置静态文件夹 创建聊天客户端 连接WebSocket 发送消息 接收消息 处理消息 实现私聊功能 实现群聊功能 用户身份验证 存储聊天记录 使用WebSocket进行文件传输 使用WebSocket进行屏…...

STM32F103C8T6学习笔记1--新建工程模板
1、简介 STM32是一系列由STMicroelectronics(瑞士意法半导体)公司设计和生产的32位微控制器产品线。这些微控制器基于ARM Cortex-M内核,并具有高性能、低功耗和多种外设接口的特点。STM32处理器被广泛应用于各种嵌入式系统领域,包…...

RK3568平台开发系列讲解(内存篇)Linux 内存优化
🚀返回专栏总目录 文章目录 一、如何正确采集 Linux 内存可用空间二、系统内存优化参考步骤2.1、优化 Linux Kernel module 所占用的内存空间2.3、将 Wi-Fi built-in kernel2.4、优化 Linux 文件系统所占用的内存空间2.5、上层优化2.6、客户定制优化2.6.1、对具体客户的具体产…...

企业数字化转型实施中的挑战与解决方案:架构引领的战略路径
在企业推动数字化转型的过程中,通常会面临复杂的挑战。随着技术的不断演进和业务环境的变化,企业架构(Enterprise Architecture, EA)成为帮助企业应对这些挑战的关键工具。通过提供一个全面的战略蓝图,EA使企业能够在保…...

《数字图像处理基础》学习05-数字图像的灰度直方图
目录 一,数字图像的数值描述 1,二值图像 2,灰度图像 3,彩色图像 二,数字图像的灰度直方图 一,数字图像的数值描述 在之前的学习中,我知道了图像都是二维信息&…...

【漏洞复现】某平台-QRcodeBuildAction-LoginSSO-delay-mssql-sql注入漏洞
《Java代码审计》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484219&idx1&sn73564e316a4c9794019f15dd6b3ba9f6&chksmc0e47a67f793f371e9f6a4fbc06e7929cb1480b7320fae34c32563307df3a28aca49d1a4addd&scene21#wechat_redirect 《Web安全》h…...

Centos安装ZooKeeper教程(单机版)
本章教程介绍,如何在Centos7中,安装ZooKeeper 3.9.3版本。 一、什么是ZooKeeper ? Apache ZooKeeper 是一个分布式协调服务,用于大型分布式系统中的管理和协调。它为分布式应用提供了一个高性能的通信框架,简化了开发人员在构建复杂分布式系统的任务。ZooKeeper 能够解决一…...

A011-基于SpringBoot的视频点播系统设计与实现
摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装视频点播系统软件来发挥其高效地信息处理的作用,…...

云原生+AI核心技术&最佳实践
以下内容是我在陕西理工大学2023级人工智能专业和网络专业的演讲内容,分享给大家。 各位老师、同学们,大家好啊!能在这里跟大家一起聊聊咱们计算机专业那些事儿,我真的觉得超级兴奋! 首先,自我介绍一下&am…...

【Android】Service
文章目录 1.service2.startService3.bindService4.区别 1.service 在Android开发中,Service 是一个可以在后台长时间运行的组件,用于执行耗时操作或执行那些不需要与用户界面直接交互的任务。Service 不依赖于用户界面,即使用户切换到其他应…...

2-142【软件无线电原理与应用作业】基于matlab的圆形阵列的波束形成进行仿真
【软件无线电原理与应用作业】基于matlab的圆形阵列的波束形成进行仿真,具有14页文档。假设发射信号载频为1GHz,圆形阵列半径为0.8米,在圆周上均匀布置30个阵元。1.画出指向0度的方向图。2.如果目标在0度,有一不相干的干扰信号在3…...

在目录中按扩展名分类文件(python学习)(11.1)
# -*- coding:utf-8 -*- # FileName :SortBySuffix.py # Time :2024/11/1 15:13 # Author :liyiwei# Python脚本按扩展名在目录中对文件进行排序 import os from shutil import movedef sort_files(directory_path):# 遍历指定目录下的所有文件和文件夹for filename i…...

【网络安全 | 漏洞挖掘】逻辑漏洞+无限制爆破实现业务瘫痪
未经许可,不得转载。 文章目录 前言正文前言 目标:target.com,是一个为设计团队服务的工作平台。 该程序允许用户创建账户并组建团队,指定的领导者担任管理员。团队类型包括: 1、免费团队:限于一个项目,最多三份文件。 2、学生团队:项目和文件无限制,学生可免费获…...

【WRF工具】MPAS(多尺度预测模型)-输出WRF初始和横向边界条件
【WRF工具】MPAS(多尺度预测模型)-输出WRF初始和横向边界条件 MPAS概述模型概述主要特点 使用MPAS输出WRF初始和横向边界条件参考 从WPS的v3.9版本开始,metgrid.exe程序能够从 跨尺度预测模型(The Model for Prediction Across Sc…...

分数阶傅里叶变换与信息熵怎么用于信号处理?
天马行空的理解与思考方式:分数阶傅里叶变换与信息熵怎么用于信号处理? ChiX-Y 快速学习,快速尝试,快速失败 已关注 35 人赞同了该文章 这篇文章希望能写的有趣,同时有质量,学习就是要多维度多角度&…...

web3.0 开发实践
优质博文:IT-BLOG-CN 一、简介 Web3.0也称为去中心化网络,是对互联网未来演进的一种概念性描述。它代表着对现有互联网的下一代版本的设想和期望。Web3.0的目标是通过整合区块链技术、分布式系统和加密技术等新兴技术,构建一个更加去中心化…...

【华为HCIP实战课程三十】中间到中间系统协议IS-IS路由渗透及TAG标识详解,网络工程师
一、路由泄露 1、默认情况Level 1不会学到Level2的明细路由,L2可以学到L1的明细路由 2、FIB数据转发,路由负载,通过随机数据中的五元组hash,hash值决定数据走哪条链路 R1设备ping和telnet通过抓包查看走的都是S1/0/0接口 抓包进行过滤;ip.a…...

大模型论文精华-20241104
工具而不是对等:框架如何影响人们对 Teams 中 AI 代理的看法 研究问题 随着人工智能技术的发展及其在团队环境中日益广泛的应用,人们对于如何理解和评价AI代理的态度和看法变得尤为重要。该研究关注于探讨不同框架下人们对AI代理的感知差异,…...

mac ssh 连接 linux 服务器
生成 SSH 密钥对 打开终端: 你可以通过 Spotlight 搜索 “Terminal” 打开终端。 生成密钥对: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 手动复制公钥(可选) 如果 ssh-copy-id 命令不可用࿰…...