Linux之管道,system V的共享内存,消息队列和信号量

Linux之管道,systemV共享内存和信号量

  • 一.进程间通信
    • 1.1进程间通信的目的
    • 1.2进程间通信的方式
  • 二.管道
    • 2.1管道的概念
    • 2.2匿名管道
    • 2.3命名管道
  • 三.system V
    • 3.1共享内存
    • 3.2消息队列
    • 3.3信号量

一.进程间通信

在我们之前有关Linux指令的学习时我们使用过“|”这个命令,当时我们说这是管道文件,那么什么是管道?管道的作用是什么?
管道是进程间通信的一种方式,在我们之前的学习中我们反复强调进程是具有独立性的所以不同的进程很难相互影响但是在项目的开发中我们不可能只使用一个进程或者个别进程来进行开发,在进程的数量多了起来之后数据也就多了起来那么我们就需要在进程之间传输数据或共享数据。而且我们有时候还需要利用一个进程来通知另外一个进程发生了什么事件或者控制另外一个进程进行某种操作。这些都是我们进程间通信的目的!

1.1进程间通信的目的

  • 传输数据:一个进程需要将它的数据发送给另一个进程
  • 共享资源:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

1.2进程间通信的方式

  1. 管道
  • 匿名管道
  • 命名管道
  1. system V
  • system V 共享内存
  • system V 消息队列
  • system V 信号量

二.管道

2.1管道的概念

管道是最古老的一种通信方式但是时至今日它仍然是我们需要学习的知识,古老但并不落后。
对于想要让不同的进程进行通信那么我们可以先思考一个问题:进程间通信的本质是什么?
本来我们强调进程要具有独立性所以我们无法让一个进程中含有另外一个进程那么一个进程要如何读取到另外一个进程写入的信息呢?
读取?写入?如果我们让一个进程向一个文件中写入数据然后让另外一个进程读取该文件的数据,这是不是就是完成了进程间的通信呢?那么这种方法的本质是什么?
所以进程间通信的本质是让不同的进程看见了同一份资源

所以管道就诞生了而那个作为中转的文件就是管道而可以叫做管道文件。
那么如何理解让不同的进程看见同一份资源呢?我们先以父子进程为例并配图来展示。
在这里插入图片描述
同时我们要注意管道是单向的所以只能让一个文件写入一个文件读取
并且有两种管道之分:匿名管道和命名管道。这两种管道有什么区别我也可以提前告诉各位,匿名管道只能让有“亲缘关系”的进程即父子,兄弟,爷孙等等进行通信而命名管道可以让任意两个进程进行通信。

2.2匿名管道

在Linux中想要创建匿名管道需要用到系统调用pipe
在这里插入图片描述
在这里插入图片描述

这个系统调用很简单,成功就返回零,失败就返回-1同时设置错误码。而参数int pipefd[2]在我们刚刚看见时可能会有点奇怪,我们知道这是一个整型数组但是为什么会有个2呢?
我们只需要回想有关数组传参的知识就可以知道对于数组来说传参传的都是数组的首地址所以这个2是没有用的更多的是起到一种提示的作用。那么在提示什么呢?我们来深入了解一下匿名管道的原理就可以知道了,一样我们结合图来讲述。
在这里插入图片描述
在了解了其中的原理之后我们再回过头来看这个接口就能知道这个参数其实就是文件描述符的数组而那个2也就是提示我们只有两个文件描述符。那么问题又出现了这两个文件描述符中哪个是写端哪个是读端呢?我们从手册中就能知道。
在这里插入图片描述
所以文件描述符数组中第一个文件描述符是读端,第二个文件描述符是写端。在了解了哪个是读写端后我们就可以尝试使用匿名管道来传输数据了,并且通过对匿名管道的不同使用场景我们可以总结出来四种情况和五种特性假设为子写父读的情况!

