当前位置: 首页 > ai >正文

华清远见25072班I/O学习day5

重点内容:

共享内存:

1>原理图:

2> 有关共享内存的相关API

        key_t ftok(const char *pathname, int proj_id);

        功能:通过给定的文件或目录路径(必须存在)以及一个随机数据来生成一个key值,用于创建IPC对象 ftok("\", 'k')

        参数1:已经存在的文件路径,一般我们使用 "\"

        参数2:一个任意给定的随机数,只需要低8位即可

        返回值:成功返回ipc对象的key值,失败返回(key_t)-1并置位错误码

        int shmget(key_t key, size_t size, int shmflg);

        功能:将想要共享的size大小的物理内存映射成一个共享内存页表,表以PAGE_SIZE为单位         参数1:创建共享内存的key值,可以是IPC_PRIVATE,也可以是由ftok创建出来的

        参数2:要申请的大小,以页为单位,向上取整

        参数3:创建标识

                IPC_CREAT:打开一个key所对应的共享内存,如果不存在则创建一个共享内存                 IPC_EXCL:确保创建了一个共享内存

                0664:表示创建的权限

        返回值:成功返回创建的共享内存的id,失败返回-1并置位错误码

        void *shmat(int shmid, const void *shmaddr, int shmflg);

        功能:将共享内存的地址,映射到进程的地址空间中(将共享内存的起始地址返回到程序中)

        参数1:共享内存的id号

        参数2:共享内存起始地址偏移页,一般填NULL,让系统自动分配一个合适的偏移页

        参数3:对共享内存的操作方式

                0:表示读写的方式

                SHM_RDONLY:表示对共享内存只读权限         

        返回值:成功返回共享内存映射的起始地址,失败返回(void*)-1,并置位错误码

        int shmdt(const void *shmaddr);

        功能:将共享内存的地址,与进程断开连接

        参数:指向共享内存的指针

        返回值:成功返回0,失败返回-1并置位错误码

        int shmctl(int shmid, int cmd, struct shmid_ds *buf);

        功能:用于控制共享内存的相关操作,主要用于删除共享内存

        参数1:共享内存的id号

        参数2:要执行的操作,删除共享内存该值为 IPC_RMID

        参数3:如果参数2是删除共享内存,则该参数可以忽略填NULL即可

        返回值:成功返回0,失败返回-1并置位错误码

信号量集(信号灯集)

1> 信号量集主要完成的是多个进程间同步的问题(多个进程有先后顺序的执行)

2> 信号量集的原理图:

3> 信号量集相关API

         key_t ftok(const char *pathname, int proj_id);

        功能:通过给定的文件或目录路径(必须存在)以及一个随机数据来生成一个key值,用于创建IPC对象 ftok("\", 'k')

        参数1:已经存在的文件路径,一般我们使用 "\"

        参数2:一个任意给定的随机数,只需要低8位即可

        返回值:成功返回ipc对象的key值,失败返回(key_t)-1并置位错误码

        int semget(key_t key, int nsems, int semflg);

        功能:通过给定的key值创建一个系统5信号量集容器

        参数1:key值,可以是IPC_PRIVATE,也可以是使用ftok创建出来的key值

        参数2:信号量集中的信号的个数

        参数3:创建标识

                IPC_CREAT:打开一个信号量集,如果不存在,则创建一个信号量集

                IPC_EXCL:确保创建出一个新的信号量集

                0664:权限

        返回值:成功返回创建出来的信号量集的id号,失败返回-1并置位错误码

        int semop(int semid, struct sembuf *sops, size_t nsops);

        功能:对信号量数组进行操作函数

        参数1:信号量数组id号

        参数2:操作,是一个结构体包含如下数据

                 unsigned short sem_num; /* 要操作的资源编号,下标从0开始 */

                short sem_op; /* 要执行的操作,负数表示申请资源操作,正数表示释放资源操作 */                 short sem_flg; /* 是否阻塞,0表示阻塞执行,IPC_NOWAIT表示非阻塞执行 */

        参数3:需要执行的资源个数

        返回值:成功返回0.失败返回-1并置位错误码

eg:申请0号信号灯的资源

        struct sembuf op;

        op.sem_num = 0; //要操作的信号号

        op.sem_op = -1; //要进行的操作

        op.sem_flg = 0; //阻塞形式申请

        semop(semid, &op, 1);

        int semctl(int semid, int semnum, int cmd, ...);

        功能:对信号量数组进行控制函数,该函数可以有三个参数也可以有四个参数,取决于cmd         参数1:要操作的信号量数组id

        参数2:要被操作的信号灯编号

        参数3:操作指令

                IPC_RMID:表示删除信号量数组,此时参数2可以忽略,参数4可以不填

                SETVAL:表示要给编号为semnum的那个信号灯进行设置值,此时需要填写参数4,参数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) */

};

        参数4:取决于参数3

        返回值:成功返回0失败返回-1并置位错误码

