C/C++ 进程间通信system V IPC对象超详细讲解(系统性学习day9)

 

目录

前言

一、system V IPC对象图解

1.流程图解:

​编辑

2.查看linux内核中的ipc对象:

 二、消息队列

1.消息队列的原理

2.消息队列相关的API

2.1 获取或创建消息队列(msgget)

 实例代码如下:

2.2 发送消息到消息队列中

 实例代码如下:

2.3 从消息队列中获取消息  

实例代码如下: 

 2.4 消息队列相关的命令

 2.5 管理消息队列

 实例代码如下:

三、共享内存

1.概念

2.原理图解

3.相关的api函数

3.1 创建共享内存对象

 实例代码如下:

3.2 映射共享内存  

 3.3 取消映射

 实例代码reader.c如下:

 实例代码writer.c如下:

实例代码shm.h 如下:

 3.4 管理共享内存

实例代码如下: 

四、信号灯 (信号量)

1.概念

2.信号灯相关的api函数

2.1 创建或获取信号灯对象

实例代码如下:

2.2 实现P操作和V操作  

实例代码如下:

2.3 管理信号灯  

实例代码如下: 

总结


前言

System V IPC(Inter-Process Communication)对象是一种用于在不同进程之间进行通信的机制。它包括三种类型的对象:消息队列(Message Queue)、信号量(Semaphore)和共享内存(Shared Memory)。


一、system V IPC对象图解

1.流程图解:

e3b7f73d700f4fe085171956b7ac8851.png

2.查看linux内核中的ipc对象:

fbaf8bbd33cc44b59fc87277a69541ee.png

 

 二、消息队列

1.消息队列的原理

d8f9942acfe846f0a214a7fade16240d.png

 

2.消息队列相关的API

2.1 获取或创建消息队列(msgget)

头文件:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);
//参数1  ----- key :
               动态获取key:  ftok()
                    #include <sys/types.h>
                    #include <sys/ipc.h>
                    key_t ftok(const char *pathname, int proj_id);
                    //参数1  ---- 工程目录
                    //参数2  ---- 工程编号
                    //返回值 ----- 成功,返回key值,失败:-1
               静态分配: IPC_PRIVATE
//参数2 ------ msgflg:如果消息队列不存在,需要给出创建的关键字,并设置权限 IPC_CREAT | 0666
//返回值 ----- 成功:消息队列的ID,失败:-1    

 实例代码如下:

int main(void)
{key_t key;int msg_id;//获取key值key = ftok("./",0xa);if(key < 0){perror("ftok");exit(1);}//创建或获取消息对象msg_id = msgget(key,IPC_CREAT|0666);if(msg_id < 0){perror("msgget");exit(1);}return 0;
}

2.2 发送消息到消息队列中

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//参数1 ----- 消息队列的id
//参数2 -----  指向struct msgbuf结构体的指针,该结构体需要自己定义,如下:
                struct msgbuf {
                   long mtype;       /* message type, must be > 0 消息类型*/
                   char mtext[1];    /* message data  消息正文*/
                };
//参数3 ---- 要发送的消息的长度 
//参数4 ---- msgflg值如下:
            IPC_NOWAIT  消息没有发送完成函数也会立即返回。
            0:直到发送完成函数才返回
//返回值 --- 成功:0,失败:-1

 实例代码如下:

int main(void)
{key_t key;int msg_id;//获取key值key = ftok("./",0xa);if(key < 0){perror("ftok");exit(1);}//创建或获取消息对象msg_id = msgget(key,IPC_CREAT|0666);if(msg_id < 0){perror("msgget");exit(1);}//向消息队列中发送消息while(1){bzero(&buf,sizeof(buf));printf("请输入消息的类型:");scanf("%ld",&buf.mtype);printf("请输入消息:");while(getchar() != '\n');   //清空输入缓冲区fgets(buf.mtext,sizeof(buf.mtext),stdin);buf.mtext[strlen(buf.mtext)-1] = '\0';if(msgsnd(msg_id,&buf,strlen(buf.mtext),0) < 0){perror("msgsnd");exit(1);}}return 0;
}

