Linux操作系统学习(进程间通信)

news2024/12/28 4:41:54

文章目录

  • 进程间通信
    • 进程通信的意义
    • 进程通信的方式
      • 1.基于文件的方式
        • 匿名管道
        • 命名管道
      • 2.基于内存的通信方式
        • 共享内存
        • 验证
        • 内核相关的数据结构了解

进程间通信

进程通信的意义

​ 当我们和另一个人打电话时两部手机都是独立的,通过基站传递信号等等复杂的过程就实现了通信。那么进程间是如何进行通信的呢?

​ 我们知道进程是具有独立性的,即使是fork创建的子进程也一样;那么有时我们需要让两个进程进行协同工作,例如一个进程发送信号另一个进程接收信号执行对应的操作,再或者子进程把自己的数据交付给父进程让其处理拦截一些异常信号等等。

​ 由于进程是独立的,是互相看不到对方的数据的,所以想要进行通信就必须看到一份公共资源,这里的公共资源是操作系统提供的一段内存是属于操作系统的。这段内存由于提供的方式不一样会使通信的方式多样性(例如文件struct file方式、队列、数组、原始内存块等等),所以进程间通信是具有一定的成本的。

进程通信的方式

1.基于文件的方式

​ 先来梳理一下当一个进程打开文件时的进程内核角度是怎么样的:

​ 那么fork之后的子进程是什么样的呢?

​ 也就是拷贝了父进程的代码和数据以父进程为模板初始化(浅拷贝),所以子进程和父进程就指向了同一个文件,此时这个文件就是公共资源。

​ 那么我们可以让父子进程在指向同一个文件的前提下,一个负责读缓冲区一个负责往缓冲区写,但不调用底层的读写函数不往文件上刷新,就实现了进程间的通信,这种基于文件的通信方式就叫做管道

(站在操作系统角度下一切皆文件)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8UjEqdh4-1677869434020)(G:\Typora\图片保存\image-20221211170529989.png)]

所以管道是一个单向通信的通信通道(半双工)

匿名管道

​ 进程是具有独立性的,想让进程间通信其实成本是比较高的,因为必须解决一个问题,创建出一份公共资源(内存、队列、文件、缓冲区)让不同的进程看到,因此操作系统提供了一个函数接口 int pipe(int pipefd[2]),可以创建一个匿名管道

管道没有名字,只能在具有公共祖先的进程(父进程和子进程,或两个兄弟进程)之间使用,所以叫做匿名管道

头文件 #include <unistd.h>
参数:pipefd[2]是一个输出型参数,通过这个参数读取到打开的两个fd
返回值:若成功,返回0;若出错,返回-1并设置errno

​ 匿名管道实际上是一种固定大小的缓冲区,匿名管道对于管道两端的进程而言就是一个文件,可以看成它是一种特殊的文件,并且只存在于内存中。

如何利用pipe实现通信?

  • 父进程利用pipe获取到两个文件描述符
  • 父进程fork出子进程,子进程以父进程为模板初始化得到一份相同的struct file* array[ ]
  • 一个负责读,一个负责写,但要不能冲突,且要关闭不用的文件描述符接口

示例1:

写端写的慢(或者不写),读端要等写端

管道带有同步机制,读走的数据和写入数据是在管道内部是同步的,且数据一旦被读走,它就从管道中丢弃


写端退出,读端读完pipe内部数据后也退出

示例2:

读端读的慢或读端不读,写端要等读端

现象1:为什么一行读取很多个 hello 你好?

只要匿名管道里还有空间,写的一端就会一直按字节为单位地写,只要管道内还有数据,读的一端就会一直按字节为单位地读,直到返回0表明读到文件末尾,是面向字节流的

字节流:按字节写入,按字节读取

也就是子进程不断在写,而父进程延时读取,读的时候,管道有多少字节读都多少字节,所以可以看到读取很多

现象2:为什么读到65536就不读了?

实际是因为,管道内的大小为64KB(65536/1024 = 64)

所以写端在等读端读取(write置阻塞等待),读取一定的大的字节后才会继续写入