eg:删除信号量数组:

        semctl(semid, 0, IPC_RMID); 给编号为0的信号灯设置初始值为 1

        union semun us;

        us.val = 1;

        semctl(semid, 0, SETVAL, us)


作业:

1> 使用已经封装好的信号量数组,实现三个进程的同步执行,进程1打印A、进程2打印B,进程3打印C,最终效果是:

ABCABCABCABCABC...

test.c源码:

#include <25072head.h>
#include"sem.h"
#define SEM_A 0
#define SEM_B 1
#define SEM_C 2
// 打印次数
// 进程1:打印A
void processA(int semid) {
while(1)
{
// 等待A的信号量
P(semid, SEM_A);
printf("A");
fflush(stdout);  // 立即刷新输出缓冲区
// 允许B打印
V(semid, SEM_B);
usleep(300000);
}
}

// 进程2:打印B
void processB(int semid) {
while(1)
{
// 等待B的信号量
P(semid, SEM_B);
printf("B");
fflush(stdout);
// 允许C打印
V(semid, SEM_C);
usleep(300000);
}
}
// 进程3:打印C
void processC(int semid) {
while(1)
{
// 等待C的信号量
P(semid, SEM_C);
printf("C");
fflush(stdout);
// 允许A打印,形成循环
V(semid, SEM_A);
usleep(300000);
}
}
int main(int argc, const char *argv[])
{
int semid;
semid=sem_create(3);
if(semid==-1)
{
perror("semid error");
return -1;
}
pid_t pid1=-1;
pid1=fork();
pid_t pid2=-1;
pid_t pid3=-1;
pid2=fork();
pid3=fork();
if(pid1>0)
{
wait(NULL);
}
else if(pid1==0)
{
processA(semid);
exit(EXIT_SUCCESS);
}
if(pid2>0)
{
wait(NULL);
}
else if(pid2==0)
{
processB(semid);
exit(EXIT_SUCCESS);
}
if(pid3>0)
{
wait(NULL);
}

    else if(pid3==0)
{
processC(semid);
exit(EXIT_SUCCESS);
}
sem_del(semid);
return 0;
}

sem.h源码:

#ifndef __MYSEM_H__
#define __MYSEM_H__
#include<25072head.h>
//用于semctl函数的共用体
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) */
};
//1.创建并打开一个信号量数组
//参数:要创建出来的信号量集中信号灯的个数
//返回值:成功打开或创建的信号量数组id
int sem_create(int semcount);

//2.申请某一个信号灯的资源的操作
//参数:信号灯集的id号
//   要操作的信号灯的编号
int P(int semid, int semnum);

//3.释放某一个信号灯的资源的操作
//参数:信号灯集的id号
//   要操作的信号灯的编号
int V(int semid, int semnum);

//4.删除信号灯集的操作
int sem_del(int semid);
#endif

sem.c源码:

#include"sem.h"
//定义初始化某个信号灯的函数
int init_semnum(int semid, int semnum)
{
//1.获取要设置的值
int val = 0;
printf("请输入第%d号灯的初始值:", semnum+1);
scanf("%d", &val);
getchar();

    //2.将输入的值设置到灯中
union semun buf;
buf.val = val;          //将输入的值放入共用体变量中
//3.调用semctl设置初始值
if(semctl(semid, semnum, SETVAL, buf) ==-1)
{
perror("semctl error");
return -1;
}

    return 0;
}
//1.创建并打开一个信号量数组
//参数:要创建出来的信号量集中信号灯的个数
//返回值:成功打开或创建的信号量数组id
int sem_create(int semcount)
{
//1.创建一个key值
key_t key = ftok("/", 's');
if(key == -1)
{
perror("ftok error");
return -1;
}

    //2.打开或创建一个信号量数组
int semid = semget(key, semcount, IPC_CREAT|IPC_EXCL|0664);
if(semid == -1)
{
//对错误码进行判断
if(errno == EEXIST)
{
//表示信号量数组已经存在,直接打开即可,无需给里面的信号灯初始化
semid = semget(key, semcount, IPC_CREAT);
return semid;
}
perror("semget error");
return -1;
}
//3.给每个信号灯进行初始化值
for(int i=0; i<semcount; i++)
{
//给semid信号量数组的下标为i的灯设置初始值
init_semnum(semid, i);
}
//4.将semid返回
return semid;
}
//2.申请某一个信号灯的资源的操作
//参数:信号灯集的id号
//   要操作的信号灯的编号
int P(int semid, int semnum)
{
//定义一个用于操作的结构体变量
struct sembuf buf;
buf.sem_num = semnum;          //要操作的信号灯编号
buf.sem_op = -1;               //表示要进行申请资源操作
buf.sem_flg = 0;               //阻塞形式申请资源
//调用semop函数申请资源,如果该信号灯没有资源,
//会在该函数处阻塞
if(semop(semid, &buf, 1) == -1)
{
perror("P error");
return -1;
}
//成功申请返回0
return 0;
}
//3.释放某一个信号灯的资源的操作
//参数:信号灯集的id号
//   要操作的信号灯的编号
int V(int semid, int semnum)
{
//定义一个用于操作的结构体变量
struct sembuf buf;
buf.sem_num = semnum;          //要操作的信号灯编号
buf.sem_op = 1;               //表示要进行释放资源操作
buf.sem_flg = 0;               //阻塞形式申请资源

    //调用semop函数申请资源,如果该信号灯没有资源,
//会在该函数处阻塞
if(semop(semid, &buf, 1) == -1)
{
perror("P error");
return -1;
}

    //成功申请返回0
return 0;
}

//4、删除信号灯集的操作
int sem_del(int semid)
{
if(semctl(semid, 0, IPC_RMID)==-1)
{
perror("del error");
return -1;
}

    //正常删除
printf("删除成功\n");
return 0;
}

2> 思维导图

3> 牛客网

http://www.xdnf.cn/news/19893.html

相关文章:

  • 嵌入式硬件 - 51单片机3
  • NotePad++下载安装与设置
  • 使用a标签跳转之后,会刷新一次,这个a标签添加的样式就会消失
  • 搞懂IFNULL 和 NULLIF
  • 【C++详解】异常概念、抛出捕获与处理机制全解析
  • 一文读懂数据分类分级:企业安全治理的基石
  • 深入理解Linux进程信号机制
  • 残差神经网络的案例
  • 【面试题】LangChain与LlamaIndex核心概念详解
  • 聚焦GISBox矢量服务:数据管理、数据库连接与框架预览全攻略
  • 分布式电源接入电网进行潮流计算
  • Linux笔记---UDP套接字实战:简易聊天室
  • 服务器不支持node.js16以上版本安装?用Docker轻松部署Node.js 20+环境运行Strapi项目
  • 新规则,新游戏:AI时代下的战略重构与商业实践
  • 安全领域必须关注每年发布一次“最危险的25种软件弱点”清单 —— CWE Top 25(内附2024 CWE Top 25清单详情)
  • Boost搜索引擎 数据清洗与去标签(1)
  • 【OpenHarmony文件管理子系统】文件访问接口mod_fs解析
  • ECMAScript(2)核心语法课件(Node.js/React 环境)
  • uniapp的上拉加载H5和小程序
  • PDF.AI-与你的PDF文档对话
  • C++虚函数虚析构函数纯虚函数的使用说明和理解
  • redisson延迟队列报错Sync methods can‘t be invoked from async_rx_reactive listeners
  • 快速排序算法详解
  • 【mysql】SQL自连接实战:查询温度升高的日期
  • 三维多相机光场扫描:打造元宇宙时代的“数字自我”
  • React学习教程,从入门到精通, React 嵌套组件语法知识点(10)
  • 公司机密视频泄露频发?如何让机密视频只在公司内部播放
  • 数据采集机器人哪家好?2025 年实测推荐:千里聆 RPA 凭什么成企业首选?
  • 机器人智能控制领域技术路线
  • 嵌入式 - 硬件:51单片机(3)uart串口