2.3 从消息队列中获取消息  

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
//参数1 ----- 消息队列的id
//参数2 -----  指向struct msgbuf结构体的指针,该结构体需要自己定义,如下:
                struct msgbuf {
                   long mtype;       /* message type, must be > 0 消息类型*/
                   char mtext[1];    /* message data  消息正文*/
                };
//参数3 ----  mtext的长度
//参数4 ----  msgtyp:要接收的消息类型
             msgtyp > 0 ,表示接收指定类型的消息 
             msgtyp = 0 ,按先后顺序依次接收不同类型消息
             msgtyp < 0 ,优先接收消息类型不大于|msgtyp|的最小类型的消息
//参数5 ---- msgflg值如下:
            IPC_NOWAIT  消息没有发送完成函数也会立即返回。
            0:直到发送完成函数才返回
//返回值 --- 成功:0,失败:-1

实例代码如下: 

int main(void)
{key_t key;int msg_id;//获取key值key = ftok("./",0xa);if(key < 0){perror("ftok");exit(1);}//创建或获取消息对象msg_id = msgget(key,IPC_CREAT|0666);if(msg_id < 0){perror("msgget");exit(1);}//从消息队列中获取消息while(1){bzero(&buf,sizeof(buf));printf("请输入消息的类型:");scanf("%ld",&buf.mtype);if(msgrcv(msg_id,&buf,sizeof(buf.mtext),buf.mtype,0) < 0){perror("msgsnd");exit(1);}printf("msg:%s\n",buf.mtext);}return 0;
}

 2.4 消息队列相关的命令

peter@ubuntu:~/2308/proc/day04_code$ ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages
0x0a010356 0          peter      666        61           5

peter@ubuntu:~/2308/proc/day04_code$ ipcrm -q 0
peter@ubuntu:~/2308/proc/day04_code$ ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

 2.5 管理消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
 //参数1 ---- 消息队列ID
 //参数2 ---- 功能码:
                 IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
                IPC_SET:设置消息队列的属性。这个值取自buf参数。
                IPC_RMID:从系统中删除消息队列。    
 //参数3 ----struct msqid_ds 结构体指针
                 struct msqid_ds {
                       struct ipc_perm msg_perm;     /* Ownership and permissions */
                       time_t          msg_stime;    /* Time of last msgsnd(2) */
                       time_t          msg_rtime;    /* Time of last msgrcv(2) */
                       time_t          msg_ctime;    /* Time of last change */
                       unsigned long   __msg_cbytes; /* Current number of bytes in
                                                        queue (nonstandard) */
                       msgqnum_t       msg_qnum;     /* Current number of messages
                                                        in queue */
                       msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                        allowed in queue */
                       pid_t           msg_lspid;    /* PID of last msgsnd(2) */
                       pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
                   };

 实例代码如下:

int main(int argc,char **argv)
{int msg_id;msg_id = atoi(argv[1]);if(msgctl(msg_id,IPC_RMID,NULL) < 0){perror("msgget");exit(1);}return 0;
}

三、共享内存

1.概念

共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝

为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间

进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。

由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等 

2.原理图解

 d3ea3585b84f4dd1a960bd7045694f9f.png

 

3.相关的api函数

3.1 创建共享内存对象

头文件:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
//参数1 ---- 动态获取key:  ftok()
                    #include <sys/types.h>
                    #include <sys/ipc.h>
                    key_t ftok(const char *pathname, int proj_id);
                    //参数1  ---- 工程目录
                    //参数2  ---- 工程编号
                    //返回值 ----- 成功,返回key值,失败:-1
               静态分配: IPC_PRIVATE
