【Linux进程通信 —— 管道】

Linux进程通信 —— 管道

  • 进程间通信介绍
    • 进程间通信的概念
    • 进程间通信的目的
    • 进程间通信的本质
    • 进程间通信的分类
  • 管道
    • 什么是管道
    • 匿名管道
        • 匿名管道的原理
        • pipe
        • 用fork来共享管道原理
        • 站在文件描述符角度-深度理解管道
        • 站在内核角度-管道本质
        • 管道读写规则
        • 管道的特点
        • 管道的四种特殊情况
        • 管道的大小
    • 命名管道
        • 命名管道的原理
        • 创建一个命名管道
        • 命名管道的打开规则
        • 用命名管道实现 serve&client 通信
        • 命名管道和匿名管道的区别

进程间通信介绍

进程间通信的概念

在 Linux 中,进程间通信(IPC,Inter-Process Communication)是指 不同进程之间交换数据和信息的一种机制。 这种通信可以是在同一台计算机上的不同进程之间,也可以是在不同计算机之间的进程之间。

进程间通信的目的

进程间通信(IPC) 的主要目的是实现不同进程之间的数据交换和协作,从而实现更复杂的任务和功能。以下是进程间通信的几个主要目的:

  1. 数据交换:进程间通信允许不同进程之间交换数据和信息。这些数据可以是简单的消息、文件、共享内存中的数据等。通过数据交换,不同进程可以共享信息,协作完成复杂的任务。

  2. 协作:进程间通信使得不同进程能够协同工作,共同完成某些任务。例如,一个进程负责生成数据,另一个进程负责处理数据,它们之间通过通信来协调工作。

  3. 资源共享:进程间通信可以实现共享资源,如共享内存、文件、设备等。多个进程可以同时访问和操作共享资源,从而提高系统的利用率和效率。

  4. 进程同步:进程间通信可以实现进程之间的同步操作,确保它们按照一定的顺序执行。例如,使用信号量来控制对共享资源的访问,或者使用消息队列来实现进程间的同步消息传递。

  5. 并发控制:进程间通信可以实现对并发访问的控制,避免竞态条件和数据不一致性。例如,通过信号量或互斥锁来控制对共享资源的访问,以确保数据的一致性和可靠性。

总的来说,进程间通信的目的是实现不同进程之间的数据交换、协作和同步,从而实现更复杂、更高效的系统功能和任务。它是操作系统中的重要概念,对于实现多任务处理、并发编程和分布式系统等方面具有重要意义。

进程间通信的本质

进程间通信的本质就是,让不同的进程看到同一份资源。

在一个正在运行的操作系统中,存在许多相互独立的进程,它们需要相互协作以确保操作系统的正常运行。为了实现这种协作,这些进程通过进程间通信来共享资源,即让不同的进程能够访问并操作同一份共享资源,从而实现数据共享和协作。
在这里插入图片描述

一个简单的例子是父子进程间的通信。假设父进程需要向子进程发送一个命令,子进程收到命令后执行相应的操作,并将结果返回给父进程。这里的通信可以通过管道、消息队列或共享内存来实现。父进程向管道写入命令,子进程从管道中读取命令并执行,然后将结果写回管道,父进程再从管道中读取结果。这样,父子进程之间就实现了简单的数据交换和通信。

进程间通信的分类

管道

  • 匿名管道pipe
  • 命名管道

System V IPC

  • System V消息队列
  • SystemV 共享内存
  • SystemV信号量

POSIXIPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

管道

什么是管道

管道(Pipe) 是一种用于进程间通信的机制,允许一个进程的输出直接成为另一个进程的输入。它主要用于在父进程和子进程之间或者在同时运行的两个进程之间进行通信。管道可以分为匿名管道(Anonymous Pipe)命名管道(Named Pipe) 两种类型。
在这里插入图片描述

匿名管道

匿名管道的原理

匿名管道用于进程间通信,且仅限于本地父子进程之间的通信。

匿名管道是一种单向通信管道,只能在相关的父子进程间使用。它是通过调用pipe()系统调用创建的,具有读端写端 。匿名管道的数据流向是单向的,即数据只能从写端流入到读端。

在这里插入图片描述
注意:操作系统维护父子进程共享的文件资源时,并不会在父子进程间进行数据的写时拷贝。管道使用文件的概念,但操作系统并不会将进程通信的数据刷新到磁盘上,因为这样做既会涉及到IO操作从而降低效率,也是没有必要的。换言之,这些文件通常只存在于内存中,而不会被写入到磁盘上。