即:

  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

写端写满后,读端至少读取4KB数据才能唤醒写端使写端继续写,同时写说明管道自带同步机制,不会出现新老数据覆盖的问题。

读端关闭,写端也退出产生终止信号并导致子进程退出,只有在管道的读端存在时,向管道中写入数据才有意义

​ 否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)

匿名管道读写规则:

  • 读端不读或者读端读的慢,写端要等读端
  • 管道的大小是64kb,管道写满时,最少读走4KB,才会继续写入(write挂起等待,暂停写入)
  • 写端不写或者写端写的慢,读端要等写端(没有数据时,read挂起等待,暂停读取)
  • 读端关闭,写端产生终止信号13(SIGPIPE)直接终止
  • 写端关闭,读端读完内部数据后关闭(read返回0,代表读到末尾了)

管道的特点:

  • 管道是一个单向通信的通道(半双工)

  • 管道是面向字节流的(具体体现在,写端等读端,读端读的时候读取所有写入的字节,按字节写入按字节读取),所以一般双方会规定一些协议

  • 管道自带同步机制,原子性写入(读过的数据和写入的数据在管道内部是同步的,所以不会出现读到老数据或新数据覆盖问题)

  • 匿名管道是在带有继承关系的进程下完成的

  • 管道的生命周期是随着进程的

    ​ 管道也是文件,只要被一个进程打开,struct file的引用计数就会+1,相关进程退出,引用计数就-1,引用计数为0时,OS会自动关闭管道文件


命名管道

​ 匿名管道只能用于具有继承关系的进程间通信,这就导致有很大局限性,无法在两个不相干的进程间通信。

​ 假如在目录中创建一个真实存在的文件,就可以让不同的进程打开同一份文件了,那么操作系统提供了mkfifo函数可创建一个命名管道文件

int mkfifo(const char *pathname, mode_t mode);
// 返回值:成功返回0,出错返回-1
头文件#include <sys/stat.h>
pathname:文件名+路径
mode:参数与open函数中的 mode 相同,表示创建这个文件的权限
int main()    
{    
  umask(0);    
  if(mkfifo("./myfifo",0666) < 0)    
  {    
    perror("mkfifo:");    
    return 1;    
  }    
    
  return 0;    
}    

命名管道文件有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中

命名管道文件不会重复创建

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y0gtfzU8-1677869434021)(G:\Typora\图片保存\image-20221212195815540.png)]

命名管道具有匿名管道所有特点,创建好管道文件后,只需要像文件操作一样即可实现不同进程间通信

/*************************************out.c*************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/wait.h>
int main()
{
  //创建管道文件
  umask(0);
  if(mkfifo("./myfifo",0666) < 0)
  {
    perror("mkfifo:");
    return 1;
  }
  
  //打开文件
  int fd = open("./myfifo",O_RDONLY);
  if(fd < 0)
  {
    perror("open:");
    return 1;
  }

  while(1)
  {
    char buffer[64] = {0};
    ssize_t ret = read(fd,buffer,sizeof(buffer)-1 );		
    //期望读取63个但读到末尾就停止,留出一个位置 置一个\0,虽然内核存储没有\0,但是使用printf打印是C的接口,需要置一个\0
    buffer[ret] = 0; 		//置\0                                       
    if(ret > 0)
    {
      if(strcmp(buffer,"show") == 0)		
      {
        if(fork() == 0)									//利用子进程替父进程执行命令
        {
          execl("/usr/bin/ls","ls","-a","-l",NULL);		//进程替换
          exit(-1);										
        }
        int status = 0;
        waitpid(-1,&status,0);							//进程等待
        if(WIFEXITED(status))							//获取进程正常执行退出的退出码
          printf("exit code:%d\n",WEXITSTATUS(status));
        else if(WIFSIGNALED(status))					//获取进程退出异常的信号
          printf("exit signal:%d\n",WTERMSIG(status));       
      }
      printf("%s\n",buffer);
      

    }
    else if(ret == 0)
    {
      printf("out quit...\n");
      break;
    }
    else 
    {
      perror("read:");
      break; 
    }
  }

  close(fd);
  return 0;
}

                                                    

/*************************************put.c*************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main()
{
  //有一个创建管道文件即可,管道文件不可重复创建
 /* umask(0);
  if(mkfifo("./myfifo",0666) < 0)
  {
    perror("mkfifo:");
    return 1;
  }
*/