#include <iostream>
#include <unistd.h>
#include <cassert>
#include <stdio.h>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{// 打开管道int pipefd[2] = {};int n = pipe(pipefd);assert(n == 0);// 使用断言来判断pipe的返回值,// 因为pipe大概率是创建成功的所以使用断言比使用if更加的方便(void *)n;// 为了避免编译器因为定义了变量但是没有使用从而产生报错所以随意使用一下变量ncout << "fd[1]:" << pipefd[0] << " " << "fd[2]:" << pipefd[1] << endl;// 创建子进程pid_t id = fork();if (id < 0){perror("fork");return 1;}else if (id == 0){// 子进程// 子进程为写入端所以要关闭读取端即pipefd[0]close(pipefd[0]);// 向管道中写入// 正常情况// int cnt = 5;// while (cnt--)// {//     ssize_t n = write(pipefd[1], buffer, strlen(buffer));//     if (n < 0)//     {//         perror("write");//         return 1;//     }//     sleep(1);// }// 情况4while (true){char buffer[1024] = {};snprintf(buffer, sizeof(buffer), "i am child , pid:%d , ppid:%d\n", getpid(), getppid());ssize_t n = write(pipefd[1], buffer, strlen(buffer));if (n < 0){perror("write");return 1;}sleep(1);}cout << "write point quit" << endl;close(pipefd[1]);exit(0);}else{// 父进程// 父进程为读取端所以要关闭写入端即pipefd[1]close(pipefd[1]);// 从管道中读取数据char buffer1[1024] = {};// 正常情况// while (true)// {//     ssize_t n = read(0, buffer1, sizeof(buffer1));//     if (n == 0)//     {//         break;//     }//     else if (n > 0)//     {//         buffer1[n] = 0;//         cout << "child say:" << buffer1;//     }//     else//     {//         perror("read");//         return 1;//     }//     sleep(1);// }// 情况3// while(true)// {//     ssize_t n = read(0, buffer1, sizeof(buffer1)-1);//     if(n > 0)//     {//         buffer1[n] = 0;//         cout << "child say:" << buffer1;//         sleep(2);//     }//     else if(n == 0)//     {//         cout << "write point is close , i read the file's end" << endl;//         break;//     }//     else//     {//         perror("read");//         return 1;//     }// }// 情况4while (true){ssize_t n = read(pipefd[0], buffer1, sizeof(buffer1));if (n > 0){buffer1[n] = 0;cout << "child say:" << buffer1;sleep(1);}else if (n == 0){cout << "write point is close , i read the file's end" << endl;break;}else{perror("read");return 1;}break;}close(pipefd[0]);cout << "read point quit" << endl;// 等待子进程// 正常情况//  pid_t rid = waitpid(id,NULL,0);//  if(rid == id)//  {//      cout << "wait success" << endl;//  }// 情况4sleep(3);int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid == id){cout << "wait success " << "child quit sig:" << (status&0x7F) << endl;}}return 0;
}

在这里插入图片描述
而从这些代码中我们可以总结出来匿名管道的四种情况和五种特性:
四种情况

  1. 正常情况下,如果管道没有数据了那么读端必须等待直到管道中有数据即写端写入数据
  2. 正常情况下,如果管道中数据满了那么写端必须等待直到管道中有空间即读端读取数据
  3. 写端关闭,读端一直读取,读端会读到read返回值为0,表示读到文件结尾
    在这里插入图片描述
  4. 读端关闭,写端一直写入,操作系统会通过向写端进程发送sig 13信号来终止写端进程
    在这里插入图片描述

五种特性

  1. 匿名管道只允许有亲缘关系的进程之间进行通信,常见的为父子
  2. 匿名管道默认要给予读写段提供同步机制 ——了解现象即可
  3. 管道是面对字节流的 ——了解现象即可
  4. 管道的生命周期是随进程的
  5. 管道是单项通信的,也是半双工通信的一种

2.3命名管道

匿名管道是只能让有亲缘关系的进程通信而命名管道则可以让任意的两个进程互相通信,而命名管道的原理和匿名管道也是相似的。