//参数2 ---- 要创建或获取的共享内存的大小
//参数3 ---- 权限:IPC_CREAT | 0666
//返回值 ---- 成功:共享内存ID,失败:-1

 实例代码如下:

int main(void)
{key_t key;int shm_id;//获取key值key = ftok("./",0xa);if(key < 0){perror("ftok");exit(1);}//创建或获取共享内存对象shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);if(shm_id < 0){perror("shmget");exit(1);}return 0;
}

3.2 映射共享内存  

void *shmat(int shmid, const void *shmaddr, int shmflg);
//参数1 ---- 共享内存ID
//参数2 ---- 指定进程虚拟空间的映射的起始地址,一般为NULL:让系统分配一个起始地址
//参数3 ---- 访问权限:SHM_RDONLY:共享内存只读
                    默认0:共享内存可读写
//返回值 --- 成功:映射的虚拟空间地址,失败:-1

 3.3 取消映射

int shmdt(const void *shmaddr);
//参数 ----映射的虚拟空间的起始地址
//返回值 ----成功:0,失败:-1

 实例代码reader.c如下:

#include "shm.h"int main(void)
{key_t key;int shm_id;char *buf;//获取key值key = ftok("./",0xa);if(key < 0){perror("ftok");exit(1);}//创建或获取共享内存对象shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);if(shm_id < 0){perror("shmget");exit(1);}//将共享内存映射到进程的虚拟空间中buf = (char*)shmat(shm_id,NULL,0);if(buf < 0){perror("shmat");exit(1);}//打印共享内存中的数据while(1){printf("%s",buf);sleep(1);}//解除映射if(shmdt(buf) < 0){perror("shmdt");exit(1);}return 0;
}

 实例代码writer.c如下:

#include "shm.h"int main(void)
{key_t key;int shm_id;char *buf;//获取key值key = ftok("./",0xa);if(key < 0){perror("ftok");exit(1);}//创建或获取共享内存对象shm_id = shmget(key,SHM_SIZE, IPC_CREAT|0666);if(shm_id < 0){perror("shmget");exit(1);}//将共享内存映射到进程的虚拟空间中buf = (char*)shmat(shm_id,NULL,0);if(buf < 0){perror("shmat");exit(1);}//向共享内存写数据while(1){printf("请输入字符串:");fgets(buf,SHM_SIZE,stdin);}//解除映射if(shmdt(buf) < 0){perror("shmdt");exit(1);}return 0;
}

实例代码shm.h 如下:

#ifndef __SHM_H__
#define __SHM_H__#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define SHM_SIZE  1024#endif

 3.4 管理共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//参数1 ---- 共享内存的ID
//参数2 ---- 功能码:
            IPC_STAT  (获取对象属性)
            IPC_SET (设置对象属性)
            IPC_RMID (删除对象)    
//参数3 ----struct shmid_ds 结构体指针
            struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* Size of segment (bytes) */
               time_t          shm_atime;   /* Last attach time */
               time_t          shm_dtime;   /* Last detach time */
               time_t          shm_ctime;   /* Last change time */
               pid_t           shm_cpid;    /* PID of creator */
               pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
               shmatt_t        shm_nattch;  /* No. of current attaches */
               ...
           };

实例代码如下: 

int main(int argc,char **argv)
{int shm_id;shm_id = atoi(argv[1]);if(shmctl(shm_id,IPC_RMID,NULL) < 0){perror("shmget");exit(1);}return 0;
}

四、信号灯 (信号量)

1.概念

信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制。
信号灯种类:
    posix有名信号灯
    posix基于内存的信号灯(无名信号灯)
    System V信号灯(IPC对象)

1》 二值信号灯:用于表示资源是否可用
    值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0。

2》 计数信号灯:用于表示资源的数量
    值在0到n之间。用来统计资源,其值代表可用资源数

3》 等待操作,也称为P操作
    是等待信号灯的值变为大于0,然后将其减1;