pipe

pipe函数是Unix/Linux操作系统提供的一个系统调用,用于创建一个匿名管道。它的原型如下:

#include <unistd.h>int pipe(int pipefd[2]);
  • 参数pipefd是一个包含两个整数元素的数组,用于返回新创建的管道的文件描述符。pipefd[0]用于从管道中读取数据,pipefd[1]用于向管道中写入数据。

  • 调用 成功 时,返回值为 0 ;调用 失败 时,返回值为 -1 ,并设置全局变量errno来指示错误类型。

用fork来共享管道原理

在创建匿名管道实现父子进程间通信的过程中,需要pipe函数和fork函数搭配使用,具体步骤如下:

1、父进程调用pipe函数创建管道。
在这里插入图片描述
2、父进程创建子进程
在这里插入图片描述

3、父进程关闭写端,子进程关闭读端。

在这里插入图片描述

  1. 管道只能够进行单向通信,因此当父进程创建完子进程后,需要确认父子进程谁读谁写,然后关闭相应的读写端。
  2. 从管道写端写入的数据会被内核缓冲,直到从管道的读端被读取。
站在文件描述符角度-深度理解管道

在这里插入图片描述

站在内核角度-管道本质

在这里插入图片描述

管道读写规则

pipe2函数与pipe函数类似,同样用于创建一个匿名管道。它的原型如下:

#include <unistd.h>int pipe2(int pipefd[2], int flags);

pipe 函数不同的是,pipe2 函数允许通过参数 flags 设置一些附加的选项,以控制管道的行为。常用的选项包括:

  • O_CLOEXEC:在父进程执行 fork 创建子进程时,子进程会自动关闭父进程中不需要的文件描述符。这可以通过设置 O_CLOEXEC 标志来实现,以确保在子进程中关闭管道的文件描述符。
  • O_NONBLOCK:设置管道的读取和写入操作为非阻塞模式。在非阻塞模式下,读取和写入操作会立即返回,不会等待直到管道中有数据可读或有空间可写。

这两个选项可以通过按位或运算组合使用。例如,要创建一个非阻塞的管道并在父进程中关闭不需要的文件描述符,可以将 flags 设置为 O_NONBLOCK | O_CLOEXEC

pipe2 函数的返回值和 pipe 函数类似,成功时返回0,失败时返回-1,并设置全局变量 errno 来指示错误类型。

管道的特点
  • 单向性:管道是单向的,只能用于单向数据流的传输。通常有两种类型的管道:单向管道和双向管道。单向管道只能实现单向数据流的传输,而双向管道则可以实现双向数据流的传输。

  • 半双工:管道是半双工的,即同一时间只能有一个方向的数据流动。在一个管道中,数据只能单向流动,要么从父进程流向子进程,要么从子进程流向父进程。

  • 适用于有亲缘关系的进程:管道通常用于具有亲缘关系的进程之间进行通信,例如父子进程之间。因为管道是通过 fork 系统调用创建的,只有具有亲缘关系的进程才能共享同一个管道。

  • 有限缓冲区:管道具有有限的缓冲区,因此在读取端没有读取数据时,写入端会被阻塞。当管道的缓冲区已满时,写入端也会被阻塞,直到缓冲区有足够的空间来容纳写入的数据。

  • 不支持随机访问:管道是顺序访问的,不支持随机访问。也就是说,只能按照数据写入的顺序依次读取数据,不能直接定位到某个位置读取数据。

  • 自动关闭:在进程终止时,管道会自动关闭。当所有指向管道的文件描述符都被关闭时,管道将被系统自动释放。

管道的四种特殊情况

管道的四种特殊情况:

  1. 写端进程不写,读端进程一直读

    • 情况描述:写端进程不向管道写入数据,但读端进程一直尝试读取数据。
    • 表现:读端进程会被挂起,直到管道中有数据可读。
    • 解释:读端进程在读取数据时,如果管道中没有数据可读,则会被挂起,直到有数据可供读取。
  2. 读端进程不读,写端进程一直写

    • 情况描述:写端进程不断向管道写入数据,但读端进程没有读取数据。
    • 表现:当管道被写满后,写端进程会被挂起,直到管道中的数据被读取后才会继续写入。
    • 解释:当写端不断写入数据,而读端没有读取时,管道会被写满。此时写端进程会被挂起,直到读端读取数据释放空间。
  3. 写端进程写完后关闭写端

    • 情况描述:写端进程将数据写入管道后关闭了写端。
    • 表现:读端进程将管道中的数据读完后,继续执行后续代码,而不会被挂起。
    • 解释:写端进程关闭写端后,读端进程读取完管道中的数据后会收到EOF,继续执行后续代码。
  4. 读端进程关闭读端

    • 情况描述:读端进程关闭了读端,但写端进程仍在向管道写入数据。
    • 表现:操作系统会将写端进程杀掉。
    • 解释:当读端进程关闭读端后,写端继续向管道写入数据,此时操作系统会向写端进程发送信号,通知其管道已关闭,然后将写端进程杀掉。