那么想要创建一个命名管道有两种方法:一种是指令级的一种是代码级的。

  1. 指令级
    在这里插入图片描述
  2. 代码级
    在这里插入图片描述
//connect.hpp
//为了符合两个进程都能看见同一个资源的本质
//所以我们创建一个.hpp文件来存储那些公用的数据和接口
#pragma once#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <cassert>
#include <fcntl.h>
#include <cstring>#define FILENAME "./FIFO"
#define SIZE 1024
//Makefile
##利用伪目标达成一次性生成多个可执行文件
.PHONY:all
all:launch receptionlaunch:launch.ccg++ -o $@ $^ -std=c++11reception:reception.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f FIFO launch reception
//launch.cc
#include <iostream>
#include "connect.hpp"using namespace std;int main()
{// 创建命名管道mkfifo(FILENAME, 0666);// 写端要以写入模式打开命名管道int fd = open(FILENAME, O_WRONLY);if (fd == -1){perror("open");return 1;}// 向命名管道中写入数据int cnt = 10;while (cnt--){char buffer[SIZE] = {};cout << "i am wirting message..." << endl;snprintf(buffer, sizeof(buffer), "message is pid:%d ppid:%d\n", getpid(), getppid());write(fd, buffer, strlen(buffer));sleep(1);}return 0;
}
//reception.cc
#include <iostream>
#include "connect.hpp"using namespace std;int main()
{// 读端要以读取的模式打开命名管道int fd = open(FILENAME, O_RDONLY);if (fd == -1){perror("open");return 1;}// 从命名管道中读取数据int buffer[SIZE] = {};while (true){int n = read(fd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;cout << "i am receiving meesge..." << endl;cout << "message is:" << buffer << endl;}else if(n == 0){cout << "write point quit , me too" << endl;break;}else{perror("read");return 1;}sleep(1);}return 0;
}

在这里插入图片描述
在这里插入图片描述

当我们利用命名管道进行通信时我们会发现如果读写端任意一个先使用open打开了命名管道后这个进程会卡住不会向下继续运行代码直到另外一个进程也使用open打开命名管道后,这也符合了我们五种特性中的一种即管道默认要给予读写段提供同步机制

对于命名管道而言,它也有四种情况和五种特性但是大部分都和匿名管道相同只有一个特性不同命名管道可以让任意两个进程进行通信

匿名管道是通过亲缘关系继承制的看见同一份资源但是命名管道可以让没有亲缘关系的两个进程看见同一份资源这是怎么做到的呢?
在我们创建命名管道时大家有没有注意到其中的参数是什么
在这里插入图片描述
第二个参数是创建管道的权限这个无需多谈因为管道也是个文件所以一定需要设置权限,重点在第一个参数路径加文件名,两个进程正是通过路径加文件名来看到同一个资源的
当我们没有了继承制后我们想要让两个不同的进程看见同一份资源那么一定要让这两个进程找到这一份资源而在我们学习了文件系统和软硬连接后我们知道在一个分区下文件名是唯一的因为inode是唯一而文件名会和inode形成映射关系所以文件名也是唯一的,同时我们也知道查找一个文件的方法是通过路径那么利用路径加文件名是不是就能做到准确的找到这个文件并且还保持着唯一性。所以我们利用路径加文件名的方式让两个进程找到同一份资源!!!

三.system V

通过对管道的学习我们大致了解了进程间通信的概念和本质,所以我们现在来介绍另外一种进程间通信的方法即system V。
System V它最初由AT&T开发,曾经也被称为AT&T System V,是Unix操作系统众多版本中的一支。而在system V中存在着三种进程间通信的方式。

3.1共享内存