//推荐系统调用接口,可以减少一些拷贝,如C提供的用户缓冲区
  int fd = open("./myfifo",O_WRONLY);			//只写
  if(fd < 0)
  {
    perror("open:");
    return 1;
  }


  //发布命令
  while(1)
  {
    char buffer[64] = {0};							//先把要发布的命令保存
    printf("请输入:");
    fflush(stdout);
    ssize_t ret = read(0,buffer,sizeof(buffer)-1);	//\0只是C语言规定的,read是系统调用接口,是没有\0这个概念的
    if(ret > 0)
    {
      buffer[ret - 1] = 0;                          //因为输入完要按回车,所以要去除\n
      write(fd,buffer,ret);							//发布命令
    }
  }

  close(fd);
  return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CdUyyKHZ-1677869434022)(G:\Typora\图片保存\image-20221212212058593.png)]

现在让out不读,而put一直写:

管道文件不会真的刷新到磁盘,只会在缓冲区中进行读写,可以节省效率

管道总结:

​ 管道是最基本的进程间通讯,它是单向通讯(类似半双工)。它属于内存缓冲区中申请的一份公共资源,是一种特殊的文件,用于连接一个写进程一个读进程。一个进程把数据写入管道,由内核定向的流入另一个读进程。

命名管道:为了保证不同的进程看到同一个文件,必须有名字

匿名管道:文件没有名字,因为他是通过进程继承方式看到同一份资源的,不需要名字标识

读写规则:

  • 读端不读或者读端读的慢,写端要等读端
  • 管道的大小是64kb,管道写满时,最少读走4KB,才会继续写入(write挂起等待,暂停写入)
  • 写端不写或者写端写的慢,读端要等写端(没有数据时,read挂起等待,暂停读取)
  • 读端关闭,写端产生终止信号13(SIGPIPE)直接终止
  • 写端关闭,读端读完内部数据后关闭(read返回0,代表读到末尾了)

管道的特点:

  • 管道是一个单向通信的通道(半双工)

  • 管道是面向字节流的(具体体现在,写端等读端,读端读的时候读取所有写入的字节,按字节写入按字节读取),所以一般双方会规定一些协议

  • 管道自带同步机制,原子性写入(读过的数据和写入的数据在管道内部是同步的,所以不会出现读到老数据或新数据覆盖问题)

  • 匿名管道是在带有继承关系的进程下完成的

  • 管道的生命周期是随着进程的

    ​ 管道也是文件,只要是文件被一个进程打开,struct file的引用计数就会+1,相关进程退出,引用计数就-1,引用计数为0时,OS会自动关闭管道文件

2.基于内存的通信方式

共享内存

​ 进程间通信的本质是先让不同的进程看到同一份资源,除了前面介绍的管道通信还有一些其他的方式,systemV标准是在操作系统层面专门为同一主机内的进程间通信设计的一个方案。

systemV常见的通信方式有以下3种:

  • 共享内存(本篇介绍)
  • 消息队列
  • 信号量

​ 共享内存就是让多个进程地址空间在同一个物理内存通过映射的方式在页表中建立的联系


那么我们可以通过某种方式,在内存中申请一块空间,让进程都“挂接“上这块内存,等于让不同的进程看到了同一份资源

​ 那么上述介绍的就是共享内存的原理

那么如何实现申请和挂接呢?若是不用了以后呢?所以可分为以下4个步骤

  1. 申请内存
  2. 挂接到内存
  3. 去除挂接
  4. 释放内存