示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {int pipefd[2];pid_t pid;char buffer[10];// 创建管道if (pipe(pipefd) == -1) {perror("pipe");exit(EXIT_FAILURE);}// 创建子进程pid = fork();if (pid == -1) {perror("fork");exit(EXIT_FAILURE);}if (pid == 0) {  // 子进程// 关闭写端,子进程从管道中读取数据close(pipefd[1]);// 读取数据read(pipefd[0], buffer, sizeof(buffer));printf("Child Process: Data read from pipe: %s\n", buffer);// 关闭读端close(pipefd[0]);exit(EXIT_SUCCESS);} else {  // 父进程// 关闭读端,父进程向管道中写入数据close(pipefd[0]);// 写入数据write(pipefd[1], "Hello", 5);printf("Parent Process: Data written to pipe: Hello\n");// 关闭写端close(pipefd[1]);// 等待子进程结束wait(NULL);exit(EXIT_SUCCESS);}return 0;
}

在这个示例中,父进程创建了一个管道并生成了一个子进程。父进程通过管道将字符串"Hello"写入管道,而子进程则从管道中读取数据并打印到控制台上。在这个过程中,我们可以观察到以下特殊情况:

  • 写端进程不写,读端进程一直读:在这个示例中,如果父进程不写入数据,子进程会一直阻塞在读取管道的操作,直到有数据可读。
  • 读端进程不读,写端进程一直写:如果子进程不读取管道中的数据,而父进程持续写入数据,那么管道会被写满,父进程的写操作会阻塞,直到有空间可写。
  • 写端进程写完后关闭写端:在父进程写入数据后,关闭了写端。子进程读取完数据后,管道的读端会返回EOF,子进程继续执行后续代码。
  • 读端进程关闭读端:在子进程读取完数据后,关闭了读端。如果父进程继续写入数据,操作系统会向父进程发送信号,告知读端已关闭,然后父进程被杀死。
管道的大小

我们可以利用管道四种特殊情况中的第二种 读端进程不读,写端进程一直写 来验证:
让读端不读,写端进程一直写,直到写端被写满无法继续写时,则可以得到管道具体的大小。

#include <iostream>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>const int size = 1024;//写
void SubProcessWrite(int wfd)
{int pipesize = 0;std::string message = "father, i am your son process!";while(true){char c = 'A';write(wfd,&c,1);std::cout << "pipesize = " << ++pipesize << std::endl;}
}//读
void FatherProcessRead(int rfd)
{// sleep(500);char inbuffer[size];    //用于储存读到的信息的缓冲区while(true){// sleep(4);ssize_t n = read(rfd, inbuffer, sizeof(inbuffer) - 1);if(n > 0){inbuffer[n] = 0;std::cout << "father get message"  << inbuffer << std::endl;}}
}int main()
{//创建管道int pipefd[2];int n = pipe(pipefd);   //输出型参数if(n != 0){std::cerr << "errno: " << errno << ":" << "errstring: " << strerror(errno) << std::endl;return 1;}//pipefd[0] ->0 -r 嘴巴 读   pipefd[1] ->1 -w 🖊 写std::cout << "pipefd[0]: " << pipefd[0] << " " << "pipefd[1]: "<< pipefd[1] << std::endl;//创建子进程pid_t id = fork();if(id == 0){std::cout << "子进程已经关闭了读fd,保留写fd,准备开始写消息了。" << std::endl;//关闭不必要的fd//子进程//writeclose(pipefd[0]);SubProcessWrite(pipefd[1]);close(pipefd[1]);exit(0);}//父进程//readstd::cout << "父进程已经关闭了写fd,保留读fd,准备开始读消息了。" << std::endl;close(pipefd[1]);// FatherProcessRead(pipefd[0]);// close(pipefd[0]);int status = 0;pid_t rid = waitpid(id,&status,0);if(rid > 0){std::cout << "wait child process done, exit sig:" << (status&0x7f) << std::endl;std::cout << "wait chile process done, exit code(ign)" << ((status>>8)&0xff) << std::endl;}return 0;
}

