操作系统:进程间通信 | System V IPC

news2024/11/20 14:26:32

目录

前言:

1.共享内存

1.1.什么是共享内存

1.2.共享内存使用接口

shmget函数

shmat函数

shmdt函数

shmctl函数

2.共享内存实现通信

2.1.代码实现

comm.hpp

 server,cpp

client.cpp

2.2.共享内存的缺点

2.3.实现通信的同步化

 2.4共享内存通信的优势

3.初识消息队列和信号量

3.1.消息队列

3.2.信号量 

4.System V IPC的统一管理


前言:

System V IPC(系统5的进程间通信)是在Unix操作系统上实现进程间通信(IPC)的一种机制。它引入了一大类进程间通信方法,包括共享内存、消息队列和信号灯集等IPC对象。每个IPC对象都有唯一的ID,这个ID在创建时由系统分配,并且IPC对象在创建后一直存在,直到被显式地删除或系统关闭时自动释放。

在这篇博客中,我们重点介绍System V IPC中的共享内存部分,以及学习消息队列、信号量的一些接口的使用和 Linux下实现的System V IPC统一的数据结构体系……

1.共享内存

1.1.什么是共享内存

 我们已经清楚:进程间通信的本质是让不同的进程看到同一份资源。那么共享内存这种进程间通信方式的实现是通过让不同的进程通过“共享”某一块内存来实现的。

共享内存是通过在物理内存中开辟一块内存,使得这块内存通过页表映射,到两个(多个)不同进程的进程地址空间中,从而让不同的进程可以访问到这一块物理内存,实现间接的数据交互的一种进程间通信方式。  

讲完生硬的概念,我们来进入一个场景,共享内存就相当于隔绝了两个村子的河流,而两个进程就相当于这两个村子的两个人,当这两个人(进程)需要传送东西(进行数据交互)就能通过这个河流(共享内存) 来实现,那么通过这个河流,就能实现两个人的通信了……

1.2.共享内存使用接口

shmget函数

在这个图中:我们看到了key是共享内存的名字,那这个key我们怎么获得的呢?

// 通过ftok算法形成一个唯一的key
key_t key = ftok("名称", 传入一个int参数);

因为实际场景下会出现许多个共享内存同时进行通信的情况,这时系统为了对这些共享内存进行管理就需要给他们一个唯一的标识符shmid(类比:文件fd、进程pid),而这个shmid是通过一个唯一的key值来实现的。并且如果我们需要进程进程间通信,那么我们也需要让不同的进程能够访问到同一个key和同一个shmid。

具体实现如下: 

// 同一个文件名
const string filename = "/home/Czh_Linux/code/shared_memory";
// 同一个proj_id
const int proj_id = 2022044026;

// 获取唯一标识符
key_t GetKey()
{
    // 通过ftok算法形成一个唯一的key
    // 通过相同的filename、和project_id
    key_t key = ftok(filename.c_str(), proj_id);
    if (key < 0)
    {
        cerr << "errno: " << errno << ", errstring: " << strerror(errno) << endl;
        exit(1);
    }
    return key;
}

这里我们传入文件名的原因:可以让不同的进程更加方便的访问到同一个key……我们在获取了唯一的key之后,接下来就是学习shmget如何使用了。


shmget的具体使用分为两种情形:

创建一段新的共享内存:

    // 参数(唯一标识符key,共享内存的大小,打开方式|权限)
    int shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0644); // 共享内存的id

这段代码中 :表示创建一段共享内存,则需传入权限,并且通过IPC_EXCL防止重复创建,并且提供权限

 打开一段已有的共享内存:

// 打开方式直接设置为0即可
int shmid = shmget(key, 4096, 0);

但是实际上我们使用时,一般将这两种情况进行封装改造一下:

// 创建/打开 共享内存封装函数
int ShmOption(key_t key, int flag)
{

    // 参数(唯一标识符key,共享内存的大小,打开方式|权限)
    int shmid = shmget(key, MEMORYSIZE, flag); // 共享内存的id
    cout << shmid << endl;
    // key作为在内核中标识shm的唯一性
    // 对共享内存进行操作时,我们是通过shmid来进行的
    if (shmid < 0)
    {
        cerr << "shmid error: " << errno << ", errstring: " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

// 创建新的共享内存,并返回shmid
int CreateShm(key_t key)
{
    cout << "新的共享内存创建完成" << endl;
    // 创建,则需传入权限,并且通过O_EXCL防止重复创建,并且提供权限
    return ShmOption(key, IPC_CREAT | IPC_EXCL | 0644);
}

// 打开原有共享内存,并返回shmid
int GetShm(key_t key)
{
    // 打开,则只需通过O_CREAT打开
    return ShmOption(key, 0);
}

 所以我们外部调用的时候,通过不同的接口实现即可,另外GetKey()函数在上面有,也是我们自己实现的封装的小模块


值得一提的是:我们可以分别通过下面这两个Linux指令实现对共享内存的查看和删除

// 查看当前的共享内存
ipcs -m
// 删除当前共享内存
ipcrm -m "对应的shmid"

另外:如果我们在进程中通过shmget开辟出一块共享内存,却没有通过shmctl进行释放,我们调用ipcs -m就会发现:即使进程退出后,这块共享内存也依旧保留着物理内存中,也就是:共享内存的生命周期是随内核的

shmat函数

我们在1.1.中讲述了,当我们创建了共享内存,只是在物理内存中开辟了一块区域,而我们要通过进程使用这块区域,就需要将这块区域从物理内存挂接到进程地址空间,也就是实现页表的链接。这时我们可以通过shmat函数来实现…… 

// 以一个字符串数组为例
// 传入nullptr表示不对地址有过多的要求,传入0表示我们正常挂接
char *str = (char *)shmat(shmid, nullptr, 0);

 当我们创建了一个str,操作系统就会为它从进程地址空间中找到一块虚拟地址来存放,而通过shmat函数,就实现了这一块虚拟地址映射到共享内存中,最终能够实现:某一个进程对str进行一些操作,其他的进程就能知道操作了什么,进而获取了信息。那么str在这里就扮演着,被进程们看到的一份资源的角色。

shmdt函数

对应上面我们挂接到共享内存的str,那么我们接触链接就通过共享内存式的释放这个str即可

// 内填挂接到共享内存的进程地址空间的地址
shmdt(str);

shmctl函数

值得注意:这里的表述是控制共享内存,而不是删除共享内存,但是这个接口可以实现删除这段共享内存的功能,从物理内存上释放这块内存……

// 找到共享内存的id,通过传入IPC_RMID 来实现
shmctl(shmid, IPC_RMID, nullptr);

到了这里我们对共享内存接口的学习就结束了,接下来我们将有一个demo级别的共享内存通信。

2.共享内存实现通信

2.1.代码实现

comm.hpp

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

// 同一个文件名
const string filename = "/home/Czh_Linux/code/shared_memory";
// 同一个proj_id
const int proj_id = 2022044026;

// 建议设置为4096的整数倍
#define MEMORYSIZE 4096 // 设定的共享内存大小

// 获取唯一标识符
key_t GetKey()
{
    // 通过ftok算法形成一个唯一的key
    // 通过相同的filename、和project_id
    key_t key = ftok(filename.c_str(), proj_id);
    if (key < 0)
    {
        cerr << "errno: " << errno << ", errstring: " << strerror(errno) << endl;
        exit(1);
    }

    return key;
}

// 将key转化为16进制
string ToHex(int id)
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "0x%x", id);
    return buffer;
}