我们创建管道文件并且利用管道完成进程间通信,本质是向管道文件的文件缓冲区写入读取。但是使用管道我们还需要调用系统接口向缓冲区中写入,而使用接口的过程中其实是比较浪费时间的那么我们是否可以之间向内存中写入读取数据呢?
这就是system V共享内存,共享内存是物理内存中的一块内存而在虚拟地址空间中则是映射到共享区的和动静态库一样,而且共享内存是可以被开辟多块的。那么问题也就出来了对于管道而言我们是通过继承制和路径加文件名来看见同一份资源的,而共享内存要如何看见同一份资源即看见同一块共享内存呢?我们结合图来讲述。
在这里插入图片描述
所以我们知道了共享内存是通过创建时设立一个标识符来达到让另外一个进程找到同一个共享内存的,并且这个标识符是存储在共享内存的结构体中的。想要证明这些我们就需要使用接口了

共享内存的接口很多,其中包括了创建,挂载,卸载,控制。

  1. 创建:创建共享内存
    在这里插入图片描述
//connect.hpp
#pragma once#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>#define FILENAME "/home/ly/lesson7"
#define ID 0x12345678
#define SIZE 4096
//Makefile
.PHONY:all
all:server clientserver:server.ccg++ -o $@ $^ -std=c++11client:client.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f client server	
#include <iostream>
#include "connect.hpp"
using namespace std;int main()
{//获取keykey_t key = ftok(FILENAME,getpid());if(key == -1){cout << "errno:" << errno << ", errstring" << strerror(errno) << endl;}//创建共享内存int n = shmget(key,SIZE,IPC_CREAT|0666);if(n == -1){cout << "errno:" << errno << ", errstring" << strerror(errno) << endl;}return 0;
}

如果我们想要查看创建的共享内存则可以使用ipcs -m命令
在这里插入图片描述

其中key是标识符,shmid就是共享内存的id,owner是创建者,perms则是权限,bytes则是共享内存的大小,nattch则是共享内存挂载到了多少个虚拟地址空间中。

2.挂载:将共享内存链接到进程的虚拟内存空间中
在这里插入图片描述

//connect.hpp
#pragma once#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <iostream>#define FILENAME "/home/ly/lesson7"
#define ID 0x12345678
#define SIZE 4096using namespace std;//获取key
key_t GetKey()
{key_t key = ftok(FILENAME,ID);if(key == -1){cout << "errno:" << errno << ", errstring:" << strerror(errno) << endl;exit(1);}return key;
}//将key转为会16进制打出
string ToHex(key_t key)
{char buffer[1024];snprintf(buffer,sizeof(buffer),"0x%x",key);return buffer;
}//创建或者获得共享内存
int CreatShm(key_t key)
{int id = shmget(key,SIZE,IPC_CREAT|0666);if(id == -1){cout << "errno:" << errno << ", errstring:" << strerror(errno) << endl;exit(2);}return id;
}
//server.cc
#include "connect.hpp"
#include <iostream>using namespace std;int main()
{// 获取keykey_t key = GetKey();cout << "key:" << ToHex(key) << endl;// 创建共享内存int id = CreatShm(key);cout << "id:" << id << endl;// 将共享内存挂载到进程的虚拟地址空间中char *s = (char *)shmat(id, nullptr, 0);if (*s == -1){cout << "errno:" << errno << ", errstring:" << strerror(errno) << endl;}else{cout << "server attch shm done" << endl;}return 0;
}
//client.cc
#include "connect.hpp"
#include <iostream>using namespace std;int main()
{// 获取keykey_t key = GetKey();cout << "key:" << ToHex(key) << endl;// 获得共享内存int id = CreatShm(key);cout << "id:" << id << endl;// 挂载char *s = (char *)shmat(id, NULL, 0);if (*s == -1){cout << "errno:" << errno << ", errstring:" << strerror(errno) << endl;}else{cout << "server attch shm done" << endl;}return 0;
}
  1. 卸载:将共享内存和虚拟地址空间之间的链接去除
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
可以发现在我们卸载了之后共享内存并没有被删除说明共享内存的生命不是随进程的,而是随内核的。所以如果我们想要删除一个共享内存要怎么做呢?
4.控制:删除共享内存或者查找更改共享内存的属性
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

除了使用shmctl接口外我们还可以通过指令来删除共享内存即ipcrm -m +shmid
在这里插入图片描述