在这里插入图片描述
可知:我当前Linux版本中管道的最大容量是65536字节。

命名管道

命名管道的原理

命名管道的原理

命名管道是一种特殊类型的文件系统对象,它允许不同进程通过文件来进行通信。其原理基于文件系统的特性,它实际上是一个由操作系统维护的特殊文件,具有磁盘上的路径名,可以在文件系统中找到。与匿名管道不同,命名管道可以通过文件系统中的路径名进行访问,从而允许不同进程在不同的时间段内进行通信。

创建一个命名管道

我们可以使用mkfifo命令创建一个命名管道。

qq@iZ0jl65jmm6w9evbwz2zuoZ:~/bt111/Linux/5_09/test$ mkfifo fifo

在这里插入图片描述
可以看到,创建出来的文件的类型是p,代表该文件是命名管道文件。
在这里插入图片描述
我们这里通过shell简单的进行两个进程的通信,这里可以看到左边的进程使用while done 来向fifo写入数据,右边进程来读取fifo的信息。

在这里插入图片描述


我们也可以使用mkfifo函数来创建命名管道

mkfifo函数
mkfifo函数是一个用于创建命名管道的Unix/Linux函数。他的原型如下:

#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);

参数:

  • pathname:指定要创建的命名管道的路径名。
  • mode:指定创建的管道的权限模式(权限位)。三位八进制数字。具体的权限可查看这篇文章Linux当中的权限问题
  1. 创建命名管道: 首先,需要使用特定的系统调用(如mkfifo函数)在文件系统中创建一个命名管道。这个系统调用将在文件系统中创建一个特殊类型的文件,其类型为FIFO(先进先出),并分配一个唯一的路径名。

  2. 进程打开管道: 创建命名管道后,进程可以通过打开文件系统中的路径名来访问该管道。进程可以像打开普通文件一样打开命名管道,并且可以使用文件描述符来进行读取和写入操作。

  3. 进程读写管道: 一旦管道被打开,进程就可以使用相应的文件描述符进行读取和写入操作。写入到管道的数据会按照先进先出的顺序被读取出来。

  4. 进程关闭管道: 当进程不再需要使用管道时,应该关闭相应的文件描述符,以释放系统资源。

总的来说,命名管道的原理是利用文件系统的特性创建一个特殊类型的文件对象,允许不同进程通过文件进行通信。这种通信方式具有持久性和可靠性,不同进程可以在不同的时间段内进行通信,而不受进程生命周期的限制。

命名管道的打开规则
  • 如果当前打开操作是为读而打开FIFO时
    • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
    • O_NONBLOCK enable:立刻返回成功
  • 如果当前打开操作是为写而打开FIFO时
    • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
    • O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
用命名管道实现 serve&client 通信

我们先来写一个管道文件,如何在serve 和 client 端分别打开这个管道文件。
在这个管道文件中分别封装 读操作接口写操作接口 以供使用。

namePipe.hpp

#pragma once#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string comm_path = "./myfifo";#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BassSize 4096class NamePiped
{
private:bool OpenNamePipd(int mode)     //打开对应管道  mode决定打开方式{_fd = open(_fifo_path.c_str(),mode);if(_fd < 0){return false;}return true;}public:NamePiped(const std::string &path, int who):_fifo_path(path),_id(who){int res = mkfifo(_fifo_path.c_str(),0666);      //创建命名管道  名字是myfifo if(res != 0){//创建失败perror("mkfifo");}std::cout << "Creater create named pipe" << std::endl;}//创建打开接口//读bool OpenForRead(){return OpenNamePipd(Read);}//写bool OpenForWrite(){return OpenNamePipd(Write);}//write     输入项参数int WriteNamePipe(const std::string &in){return write(_fd,in.c_str(),in.size());}//read      输出型参数int ReadNamePipe(std::string *out){char buffer[BassSize];int n = read(_fd,buffer,sizeof(buffer));if(n > 0){buffer[n] = 0;*out = buffer;}return n;}~NamePiped(){if(_id == Creater){int res = unlink(_fifo_path.c_str());if(res != 0){perror("unlink");}std::cout << "creater free named pipe" << std::endl;}if(_fd != DefaultFd)    close(_fd);}
private:const std::string _fifo_path;   //命名管道的地址int _fd;    //fd  文件描述符int _id;    //id  用于判断Creater | User
};

