Linux 第二十七章

news2024/11/16 15:56:23

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

可执行程序加载的时候,动态库也需要加载

符号表

在磁盘中,习惯叫逻辑地址:起始地址+偏移量

进程间通信 

进程间通信的目的

进程间通信的本质

匿名管道通信

pipe()

进程池

to_string

function()>

匿名管道的实现

processpool.cc

task.hpp

Makefile


可执行程序加载的时候,动态库也需要加载

动态库加载的时候,会从磁盘写入内存,然后通过页表映射到进程虚拟地址空间中的共享区中

我们要做到让库在共享区的任意位置,都可以正确运行

符号表

在 Linux 中,符号表是一个记录了程序中各个函数、变量以及其他符号的名称和地址映射关系的数据结构。在编译程序时,编译器会生成符号表并将其嵌入到可执行文件中。当程序运行时,操作系统会将符号表加载到内存中,并使用它来解析程序中的符号引用

如果一个程序运行时已经加载了一个动态库 libxxx.so,然后另一个程序运行时,该程序也需使用动态库libxxx.so,那么该程序还需要加载动态库 libxxx.so吗?

不需要。因为我们动态库 libxxx.so已经加载到内存中,我们只需要使用该进程的虚拟地址空间去映射内存中的已经加载的动态库 libxxx.so

所以不管多少个进程使用同一个动态库,内存中只需要一份动态库

在磁盘中,习惯叫逻辑地址:起始地址+偏移量

当一个程序被执行时,它的可执行文件(通常是 ELF 格式)会被加载到内存中的代码段(text segment)。这个可执行文件可能会引用一些静态库,这些静态库也会被加载到内存中。

静态库是在编译时将库的代码和数据包含在可执行文件中的。当程序加载到内存时,操作系统会将静态库的内容复制到进程的地址空间中,使程序能够访问静态库中的函数和数据。这样,程序就可以直接调用静态库中定义的函数,而不需要在运行时再去查找和加载动态库。

当一个程序被执行时,它的可执行文件(通常是 ELF 格式)会被加载到内存中的代码段(text segment)。这个可执行文件可能会依赖一些动态库,这些动态库也需要被加载到内存中。

动态库是以共享对象(Shared Object)的形式存在的,它们的代码和数据并不包含在可执行文件中,而是作为独立的文件存在。当程序加载到内存时,操作系统会根据可执行文件中的信息找到并加载所依赖的动态库。加载动态库的过程包括将动态库的代码和数据复制到进程的地址空间中,并进行符号重定位等操作,使得程序能够正确地使用动态库中的函数和数据。

cpu中的指令寄存器,可以通过虚拟地址,然后通过页表,找到物理地址,然后找到内存里的命令

进程间通信 

vscode的使用

1)下载vscode

2)然后下载中文插件

3)下载ssh remote插件(用于远程连接)

