[linux仓库]System V 进程通信详解:System V消息队列、信号量

🌟 各位看官好,我是!****

🌍 Linux == Linux is not Unix !

🚀 今天来学习Linux的消息队列以及信号量,从底层剖析内核如何管理IPC资源。

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享更多人哦!

目录

消息队列

​编辑

了解消息队列

信号量

理解信号量

信号量实操

PV操作

​编辑

总结

内核如何组织管理IPC资源

minishell添加管道实现


消息队列

通信的本质是什么?

让不同进程看到同一份资源啊!

根据前面所学这一份资源可以是基于文件的管道,在内存级申请一块资源,或者是一个被特殊处理的文件.它也可以是真真实实地在物理内存申请资源的共享内存,那么在内核里是否可以存在一串公共的队列结构里呢?这完全可以啊,我管道都能在内核中申请,这队OS来说也是easy的!

  • 消息队列提供了⼀个从⼀个进程向另外⼀个进程发送⼀块数据的方法
  • 每个数据块都被认为是有⼀个类型,接收者进程接收的数据块可以有不同的类型值
  • 特性方面 : IPC资源必须删除,否则不会自动清除,除⾮重启,所以system V IPC资源的⽣命周期随内核

既然可以在内核中申请消息队列,那么如果有多个进程创建多个消息队列,势必会有许多消息队列,哪些队列是刚创建的,哪些被销毁了,哪些是属于谁创建的。OS是否需要管理呢?要管理:

先描述 ! 再组织 !

了解消息队列

实际上消息队列与共享内存是非常类似的,只是接口需要熟悉下.为什么会这样呢?因为它们都是遵守System V 标准的哇!!!

但是消息队列不能像消息内存一样直接访问物理内存,而是需要通过系统调用

信号量

  • 多个执行流(进程), 能看到的同⼀份公共资源:共享资源
  • 被保护起来 的资源叫做临界资源
  • 保护的⽅式常⻅:互斥与同步
  • 任何时刻,只允许⼀个执⾏流访问资源,叫做互斥
  • 多个执⾏流,访问临界资源的时候,具有**⼀定的顺序性**,叫做同步
  • 系统中某些资源⼀次只允许⼀个进程使⽤,称这样的资源为临界资源或互斥资源。
  • 在进程中涉及到互斥资源的程序段叫临界区(简单来说就是访问资源的那部分代码)。你写的代码=访问临界资源的代码(临界区)+不访问临界资源的代码(非临界区)
  • 所谓的对共享资源进行保护,本质是对访问共享资源的代码进行保护(如何保护呢? --> 写代码啊 --> 同步和互斥、加锁和解锁)

理解信号量

对资源的理解无过于两种:这份资源是作为资源整体被使用;亦或是这份资源被分成很多份,并不是作为整体使用.

为了进一步理解信号量,这里举两个例子:

举个ATM机例子:

我们去银行卡取钱的时候,会限制一个门只能有一个人进行使用,其他人是不能看的.

举个看电影的例子:

对观影者:在看电影之前,我们必须要做什么 ? 买票 ! --> 那么买了这张票,这个座位是否就属于我们呢? --> 是属于的,只要买了票,座位这个资源就是我们的! --> 买票的本质:对资源的预定机制!

对经营者:电影院就那么多座位,我的这个票不能卖多啊;卖重复的票(不考虑) --> 那么该如何防止卖多票的情况呢? --> 用计数器啊! int count =100 ... 0.

那么什么是信号量呢?

信号量的本质:就是一把计数器 --> 描述临界资源数量多少的计数器.

保证:

  1. 资源不会出现多申请的情况
  2. 所有进程未来想通过临界区,访问临界资源 --> 先申请信号量(买票,完成对公共资源的一部分预定)

信号量本质:是一种描述临界资源数量的 原子性 的计数器,是对资源的 预定 机制

那么如果电影院就卖一张超级VIP票呢?刚好被一个富豪买走了.

此时其他人想进去,就只能等这个富豪看完电影,票又不为空才能买.等等,这不就是所谓的互斥机制吗?一次只允许一个人访问这份资源 --> 资源被作为整体使用.

信号量实操

semget():用于创建一个信号量集,返回信号量集的标识符。

bash 复制代码
int semget(key_t key, int nsems, int semflg);
  • key:信号量集的键,可以通过 ftok() 函数生成。
  • nsems:信号量集的大小,表示该信号量集包含的信号量个数。
  • semflg:操作标志,例如 IPC_CREAT 表示创建信号量集,0666 表示权限
  • 返回值是信号量集的标识符(semid)。

semctl():用于获取或设置信号量的控制信息,例如删除信号量等。

PV操作

semop():用于执行 P 操作(wait)和 V 操作(signal)。

信号量是用来保护资源的(最典型的就是共享内存)

总结

所有的system V资源,生命周期都随内核!

所有的system V资源,都要被OS管理起来!

无论是共享内存、消息队列,还是信号量,都是遵守System V标准的.并且它们的结构体里都包含了一个共同的结构体:struct ipc_perm

为什么是这样子的呢?需要从内核剖析IPC资源.

内核如何组织管理IPC资源

结论1:内核中管理IPC资源,用的是柔性数组管理!这是因为IPC资源不是固定的,会有变化,设计成柔性数组可以应对变化.

为什么要保存key?