client.cc

#include "namePipe.hpp"//write 写
int main()
{NamePiped fifo(comm_path,User);if(fifo.OpenForWrite()){std::cout << "client open named pipe done" << std::endl;while(true){std::cout << "Pleanse Enter>";std::string message;std::getline(std::cin,message);fifo.WriteNamePipe(message);}}return 0;
}

server.cc

#include "namePipe.hpp"//read 读
int main()
{NamePiped fifo(comm_path,Creater);if(fifo.OpenForRead()){std::cout << "server open named pipe done" << std::endl;while(true){std::string message;int n = fifo.ReadNamePipe(&message);if(n > 0){std::cout << "Client say > " << message << std::endl;}else if(n == 0){std::cout << "Client quit, Server Tool" << std::endl;}else{std::cout << "fifo.ReadNamedPipe Error" << std::endl;break;}}}return 0;
}

演示:

在这里插入图片描述

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

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

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

相关文章

2024软件测试必问的常见面试题1000问!

01、您所熟悉的测试用例设计方法都有哪些&#xff1f;请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。 答&#xff1a;有黑盒和白盒两种测试种类&#xff0c;黑盒有等价类划分法&#xff0c;边界分析法&#xff0c;因果图法和错误猜测法。白盒有逻辑覆盖法&…

三子棋游戏

1、实现三子棋 test.c //测试游戏的逻辑 game.c //游戏代码的实现 game.h //游戏代码的声明&#xff08;函数声明&#xff0c;符号定义&#xff09; 1、 在主函数里实现&#xff0c;首先有个界面&#xff0c;让玩家选择是否玩游戏。上来就先打印菜单&#xff0c;用do ... …

优秀测试的核心能力!2招高效定位分析BUG!

之所以写这一篇文章&#xff0c;是突然想起来曾经在测试过程中被开发嘲讽过&#xff0c;事情是这样的&#xff0c;当时发现了一个疑似前端的Bug就草草提交到了禅道&#xff0c;结果刚来的女前端看到了就有点生气地问我为啥不查清到底是前后端问题就直接派给她前端了&#xff0c…

【python量化交易】—— Alpha选股策略 - Qteasy自定义交易策略【附源码】

使用qteasy创建并回测Alpha选股交易策略 使用qteasy创建并回测Alpha选股交易策略策略思想第一种自定义策略设置方法&#xff0c;使用持仓数据和选股数据直接生成比例交易信号PS信号&#xff1a;第二种自定义策略设置方法&#xff0c;使用PT交易信号设置持仓目标&#xff1a;第三…

【数组中重复的数据】leetcode,python

和上题一样&#xff0c;【找到所有数组中消失的数字】 换个判断条件就行 class Solution:def findDuplicates(self, nums: List[int]) -> List[int]:nlen(nums)for i in nums:x(i-1)%nnums[x]n#只需要替换条件即可return [i1 for i,num in enumerate(nums) if num>(2*n)…

海外云手机的运作原理和适用场景

海外云手机是一种基于云计算技术的虚拟手机服务&#xff0c;通过将手机操作系统和应用程序托管在远程服务器上&#xff0c;实现用户可以通过互联网连接来使用和管理手机功能&#xff0c;而无需实际拥有物理手机。以下是有关海外云手机的相关信息&#xff1a; 海外云手机的运作原…

如何利用命令提示符列出文件?这里提供了几个实例供你参考

序言 什么命令可以用来列出目录中的文件&#xff1f;如何在命令提示符Windows 10/11中列出文件&#xff1f;很多人对这些问题感到困惑。在这篇文章中&#xff0c;我们详细解释了命令提示符列出文件的主题。 CMD&#xff08;命令提示符&#xff09;是一个功能强大的Windows内置…

Keil问题解决:结构体数组初始化,初始化后的值不是目标值

省流&#xff1a;使用的编译器为compiler version 6&#xff0c;切换为compiler version 5 如果缺少编译器&#xff0c;请参考&#xff1a;Keil手动安装编译器V5版本 结构体定义&#xff1a; typedef struct _TASK_COMPONENTS {uint8_t Run; // 程序运行标…

雍禾植发张东宏:以诚相待毛发患者

医学道路上的奋斗往往需要坚定的信念和不懈的努力。对于张东宏医生来说&#xff0c;医学并非止步于书本知识&#xff0c;而是一次次与患者对话、一次次实操中的历练和积累。在他的成长历程中&#xff0c;医学之路如同一棵参天大树&#xff0c;每一步都是扎实的打磨&#xff0c;…