我们通过shmctl手册中还可以看见有关共享内存的结构体以及里面存储的属性那么是是否就证明了我们之前说的对共享内存的管理工作是存在并且正确的呢?
在这里插入图片描述

在了解了那几个接口之后我们就要尝试向共享内存中写入和读取数据了从而来观察出共享内存的通信和管道的通信有什么区别。
在这里插入图片描述

在这里插入图片描述

至于共享内存和管道这两个通信方式有什么区别,只能说共享内存是所有通信方式中最快的,而原因就是利用共享内存来通信减少了数据的迁移也可以说是数据的拷贝。
我们可以大致思考一些管道和共享内存中分别需要数据迁移多少次

  • 管道:我们首先要将数据从键盘迁移到读端自己定义的缓冲区中然后使用write接口将缓冲区的数据迁移到管道文件的缓冲区中,之后我们想要读取数据还需要将数据从管道文件的缓冲区中迁移到写端自己的缓冲区中最后再将其迁移到显示屏文件中。中间甚至还省略了一些语言层面的迁移,所以使用管道来通信的话至少需要4次数据迁移。
  • 共享内存:在我们创建并且分别挂载到两个进程后,我们只需要将数据从键盘中迁移到共享内存中然后再将数据从共享内存迁移到显示屏文件中,所以使用共享内存通信我们只需要2次数据迁移。

从迁移的数量上我们就可以看出来使用共享内存通信的速度是远远大于使用管道的,其主要原因就是共享内存我们只需要向内存中写入读取即可省略了那些繁琐的接口。

3.2消息队列

system V中的消息队列则是提供了一个进程向另外一个进程发送数据块的能力从而实现进程间通信。
在这里插入图片描述
对于消息队列同样也有系统接口,而消息队列的系统接口和共享内存的接口是相似的。

  1. 创建
    在这里插入图片描述
    这与共享内存的则基本相同,参数分别为标识符和权限。返回值则为msgid
  2. 接受消息
    在这里插入图片描述
    在这里插入图片描述

第一个参数是消息队列的id,第二个参数是一个结构体存储了消息的类型和消息的内容,第三个参数则是发送消息的大小,第四个参数则和共享内存挂载时相同是以什么模式发送消息。
返回值则为常见的成功返回0,失败返回-1

  1. 接收消息
    在这里插入图片描述
    第一个参数是消息队列的id,第二个参数则和发送消息时相同也为一个结构体,第三个参数则是想要接收多少大小的消息,第四个参数为模式选择。
    返回值则是成功就返回接收消息的大小即msgsz,失败返回-1。

  2. 控制
    在这里插入图片描述
    控制接口则与共享内存完全相同。
    同时在消息队列中我们也可以找到它的结构体,而且我们发现消息队列的结构体第一个变量也是这个ipc_perm这个结构体。
    在这里插入图片描述

3.3信号量

在理解信号量的概念之前我们需要了解一些简单的概念
为了完成进程间的通信我们需要做到让不同的进程看见同一份资源而这份资源也被叫做公共资源,并且在开发中可能会有很多的进程同时访问这份资源这也就叫做并发开发,但是在我们之前学习共享内存的时候我们发现共享内存是被暴露给所有的使用者的,那么对于共享内存这份公共资源来说是具有数据安全的问题的,所以对于公共资源我们是需要保护起来的而保护的手段主要分为互斥和同步。
而对于公共资源的保护是可以由用户来做或者由操作系统来做的,比如匿名命名管道和消息队列都是由操作系统做了保护就像管道的同步机制一样,而例如共享内存的保护就是由我们用户来做的就像我们之前利用管道完成了共享内存的同步机制。