结论2 : 可以通过ipc_ids的指针entries找到ipc_id_ary,ipc_id_ary里面含有kern ipc_perm指针类型的柔性数组,由于每一个数组下标都是指针类型,指向kern_ipc_perm结构体,这个结构体含有key。因此,通过遍历柔性数组指向结构体中的每一个key来确定是不是我们所需要的IPC资源.

那为什么又要有shmid这样的返回值呢?

结论3 : 遍历这个柔性数组的是O(n)时间复杂度,而shmid就是柔性数组的下标啊!可以直接定位这个资源在哪啊!
为什么共享内存要有file?

动态库是如何映射到我们的进程地址空间?

是如何找到动态库加载的起始地址?不过是多了个加载步骤

是如何做到加载时地址got重定向?

minishell添加管道实现

bash 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#define MAX_CMD 1024
char command[MAX_CMD];

int do_face()
{
    memset(command, 0x00, MAX_CMD);
    printf("minishell$ ");
    fflush(stdout);
    if (scanf("%[^\n]%*c", command) == 0)
    {
        getchar();
        return -1;
    }
    return 0;
}

char **do_parse(char *buff)
{
    int argc = 0;
    static char *argv[32];
    char *ptr = buff;
    while (*ptr != '\0')
    {
        if (!isspace(*ptr))
        {
            argv[argc++] = ptr;
            while ((!isspace(*ptr)) && (*ptr) != '\0')
            {
                ptr++;
            }
            continue;
        }
        *ptr = '\0';
        ptr++;
    }
    argv[argc] = NULL;
    return argv;
}

int do_redirect(char *buff)
{
    char *ptr = buff, *file = NULL;
    int type = 0, fd, redirect_type = -1;
    while (*ptr != '\0')
    {
        if (*ptr == '>')
        {
            *ptr++ = '\0';
            redirect_type++;
            if (*ptr == '>')
            {
                *ptr++ = '\0';
                redirect_type++;
            }
            while (isspace(*ptr))
            {
                ptr++;
            }
            file = ptr;
            while ((!isspace(*ptr)) && *ptr != '\0')
            {
                ptr++;
            }
            *ptr = '\0';
            if (redirect_type == 0)
            {
                fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0664);
            }
            else
            {
                fd = open(file, O_CREAT | O_APPEND | O_WRONLY, 0664);
            }
            dup2(fd, 1);
        }
        ptr++;
    }
    return 0;
}

int do_command(char *buff)
{
    int pipe_num = 0, i;
    char *ptr = buff;
    int pipefd[32][2] = {{-1}};
    int pid = -1;
    pipe_command[pipe_num] = ptr;
    while (*ptr != '\0')
    {
        if (*ptr == '|')
        {
            pipe_num++;
            *ptr++ = '\0';
            pipe_command[pipe_num] = ptr;
            continue;
        }
        ptr++;
    }
    pipe_command[pipe_num + 1] = NULL;
    return pipe_num;
}

int do_pipe(int pipe_num)
{
    int pid = 0, i;
    int pipefd[10][2] = {{0}};
    char **argv = {NULL};
    for (i = 0; i <= pipe_num; i++)
    {
        pipe(pipefd[i]);
    }
    for (i = 0; i <= pipe_num; i++)
    {
        pid = fork();
        if (pid == 0)
        {
            do_redirect(pipe_command[i]);
            argv = do_parse(pipe_command[i]);
            if (i != 0)
            {
                close(pipefd[i][1]);
                dup2(pipefd[i][0], 0);
            }
            if (i != pipe_num)
            {
                close(pipefd[i + 1][0]);
                dup2(pipefd[i + 1][1], 1);
            }
            execvp(argv[0], argv);
        }
        else
        {
            close(pipefd[i][0]);
            close(pipefd[i][1]);
            waitpid(pid, NULL, 0);
        }
    }
    return 0;
}

int main(int argc, char *argv[])
{
    int num = 0;
    while (1)
    {
        if (do_face() < 0)
            continue;
        num = do_command(command);
        do_pipe(num);
    }
    return 0;
}
相关推荐
huangyuchi.2 小时前
【Linux实战 】Linux 线程池的设计、实现与单例模式应用
linux·c++·单例模式·线程池·懒汉模式·项目·linux系统
Archie_IT2 小时前
「深入浅出」嵌入式八股文—P2 内存篇
c语言·开发语言·数据结构·数据库·c++·算法
Lenyiin3 小时前
《 Linux 点滴漫谈: 三 》Linux 的骨架:文件系统与目录结构的完整图谱
linux·运维·服务器·lenyiin
ZLRRLZ3 小时前
【Linux操作系统】进程概念
linux·运维·服务器
XCOSnTh4 小时前
单片机入门的相关工具XCOSnTh
c语言·单片机·嵌入式硬件·xcosnth·单片机入门
byte轻骑兵4 小时前
Windows 安全分割利器:strtok_s () 详解
c语言·开发语言·windows·安全
做运维的阿瑞4 小时前
GPU即服务:Linux与云原生如何联手开启AI算力“自来水“时代
linux·人工智能·云原生
骄傲的心别枯萎4 小时前
项目1:FFMPEG推流器讲解(一):FFMPEG重要结构体讲解
linux·ffmpeg·音视频·视频编解码·rv1126
报错小能手5 小时前
linux学习笔记(16)进程间通信——管道
linux·笔记·学习