4》 释放操作,也称为V操作
     用来唤醒等待资源的进程或者线程

5》System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯
 

2.信号灯相关的api函数

2.1 创建或获取信号灯对象

头文件:

 #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
//参数1 ---- 动态获取key:  ftok()
                    #include <sys/types.h>
                    #include <sys/ipc.h>
                    key_t ftok(const char *pathname, int proj_id);
                    //参数1  ---- 工程目录
                    //参数2  ---- 工程编号
                    //返回值 ----- 成功,返回key值,失败:-1
               静态分配: IPC_PRIVATE
//参数2 ---- 集合中信号灯的个数
//参数3 ---- 访问权限:IPC_CREAT | 0666
//返回值 ---- 成功:信号灯对象ID,失败:-1

实例代码如下:

int main(void)
{key_t key;int sem_id;//获取key值key = ftok("./",0xa);if(key < 0){perror("ftok");exit(1);}//创建或获取共享内存对象sem_id = semget(key,1, IPC_CREAT|0666);if(sem_id < 0){perror("semget");exit(1);}return 0;
}

2.2 实现P操作和V操作  

int semop(int semid, struct sembuf *sops, size_t nsops);
//参数1 ----- 信号灯对象的ID
//参数2 ----- 结构体指针
                struct sembuf {
                   short  sem_num;  //  要操作的信号灯的编号
                   short  sem_op;   //    0 :  等待,直到信号灯的值变成0
                                               //   1  :  释放资源,V操作
                                               //   -1 :  分配资源,P操作                   
                   short  sem_flg; // 0,  IPC_NOWAIT,  SEM_UNDO
                };

//参数3 -----nops:  要操作的信号灯的个数
//返回值 ---- 成功:0,失败:-1

实例代码如下:

//1》实现P操作void sem_p(int sem_id,int index){struct sembuf buf = {index,-1,0};if(semop(sem_id,&buf,1) < 0){perror("semop");exit(1);}}
//2》实现v操作void sem_v(int sem_id,int index){struct sembuf buf = {index,1,0};if(semop(sem_id,&buf,1) < 0){perror("semop");exit(1);}}

2.3 管理信号灯  

int semctl(int semid, int semnum, int cmd, ...);
//参数1 ---- 信号灯对象ID
//参数2 ---- 集合中信号灯的编号
//参数3 ---- 功能码:
            IPC_STAT  ----获取信号灯对象属性
            IPC_SET   ----设置信号灯对象属性
            IPC_RMID  ----从内核中删除信号灯对象
            SETALL      ----设置集合中所有信号灯的值
            SETVAL    ----设置集合中编号为semnum的信号灯的值
//参数4 ---- 联合体变量,类型如下:
            union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
           };
//返回值 -----成功:0,失败:-1

实例代码如下: 

//初始化指定的信号灯
void sem_init(int sem_id,int semnum,int value)
{union semun su;su.val = value;if(semctl(sem_id,semnum,SETVAL,su) < 0){perror("semctl");exit(1);}
}
//初始化所有信号灯
void sem_init_all(int sem_id,unsigned short vals[])
{union semun su;su.array = vals;if(semctl(sem_id,0,SETALL,su) < 0){perror("semctl");exit(1);}
}

 


总结

        本篇文章针对进程间通信system V IPC对象进行详细讲解,希望能够帮到大家!

        以后还会给大家展现更多关于嵌入式和C语言的其他重要的基础知识,感谢大家支持懒大王!

       希望这篇博客能给各位朋友们带来帮助,最后懒大王请来过的朋友们留下你们宝贵的三连以及关注,感谢你们!

 

 

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

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

相关文章

c++视觉图像线性混合

图像线性混合 使用 cv::addWeighted() 函数对两幅图像进行线性混合。alpha 和 beta 是两幅图像的权重&#xff0c;它们之和应该等于1。gamma 是一个可选的增益&#xff0c;这里设置为0。 你可以通过调整 alpha 的值来改变混合比例。如果 alpha0.5&#xff0c;则两幅图像等权重…