STM32F407 2个高级定时器生成2路无刷电机波形以及相电流采集程序(寄存器版)

stm32f407 高级定时1、定时8 生成20k 中心PWM 波形 并分别用其通道4 触发ADC1 ADC2 采样 用于分别两无刷电机foc 电流环控制&#xff0c;ADC1产生50us的电流采集完成中断&#xff0c;用于foc算法周期运算 主要参考高级定时器的寄存器和ADC寄存器 首先&#xff0c;要使用STM32F…

代码复现|Demucs Music Source Separation

一、背景介绍 Demucs是一个开源的音源分离项目。 Demucs在算法层面前后经历了三次大版本的进化&#xff0c;最原始的V1版本是&#xff1a;编解码LSTM。具体算法原理图如下所示。该版本在时域进行音源分离。关于阅读笔记请点击这篇文章。 V1版本原理图 V2版本是同时使用时域和频…

2、快速搭建Vue框架以及项目工程

本篇文章详细讲解在配置完vue2环境后如何快速搭建一个Vue框架和项目工程。&#xff08;以智慧农业云平台为例&#xff09; 2.1 Vue工程创建 2.1.1创建想要存放的Vue文件夹 找到想要存放的文件夹并在目录搜索框中&#xff0c;并用管理员的方式打开。 2.1.2创建Vue工程 2、安装…

prompt工程策略(一:使用 CO-STAR 框架来搭建 prompt 的结构)

原文&#xff1a;我是如何赢得GPT-4提示工程大赛冠军的 原文的原文&#xff1a; How I Won Singapore’s GPT-4 Prompt Engineering Competition 为了让 LLM 给出最优响应&#xff0c;为 prompt 设置有效的结构至关重要。CO-STAR 框架是一种可以方便用于设计 prompt 结构的模板…

2024最新Kali Linux安装教程(非常详细)从零基础入门到精通(附安装包)!

什么是Kali Linux&#xff1f; Kali Linux是一个高级渗透测试和安全审计Linux发行版&#xff0c;其功能非常强大&#xff0c;能够进行信息取证、渗透测试、攻击WPA / WPA2保护的无线网络、离线破解哈希密码、将android、Java、C编写的程序反编译成代码等等&#xff0c;是黑客的…

Invalid bound statement (not found) 六种解决方法

前五种参考博文&#xff1a; Invalid bound statement (not found) 五种解决方法-CSDN博客 第六种&#xff1a; 在启动类上加上MapperScan&#xff0c;指定扫描包

【C++】list原理讲解及其实现

目录 一、认识list底层结构 二、list的构造类函数 三、迭代器 四、数据的访问 五、容量相关的函数 六、关于数据的增删查改操作 七、list和vector的比较 前言 要模拟实现list&#xff0c;必须要熟悉list的底层结构以及其接口的含义&#xff0c;在上一篇我们仔细讲解了list的…

Gradio 案例——将 dicom 文件转为 nii文件

文章目录 Gradio 案例——将 dicom 文件转为 nii文件界面截图依赖安装项目目录结构代码 Gradio 案例——将 dicom 文件转为 nii文件 利用 SimpleITK 库&#xff0c;将 dicom 文件转为 nii文件更完整、丰富的示例项目见 GitHub - AlionSSS/dcm2niix-webui: The web UI for dcm2…

JavaScript精粹(一)- 基础入门

JavaScript&#xff08;简称为JS&#xff09;是一种广泛应用于网页开发的脚本语言&#xff0c;具有以下几个主要作用&#xff1a; 网页交互&#xff1a;JavaScript 可以用于创建动态的网页效果&#xff0c;例如响应用户的操作&#xff0c;实现页面内容的动态更新&#xff0c;以…

【Java基础】集合(2) —— List

List 存储的对象是有序的&#xff08;集合中存储对象的顺序和使用add方法添加对象的顺序一致&#xff09;&#xff0c;存储的对象是可重复的。 List的特有的功能: 都是可以操作索引的功能。 增: void add(int index, E element )boolean addAll(int index, Collection<? …

mysql的explain

explain可以用于select&#xff0c;delete&#xff0c;insert&#xff0c;update的statement。 当explain用于statement时&#xff0c;mysql将会给出其优化器&#xff08;optimizer&#xff09;的执行计划。 通过explain字段生成执行计划表。下面来解析这个执行计划表的每一列…