对于互斥和同步我们现在暂时不需要深入了解只需要知道简单的概念和现象就可以,我们会在多线程的部分再次学习到。
互斥:任何一个时刻只允许一个进程访问公共资源。
同步:多个进程访问一个公共资源时是按照一定的顺序来访问的。
我们对于这些被保护起来的公共资源叫做临界资源,而对于那些访问临界资源的代码叫做临界区。
例如我们使用管道保护起来的共享内存就是一个临界资源,而在代码中我们向共享内存中写入数据读取数据的代码就是临界区。
而且我们可以很简单的发现我们想要维护临界资源其实只需要维护临界区就可以了,代码一旦被维护了那么资源就自然而然的也被维护了。

同时我们再来介绍一个概念:原子性
原子性是数据库事务的四大特性之一,它确保事务中的所有操作要么全部成功,要么全部失败回滚。这意味着事务的操作如果成功就必须完全应用到数据库,如果操作失败则不能对数据库有任何影响。
就像我们小时候吹牛说我以后只会考一百分,那么这句话带来的后果只有两个:考到一百分,没考到一百分。其中的其他情况我们都不需要考虑。

在了解了这些概念后我们就可以来谈谈信号量了
要如何理解信号量呢我们可以用一个例子来引入:看电影。
在我们去看电影的时候我我们的座位是在我们买票的时候就已经决定了还是在我们坐到座位的时候就已经决定了呢?
毫无疑问是在我们买票的时候就已经决定了,无论我们去不去看这场电影电影院都必须给我们把这个座位留着。
在这里插入图片描述
而信号量就是这个计数器,所以它的作用也就是保护临界资源。
信号量:表示对资源数量的计数器,每一个进程想要访问公共资源的某一份资源,就必须在访问之前申请一份信号量资源而本质就是对计数器进行–操作,只要–成功就代表该进程已经预定了资源,而如果一个进程–失败了就必须被挂起阻塞。

想要完成这种对信号量的访问我们只需要在申请资源的代码即临界区之前增加对计数器的–操作以及在代码的结尾增加对计数器的++操作即可。
在这里插入图片描述

那么如果信号量的资源量只有1呢?同时只能有一个进程访问这个资源,这是不是就完成了一种互斥的功能,这就是互斥锁而这种信号量就被叫做二元信号量。

在介绍了信号量的大概概念后我们可以提出两个问题:

  1. 在了解了信号量的作用后我们发现如果想要信号量有作用就必须让每个进程都看见同一个信号量资源,这又要如何做到呢?
    对于问题我们可以采用和共享内存以及消息队列同样的操作即使用key,但是这个key就必须是操作系统提供的了。
  2. 信号量是为了保护公共资源所以进程在访问资源之前都会先访问信号量,那么信号量是不是就变成了一个公共资源呢?那么信号量是不是也要被保护呢?它又要怎么被保护呢?
    信号量本身就是保护公共资源但是现在发现信号量本身也需要被保护这不就陷入了一种循环中,所以对于信号量的保护我们需要换一种方式,这件需要使用到我们刚刚提到的原子性,只要让进程对信号量的访问成功就完全应用,失败就完全不影响。这就完成了对信号量的保护。所以我们对访问信号量申请资源的操作叫做p操作,释放资源的操作叫做v操作,总结就是只提供pv操作来完成保护。
  3. 如何让进程在访问信号量申请资源失败时完成挂起阻塞操作
    在信号量中不仅有计数器来完成–和++操作还会存在一个阻塞队列,只要是申请资源失败的进程就会被移入到阻塞队列中。
    在这里插入图片描述

现在我们要来熟悉一下信号量的接口
1.创建
在这里插入图片描述

  1. 申请资源
    在这里插入图片描述

3.控制
在这里插入图片描述

在这里插入图片描述
对于信号量我们同样可以找到它的结构体所以信号量=信号量的内容+信号量的属性。同时我们可以发现信号量的结构体也有一个ipc_serm。所以我们发现从共享内存到消息队列再到信号量都有ipc_perm这个结构体。

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

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

相关文章

使用ChatGPT生成和优化电子商务用户需求规格说明书