最短路径专题8 交通枢纽 (Floyd求最短路 )

题目&#xff1a; 样例&#xff1a; 输入 4 5 2 0 1 1 0 2 5 0 3 3 1 2 2 2 3 4 0 2 输出 0 7 思路&#xff1a; 由题意&#xff0c;绘制了该城市的地图之后&#xff0c;由给出的 k 个编号作为起点&#xff0c;求该点到各个点之间的最短距离之和最小的点是哪个&#xff0c;并…

C语言学生成绩录入系统

一、系统概述 该系统是一个由链表创建主菜单的框架&#xff0c;旨在快速创建学生成绩录入系统的主菜单结构。其主要任务包括&#xff1a; 实现链表的创建、插入和遍历功能&#xff0c;用于存储和展示学生成绩录入系统各个模块的菜单项。 2. 提供用户友好的主菜单界面&#xf…

Redis的五种常用数据类型

1.字符串 String的数据结构是简单的Key-Value模型&#xff0c;Value可以是字符串&#xff0c;也可以是数字。 String是Redis最基本的类型&#xff0c;是二进制安全的&#xff0c;意味着Redis的string可以包含任何数据&#xff0c;比如jpg图片。 一个redis中字符串value最大是…

AT9110H-单通道低压 H桥电机驱动芯片

AT9110H能够驱动一个直流有刷电机或其它诸如螺线管的器件。输出驱动模块由PMOSNMOS功率管构成的H桥组成&#xff0c;以驱动电机绕组。AT9110H能够提供高达12V1A的驱动输出。 AT9110H是SOP8封装&#xff0c;且是无铅产品&#xff0c;符合环保标准。 AT9110H具有一个PWM (IN1/IN2…

SpringBoot-黑马程序员-学习笔记(一)

8.pom文件中的parent 我们使用普通maven项目导入依赖时&#xff0c;通常需要在导入依赖的时候指定版本号&#xff0c;而springboot项目不需要指定版本号&#xff0c;会根据当前springboot的版本来下载对应的最稳定的依赖版本。 点开pom文件会看到这个&#xff1a; 继承了一个…

arm-三盏灯流水

.text .global _start _start: 1.设置GPIOE寄存器的时钟使能 RCC_MP_AHB4ENSETR[4]->1 0x50000a28 LDR R0,0x50000A28 LDR R1,[R0] ORR R1,R1,#(0x3<<4) 第四位第五位都设置为1 STR R1,[R0] 写回2.设置PE10管脚为输出模式 GPIOE_MODER[21:20]->01 0x5000…

扭线机控制

扭线机属于线缆加工设备&#xff0c;线缆加工设备种类非常多。有用于网线绞合的单绞&#xff0c;双绞机等&#xff0c;有关单绞机相关算法介绍&#xff0c;大家可以查看专栏相关文章&#xff0c;有详细介绍&#xff0c;常用链接如下&#xff1a; 线缆行业单绞机控制算法&#…

【软考】9.1 顺序表/链表/栈和队列

《线性结构》 顺序存储和链表存储 每个元素最多只有一个出度和一个入度&#xff0c;表现为一条线状链表存储结构&#xff1a;每个节点有两个域&#xff0c;即数据&#xff0c;指针域&#xff08;指向下一个逻辑上相邻的节点&#xff09; 时间复杂度&#xff1a;与其数量级成正…

【Docker】简易版harbor部署

文章目录 依赖于docker-compose下载添加执行权限测试 安装harbor下载解压修改配置文件部署配置开机自启动登录验证 使用harbor登录打标签上传下载 常见问题 依赖于docker-compose 下载 curl -L “https://github.com/docker/compose/releases/download/2.22.0/docker-compose-…