// 创建/打开 共享内存封装函数
int ShmOption(key_t key, int flag)
{

    // 参数(唯一标识符key,共享内存的大小,打开方式|权限)
    int shmid = shmget(key, MEMORYSIZE, flag); // 共享内存的id
    cout << shmid << endl;
    // key作为在内核中标识shm的唯一性
    // 对共享内存进行操作时,我们是通过shmid来进行的
    if (shmid < 0)
    {
        cerr << "shmid error: " << errno << ", errstring: " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

// 创建新的共享内存,并返回shmid
int CreateShm(key_t key)
{
    cout << "新的共享内存创建完成" << endl;
    // 创建,则需传入权限,并且通过O_EXCL防止重复创建,并且提供权限
    return ShmOption(key, IPC_CREAT | IPC_EXCL | 0644);
}

// 打开原有共享内存,并返回shmid
int GetShm(key_t key)
{
    // 打开,则只需通过O_CREAT打开
    return ShmOption(key, 0);
}

 server,cpp

#include "comm.hpp"

int main()
{
    key_t key = GetKey();
    cout << "key: " << ToHex(key) << endl;

    int shmid = CreateShm(key);
    cout << "共享内存id: " << shmid << endl;
    sleep(5);
    // 挂接到共享内存
    char *str = (char *)shmat(shmid, nullptr, 0);

    // 实现通信
    cout << "开始进行通信" << endl;
    while(1)
    {
        sleep(1);
        cout<<"挂接到共享内存的内容:"<<str<<endl;
    }
    shmdt(str);
    cout << "将shm从进程地址空间中移除……" << endl;

    sleep(5);
    shmctl(shmid, IPC_RMID, nullptr);
}

这段代码中,我们通过创建共享内存再实现str挂接到共享内存中,接着通过打印str内容。 

client.cpp

#include "comm.hpp"

int main()
{
    key_t key = GetKey();
    cout << "key: " << ToHex(key) << endl;
    int shmid = GetShm(key);
    cout << "client 连接上共享内存……, id: " << shmid << endl;
    sleep(5);
    char *str = (char *)shmat(shmid, nullptr, 0);
    // 进行通信
    char c = 'a';
    for (; c <= 'z'; c++)
    {
        // 对共享内存对应的地址空间进行写入
        str[c - 'a'] = c;
        cout << "write: " << c << " done" << endl;
        sleep(2);
    }
    shmdt(str);
}

 client作为实现通信数据的发送端,这里我们不断的向str中插入abcd……这一系列字符然后,再server端打印,获取结果,这时server就能够通过str了解到共享内存的变化,也就是验证了能看到同一份资源。

2.2.共享内存的缺点

当我们运行1.3.这个代码demo时,发现我们数据的读取并不是同步的,并不是:写段发送一段数据,读端才进行接收,而是读、写段互不干扰,这里我们可以通过修改sleep的时间来体现,并且这些通信的数据是可以被任意修改的……

那么就有: 

  1. 共享内存通信,没有提供同步的机制,并且数据直接裸露给所有使用者,需要考虑使用安全问题
  2. 会导致数据流无法同步,使得接受端、发送端的信息不一致

 因为共享内存原生的通信方式无法进行同步通信,那么我们能不能嵌套一下我们学过的管道通信这一种同步通信方式来实现同步通信呢?

2.3.实现通信的同步化

这里我们只用修改一下,client和server的代码即可,需要进行测试就,更改对应代码

// 共享内存 + 命名管道通信
void client2()
{
    key_t key = GetKey();
    cout << "key: " << ToHex(key) << endl;
    int shmid = GetShm(key);
    cout << "client 连接上共享内存……, id: " << shmid << endl;
    sleep(5);
    char *str = (char *)shmat(shmid, nullptr, 0);
    // 进行通信
    int r_open = open("fifo", O_WRONLY);

    char c = 'a';
    for (; c <= 'z'; c++)
    {
        // 对共享内存对应的地址空间进行写入
        str[c - 'a'] = c;
        cout << "write: " << c << " done" << endl;
        write(r_open, str, sizeof(str));
        sleep(2);
    }
    close(r_open);
    shmdt(str);
}

// 通过共享内存和命名管道进行通信
void server2()
{
    // 创建管道文件fifo,权限0666
    mkfifo("fifo", 0666);
    key_t key = GetKey();
    cout << "key: " << ToHex(key) << endl;

    int shmid = CreateShm(key);
    cout << "共享内存id: " << shmid << endl;
    sleep(5);

    char *str = (char *)shmat(shmid, nullptr, 0);

    // 通过只读方式接收信息
    int r_open = open("fifo", O_RDONLY);

    // 实现通信
    cout << "开始进行通信" << endl;
    while (1)
    {
        read(r_open, str, sizeof(str));
        cout << "挂接到共享内存的内容:" << str << endl;
        sleep(1);
    }

    shmdt(str);
    cout << "将shm从进程地址空间中移除……" << endl;

    sleep(5);
    close(r_open);
    shmctl(shmid, IPC_RMID, nullptr);
}

如图: 当我们打开服务器时,cilent还没有运行之前,我们发现server处于阻塞状态,这时因为管道文件的读功能,在没有数据进行写入时,会等待直到管道的另一端进行写入。

而当我们进行调用时:发现数据是具有同步性的,并且当我们关掉client后,server端也没有新的数据写入,而是在while(1)中因为代码逻辑不断打印着当前管道的内容…… 


 2.4共享内存通信的优势

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

对于管道通信,我们知道管道通信的本质就是:数据通过管道文件的缓冲区从一个进程到另一个进程的转移,而当涉及到数据的转移其实就是,数据从一块缓冲区到另一块缓冲区的拷贝……

如图:在上层中这一句Hello World从键盘写入到进程,然后通过管道被另一个进程读取,然后打印在显示屏上。而实际上这一句Hello World是通过键盘缓冲区拷贝到用户级缓冲区再拷贝到管道文件缓冲区再拷贝到用户缓冲区最后拷贝到显示器上。 

在这一个简单的场景中就需要实现4次拷贝,而拷贝是一种较大的系统资源消耗的行为,这也导致可管道通信的效率不是最优的,因为文件管理和内存管理是割裂开来的,无法避免拷贝的行为。


而对于共享内存,这段数据并不用通过进程的缓冲区进行互相拷贝,而是只用通过传输到物理内存中,直接通过写入、读取挂接到物理内存中的数据即可,减少了文件缓冲区这一块不必要的拷贝

 所以通过共享内存这个进程间通信,我们可以尽可能的减少拷贝和系统接口的调用,这也就是共享内存是最快的进程间通信方式,实现进程不再通过执行进入内核的系统调用来传递彼此的数据

3.初识消息队列和信号量

3.1.消息队列

消息队列进程间通信机制中的一种重要组件,它允许不同进程之间进行数据的发送和接收。消息队列以链表式的结构组织数据,并存放在内核中,由各个进程通过特定的消息队列标识符来引用和进行数据传送。

消息队列的通信本质:提供一个队列,允许进程往这个队列中,发送、接收一个一个数据块,这个数据块的本质就是一个结构体,内部存储着 数据内容 和 发送方信息。

通过这个机制,我们随意传入数据进入消息队列,接收数据块时只要判断一下传入者信息就能够获取想要的得到的信息,并且当我们想要接收数据时,我们可以在任意时刻接收某一个数据,也就是消息队列允许进程间进行异步通信,又因为它的独特机制(比较于管道)对数据块进行标识,可以更加灵活地对数据进行读取

具体的接口使用可以看一位大神写的这篇优秀的博客:Linux进程间通信-消息队列(IPC、mq)C/C++代码接口_c语言消息队列-CSDN博客

这里建议大家看完这篇博客后,自己实现一下server、client通过消息队列通信的模块!!!

值得一提的是:当我们学习完相关接口的使用时,我们发现共享内存、消息队列的接口出奇的相似,这也是System V IPC标准的一种体现。同理通过msgctl这个函数,我们也可以确定消息队列的生命周期是内核级别的。而因为息队列存放在内核中,并由内核来维护,具有较高的可靠性和安全性。


3.2.信号量 

信号量(Semaphore)是在多线程或多进程环境下使用的一种设施,主要用于控制对共享资源的访问。它可以看作是一个计数器,用于记录可用资源的数量。信号量的主要目的是实现进程或线程间的同步与互斥,以确保对共享资源的正确和安全的访问。

这一部分,因为涉及的内容过多,我们在下一篇博客中具体讲解,但是又因为信号量也是属于System V IPC这个标准的,我们可以知道它函数接口的调用也是类似与共享内存和消息队列的。

4.System V IPC的统一管理

操作系统在实际场景中,往往是需要多块共享内存、多个消息队列、多个信号量机制来进行不同进程间的通信,那么这些共享内存、消息队列、信号量要如何进行维护、进行管理的呢?那就又回到了操作系统的六字真言:先描述再组织

以共享内存的代码为例: 

void test()
{
    key_t key = GetKey();
    cout << "key: " << ToHex(key) << endl;
    int shmid = CreateShm(key);

    struct shmid_ds ds;
    // 通过IPC_STAT将该shmid中维护的结构体数据加载进ds中
    shmctl(shmid, IPC_STAT, &ds);

    cout << ds.shm_perm.__key << endl;
    cout << ToHex(ds.shm_perm.__key) << endl;
    cout << ds.shm_nattch << endl;

    sleep(5);
    shmctl(shmid, IPC_RMID, nullptr);
}
  1. 代码中的struct shmid_ds是操作系统维护共享内存的结构体对象,内部存储着该共享内存的属性,那么我们就能够延伸到操作系统对共享内存的管理,本质上就是对管理着共享内存结构体的结构体数据进行增删查改
  2. 当我们通过共享内存控制的接口,可以将对应的shmid共享内存的数据加载进我们定义的对象,这样子就能够实现对管理信息的访问了。

接着我们看一下IPC结构体是如何管理各自的数据的:

 那么操作系统是如何管理这些不同的IPC对象的呢?我们在上图中看到不同的IPC对象中维护着一个相同的结构体struct ipc_perm,而这个结构体对象就是操作系统实际上管理IPC对象的载体。

结合这两张图,我们发现struct ipc_perm就是操作系统维护IPC对象的最基本的单位,也可以看做一个“基类”,只要我们管理好最基本的struct ipc_perm,就能对不同的IPC对象进行较好的管理。到了这里我们就对System V IPC这个体系有了较好的理解了……

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

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

相关文章

Nginx下载安装,什么是nginx,什么是反向代理,Windows下、linux下安装nginx(保姆级教程)

文章目录 一、Nginx简介为什么要使用NginxNginx的特点Nginx的相关概念正向代理反向代理动静分离负载均衡 二、Nginx安装1. Windows安装2. Linux安装 一、Nginx简介 Nginx 是一个高性能的 HTTP&#xff08;静态资源服务器&#xff09; 和 反向代理 Web 服务器。 为什么要使用N…

【以奖代补】诗情画意润童心 书香课堂志愿行

中华古诗词历史源远流长&#xff0c;名篇佳作数不胜数。为弘扬民族文化精髓&#xff0c;丰富乡村儿童假期生活。2024年4月21日上午&#xff0c;襄州区社会工作者协会联合襄州区张家集镇社工站、张集村“童叟乐园”志愿服务队在张集村开展“诗情画意润童心 书香课堂志愿行”志愿…

数据库安全如何保障?YashanDB有妙招(上篇)

数据库作为信息系统的核心&#xff0c;不仅承载着海量的关键数据&#xff0c;还负责向各类用户提供高效、可靠的信息服务&#xff0c;数据库的安全性显得尤为关键&#xff0c;已成为信息安全体系的重中之重。 什么是数据库安全&#xff1f; 数据库安全是数据安全的一个子集&…

Linux——界面和用户

本篇文章所写的都是基于centos 7 64位&#xff08;通过虚拟机运行&#xff09;。 一、Linux的界面 Linux操作系统提供了多种用户界面&#xff0c;主要分为图形用户界面&#xff08;GUI&#xff09;和命令行界面&#xff08;CLI&#xff09;。 1、图形用户界面(GUI)&#xff…

第十五届蓝桥杯省赛第二场C/C++B组D题【前缀总分】题解(AC)

暴力解法 O ( 26 n 5 ) O(26n^5) O(26n5) 枚举将第 i i i 个字符串的第 j j j 个字符改为 c c c 的所有方案&#xff0c;时间复杂度 O ( 26 n 2 ) O(26n^2) O(26n2)&#xff0c;修改并计算总分&#xff0c; O ( n 3 ) O(n^3) O(n3)。 暴力优化 O ( 26 n 3 log ⁡ n ) O…

IDEA使用技巧(常用设置、快捷键等)

IDEA使用技巧 一、IDEA常用基本设置设置代码背景颜色/主题/字体Ctrl鼠标滚轮缩放字体大小设置字符编码左右两侧的Project&#xff0c;Structure&#xff0c;Maven等按钮消失新增类似sout,psvm的模版切换某个模块编译的JDK版本 二、常用快捷键CtrlAltT包裹代码Alt回车联想补全Ct…

linux系统下载huggingface文件教程

文章目录 准备工作添加SSH Key生成Access Token 模型下载公开模型下载&#xff08;bert-base-chinese为例&#xff09;非公开模型下载&#xff08;Llama3为例&#xff09;权限申请官网预训练模型下载huggingface仓库下载 准备工作 添加SSH Key # 本地机器生成ssh key # step1…

Spring 5源码学习

文章目录 一. 访问[spring官网], 找到Spring Framework&#xff0c;点击红色标记github仓库&#xff0c;下载对应的分支代码&#xff0c;本人下载5.1.x二. 安装gradle三. 调整spring-framework配置四. 开始编译五.导入idea 一. 访问[spring官网], 找到Spring Framework&#xf…

Linux离线安装Harbor镜像仓库

一、Harbor简介 Harbor是一个开源的企业级Docker Registry管理项目&#xff0c;由VMware公司开源。它提供了比Docker官方公共镜像仓库更为丰富和安全的功能&#xff0c;尤其适合企业环境使用。Harbor的关键特性包括权限管理&#xff08;RBAC&#xff09;、LDAP集成、日志审计、…

《动手学深度学习(Pytorch版)》Task02:预备知识——4.25打卡

《动手学深度学习&#xff08;Pytorch版&#xff09;》Task02&#xff1a;预备知识——4.25打卡 数据操作N维数组——张量创建数组访问元素入门初始化矩阵 运算符广播机制索引和切片节省内存转换为其他Python对象转换为NumPy张量ndarray张量转换为Python标量 数据预处理安装pan…

2023平航杯——介质取证部分复现

闻早起的电脑 教徒“闻早起”所使用的笔记本电脑使用何种加密程式&#xff1f; VeraCrypt 教徒“闻早起”所使用的笔记本电脑中安装了一款还原软件&#xff0c;其版本号为&#xff1f;【标准格式&#xff1a;1.2.3.4】 8.71.020.5734 教徒“闻早起”所使用的笔记本电脑中登…

【SpringCloud】OpenFeign高级特性

【SpringCloud】OpenFeign高级特性 文章目录 【SpringCloud】OpenFeign高级特性1. 超时控制1.1 全局配置1.2 指定配置 2. 重试机制3. 替换Http客户端3.1 引入依赖3.2 配置 4. 请求/响应压缩5. 日志打印6. 综合配置 1. 超时控制 默认OpenFeign客户端等待60秒钟&#xff0c;但是服…

【硬件设计】基于LM22679芯片的DC-DC转换器

文章目录 1. DC-DC转换器的基本原理1.1 DC-DC转换器是什么1.2 DC-DC转换器的应用场景 2. LM22679芯片2.1 LM22679芯片特性和基本参数2.2 LM22679原理 3. LM22679电路设计实例 1. DC-DC转换器的基本原理 1.1 DC-DC转换器是什么 DC-DC转换器是一种用于将直流&#xff08;DC&…

Python打怪升级(4)

在计算机领域常常有说"合法"和"非法"指的是:是否合理&#xff0c;是否有效&#xff0c;并不是指触犯了法律。 random.randint(begin,end) 详细讲解一下这个random是指模板&#xff0c;也就是别人写好的代码直接来用&#xff0c;在Python当中&#xff0c;…

利用弹性云主机部署高效数据库系统

在当今云计算的时代&#xff0c;弹性云主机&#xff08;EC2&#xff09;为我们提供了前所未有的灵活性和可扩展性&#xff0c;使得在云端部署高效的数据库系统成为可能。本文将从选择适当的云服务配置、优化数据库设置、建立完备的数据备份与恢复策略&#xff0c;以及加强数据库…

forward 函数在深度神经网络程序中确实用于表示程序逻辑,特别是网络的前向传播过程。它描述了输入数据如何通过网络的每一层,并最终得到输出预测值的流程

在深度神经网络&#xff08;例如卷积神经网络&#xff0c;CNN&#xff09;的程序中&#xff0c;forward 函数通常用于描述网络的前向传播&#xff08;forward pass&#xff09;过程。前向传播是神经网络的核心操作之一&#xff0c;它指的是输入数据通过网络的每一层&#xff0c…

【漏洞复现】WebLogic XMLDecoder反序列化(CVE-2017-10271)

1、漏洞描述 CVE-2017-10271漏洞产生的原因大致是Weblogic的WLS Security组件对外提供webservice服务&#xff0c;其中使用了XMLDecoder来解析用户传入的XML数据&#xff0c;在解析的过程中出现反序列化漏洞&#xff0c;导致可执行任意命令。攻击者发送精心构造的xml数据甚至能…

基于小程序实现的惠农小店系统设计与开发

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

蓝桥杯python考级整理

4_1:算术运算符 4_2:基本语法 4_3:基本语法 4_4:列表 4_5:函数 4_6:字符串 4_7:列表 4_8:逻辑运算符 4_9:字典 4_10:函数

ds18b20温度传感器驱动程序

ds18b20驱动程序 有了之前延时的方法&#xff0c;那么实现一个单总线数据传输的传感器驱动程序就非常简单了。下面我们套用杂项驱动框架来编写ds18b20驱动程序。 实现需要明确的是&#xff1a;**ds18b20驱动的本质是通过2440的gpio&#xff0c;通过给定的时序对ds18b20的读写数…