control+`:就是打开终端和在final shell操作一样

主要是用vscode取代vim,但是vim还是有用的,我们是远程开发,我安装的插件安装到linux下

进程间通信的目的

数据传输:一个进程需要将它的数据发送给另一个进程

资源共享:多个进程之间共享同样的资源。

通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变

进程间通信的本质

让不同的进程先看到同一份资源(例如母亲通过孩子给父亲进行交流),由操作系统提供

匿名管道通信

我们如何让不同进程看到同一个管道文件?

通过fork创建子进程的时候实现的

pipe()

在 Linux 中,pipe() 函数是一个用于创建管道的系统调用。它允许在两个进程之间建立一个匿名的管道,实现进程间通信。

pipe() 函数的原型定义在 unistd.h 头文件中,其原型为:
int pipe(int pipefd[2]);

其中,pipefd 是一个整型数组,用来存储管道的文件描述符。
pipefd[0] 用于从管道读取数据,pipefd[1] 用于向管道写入数据。


调用 pipe() 函数会创建一个管道,并将相关的文件描述符存储在 pipefd 数组中。一旦管道创建成功,就可以在父进程和子进程之间进行通信,父进程可以通过 pipefd[1] 向管道写入数据,子进程则可以通过 pipefd[0] 从管道读取数据。

vscode中保存代码:cmmand+s

管道文件大小通常是64kb

1)管道四种情况
1.正常情况,如果管道里没有数据,读端必须等待,直到有数据为止(以前我们创建父子进程,让他们各自向屏幕输出东西时,他们都会各自输出自己的,不会管对方)
2.正常情况,如果管道被写满了,写端必须等待,直到有空间为止
3.写端关闭,读端一直读取,读端会读到read返回值为0,表示读到文件结尾
4.读端关闭,写端一直写入,OS会直接杀掉写端进程,通过向目标进程发送SIGPIPE(13)信号,终止目标进程

2)管道五种特性
1.匿名管道,可以允许具有血缘关系的进程间通信,常用于父子,父孙也可以
2.匿名管道,默认给读写端提供同步机制
3.面向字节流的(可能读取出来的,不一定是完整的字符串)
4.管道的生命周期是随进程的
5.管道是单向通信的,半双工通信一种特殊情况(一方输出,一方接收)

事例

#include <iostream>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;

#define MAX 1024

int main()

{
    // 第一步创建管道
    int pipefd[2] = {0};

    int n = pipe(pipefd);

    assert(n == 0);

    cout << "pipefd[0]: " << pipefd[0] << " " << "pipefd[0]: " << pipefd[1] << endl;

    // 第二步创建子进程
    pid_t id = fork();

    if (id < 0)
    {
        perror("fork:");
        return 1;
    }

    // 子写父读
    // 第三步,父子关闭不需要的fd,形成单向通信的管道
    else if (id == 0)

    {
        // child
        // 因为子进程使用过写管道,需要关闭读管道
        close(pipefd[0]);

        // w-只向管道写入,没有打印
        int cnt = 10;

        while (cnt--)
        {
            char message[MAX];

            snprintf(message, sizeof(message) - 1, "hello father, I am a child,pid:%d ,cnt:%d", getpid(), cnt);
            write(pipefd[1], message, strlen(message));
            sleep(1);
        }
    }
    else
    {
        // father
        // 因为父进程使用过读管道,需要关闭写管道
        close(pipefd[1]);

        char buffer[MAX];

        while (true)
        {
            ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1);

            if (n > 0)
            {
                buffer[n] = 0; // 防止末尾没有'\0'
                cout << getpid() << ": " << "child say: " << buffer << " " << "to me!" << endl;
            }
        }

        pid_t rid = waitpid(id, nullptr, 0);

        if (rid == id)
            cout << "wait success" << endl;
    }
}

进程池

to_string

to_string 是 C++11 中新增加的一个函数,可以将数值类型(如 int、float、double 等)转换成字符串类型。

function<void()>

function<void()>是一个函数类型,它表示一个不接受任何参数且不返回任何值的函数。在C++中,std::function是一个通用的函数封装器类模板,可以用来包装各种可调用对象,包括函数指针、函数对象、Lambda表达式等。

具体而言,std::function<void()>表示一个可以调用的函数对象,它没有参数并且没有返回值。通过将一个不接受任何参数且不返回任何值的函数或可调用对象传递给std::function<void()>,就可以创建一个可以调用的对象,该对象可以像函数一样被调用。

以下是使用std::function<void()>的示例:

#include <iostream>
#include <functional>

void hello() {
    std::cout << "Hello, world!" << std::endl;
}

int main() {
    std::function<void()> func = hello;
    func(); // 调用hello函数

    return 0;
}

在上述示例中,我们定义了一个名为hello的函数,它不接受任何参数并且没有返回值。然后,我们创建了一个std::function<void()>对象func,将hello函数赋值给它。最后,我们通过调用func()来调用hello函数。

需要注意的是,std::function可以用于包装各种不同类型的可调用对象,只要它们的签名(参数和返回值)与std::function的模板参数匹配即可。这使得std::function非常灵活,可以在函数指针、函数对象、Lambda表达式等之间进行切换和传递。

匿名管道的实现

processpool.cc
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"

using namespace std;
const int num = 5;
static int number = 1;

class channel

{
public:
    channel(int fd, pid_t id)

        : ctrlfd(fd), workerid(id)

    {
        name = "channel-" + to_string(number++);
    }
    int ctrlfd; // 信道

    pid_t workerid; // 进程id

    string name; // 信道名称
};

void work()

{
    while (true)

    {
        int code = 0;

        ssize_t n = read(0, &code, sizeof(code));

        if (n == sizeof(code))

        {
            if (!IN1.checksafe(code))

                continue;
            IN1.runtask(code);
        }
        else if (n == 0)

        {
            break;
        }
        else
        {
        }
    }
    cout << "子进程退出" << endl;
}

// 传参形式
// 1.输入参数:const &
// 2.输出参数:*
// 3.输入输出参数:&

void printfd(const vector<int> &fds)

{
    cout << getpid() << "close fds:";
    for (auto fd : fds)

    {
        cout << fd << " ";
    }
    cout << endl;
}

void createchannels(vector<channel> *c)

{
    //
    vector<int> tmp;

    // 1.定义并创建管道
    int i = 0;

    for (i = 0; i < num; i++)

    {
        int pipefd[2];

        int n = pipe(pipefd);

        assert(n == 0);

        // 2.创建进程
        pid_t id = fork();

        // 3.构建单向通信的信道
        if (id == 0) // child

        {
            if (!tmp.empty())
            {
                for (auto fd : tmp)

                {
                    close(fd);
                }
                printfd(tmp);
            }
            close(pipefd[1]);
            dup2(pipefd[0], 0);

            work();
            // sleep(1);
            exit(0);
        }

        // father
        close(pipefd[0]);
        c->push_back(channel(pipefd[1], id));

        tmp.push_back(pipefd[1]);
    }
}

void printdebug(vector<channel> &c)

{
    for (auto &ch : c)

    {
        cout << ch.name << " " << ch.ctrlfd << " " << ch.workerid << endl;
    }
}

void sendcommand(const vector<channel> &ch, bool flag, int num = -1)

{
    int pos = 0;

    while (true)

    {
        // 开始完成任务
        // 1.选择任务
        int command = IN1.selecttask();

        // 2.选择信道
        const auto &c = ch[pos++];

        pos %= ch.size();

        // debug
        cout << "send command :" << IN1.todesc(command) << " to " << c.name << " worker is :" << c.workerid << endl;

        // 3.发送任务
        write(c.ctrlfd, &command, sizeof(command));

        // 4.判断是否要退出
        if (!flag)

        {
            num--;
            if (num <= 0)

                break;
        }
        sleep(1);
    }
    cout << "发送任务完成了" << endl;
}

void releasechannels(vector<channel> channels)

{
    // verson2

    // verson1
    for (const auto &ch : channels)

    {
        close(ch.ctrlfd);
    }
    for (const auto &ch : channels)

    {
        pid_t rid = waitpid(ch.workerid, nullptr, 0);

        if (rid == ch.workerid)

        {
            cout << "wait child :" << ch.workerid << " success" << endl;
        }
    }
}

int main()

{
    vector<channel> channels;

    // 1.创建信道,创建进程
    createchannels(&channels);
    // 2.开始发送任务
    const bool g_always_loop = true;

    // sendcommand(channels,g_always_loop);
    sendcommand(channels, !g_always_loop, 10);

    // 3.回收资源,想让子进程退出,并且释放管道,只要关闭写端
    releasechannels(channels);
    return 0;
}
task.hpp
#pragma once
#include <iostream>
#include <functional>
#include <vector>
#include <ctime>
#include <unistd.h>
using namespace std;

using task_t = function<void()>;

// typedef function<void()> task_t;//这两种写法都一样的

void download()
{
    cout << "我是一个下载任务" << "处理者:" << getpid() << endl;
}

void printlog()

{
    cout << "我是一个打印日志任务" << "处理者:" << getpid() << endl;
}

void pushvideostream()

{
    cout << "我是一个推送视频流任务" << "处理者:" << getpid() << endl;
}

class init

{
    // 任务码
    const int g_download_code = 0;

    const int g_printlog_code = 1;

    const int g_pushvideostream_code = 2;

    // 任务集合
public:
    vector<task_t> tasks;

public:
    init()
    {
        tasks.push_back(download);
        tasks.push_back(printlog);
        tasks.push_back(pushvideostream);
    }

    bool checksafe(int code)
    {
        if (code >= 0 && code < tasks.size())
            return true;

        else
            return false;
    }
    void runtask(int code)
    {
        tasks[code]();
    }
    int selecttask()
    {
        return rand() % tasks.size();
    }
    string todesc(int code)
    {
        switch (code)
        {
        case 0:
            return "download";
            break;
        case 1:
            return "printlog";
            break;
        case 2:
            return "pushvideostream";

            break;
        default:
            return "没有该任务";
        }
    }
};

init IN1; // 定义对象
Makefile
processpool:processpool.cc
g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
rm -f processpool

 🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

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

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

相关文章

答辩PPT不会做?试试这些AI工具,一键生成

在我原本的认知里面&#xff0c;答辩PPT是要包含论文各个章节的&#xff0c;在答辩时需要方方面面都讲到的&#xff0c;什么摘要、文献综述、实证分析、研究结果样样不落。但是&#xff0c;这大错特错&#xff01; 答辩PPT环节时长一般不超过5分钟&#xff0c;老师想要的答辩P…

【JavaSE】/*初识Java*/

目录 一、了解 Java 语言 二、Java 语言的重要性 2.1 使用程度 2.2 工作领域 三、Java 语言的特性 四、Java 的基础语法 五、可能遇到的错误 六、第一个 java 程序代码解析 七、Java 注释 八、Java 标识符 九、Java 关键字 一、了解 Java 语言 Java 是由 Sun Micr…

2023年建筑特种作业人员安全生产知识试题

100分题库提供安全员考试试题、建筑安全员考试预测题、建筑安全员ABC考试真题、安全员证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 判断题&#xff08;1-20&#xff09; 1.《建筑工程安全生产管理条例》是我国第一部关于…

mac苹果电脑卡顿反应慢如何解决?2024最新免费方法教程

苹果电脑以其稳定的性能、出色的设计和高效的操作系统&#xff0c;赢得了广大用户的喜爱。然而&#xff0c;随着时间的推移&#xff0c;一些用户会发现自己的苹果电脑开始出现卡顿、反应慢等问题。这不仅影响使用体验&#xff0c;还会影响工作效率。那么&#xff0c;面对这些问…

2024年旅游行业薪酬报告

来源&#xff1a;薪智 近期历史回顾&#xff1a; 2024年中国健康家电消费洞察及趋势研究报告.pdf 2024巴菲特股东大会5万字完整版.pdf 2024年全国大学生新媒体直播大赛.pdf 2024北京市高级别自动驾驶示范区数据安全治理白皮书.pdf 2024年第一季度开发者健康调查报告.pdf 2024年…

计算机毕业设计 | vue+springboot线上考试 在线测试系统(附源码)

1&#xff0c;项目介绍 项目背景 在线考试借助于网络来进行&#xff0c;传统考试所必备的考场和监考对于在线考试来说并不是必要项目&#xff0c;因此可以有效减少组织考试做需要的成本以及设施。同时&#xff0c;由于在线考试系统本身具有智能阅卷的功能&#xff0c;也大大减…

记录文件上传exists方法遇到的坑

1、问题 判断文件是否存在使用exist方法&#xff0c;官方的注释是这样的 百度翻译结果&#xff1a;true&#xff0c;当且仅当由该抽象路径名表示的文件或目录存在时&#xff1b;否则为false 2、实际返回 注意&#xff1a;实际上exsits方法的返回值与其官方注释的返回结果是相…

NSSCTF中的web学习(md5())

目录 MD5的学习 [BJDCTF 2020]easy_md5 [LitCTF 2023]Follow me and hack me [LitCTF 2023]Ping [SWPUCTF 2021 新生赛]easyupload3.0 [NSSCTF 2022 Spring Recruit]babyphp MD5的学习 md5()函数&#xff1a; md5($a)&#xff1a;返回a字符串的散列值 md5($a,TRUE)&…

一套全新的PACS医学存档影像系统源码 RIS和PACS系统分别在哪些方面发挥作用

RIS和PACS系统分别在哪些方面发挥作用 RIS系统的作用 放射信息系统&#xff08;RIS&#xff09;主要用于管理和调度患者的放射检查流程。它的主要功能包括患者管理、检查预约、报告生成等。RIS系统通常作为独立系统运行&#xff0c;侧重于临床流程管理&#xff0c;并优化放射…

关于docker network网络

首先,我们来看看Docker默认的网络模式,即docker0网桥。 每当你安装Docker时,它会创建一个名为docker0的虚拟网桥,并设置一个IP地址范围供它进行端口映射等工作。所有Docker容器在创建时,都会自动连接到这个docker0网桥,并分配一个虚拟IP地址。这样,容器与主机之间,以及容器与容…

3d里如何做螺旋状模型?---模大狮模型网

螺旋状模型在3D设计中常常被运用&#xff0c;不仅可以用于创造独特的装饰品和艺术品&#xff0c;还可以用于建筑设计、工程模拟等领域。然而&#xff0c;对于初学者而言&#xff0c;如何在3D软件中创建螺旋状模型可能是一个挑战。在本文中&#xff0c;我们将分享几种简单而有效…

Qt——信号 和 槽

目录 概述 信号和槽的使用 自定义信号和槽 带参数的信号和槽 概述 在Linux系统中&#xff0c;我们也介绍了信号的产生、信号的检测以及信号的处理机制&#xff0c;它就是系统内部的通知机制&#xff0c;也可以是一种进程间通信的方式。在系统中有很多信号&#xff0c;我们可…

镜舟科技亮相2024中国移动算力网络大会、Qcon、DTC等多项活动

在刚刚过去的 4 月份&#xff0c;镜舟科技受邀参与一系列技术交流活动&#xff0c;与移动云、金科创新社、infoQ、墨天轮、开科唯识等媒体及合作伙伴展开积极交流&#xff0c;并分享其在数据技术、金融等垂直行业领域的创新实践&#xff0c;从产业侧、业务侧、技术侧洞察需求、…

React:Router-3.路由懒加载

在 React&#xff1a;Router-1.BrowserRouter组件式 和 React&#xff1a;Router-2. createBrowserRouter函数式 两篇文章中我们已经完成了路由的创建。但是这种方式&#xff0c;会全量加载路由资源&#xff0c;如果项目较复杂&#xff0c;会产生性能问题。 为了优化项目性能&…

螺栓扭矩设计多大?原来如此简单!——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-扭矩自动控制系统-SunTorque 螺栓扭矩设计&#xff0c;看似复杂&#xff0c;实则遵循一定的科学原理和实践经验。本文将深入浅出地探讨螺栓扭矩设计的原理和方法&#xff0c;帮助读者轻松掌握这一技能。 首先&#xff0c;我们需要了解螺栓扭矩的基…

Simulink从0搭建模型04-练习_一阶低通滤波器的实现

Simulink从0搭建模型04-练习_一阶低通滤波器的实现 1. 前言1.1. 参考1.2. 好习惯&#xff08;初始设置&#xff09; 2. 一阶低通滤波的实现2.1. 根据公式在Simulink中搭模型2.1.1. 一阶低通滤波公式2.1.2. 搭建一阶低通滤波 2.2. 把模型装进子系统的2种方式2.2.1. 方式12.2.2. …

【联通支付注册/登录安全分析报告】

联通支付注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨…

26、Qt使用QFontDatabase类加载ttf文件更改图标颜色

一、图标下载 iconfont-阿里巴巴矢量图标库 点击上面的链接&#xff0c;在打开的网页中搜索自己要使用的图标&#xff0c;如&#xff1a;最大化 找到一个自己想用图标&#xff0c;选择“添加入库” 点击“购物车”图标 能看到刚才添加的图标&#xff0c;点击“下载代码”(需要…

安全工程师基础模拟试题

安全工程师基础模拟试题作为一名安全工程师&#xff0c;掌握基本的安全知识和技能是必不可少的。下面是一些基础模拟试题&#xff0c;帮助您检验自己的安全工程师能力。1.在网络安全中&#xff0c;什么是… 1安全工程师基础模拟试题 作为一名安全工程师&#xff0c;掌握基本的…

【机器学习300问】85、Adam梯度下降优化算法的原理是什么?

Adam优化算法取了两个算法名称的首字母——Adaptive Moment Estimation的缩写&#xff0c;结合了Momentum算法和RMSprop算法的优点。在Momentum中&#xff0c;会计算前一时刻的梯度&#xff0c;并将其用于当前时刻的梯度更新&#xff1b;而RMSprop会对梯度的大小进行自适应调整…