这4个步骤都是利用OS提供的接口函数

  1. 申请内存,需要用到系统提供的 int shmget(key_t key, size_t size, int shmflg); 函数接口

    key:

    key表示共享内存的键值,可以自己填写,也可以由一个算法生成(key_t ftok(const char *pathname, int proj_id);

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3rqXSiQR-1677869434022)(G:\Typora\图片保存\image-20221213121549158.png)]

    • 生成成功则会返回 路径+id转换的IPC键值

    • 生成失败返回 -1

    size:

    size表示想申请的空间大小,os是按4kb为基准值分配的,不满4kb的会向上调整分配为4kb,所以这里建议按4kb的倍数申请

    shmflg:

    shmflg可以设置申请方式以及权限,这里介绍两种:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fj9mo1tV-1677869434023)(G:\Typora\图片保存\image-20221213122409146.png)]

    • IPC_CREAT:单独使用或者不用(即shmflg=0)时表示:申请的内存时的key值若存在,则返回这个内存的shmid号(返回值),若不存在则申请到共享内存

    • IPC_EXCL:IPC_EXCL单独使用没有任何意义,要配合IPC_CREAT使用,IPC_EXCL|IPC_CREAT表示:若申请内存的key已经存在则返回错误(-1),若不存在则创建

    最后在后面或上8进制形式的权限即可

    返回值:

    • 申请成功返回一个id号(shmid),类似文件描述符fd,也是一个数组的下标。

      (具体这个数组关连的哪些数据结构在后面介绍)

    • 申请失败返回-1

    ​ shmget使用类似于管道,一个申请,另一个只要保证申请时传入的key是相同就可以获取到申请内存返回的shmid;而ftok函数保证传入的自定义路径和自定义id是相同的就可以获取到相同的key值

    ​ 而通过运行结果可以反映出,程序结束并不会回收这个内存,所以out再次运行时会申请失败,put再次运行还是可以获取到

    在命令行输入 ipcs -m 可以查看共享内存使用情况

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-198GlVvx-1677869434023)(G:\Typora\图片保存\image-20221213153527815.png)]

    注意:system V的IPC资源,生命周期是随内核的,只能通过显示释放(命令、system call)或者重启OS

    命令行输入 ipcrm -m shmid号 可释放内存

  2. 释放内存,需要用到的 int shmctl(int shmid, int cmd, struct shmid_ds *buf); 函数接口

    上面介绍了命令行的释放,实际应该像申请动态内存、打开文件那样,要在程序中中做到谁申请谁释放

    所以可以用 shmctl控制内存函数

    • 成功返回0

    • 失败返回-1

    这里只介绍删除,在cmd参数传入 IPC_RMID,若cmd传的是IPC_RMID,第三个参数传NULL即可

  3. 进程挂载内存需要用到 void *shmat(int shmid, const void *shmaddr, int shmflg);接口函数

    挂接内存的本质是,让进程地址空间与共享内存在页表建立映射关系

    shmid:就是申请内存时返回的的shmid,共享内存标识符

    shmaddr:指定挂接的地址,一般填NULL即可(真实情况只有OS清楚),

    shmflg:表示挂接方式,一般设置0

    返回值:

    • 挂接成功返回首地址,也就是进程地址空间中对应页表映射的首地址,类似于malloc
    • 失败返回-1

  4. 取消挂载,用到int shmdt(const void *shmaddr);函数接口

    取消挂接的本质是取消进程地址空与共享内存在页表的映射关系

    shmaddr:挂载时返回给用户的地址

    返回值:

    • 成功返回0
    • 失败返回-1

验证

/************************************com.h***************************************/
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>

/************************************out.c***************************************/
#include "com.h"