基于SVM+TensorFlow+Django的酒店评论打分智能推荐系统——机器学习算法应用(含python工程源码)+数据集+模型(三)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据预处理2. 模型训练及保存3. 模型应用 系统测试1. 训练准确率2. 测试效果3. 模型应用 相关其它博客工程源代码下载其它资料下载 前言 本项目以支持向量机&#xff08;SVM&#xff09;技术为核心&#xff0c;…

C语言 - 数组

目录 1. 一维数组的创建和初始化 1.1 数组的创建 1.2 数组的初始化 1.3 一维数组的使用 1.4 一维数组在内存中的存储 2. 二维数组的创建和初始化 2.1 二维数组的创建 2.2 二维数组的初始化 2.3 二维数组的使用 2.4 二维数组在内存中的存储 3. 数组越界 4. 数组作为函数参数 4.1…

【Vue面试题十一】、Vue组件之间的通信方式都有哪些?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;Vue组件之间的通信方式都…

Synchronized的实现和锁升级

1.JVM是如何处理和识别Synchronized的&#xff1f; 我们从字节码角度分析synchronized的实现&#xff1a; Synchronized(锁对象){}同步代码块底层实现方式是monitorenter和monitorexit指令。 修饰普通同步方法时底层实现方式是执行指令会检查方法是否设置ACC_SYNCHRONIZED&am…

从0到1基于ChatGLM-6B使用LoRA进行参数高效微调

从0到1基于ChatGLM-6B使用LoRA进行参数高效微调 吃果冻不吐果冻皮 ​ 关注他 cliniNLPer 等 189 人赞同了该文章 ​ 目录 收起 ChatGLM-6B简介 具备的一些能力 局限性 LoRA 技术原理 环境搭建 数据集准备 数据预处理 参数高效微调 单卡模式模型训练 数据并行模式模型训练 模型推…

Nginx详细学习记录

1. Nginx概述 Nginx是一个轻量级的高性能HTTP反向代理服务器&#xff0c;同时它也是一个通用类型的代理服务器&#xff0c;支持绝大部分协议&#xff0c;如TCP、UDP、SMTP、HTTPS等。 1.1 Nginx基础架构 Nginx默认采用多进程工作方式&#xff0c;Nginx启动后&#xff0c;会运行…

阿里云RDS关系型数据库详细介绍_多版本数据库说明

阿里云RDS关系型数据库大全&#xff0c;关系型数据库包括MySQL版、PolarDB、PostgreSQL、SQL Server和MariaDB等&#xff0c;NoSQL数据库如Redis、Tair、Lindorm和MongoDB&#xff0c;阿里云百科分享阿里云RDS关系型数据库大全&#xff1a; 目录 阿里云RDS关系型数据库大全 …

编译工具链 之二 详解 ELF 格式及标准、UNIX 发展、ABI

在计算机及嵌入式系统中&#xff0c;二进制文件也有一定的标准格式&#xff0c;通常会包含在各平台的应用程序二进制接口 &#xff08;Application Binary Interface&#xff0c;ABI&#xff09;规范中。它是编译工具链必须要遵守的规范&#xff08;编译工具链产生符合 ABI 的二…

C (1094) : DS双向链表—前驱后继

Description 在双向链表中&#xff0c;A有一个指针指向了后继节点B&#xff0c;同时&#xff0c;B又有一个指向前驱节点A的指针。这样不仅能从链表头节点的位置遍历整个链表所有节点&#xff0c;也能从链表尾节点开始遍历所有节点。 对于给定的一列数据&#xff0c;按照给定的…

Legion Y9000X IRH8 2023款(82Y3)原装出厂OEM预装Windows11系统

lenovo联想电脑笔记本拯救者原厂win11系统镜像 下载链接&#xff1a;https://pan.baidu.com/s/15G01j7ROVqOFOETccQSKHg?pwdt1ju 系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;32G或以上的U盘 文件格式&#xff1a;ISO…