在电子商务项目开发中&#xff0c;用户需求规格说明书&#xff08;User Requirement Specification, URS&#xff09;是团队沟通与项目成功的基石。然而&#xff0c;面对复杂多变的需求&#xff0c;如何快速生成清晰、完整且具备说服力的文档&#xff1f;这正是AI工具的用武之地…

1+X应急响应(网络)常见网络攻击-SQL注入:

常见网络攻击-SQL注入&#xff1a; SQL注入概述&#xff1a; 动态网站的工作流程&#xff1a; SQL注入的起源&#xff1a; SQL典型的攻击手段&#xff1a; SQL注入的危害&#xff1a; SQL注入的函数&#xff1a; SQL注入类型&#xff1a; 提交方式分类&#xff1a; Get注入&am…

Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图

用户打开 PDF 文档时&#xff0c;他们会看到 PDF 的初始视图。默认情况下&#xff0c;打开 PDF 时不会显示书签面板或缩略图面板。在本文中&#xff0c;我们将演示如何设置文档属性&#xff0c;以便每次启动文件时都会打开书签面板或缩略图面板。 Spire.PDF for .NET 是一款独…

[Docker-显示所有容器IP] 显示docker-compose.yml中所有容器IP的方法

本文由Markdown语法编辑器编辑完成。 1. 需求背景: 最近在启动一个服务时&#xff0c;突然发现它的一个接口&#xff0c;被另一个服务ip频繁的请求。 按理说&#xff0c;之前设置的是&#xff0c;每隔1分钟请求一次接口。但从日志来看&#xff0c;则是1秒钟请求一次&#xff…

单片机GPIO的8种工作模式

1、输入 GPIO_MODE_AIN:模拟输入 GPIO_MODE_IN_FLOATING:浮空输入 GPIO_MODE_IPD:下拉输入 GPIO_MODE_IPU:上拉输入 2、输出 GPIO_MODE_OUT_OD:开漏输出&#xff08;特殊情况使用&#xff09; GPIO_MODE_OUT_PP&#xff1a;推挽输出-----点灯&#xff08;通用&#…

Azkaban部署

首先我们需要现在相关的组件&#xff0c;在这里已经给大家准备好了相关的安装包&#xff0c;有需要的可以自行下载。 只需要启动hadoop集群就可以&#xff0c;如果现在你的hive是打开的&#xff0c;那么请你关闭&#xff01;&#xff01;&#xff01; 如果不关会造成证书冲突…

时钟使能、

时钟使能 如果正确使用&#xff0c;时钟使能能够显著地降低系统功耗&#xff0c;同时对面积或性能的影响极小。但是如果不正确地使用时钟使能&#xff0c; 可能会造成下列后果&#xff1a; • 面积增大 • 密度减小 • 功耗上升 • 性能下降 在许多使用大量控制集的…

视觉经典神经网络与复现:深入解析与实践指南

目录 引言 经典视觉神经网络模型详解 1. LeNet-5&#xff1a;卷积神经网络的先驱 LeNet-5的关键特点&#xff1a; 2. AlexNet&#xff1a;深度学习的突破 AlexNet的关键特点&#xff1a; 3. VGGNet&#xff1a;深度与简洁的平衡 VGGNet的关键特点&#xff1a; 4. ResNe…

【CSS in Depth 2 精译_060】9.3 详解 CSS 作用域的相关概念、最新 @scope 规则的应用及注意事项

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 【第九章 CSS 的模块化与作用域】 ✔️ 9.1 模块的定义 9.1.1 模块和全局样式9.1.2 一个简单的 CSS 模块9.1.3 模块的变体9.1.4 多元素模块 9.2 将模块组合为更大的结构 9.2.1 模块中多个职责的拆分…

uniapp实现开发遇到过的问题(持续更新中....)

1. 在ios模拟器上会出现底部留白的情况 解决方案&#xff1a; 在manifest.json文件&#xff0c;找到开源码视图配置&#xff0c;添加如下&#xff1a; "app-plus" : {"safearea":{"bottom":{"offset" : "none" // 底部安…

React(六)——Redux