int main()
{
  key_t key = ftok(".",0x666);
  if(key < 0)
  {
    perror("ftok:");
    return 1;
  }

  //申请共享内存
  int shmid = shmget(key,4097,IPC_CREAT|IPC_EXCL|0666);
  if(shmid < 0)
  {
    perror("shmget");
    return 1;
  }
  printf("key:%#x shmid:%d\n",key,shmid);
  printf("申请成功\n");
  sleep(10);

  

  //挂接页表-虚拟内存,挂接成功返回起始地址,挂接到页表后,返回映射的虚拟地址中的首地址
  char* addr =(char*)shmat(shmid,NULL,0);
  if(addr == NULL)
  {
    perror("shmat");
    return 1;
  }
  sleep(10);
  printf("挂接成功\n");

  //通信#############
  
  while(1)
  {
      sleep(1);
      printf("%s\n",addr);
  }

  //##################

  //取消挂接,本质是去除进程地址空间与共享内存在页表的映射关系
  shmdt(addr);
  sleep(10);
  printf("取消挂接\n");


  
  //释放共享内存
  shmctl(shmid,IPC_RMID,NULL);
  printf("释放内存\n");
  return 0;
}

/************************************put.c***************************************/

#include "com.h"

int main()
{
  key_t key = ftok(".",0x666);
  if(key < 0)
  {
    perror("ftok:");
    return 1;
  }

  //申请共享内存
  int shmid = shmget(key,4097,IPC_CREAT);//获取
  if(shmid < 0)
  {
    perror("shmget");
    return 1;
  }
  printf("key:%#x shmid:%d\n",key,shmid);
  printf("获取成功\n");
  sleep(10);
  

  //挂接页表-虚拟内存,挂接成功返回起始地址,挂接到页表后,返回映射的虚拟地址中的首地址
  char* addr =(char*)shmat(shmid,NULL,0);
  if(addr == NULL)
  {
    perror("shmat");
    return 1;
  }
  sleep(5);
  printf("挂接成功\n");

  //通信############
  while(1)
  {
      char ch = 'A';
      while(ch <= 'Z')
      {
          addr[ch - 'A'] = ch;  //[0] = A
          ch++;					//CH = B
          addr[ch - 'A'] = 0;	//[1] = B
          sleep(1);
      }
      
  }

  //#################


  //取消挂接
  shmdt(addr);
  sleep(10);
  printf("取消挂接\n");
  
  
  //释放内存
  //谁申请谁释放,这里的内存是out.c申请的
  /*
  shmctl(shmid,IPC_RMID,NULL);
  printf("key:%0x shmid:%d\n",key,shmid);
  sleep(10);
  */

  return 0;
}

​ 共享内存与管道不同,是一旦申请好直接可以拿到内存的数据,比管道读写数据快,如同malloc一样,并且不提供任何的同步性、原子性,所以这也需要自己设计读写协议

总结:

  • 共享内存生命周期随内核
  • 进程间通信速度最快(用户->内核,而管道直接调用系统接口下至少要经历 用户->缓冲区->管道)
  • 共享内存不提供任何同步机制、原子性等等,是直接可以拿到内存数据
  • 共享内存申请时是以页为单位(4kb),不满页的会向上调整成页,但是不会显示出来

内核相关的数据结构了解

​ 共享内存不仅只限于两个进程间的通信,还有可能多个进程进程之间多个内存间通信等等。为了防止这些进程之间都能正确的和对应的共享内存通信,os就需要将共享内存进行管理,只要提到管理就离不开“先描述,在组织“。

怎么描述,怎么组织?

​ 通过查看共享内存部分内核源码发现,在申请共享内存时会有结构体来记录申请时时的所有详细信息,如创建时间、申请大小、key值等等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2JvE8qG4-1677869434023)(G:\Typora\图片保存\image-20221218155242589.png)]

再查看消息队列、信号量的在内核中的结构体

发现它们都有一个共同点,第一个成员都是 struct ipc_perm

​ 所以在打印shmid时发现其好像是呈数组下标形式,其实正是由于IPC(通信)的多样性,内核的IPC资源结构类型不一样,为了方便管理,所以在每个资源的结构的第一个成员都设置成相同的。

​ 而struct ipc_perm是记录一些比较关键的信息如key值,那么这里也可以类似struct file* array[]一样有一个ipc_perm指针数组,存每个IPC资源的中的ipc_perm,所以shmid的作用就等同于文件描述符fd的作用。

