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

Linux进程间通信:【目的】【管道】【匿名管道】【命名管道】【System V 共享内存】

目录

一.进程间通信目的

二.管道

三.匿名管道

3.1用fork来共享管理管道

3.2站在文件描述符角度-深度理解管道 

3.3内核角度 

 3.4管道样例

3.4.1测试管道读写

3.4.2代码

解决方案1:倒着关闭:

解决方案2: 只让父进程一个人指向写端

 四.命名管道

4.1创建命名管道

4.2命名管道与匿名管道的区别

 4.2命名管道的打开规则

 4.3用命名管道实现server&client通信

五.system V 共享内存

5.1共享内存示意图

 5.2共享内存函数

 5.3代码


一.进程间通信目的

• 数据传输:一个进程需要将它的数据发送给另一个进程

• 资源共享:多个进程之间共享同样的资源。

• 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进 程终止时要通知父进程)。

• 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够 拦截另⼀个进程的所有陷入和异常,并能够及时知道它的状态改变。

二.管道

• 管道是Unix中最古老的进程间通信的形式。

• 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”  

三.匿名管道

#include <unistd.h>
功能:创建⼀⽆名管道
原型int pipe(int fd[2]);
参数
fd:⽂件描述符数组,其中fd[0]表⽰读端, fd[1]表⽰写端
返回值:成功返回0,失败返回错误代码

 

3.1用fork来共享管理管道

3.2站在文件描述符角度-深度理解管道 

3.3内核角度 

 3.4管道样例

3.4.1测试管道读写

第一种情况:

写慢读快:

#include<iostream>
#include<cstdio>
#include<unistd.h>
#include<cstring>
#include <sys/types.h>
#include <sys/wait.h>void ChildWrite(int wfd)
{char buffer[1024];int cnt=0;while(true){snprintf(buffer,sizeof(buffer),"I am child,pid:%d,cnt:%d",getpid(),cnt++);write(wfd,buffer,strlen(buffer));sleep(1);}
}void FatherRead(int rfd)
{char buffer[1024];while(true){//buffer[0]=0;ssize_t n=read(rfd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;std::cout<<"child say:"<<buffer<<std::endl;}}
}int main()
{int fds[2]={0};int n=pipe(fds);if(n<0){std::cerr<<"pipe error"<<std::endl;return 1;}pid_t id=fork();if(id == 0){//childclose(fds[0]);ChildWrite(fds[1]);close(fds[1]);exit(0);}//fatherclose(fds[1]);FatherRead(fds[0]);waitpid(id,nullptr,0);close(fds[0]);return 0;
}

根据代码可以看出,写端每隔一秒就写一段话,读端我们没有继续睡眠等待,这里读的速度是很快的,所以在后面read的时候就会阻塞等待,读也就是会一直卡在read里,等待子进程继续写。

第二种情况:

写快读慢:

两者切换一下顺序,写端不再睡眠,读端睡眠一秒:

这里一下就把管道文件写满了,写就会阻塞等待,等待读端来读。 

第三种情况: 

写关,继续读:

 如果父进程不加break跳出循环的就会一直读(不会阻塞):

read会读到返回值为0,表示文件结尾。 

第四种情况: 

读关闭,写继续:

也就是说子进程只写,但是没有人读,这种情况没有任何意义。因为通信就是要进行交互的。OS不会做没有意义的事情,所以会杀死进程,然后发送异常信号。

 我们把读端关掉之后,OS会直接杀死子进程,因为子进程的写端没有意义了,信号是13:

3.4.2代码

Main.cc

#include"ProcessPool.hpp"
int main()
{//构建通信管道ProcessPool pp(gdefaultnum);pp.Creat();// pp.Debug();int task_node=0;int cnt=10;while (cnt--){//1.选择一个信道//pp.PushTask(task_node++);pp.Run();sleep(1);}pp.Stop();//sleep(100);return 0;
}

ProcessPool.hpp

#ifndef __PROCESS_POOL_HPP_
#define __PROCESS_POOL_HPP_#include <iostream>
#include <vector>
#include <unistd.h>
#include <cstdlib>
#include <sys/wait.h>
#include "Task.hpp"
class Channel
{
public:Channel(int fd, pid_t id) : _wfd(fd), _subid(id){_name = "channel-" + std::to_string(_wfd) + "-" + std::to_string(_subid);}void Send(int code){int n = write(_wfd, &code, sizeof(code));(void)n;}void Close(){close(_wfd);}void Wait(){pid_t rid = waitpid(_subid, nullptr, 0);(void)rid;}int GetWfd() { return _wfd; }pid_t GetSubid() { return _subid; }std::string GetName() { return _name; }~Channel() {}private:int _wfd;pid_t _subid;std::string _name;
};class ChannelManager
{
public:ChannelManager() : _next(0){}void InsertChannel(int wfd, pid_t subid) // 插入新创建的管道,管理起来{Channel c(wfd, subid);_channels.push_back(c);}Channel &Select() // 选择一个管道工作{auto &c = _channels[_next];_next++;_next %= _channels.size();return c;}void PrintChannel(){for (auto &channel : _channels){std::cout << channel.GetName() << std::endl;}}void StopProcess(){for (auto &channel : _channels){channel.Close();std::cout << "关闭:" << channel.GetName() << std::endl;}}void WaitSubProcess(){for (auto &channel : _channels){channel.Wait();std::cout << "回收:" << channel.GetName() << std::endl;}}~ChannelManager() {}private:std::vector<Channel> _channels;int _next;
};const int gdefaultnum = 5; // 多少个管道
class ProcessPool
{
public:ProcessPool(int num) : _process_num(num){_tm.Register(PrintLog);_tm.Register(DownLoad);_tm.Register(UpLoad);}void Work(int rfd){while (true){// std::cout<<"我是子进程,我的rfd是:"<<rfd<<std::endl;// sleep(5);int code = 0;ssize_t n = read(rfd, &code, sizeof(code));if (n > 0){if (n != sizeof(code)) // 读的不规范,上去重新读{continue;}std::cout << "子进程[" << getpid() << "]收到任务码:" << code << std::endl;// 执行任务_tm.Execute(code);}else if (n == 0) // 写端关闭{std::cout << "子进程退出" << std::endl;break;}else{std::cout << "读取错误" << std::endl;break;}}}bool Creat(){for (int i = 0; i < gdefaultnum; i++){// 1.创建管道int pipefd[2] = {0};int n = pipe(pipefd);if (n < 0)return false; // 管道建立失败// 2.创建子进程pid_t subid = fork(); // 子读父写if (subid == 0){// 子进程// 3.关闭不需要的文件描述符close(pipefd[1]);Work(pipefd[0]); // 这是子进程需要做的工作close(pipefd[0]);exit(0);}else if (subid > 0){// 父进程// 3.关闭不需要的文件描述符close(pipefd[0]);_cm.InsertChannel(pipefd[1], subid); // 构建一个信道}else{// 失败return false;}}return true;}void Debug(){_cm.PrintChannel();}void Run(){// 0.选择一个任务int taskcode = _tm.Code();// 1.选择一个信道(子进程),必须负载均衡auto &c = _cm.Select();std::cout << "选择了一个子进程:" << c.GetName() << std::endl;// 2.发送任务c.Send(taskcode);std::cout << "发送一个任务码" << taskcode << std::endl;}void Stop(){// 关闭父进程所有的wfd,不再写入,子进程读的时候会读到文件末尾,我们的else if(n==0) 后面有break_cm.StopProcess();// 回收所有子进程_cm.WaitSubProcess();}~ProcessPool() {}private:ChannelManager _cm;int _process_num;TaskManager _tm;
};#endif

Task.hpp

#pragma once
#include<iostream>
#include<vector>
#include<ctime>
typedef void(*task_t)();void PrintLog()
{std::cout<<"我是一个打印日志的任务"<<std::endl;
}
void DownLoad()
{std::cout<<"我是一个下载的任务"<<std::endl;
}
void UpLoad()
{std::cout<<"我是一个上传的任务"<<std::endl;
}class TaskManager
{
public:TaskManager(){srand(time(nullptr));}void Register(task_t t){_tasks.push_back(t);}int Code(){return rand()%_tasks.size();}void Execute(int code){if(code>=0&&code<_tasks.size()){_tasks[code]();}}~TaskManager(){}
private:std::vector<task_t> _tasks;
};

上面的代码有一个问题

如果把关闭和回收放到一起,上面的是分开放的,这是没问题的,那么下面的这种写法会有什么问题?

等待十秒之后,我们预料的关闭和回收并没有被打印出来:

 我们知道每一个进程都有一个文件描述符表,其中0,1,2是被默认打开的标准输入和标准输出,当我们在创建管道文件的时候,在文件描述符表中就会写入假设是3和4。父进程在进行创建子进程的时候,子进程会继承父进程的文件描述符表。

上面的代码。在创建子进程的时候,子进程会继承父进程的文件描述符表,所以子进程的文件描述符表需要关闭写端,父进程需要关闭读端。这是第一次进入循环。

第二次循环,再次创建管道文件,因为父进程的3已经被关闭了,所以重新分配的话会分配3和5,后面依然会关闭3,此时就要创建新的子进程了,那么这个子进程有了上面的经验,这个子进程就会进程父进程所有的东西,包括上面父进程的4。此时指向第一个struct file文件的指针会多一个,它的引用计数就会变成2.

以此类推的话,创建5个管道的话就会有5个指针指向第一个管道文件,在我们关闭第一个文件的时候它的引用计数会减为4,所以父进程在close的时候只是引用计数减一,并没有确切的关闭这个文件,所以在进入后面的代码的时候,子进程会阻塞等待wait。就会卡住在第一个循环里。

 怎么解决?

解决方案1:倒着关闭:
    void CloseAndWait(){// for (auto &channel : _channels)// {//     channel.Close();//     std::cout << "关闭:" << channel.GetName() << std::endl;//     channel.Wait();//     std::cout << "回收:" << channel.GetName() << std::endl;// }for(int i=_channels.size() - 1;i>=0;i--){_channels[i].Close();std::cout << "关闭:" << _channels[i].GetName() << std::endl;_channels[i].Wait();std::cout << "回收:" << _channels[i].GetName() << std::endl;}}

解决方案2: 只让父进程一个人指向写端

上面的方法并不好,虽然解决了问题,这里我们希望只让父进程的写端指向我们的文件,所以我们在每一次的创建新的子进程的时候,让子进程把它的兄弟进程的写端给关掉,根本不增加引用计数的机会。

我们只需要在manager里添加一个方法:

    void CloseAll(){for(auto &channel:_channels){channel.Close();}}

之后运行这个: 

就没有问题了:

在子进程里加上这个方法就行了: 

比较细节的一点就是,注意我们写CloseAll函数的时候,是把里面的所有的写端都给关闭了。因为有写时拷贝的缘故,这里并不会把父进程的写端给关闭掉。比如第一次循环,_cm进程池里就什么都没有,因为创建子进程时就立刻继承了父进程。 

 四.命名管道

• 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。

• 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名 管道。

• 命名管道是一种特殊类型的文件

4.1创建命名管道

命令行创建管道:

 命名管道也可以从程序里创建,相关函数有:

int mkfifo(const char *filename,mode_t mode);

4.2命名管道与匿名管道的区别

• 匿名管道由pipe函数创建并打开。

• 命名管道由mkfifo函数创建,打开用open 

• FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些 工作完成之后,它们具有相同的语义。

 4.2命名管道的打开规则

• 如果当前打开操作是为读而打开FIFO时 

◦ O_NONBLOCK  disable:阻塞直到有相应进程为写而打开该FIFO

◦ O_NONBLOCK  enable:立刻返回成功

• 如果当前打开操作是为写而打开FIFO时 

◦ O_NONBLOCK  disable:阻塞直到有相应进程为读而打开该FIFO

◦ O_NONBLOCK  enable:立刻返回失败,错误码为ENXIO

 4.3用命名管道实现server&client通信

server.cc

#include "comm.hpp"
// int main()
// {
//     umask(0);
//     //新建管道
//     int n=mkfifo(FIFO_FILE,0666);//运行时先运行这一个,因为要创建管道
//     if(n!=0)
//     {
//         std::cerr<< "mkfifo error"<<std::endl;
//         return 1;
//     }
//     std::cout<<"make fifo success"<<std::endl;//     //打开
//     //write方没有执行open的时候,read方就要在open内部阻塞
//     //直到有人把管道文件打开了,open才会返回
//     //这也就是为什么虽然我们创建管道文件成功了,但是后面没有打印open fifo success的原因
//     int fd=open(FIFO_FILE,O_RDONLY);
//     if(fd<0)
//     {
//         std::cerr<< "open fifo error"<<std::endl;
//         return 2;
//     }
//     std::cout<<"open fifo success"<<std::endl;//     //读
//     char buffer[1024];
//     while (true)
//     {
//         int num=read(fd,buffer,sizeof(buffer)-1);
//         if(num>0)
//         {
//             buffer[num]=0;
//             std::cout<<"client say:"<<buffer<<std::endl;
//         }
//         else if(num==0)
//         {
//             std::cout<<"if client quit,server quit too"<<std::endl;
//             break;
//         }
//         else
//         {
//             std::cout<<"read error"<<std::endl;
//             break;
//         }
//     }
//     close(fd);//     //删除命名管道
//     n = unlink(FIFO_FILE);
//     if(n == 0)
//     {
//         std::cout<<"remove fifo success"<<std::endl;
//     }
//     else
//     {
//         std::cout<<"remove fifo failed"<<std::endl;
//     }//     return 0;
// }int main()
{// 创建一个类来实现NamedFifo fifo(PATH, FILENAME);//下面进行文件操作FileOper readfile(PATH,FILENAME);readfile.OpenForRead();readfile.Read();readfile.Close();return 0;
}

client.cc

// #include<iostream>
// #include<sys/types.h>
// #include<sys/stat.h>
// #include<string>
// #include<fcntl.h>
// #include<unistd.h>
#include"comm.hpp"
// int main()
// {
//     int fd=open(FIFO_FILE,O_WRONLY);
//     if(fd<0)
//     {
//         std::cerr<< "open fifo error"<<std::endl;
//         return 2;
//     }
//     pid_t id=getpid();
//     int cnt=1;
//     while (true)
//     {
//         std::cout<<"please Enter#";
//         std::string message;
//         //std::cin>>message;
//         std::getline(std::cin,message);
//         message+=(",message number:"+std::to_string(cnt++)+"["+std::to_string(id)+"]");//         int n=write(fd,message.c_str(),message.size());
//         if(n>0)
//         {
//         }
//     }//     close(fd);
//     return 0;
// }int main()
{FileOper writefile(PATH,FILENAME);writefile.OpenForWrite();writefile.Write();writefile.Close();return 0;
}

comm.hpp

#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#define FIFO_FILE "fifo"#define FILENAME "fifo"
#define PATH "."class NamedFifo
{
public:NamedFifo(const std::string &path, const std::string &name): _path(path), _name(name){_fifoname = _path + "/" + _name;umask(0);// int n = mkfifo(FIFO_FILE, 0666);int n = mkfifo(_fifoname.c_str(), 0666);if (n < 0){std::cerr << "mkfifo error" << std::endl;}else{std::cerr << "mkfifo success" << std::endl;}}~NamedFifo(){int n = unlink(_fifoname.c_str());if (n == 0){std::cout << "remove fifo success" << std::endl;}else{std::cout << "remove fifo failed" << std::endl;}}private:std::string _path;std::string _name;std::string _fifoname;
};class FileOper
{
public:FileOper(const std::string &path, const std::string &name): _path(path), _name(name), _fd(-1){_fifoname = _path + "/" + _name;}void OpenForRead(){_fd = open(FIFO_FILE, O_RDONLY);if (_fd < 0){std::cerr << "open fifo error" << std::endl;return;}std::cout << "open fifo success" << std::endl;}void OpenForWrite(){_fd = open(FIFO_FILE, O_WRONLY);if (_fd < 0){std::cerr << "open fifo error" << std::endl;return;}std::cout << "open fifo success" << std::endl;}void Write(){pid_t id = getpid();int cnt = 1;while (true){std::cout << "please Enter#";std::string message;// std::cin>>message;std::getline(std::cin, message);message += (",message number:" + std::to_string(cnt++) + "[" + std::to_string(id) + "]");int n = write(_fd, message.c_str(), message.size());if (n > 0){}}}void Read(){char buffer[1024];while (true){int num = read(_fd, buffer, sizeof(buffer) - 1);if (num > 0){buffer[num] = 0;std::cout << "client say:" << buffer << std::endl;}else if (num == 0){std::cout << "if client quit,server quit too" << std::endl;break;}else{std::cout << "read error" << std::endl;break;}}}void Close(){if (_fd > 0)close(_fd);}~FileOper(){}private:std::string _path;std::string _name;std::string _fifoname;int _fd;
};

五.system V 共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

5.1共享内存示意图

 5.2共享内存函数

shmget:

参数

key:这个共享内存段名字

size:共享内存大小 

shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的取值为

IPC_CREAT:共享内存不存在,创建并返回;共享内存已存在,获取并返回。

取值为IPC_CREAT | IPC_EXCL:共享内存不存在,创建并返回;共享内存已存在,出错返回。

返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1  

先来认识一下这个函数:

server.cc:

#include"comm.hpp"int main()
{Shm shm;shm.Create();return 0;
}

client.cc:

#include"comm.hpp"
int main()
{return 0;
}

comm.hpp:

#include <iostream>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>const int gdefaultid = -1;
const int gsize = 4096;
const std::string pathname = ".";
const int projid = 0x66;#define ERR_EXIT(m)         \do                      \{                       \perror(m);          \exit(EXIT_FAILURE); \} while (0)class Shm
{
public:Shm() : _shmid(gdefaultid), _size(gsize){}//  创建的内存要是全新的内存,注意函数的参数void Create(){key_t k = ftok(pathname.c_str(), projid);if (k < 0){ERR_EXIT("ftok");}printf("key: 0x%x\n", k);_shmid=shmget(k,_size,IPC_CREAT | IPC_EXCL);if(_shmid < 0){ERR_EXIT("shmget");}printf("shmid: %d\n", _shmid);}~Shm(){}private:int _shmid;int _size;
};

编译后运行: 

因为之前已经运行过了,根据shmget函数的个性这里会报错。

ipcs -m可以查看我们已经创建的共享内存:

 这里删除的时候要用shmid(删除和控制共享内存,在用户层我们不能用key,key未来只给内核来区分唯一性,我们用户需要用shmid来管理共享内存):

注意共享内存的生命周期,随内核。


到这里上面是用函数来创建共享内存和用指令查看共享内存的操作。

shmctl函数:

功能:⽤于控制共享内存
原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数shmid:由shmget返回的共享内存标识码cmd:将要采取的动作(有三个可取值)buf:指向⼀个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

 

用指令来删除共享内存是一种方式,这种方式太麻烦了,这里我们就用代码来删:

    void Destroy(){if(_shmid==gdefaultid) return;int n=shmctl(_shmid,IPC_RMID,NULL);if(n>0){printf("shmctl delete shm: %d success\n",_shmid);}else{ERR_EXIT("shmctl\n");}}

 shmctl函数来进行删除。

shmat:

功能:将共享内存段连接到进程地址空间
原型void *shmat(int shmid, const void *shmaddr, int shmflg);
参数shmid: 共享内存标识shmaddr:指定连接的地址shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回⼀个指针,指向共享内存第⼀个节;失败返回-1

 说明:

shmaddr为NULL,核⼼⾃动选择⼀个地址
shmaddr不为NULL且shmflg⽆SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃动向下调整为SHMLBA的整数倍。
公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表⽰连接操作⽤来只读共享内存

 5.3代码

client.cc:

#include"comm.hpp"
int main()
{// Shm shm;// shm.Get();// sleep(5);// //映射到自己的地址空间// shm.Attach();// sleep(5);// shm.Destroy();Shm shm(pathname,projid,USER);char* mem=(char*)shm.VirtualAddr();while(true){printf("%s\n",mem);sleep(1);}return 0;
}

server.cc:

#include"comm.hpp"int main()
{// Shm shm;// shm.Create();// sleep(5);// shm.Attach();// shm.VirtualAddr();// sleep(5);// shm.Destroy();Shm shm(pathname,projid,CREATER);char* mem=(char*)shm.VirtualAddr();for(char c='A';c<='Z';c++){mem[c-'A']=c;sleep(1);}return 0;
}

comm.hpp

#include <iostream>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string>const int gdefaultid = -1;
const int gsize = 4096;
const std::string pathname = ".";
const int projid = 0x66;
const int mode = 0666;
#define CREATER "creater"
#define USER "user"#define ERR_EXIT(m)         \do                      \{                       \perror(m);          \exit(EXIT_FAILURE); \} while (0)class Shm
{// 这里单独封装创建出来的共享内存void CreateHalper(int flg){// key_t k=ftok(pathname.c_str(),projid);// if(k<0)// {//     ERR_EXIT("ftok");// }printf("key: 0x%x\n", _key);_shmid = shmget(_key, _size, flg);if (_shmid < 0){ERR_EXIT("shmget");}printf("shmid: %d\n", _shmid);}//  创建的内存要是全新的内存,注意函数的参数void Create(){CreateHalper(IPC_CREAT | IPC_EXCL | mode);}// 获取共享内存void Get(){CreateHalper(IPC_CREAT); // 这里单独的封装}void Attach(){_start_mem = shmat(_shmid, nullptr, 0);if ((long long)_start_mem < 0){ERR_EXIT("shmat");}printf("attch success\n");}void Destroy(){if (_shmid == gdefaultid)return;int n = shmctl(_shmid, IPC_RMID, NULL);if (n > 0){printf("shmctl delete shm: %d success\n", _shmid);}else{ERR_EXIT("shmctl\n");}}
public:Shm(const std::string &pathname, int projid, const std::string usertype): _shmid(gdefaultid), _size(gsize), _start_mem(nullptr), _usertype(usertype){_key = ftok(pathname.c_str(), projid);if (_key < 0){ERR_EXIT("ftok");}if (usertype == CREATER)Create();else if (usertype == USER)Get();Attach();}int GetSize(){return _size;}// void Destroy()// {//     if (_shmid == gdefaultid)//         return;//     int n = shmctl(_shmid, IPC_RMID, NULL);//     if (n > 0)//     {//         printf("shmctl delete shm: %d success\n", _shmid);//     }//     else//     {//         ERR_EXIT("shmctl\n");//     }// }// void Attach()// {//     _start_mem=shmat(_shmid,nullptr,0);//     if((long long)_start_mem<0)//     {//         ERR_EXIT("shmat");//     }//     printf("attch success\n");// }void *VirtualAddr(){printf("VirtualAddr:%p\n", _start_mem);return _start_mem;}~Shm(){if(_usertype==CREATER)Destroy();}private:int _shmid;int _key;int _size;void *_start_mem;std::string _usertype;
};

现象: 

注意 

相关文章:

Linux进程间通信:【目的】【管道】【匿名管道】【命名管道】【System V 共享内存】

目录 一.进程间通信目的 二.管道 三.匿名管道 3.1用fork来共享管理管道 3.2站在文件描述符角度-深度理解管道 3.3内核角度 3.4管道样例 3.4.1测试管道读写 3.4.2代码 解决方案1&#xff1a;倒着关闭&#xff1a; 解决方案2&#xff1a; 只让父进程一个人指向写端 四…...

Python 自动化:节省时间,更智能地工作

大家好&#xff0c;这里是架构资源栈&#xff01;点击上方关注&#xff0c;添加“星标”&#xff0c;一起学习大厂前沿架构&#xff01; 时间是你最宝贵的资产。如果你花费数小时手动执行重复性任务&#xff0c;那么当 Python 可以为你完成这些任务时&#xff0c;你就是在浪费时…...

StarRocks的执行计划和Profile

文章目录 一、执行计划和Profile相关脚本二、如何分析查询1、概念了解2、Query Plan①查看 Query Plan②分析 Query Plan 3、Query hint4、Query Profile①启用 Query Profile②查看 Query Profile③分析 Query Profile 一、执行计划和Profile相关脚本 命令功能ANALYZE PROFIL…...

【设计模式】过滤器模式

过滤器顾名思义&#xff0c;定义一些过滤规则&#xff0c;将符合要求的内容筛选&#xff0c;就比如过滤不同大小或者不同颜色的水果&#xff0c;需要颜色和大小过滤器&#xff0c;筛选条件独立为对象&#xff0c;可以通过灵活组合形成过滤链条。避免大量使用判断语句。 案例代…...

Jenkins插件安装失败如何解决

问题&#xff1a;安装Jenkins时候出现插件无法安装的情况。 测试环境&#xff1a; 操作系统&#xff1a;Windows11 Jenkins&#xff1a;2.479.3 JDK&#xff1a;17.0.14&#xff08;21也可以&#xff09; 解决办法一&#xff1a; 更换当前网络&#xff0c;局域网、移动、联通…...

GO语言杂记(文章持续更新)

1、MAIN冲突 在一个文件夹下有两个go文件同时写了main函数&#xff0c;将会报错&#xff0c;main函数只能在main包中。 实则不然&#xff0c;有些环境下并不会报错。 2、gofmt命令---自动对齐 命令作用&#xff1a;将go文件代码自动缩进。 gofmt -w escapecharprac.go...

VUE如何前端控制及动态路由详细讲解

在Vue.js中&#xff0c;前端控制通常指的是通过Vue的响应式系统、组件化、路由、状态管理等技术来实现对前端应用的控制和管理 一、前端路由控制基础 使用 vue-router 管理路由&#xff0c;通过路由守卫和动态添加路由实现权限控制。 1. 安装和配置 npm install vue-router…...

【区块链安全 | 第九篇】基于Heimdall设计的智能合约反编译项目

文章目录 背景目的安装1、安装 Rust2、克隆 heimdall-dec3、编译 heimdall-dec4、运行 heimdall-dec 使用说明1、访问 Web 界面2、输入合约信息3、查看反编译结果 实战演示1、解析普通合约2、解析代理合约 背景 在区块链安全研究中&#xff0c;智能合约的审计和分析至关重要。…...

day1_Flink基础

文章目录 Flink基础今日课程内容目标为什么要学Flink技术更新迭代市场需求 流式计算批量计算概念特点 批量计算的优势和弊端流式计算生活中流场景流式计算的概念 Flink简介Flink历史Flink介绍 Flink架构体系已学过的框架技术Flink架构 Flink集群搭建Flink的集群模式Standalone模…...

43页可编辑PPT | 大数据管理中心设计规划方案大数据中心组织架构大数据组织管理

这份文档是一份关于大数据管理中心规划设计方案的详细报告&#xff0c;涵盖了背景与需求分析、整体规划方案、关键能力实现方案以及实施方案等内容。报告强调大数据在城市治理中的重要性&#xff0c;提出通过构建统一的大数据平台&#xff0c;整合城市各部门数据资源&#xff0…...

JavaScript数据结构

目录 JavaScript数据结构 一、基础数据结构 1. 数组&#xff08;Array&#xff09; 2. 对象&#xff08;Object&#xff09; 二、ES6 高级数据结构 1. Map 2. Set 3. WeakMap 与 WeakSet 三、类型化数组&#xff08;Typed Arrays&#xff09; 四、其他数据结构实现 …...

如何使用 FastAPI 构建 MCP 服务器

哎呀&#xff0c;各位算法界的小伙伴们&#xff01;今天咱们要聊聊一个超酷的话题——MCP 协议&#xff01;你可能已经听说了&#xff0c;Anthropic 推出了这个新玩意儿&#xff0c;目的是让 AI 代理和你的应用程序之间的对话变得更顺畅、更清晰。不过别担心&#xff0c;为你的…...

Js 主线程和异步队列哪个先执行

JavaScript 主线程与异步队列执行顺序详解 JavaScript 是单线程语言&#xff0c;通过事件循环&#xff08;Event Loop&#xff09;机制来处理同步和异步任务。以下是主线程与异步队列的执行顺序解析&#xff1a; 1. 执行顺序基本原则 console.log(1. 主线程同步任务);setTim…...

C#实现HTTP服务器:处理文件上传---解析MultipartFormDataContent

完整项目托管地址&#xff1a;https://github.com/sometiny/http HTTP还有重要的一块&#xff1a;文件上传。 这篇文章将详细讲解下&#xff0c;前面实现了同一个链接处理多个请求&#xff0c;为了方便&#xff0c;我们独立写了一个HTTP基类&#xff0c;专门处理HTTP请求。 ht…...

【hadoop】远程调试环境

根据上一节&#xff0c;我们已经安装完成hadoop伪分布式环境 hadoop集群环境配置_jdk1.8 441-CSDN博客 还没安装的小伙伴可以看看这个帖子 这一节我们要实现使用vscode进行远程连接&#xff0c;并且完成java配置与测试 目录 vscode 配置远程 安装java插件 新建java项目 …...

检索增强生成(RAG):强化 AI 智能体的知识 “武装”

技术点目录 第一章、智能体(Agent)入门第二章、基于字节Coze 构建智能体(Agent)第三章、基于其他平台构建智能体&#xff08;Agent&#xff09;第四章、国内外智能体(Agent)经典案例详解第五章、大语言模型应用开发框架LangChain入门第六章、基于LangChain的大模型API接入第七章…...

使用 Provider 和 GetX 实现 Flutter 局部刷新的几个示例

1. 使用 Provider 实现局部刷新 示例 1&#xff1a;ChangeNotifier Consumer 通过 ChangeNotifier 和 Consumer 实现局部刷新。 import package:flutter/material.dart; import package:provider/provider.dart;void main() {runApp(ChangeNotifierProvider(create: (_) &g…...

notepad++ 正则表达式

注意&#xff1a;Notepad正则表达式字符串最长不能超过69个字符 \ 转义字符 如&#xff1a;要使用 “\” 本身, 则应该使用“\\” \t Tab制表符 注&#xff1a;扩展和正则表达式都支持 \r 回车符CR 注&#xff1a;扩展支持&#xff0c;正则表达式不支持 \n 换行符…...

一起学大语言模型-通过ollama搭建本地大语言模型服务

文章目录 Ollama的github地址链接安装下载需求配置更改安装目录安装更改下载的模型存储位置Ollama一些目录说明日志目录 运行一个模型测试下测试下更改服务监听地址和端口号 Ollama的github地址链接 https://github.com/ollama/ollama 安装 下载 mac安装包下载地址&#xff1…...

webpack配置详解+项目实战

webpack在vue中的配置&#xff0c;适合想重新认知webpack的你 webpack配置-初级配置 1、配置入口和出口文件 2、配置loader 3、配置eslint&#xff08;可组装js、jsx检查工具&#xff09; 4、配置babel&#xff08;将高级的js语法转换成低版本的js语法&#xff09; 5、使用 ht…...

【学习笔记】文件上传漏洞--js验证、mime验证、.user.ini、短标签、过滤、文件头

概念 文件上传漏洞 什么是文件上传漏洞&#xff1f; 文件上传漏洞是指由于程序员在对用户文件上传部分的控制不足或者处理缺陷&#xff0c;而导致的用户可以越过其本身权限向服务器上上传可执行的动态脚本文件。 这里上传的文件可以是木马&#xff0c;病毒&#xff0c;恶意脚…...

经典卷积神经网络LeNet实现(pytorch版)

LeNet卷积神经网络 一、理论部分1.1 核心理论1.2 LeNet-5 网络结构1.3 关键细节1.4 后期改进1.6 意义与局限性二、代码实现2.1 导包2.1 数据加载和处理2.3 网络构建2.4 训练和测试函数2.4.1 训练函数2.4.2 测试函数2.5 训练和保存模型2.6 模型加载和预测一、理论部分 LeNet是一…...

【VM虚拟机ip问题】

我就是我&#xff0c;不一样的烟火。 文章目录 前言一、启动VM虚拟机1. 开启虚拟机2. 输入账号密码登录3. 依次输入指令 二、主机ping地址测试1. ping ip地址-成功 三、安装-MobaXterm_Personal_21.51. 点击Session2. 选择SSH连接3. 输入信息4. 首次进入5. 连接成功 总结 前言 …...

【计算机视觉】YOLO语义分割

一、语义分割简介 1. 定义 语义分割&#xff08;Semantic Segmentation&#xff09;是计算机视觉中的一项任务&#xff0c;其目标是对图像中的每一个像素赋予一个类别标签。与目标检测只给出目标的边界框不同&#xff0c;语义分割能够在像素级别上区分不同类别&#xff0c;从…...

【C++游戏引擎开发】《线性代数》(3):矩阵乘法的SIMD优化与转置加速

一、矩阵乘法数学原理与性能瓶颈 1.1 数学原理 矩阵乘法定义为:给定两个矩阵 A ( m n ) \mathrm{A}(mn) A(mn)和 B ( n p ) \mathrm{B}(np) B(np),它们的乘积 C = A B \mathrm{C}=AB C=AB 是一个 m p \mathrm{m}p mp 的矩阵,其中: C i , j = ∑ k = 1…...

聚焦交易能力提升!EagleTrader 模拟交易系统打造交易成长新路径

在全球市场波动加剧的背景下&#xff0c;交易者面临的挑战已不仅限于技术分析层面。许多交易者在实盘操作中常因情绪干扰导致决策变形&#xff0c;如何构建科学的交易心理与风险控制体系成为行业关注焦点。 国内自营交易考试EagleTrader运用自己研发的模拟交易系统&#xff0c…...

文件分片上传

1前端 <inputtype"file"accept".mp4"ref"videoInput"change"handleVideoChange"style"display: none;">2生成hash // 根据整个文件的文件名和大小组合的字符串生成hash值&#xff0c;大概率确定文件的唯一性fhash(f…...

C#Lambda表达式与委托关系

1. 核心关系图示 A[委托] --> B[提供方法容器] B --> C[Lambda表达式] C --> D[委托实例的语法糖] A --> E[类型安全约束] C --> F[编译器自动生成委托实例] 2. 本质联系 2.1 类型关系 ‌Lambda表达式‌是编译器生成的‌委托实例‌表达式自动匹配符合签名的…...

机器翻译和文本生成评估指标:BLEU 计算公式

&#x1f4cc; BLEU 计算公式 BLEU 主要由**n-gram精确匹配率&#xff08;Precision&#xff09;和长度惩罚&#xff08;Brevity Penalty, BP&#xff09;**组成。 1️⃣ n-gram 精确匹配率 计算不同长度的 n-gram&#xff08;1-gram, 2-gram, ..., n-gram&#xff09;在生成…...

24 python 类

在办公室里&#xff0c;类就像一个部门&#xff08;如销售部、财务部&#xff09;&#xff0c;定义了该部门员工的共同属性&#xff08;姓名、职位&#xff09;和行为&#xff08;处理客户、提交报表&#xff09;。 一、面向对象技术简介 作为一个要入门码农的牛马&#xff0…...

pycharm与python版本

python 3.6-3.9 pycharm 2021版本搭配最好 python 3.8 pycharm 2019版本搭配最好 pycharm各版本下载...

23种设计模式-结构型模式-外观

文章目录 简介问题解决方案示例代码总结 简介 也称&#xff1a;门面模式、Facade。外观是一种结构型设计模式&#xff0c;能为程序库、框架或其他复杂类提供一个简单的接口。 问题 假设你必须在代码中使用某个复杂的库或框架中的众多对象。正常情况下&#xff0c;你需要负责…...

open3d教程 (三)点云的显示

官方文档位置&#xff1a; Visualization - Open3D 0.19.0 documentationhttps://www.open3d.org/docs/release/tutorial/visualization/visualization.html核心方法&#xff1a; o3d.visualization.draw_geometries([几何对象列表]) import open3d as o3dprint("Load …...

node.js、npm相关知识

Node.js 是一个基于 Chrome V8 JavaScript 引擎 构建的开源、跨平台的 JavaScript 运行时环境&#xff0c;主要用于服务器端编程。它允许开发者使用 JavaScript 编写高性能的后端服务&#xff0c;突破了 JavaScript 仅在浏览器中运行的限制。 npm&#xff08;Node Package Man…...

大象如何学会太空漫步?美的:科技领先、To B和全球化

中国企业正处在转型的十字路口。一边是全新的技术、全新的市场机遇&#xff1b;一边是转型要面临的沉重负累和巨大投入&#xff0c;无数中国制造、中国品牌仍在寻路&#xff0c;而有的人已经走至半途。 近日&#xff0c;美的集团交出了一份十分亮眼的2024年财报。数据显示&…...

Go红队开发— 收官工具

文章目录 免责声明个人武器开发美观输出Whois查询反查ip目录扫描子域名爆破被动扫描主动扫描(字典爆破)CDN检测 免责声明 &#x1f4a1; 本博客绝不涉及任何非法用途。 &#x1f4a1; 使用者风险自担&#xff0c;违规后果自负。 &#x1f4a1; 守法为先&#xff0c;技术向善。 …...

Android 应用程序包的 adb 命令

查看所有已安装应用的包名 命令&#xff1a;adb shell pm list packages说明&#xff1a;该命令会列出设备上所有已安装应用的包名。可以通过管道符|结合grep命令来过滤特定的包名&#xff0c;例如adb shell pm list packages | grep com.pm&#xff0c;这将只显示包名中包含co…...

北京南文观点:后糖酒会营销,以战略传播重构品牌信心坐标

第112届全国糖酒会落下帷幕&#xff0c;参展品牌面临一个关键命题。如何在流量洪流中沉淀品牌价值&#xff1f;北京南文&#xff08;全称&#xff1a;南文乐园科技文化&#xff08;北京&#xff09;有限公司&#xff09;认为&#xff0c;糖酒会的结束恰是算法时代品牌认知战的真…...

Qt - findChild

findChild 1. 函数原型2. 功能描述3. 使用场景4. 示例代码5. 注意事项6. 总结 在 Qt 中&#xff0c;每个 QObject 都可以拥有子对象&#xff0c;而 QObject 提供的模板函数 findChild 就是用来在对象树中查找满足特定条件的子对象的工具。下面我们详细介绍一下它的使用和注意事…...

2025年3月个人工作生活总结

本文为 2025年3月工作生活总结。 研发编码 一个curl下载失败问题的记录 问题&#xff1a; 某程序&#xff0c;指定IP和账户密码配置&#xff0c;再使用curl库连接sftp服务器&#xff0c;下载文件。在CentOS系统正常&#xff0c;但在某国产操作系统中失败&#xff0c;需要用命…...

Spring Boot 七种事务传播行为只有 REQUIRES_NEW 和 NESTED 支持部分回滚的分析

Spring Boot 七种事务传播行为支持部分回滚的分析 支持部分回滚的传播行为 REQUIRES_NEW&#xff1a;始终开启新事务&#xff0c;独立于外部事务&#xff0c;失败时仅自身回滚。NESTED&#xff1a;在当前事务中创建保存点&#xff08;Savepoint&#xff09;&#xff0c;可局部…...

NVIDIA工业设施数字孪生中的机器人模拟

工业设施数字孪生中的机器人模拟 文章目录 工业设施数字孪生中的机器人模拟数字孪生技术的价值NVIDIA Omniverse平台工业机器人仿真的核心组件示例一&#xff1a;使用Isaac Sim创建基本机器人场景示例二&#xff1a;机器人运动规划和轨迹执行示例三&#xff1a;传感器集成与感知…...

docker安装jenkins

docker安装jenkins 1.安装javaJDK 服务器安装javaJDK ,因为我的服务器是直接集成了宝塔面板&#xff0c;我就直接从宝塔面板去安装JDK 最好安装17的JDK&#xff0c;因为后面会安装jenkins&#xff0c;需要17的版本 1.2查看安装是否完成 java --version 安装成功如下&#x…...

量子计算与人工智能融合的未来趋势

最近研学过程中发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击链接跳转到网站人工智能及编程语言学习教程。读者们可以通过里面的文章详细了解一下人工智能及其编程等教程和学习方法。 在当今科技飞速发展…...

人工智能在生物医药-新版ChatGPT-4o辅助一键生成机制图

新版ChatGPT-4o辅助一键生成机制图 作为一位生物医学教授专家&#xff0c;我将基于PubMed最新研究和科研大数据信息&#xff0c;遵循您的要求&#xff0c;一步一步进行思考和预测。 核心问题&#xff1a;乳酸化修饰促进肾透明细胞癌&#xff08;ccRCC&#xff09;恶性进展的机…...

支持 MCP 协议的开源 AI Agent 项目

关键要点 研究表明&#xff0c;目前有多个开源 AI Agent 项目支持 MCP 协议&#xff0c;包括 ChatMCP、HyperChat、5ire 和 Cherry Studio 等。这些项目主要用于聊天或桌面助手&#xff0c;允许通过 MCP 协议连接外部数据和工具。MCP 协议是 2024 年 11 月由 Anthropic 开源的…...

JavaRedis和数据库相关面试题

JavaRedis面试题 1. Redis是什么以及Redis为什么快&#xff1f; ​ Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的内存键值数据库&#xff0c;支持多种数据结构&#xff08;如字符串、哈希、列表、集合等&#xff09;&#xff0c;并提供持久化、复制、…...

Android开发RxJava3延迟操作

Android开发RxJava3延迟操作 直接上代码&#xff1a; /*** param timeMillis 毫秒单位* desc : 延迟多少毫秒操作,* 注&#xff1a;它和Activity生命周期绑定&#xff0c;界面关闭了不会再执行delayTodoListener.delayTodo()* author : congge on 2021-03-25 15:31**/p…...

android 设置状态栏背景

一 让activity ui界面和手机状态栏一样的背景 要让 Activity 的 UI 界面和手机状态栏具有相同的背景颜色&#xff0c;并且能够随着深色模式和非深色模式的切换而改变颜色&#xff0c;你可以按照以下步骤操作&#xff1a; 1. 让 Activity 和 状态栏背景颜色一致 使用 window.s…...

vue 常见优化手段

文章目录 vue常见的优化手段前言使用key(避免明明相同的dom,每次更新都要重新生成)使用冻结的对象(避免无意义的响应式数据)使用函数式组件(减少vue组件实例的生成)vue3vue2使用计算属性(减少数据计算的次数)非实时绑定的表单项(避免表单过多触发监听事件)保持对象的…...