文章目录 项目地址基本理解一、配置Redux store二、创建slice配置到store里并使用三、给Slice配置reducers&#xff0c;用来修改初始值 项目地址 教程作者&#xff1a;教程地址&#xff1a; 代码仓库地址&#xff1a; 所用到的框架和插件&#xff1a; dbt airflow基本理解 s…

国家级资质!同驭汽车获得CNAS实验室认证

近日&#xff0c;同驭汽车科技顺利通过中国合格评定国家认可委员会&#xff08;简称CNAS&#xff09;评审&#xff0c;获得《中国合格评定国家认可委员会实验室认可证书》。这标志着同驭已建立国际标准的实验室管理体系&#xff0c;产品的试验与检测技术能力达到了国际认可的准…

HTML5好看的音乐播放器多种风格(附源码)

文章目录 1.设计来源1.1 音乐播放器风格1效果1.2 音乐播放器风格2效果1.3 音乐播放器风格3效果1.4 音乐播放器风格4效果1.5 音乐播放器风格5效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&…

快速简单的视频下载器——lux

文章目录 前言1.环境检查1.1 检查 lux 安装1.2 检查FFmpeg安装1.3 备注 2. lux指令2.1 无OPTIONS2.2 -i 指令2.3 - f 指令2.4 -c 指令2.5 -o 指令2.6 备注 3.结语 前言 在学习之余&#xff0c;发现了一个简单并且高效的视频下载器lux,能够帮你快速且高效的下载文件&#xff08…

linux ubuntu的脚本知

目录 一、变量的引用 二、判断指定的文件是否存在 三、判断目录是否存在 四、判断最近一次命令执行是否成功 五、一些比较符号 六、"文件"的读取和写入 七、echo打印输出 八、ubuntu切换到root用户 N、其它可以参考的网址 脚本功能强大&#xff0c;用起来也…

Python爬虫进阶实战项目:使用青果网代理高效爬取某手办网详情数据

1 引言 计算机&#xff0c;获取数据&#xff0c;还在慢慢复制粘贴&#xff1f;聪明的朋友都已经用爬虫高效获取数据&#xff0c;但是经常搞爬虫遇见ip管控防爬&#xff0c;咱们怎么处理&#xff1f;在我看来随着数据需求增加&#xff0c;爬虫成为高效获取数据的工具。然而&…

大语言模型---Llama7B和Llama8B的区别;模型参数量;权重文件的不同;嵌入层权重的不同;输入序列长度的不同;应用场景

文章目录 1.概要2. 模型参数量3. 权重文件的不同4. 嵌入层权重的不同5. 输入序列长度的不同6. 应用场景 1.概要 LLaMA&#xff08;Large Language Model Meta AI&#xff09;是由Meta开发的一系列语言模型&#xff0c;其中不同版本的参数量&#xff08;如7B、8B等&#xff09;…

Linux命令思维导图

看到一个很不错的Linux命令思维导图&#xff0c;用机器翻译了一下&#xff0c;建议收藏备用。 附上英文版&#xff1a;

C++自动化测试:GTest 与 GitLab CI/CD 的完美融合

在现代软件开发中&#xff0c;自动化测试是保证代码质量和稳定性的关键手段。对于C项目而言&#xff0c;自动化测试尤为重要&#xff0c;它能有效捕捉代码中的潜在缺陷&#xff0c;提高代码的可维护性和可靠性。本文将重点介绍如何在C项目中结合使用Google Test&#xff08;GTe…

【淘汰9成NLP面试者的高频面题】LSTM中的tanh和sigmoid分别用在什么地方?为什么?

博客主页&#xff1a; [青松] 本文专栏: NLP 大模型百面百过 【淘汰9成NLP面试者的高频面题】LSTM中的tanh和sigmoid分别用在什么地方&#xff1f;为什么&#xff1f; 重要性&#xff1a;★★★ &#x1f4af; 本题主要考察面试者对以下问题的理解&#xff1a; ① 数据特征和模…