​ ( ipc_perm是第一个成员,第一个成员的地址也是IPC的首地址,可以通过强转类型的方式将每个IPC强转成ipc_perm,再利用ipc_prem* 接收,就可以拿到每个IPC中的ipc_prem了,类似C++中的切片 )

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

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

相关文章

RPC重试机制和控制方案

重试机制 因为网络抖动等原因导致 RPC 调用失败&#xff0c;这时候使用重试机制可以提高请求的最终成功率&#xff0c;减少故障影响&#xff0c;让系统运行更稳定。 重试简易实现方案 在重试的过程中&#xff0c;为了能够在约定的时间内进行安全可靠地重试&#xff0c;在每次…

计算机科学导论笔记(二)

三、数据存储 3.1 数据类型 计算机行业中使用术语“多媒体”来定义包含数字、文本、音频、图像和视频的信息。 位&#xff1a;bit&#xff0c;binary digit的缩写&#xff0c;是存储在计算机中的最小单位&#xff0c;它是0或1. 位模式&#xff1a;为了表示数据的不同类型&a…

关于Paul C.R. - Inductance_ Loop and partial-Wiley (2009)一书的概括

环感抗和部分感抗是两种不同的电路元件&#xff0c;它们通常用于描述不同类型的导体结构中的电流承载能力。 环感抗通常用于描述绕制在磁性芯上的线圈。当电流通过线圈时&#xff0c;它会在磁性芯中产生一个磁场&#xff0c;这个磁场又会对线圈产生一个磁通量。这个磁通量的大…

八股文面试day1

二分查找&#xff08;解决整数溢出&#xff09; 方法一&#xff1a;mid(leftright)/2 -> left/2right/2 -> left(-left/2right/2) -> l(r-l)/2 方法二&#xff1a;mid(lr)>>>1 字节溢出整体右移一位就是正确的值 MySQL存储引擎MyISAM和InnoDB的区别 数据…

Raspberry Pi GPIO入门指南

如果您想使用 Raspberry Pi 进行数字输入/输出操作&#xff0c;那么您需要使用 GPIO&#xff08;通用输入/输出&#xff09;引脚。在这篇文章中&#xff0c;我们将为您提供 Raspberry Pi GPIO 的基础知识&#xff0c;包括如何访问和操作 GPIO 引脚。 0.认识GPIO 树莓派上的那…

MMSeg绘制模型指定层的Heatmap热力图

文章首发及后续更新&#xff1a;https://mwhls.top/4475.html&#xff0c;无图/无目录/格式错误/更多相关请至首发页查看。 新的更新内容请到mwhls.top查看。 欢迎提出任何疑问及批评&#xff0c;非常感谢&#xff01; 摘要&#xff1a;绘制模型指定层的热力图 可视化环境安装 …

matlab - 程序流程控制、函数文件、特殊函数、调试与优化

学习视频MATLAB代码的两种执行方式&#xff1a;命令行、程序执行。1.程序流程控制1.1.m文件matlab中m文件分为两种&#xff1a;脚本文件&函数文件。脚本文件&#xff1a;实际上是一个命令的集合&#xff0c;可认为是命令行的改良版&#xff0c;方便我们去编写命令函数文件&…

守护进程 || 精灵进程

目录 守护进程&#xff08;deamon&#xff09; || 精灵进程 特点 什么是前台进程组 把自己写的服务器deamon deamon代码 守护进程&#xff08;deamon&#xff09; || 精灵进程 特点 01. 他的PPID是1&#xff08;附件特征&#xff09;02. COMMAND --- 称为进程启动的命令03…

[ vulnhub靶机通关篇 ] Empire Breakout 通关详解

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

应用层协议 HTTP HTTPS

目录 应用层 再谈 "协议" 序列化和反序列化 关于 json库 request序列化 request反序列化 response序列化 response反序列化 PS&#xff1a;命令宏 HTTP协议 认识URL urlencode和urldecode HTTP协议格式 HTTP请求 HTTP响应 请求方法 ​编辑 HT…

使用Element-UI展示数据(动态查询)

学习内容来源&#xff1a;视频P4 本篇文章进度接着之前的文章进行续写 精简前后端分离项目搭建 Vue基础容器使用 目录选择组件修改表格组件修改分页组件增加后端接口前端请求数据接口页面初始化请求数据点击页码请求数据选择组件 在官方文档中选择现成的组件&#xff0c;放在页…

XSS漏洞分类

XSS漏洞分类XSS分类反射型XSS反射XSS案例存储型XSS存储型XSS实操XSS分类 XSS根据类型和利用手法的不同&#xff0c;主要分为三大类&#xff1a; 反射型XSS存储型XSSDOM型XSS 反射型XSS 反射型XSS又称为非持久型XSS&#xff0c;是现在最容易出现的一种XSS漏洞。用户在请求某…

零死角玩转stm32初级篇4-初识 STM32 库

本篇博文目录:一.STM32库开发的相关概念知识1.什么是 STM32 库&#xff1f;2.CMSIS 标准二.STM32 结构及库层次关系1.库目录&#xff0c;文件简介2.CMSIS3.STM32F10x_StdPeriph_Driver4.STM32F10x_StdPeriph_Template5.库各文件间的关系三.使用库帮助文档1.常用官方资料2.官网手…

Math类详解与Random类、三种随机数生成方式(java)

文章目录&#x1f4d6;前言&#xff1a;&#x1f380;认识Random类&#x1f380;三种随机数生成方式&#x1f380;Math类的用途&#x1f380;Math类的方法&#x1f4d6;前言&#xff1a; 本篇博客主要以介绍Math类的常用方法及认识Random类&#xff0c;及三种随机数生成方式 …

Docker学习(二十)什么是分层存储?

目录1.简介2.什么是 Union Mount&#xff1f;3.分层介绍1&#xff09;lowerdir 层&#xff08;镜像层&#xff09;2&#xff09;upperdir 层&#xff08;容器层&#xff09;3&#xff09;merged 层4.工作原理1&#xff09;读&#xff1a;2&#xff09;写&#xff1a;3&#xff…

Java位集合之BitMap,BitSet解析

文章目录1 Java位集合1.1 Bit-Map1.1.1 简介1.1.2 添加1.1.3 清除1.1.4 查找1.2 Bitmap应用1.2.1 快速排序1.2.2 快速去重1.2.3 快速查找1.3 BitSet1.4 Bloom Filters1.4.1 简介1.4.2 BloomFilter 流程1 Java位集合 前几天刚学习了Redis中位操作命令&#xff0c;今天顺便学下j…

【unity】rts engine 6 放置并建造建筑;

一 放置并建造建筑 GameManager -> Essential -> BuildingExtension 查看 building placement building position y offset Y轴偏移&#xff0c;建筑离地距离&#xff0c;可0.1 terrain max distance 放置建筑与允许地形的最大距离&#xff0c;可1 placable terrain …

linux基本指令和权限

目录 一.shell命令以及运行原理 二.Linux常用指令 1. ls 指令 2. pwd命令 3.cd指令 4. touch指令 5.mkdir指令&#xff08;重要&#xff09; 6.rmdir指令 && rm 指令&#xff08;重要&#xff09; 7.man指令&#xff08;重要&#xff09; 8.cp指令&#xff08;重要&…

AI_Papers周刊:第四期

2023.02.28—2023.03.05 Top Papers Subjects: cs.CL 1.Language Is Not All You Need: Aligning Perception with Language Models 标题&#xff1a;KOSMOS-1&#xff1a;语言不是你所需要的全部&#xff1a;将感知与语言模型相结合 作者&#xff1a;Shaohan Huang, Li …

[C++]vector模拟实现

目录 前言&#xff1a; 1. vector结构 2. 默认成员函数 2.1 构造函数 无参构造&#xff1a; 有参构造&#xff1a; 有参构造重载&#xff1a; 2.2 赋值运算符重载、拷贝构造&#xff08;难点&#xff09; 2.3 析构函数&#xff1a; 3. 扩容 3.1 reserve 3.2 resize…