匿名管道、命名管道--Linux

2023-05-16

文章目录

  • 🚩管道的理解
  • 🚩匿名管道
    • 🍁pipe函数创建匿名管道
    • 🍁多个匿名管道的控制
  • 🚩命名管道(FIFO)
    • 🍁mkfifo函数创建命名管道
  • 🚩总结

🚩管道的理解

我们在生活中对管道并不陌生,水管、煤气管道……所有的这些管道都是用来运输某种东西,水管将水从一个地方送到另一个地方,煤气管道将煤气从一个地方送到另一个地方,完成物资的运输。在不同的进程之间,信息就是所谓的物资,进程间的信息通讯可以通过管道来实现(并不一定得是管道,还有其他方式)。举个例子,父子进程由于写时拷贝的存在,是没法直接进行信息交流的,只能借助一些手段来辅助完成。

首先,管道分为匿名管道与命名管道,管道的种类不同,作用的场景也会有所不同。但由于我是刚刚接触管道,具体应用场景使用的还不算多,因此这里就简单介绍一下匿名管道与命名管道的基本功能与使用,后面学习到更多的知识会持续更新滴✌。

🚩匿名管道

匿名管道,字面意思就是没有名字的管道,主要是在有血缘关系的进程之间(父子进程)起作用。匿名管道的是如何做到两个独立的进程之间的信息交流的呢?

进程之间进行信息交流的前提就是看到同一份资源,具体的处理方式就是通过一个文件来存放需要交流的信息。而这个文件是系统提供的函数接口创建的,将这个匿名文件的读写端都打开。之后子进程创建就会继承父进程的文件描述符表,这样父子进程都可以对这个匿名文件进行操作了。接下来我们看一下这个匿名管道创建的函数。

🍁pipe函数创建匿名管道

image-20221109235259080

头文件:unistd.h

参数:pipefd数组

返回值:如果管道创建成功,返回0,否则的话返回-1,并设置errno。

如果我们想要实现父子进程之间的通信,首先在父进程中创建管道,之后再创建子进程。这样的话就在父进程开始的时候关闭读端,在子进程开始的时候关闭写端,就会形成单向信息流的管道。

🔺这里的所谓的读端与写端,其实并不是真的把一个文件分成读端和写端两部分,而是打开文件的方式分别为读和写的方式,为了更形象的描述管道,我们就根据打开的方式对应现实中的管道两个端口。

image-20221110151840134

信息传递完之后,再关闭父进程的写端与子进程的读端,管道文件会从缓存中被清除,完成信息传递的使命。

⌨实测环节:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>
#include <vector>
#include <unordered_map>
#include <ctime>
#define processNum 3
using namespace std;
const char *msg = "It's impossible to not fall in love with you!";
int main()
{
    //循环processNum次数
    for (int i = 0; i < processNum; ++i)
    {
        int pipefd[2] = {0};
        //打开管道,并将读和写对应的文件描述符分别写入pipefd[0]和pipefd[1]。
        if (pipe(pipefd) == -1)
        {
            cerr<< strerror(errno) << endl;
        }
        //创建子进程
        pid_t id = fork();
        if (id == 0)
        {
            //子进程关闭写端,进行读取操作
            close(pipefd[1]);
            //操作
            char str[1024];
            ssize_t size = read(pipefd[0], str, sizeof(str) - 1);
            while(size)
            {
                str[size] = '\0';
                cout << str << endl;
                size=read(pipefd[0], str, sizeof(str) - 1);
            }
            //关闭读端,退出子进程
            close(pipefd[0]);
            cout<<getpid()<<" 进程已退出"<<endl;
            exit(0);
        }
        else if (id > 0)
        {
            //父进程关闭读端,进行写操作
            close(pipefd[0]);
            
            //操作
            write(pipefd[1], msg, strlen(msg));
            
            //关闭写,回收子进程资源
            close(pipefd[1]);
            if (waitpid(id, nullptr, 0))
            {
                cout << "子进程[" << id << "]"
                     << "已被回收" << endl;
            }
        }
    }
    return 0;
}

💻:

image-20221110001628470

🔺值得一提的是,假如父进程一直不写入数据,子进程就会一直阻塞等待数据的写入,一旦管道文件的写端全部被关闭,读端又把管道文件内部的数据全部读完,read函数的返回值就会是0,并不再读取数据了。我们平常在使用read函数时,并没有出现出现阻塞等待读取数据的情况。原因在于平常我们read的文件是普通文件,而这次read的是管道文件。由于文件的属性不同,读取的方式就会有所区别,这就是在设计的时候根据需求所定好的规则。(文件种类问题)

上面说的问题用图片不好证明,有兴趣的小伙伴可以自己尝试一下,将父进程中的写入操作屏蔽掉,改成一个死循环(作用就是一直不关闭父进程的写端),子进程就一直不会退出,卡在read函数那里。

🔺还有一种情况,假如子进程的读端关闭了,父进程还一直在写入数据,就会将管道文件写满,导致写入阻塞。

🍁多个匿名管道的控制

上面的代码是一个父进程通过一个匿名管道与一个子进程进行通信。假如此时想要与多个子进程通信呢?那么就需要多个匿名管道来实现了。

假设还是父进程控制写端,子进程控制读端。

首先,这里有一个需要注意的点:由于是一个父进程与多个子进程通信,因此在循环创建子进程时会将父进程的文件描述符多次拷贝到子进程中去。而父进程每控制一个管道,就要在自己的文件描述符表中添加一个写端,就会被下次循环而创建的子进程继承,但实际上子进程用不到这个被继承下来的文件描述符,因此我们在子进程开始的时候除了要把新开启的匿名管道的写端给关闭,还要将之前继承的无用写端给关闭。

🙋‍♂️:既然是无用的,不管它就是了,为什么还要多此一举去关闭它呢?

👨‍🏫:因为这是read函数读取管道文件数据的特殊性导致的哇。记不记得我们想要持续多次的从文件中获得数据,就要保证文件至少有一个写端打开才行,使得就算匿名管道没有数据,子进程也会阻塞等待数据的到来。但是我们要想通过关闭管道的写端,来实现读端最后拿到的数据字节数为0的条件判断,从而退出子进程循环的读取数据,进而退出子进程的逻辑,就得把所有的读端都给关闭才行,子进程继承下来来的读端当然得关闭啦。

🙋‍♂️:最后几句话好绕啊……

👨‍🏫:是的,因此我们再结合一下代码,画个图会比较好理解。下面的代码的核心思想就是父进程开始的时候将三个函数作为任务放进vector中去,然后打开匿名管道,接着循环创建子进程,父进程通过写入数字来控制哪个子进程执行哪个函数。细节都已经加上注释了,看的时候可以仔细想想。

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>
#include <vector>
#include <unordered_map>
#include <ctime>
#define processNum 3
using namespace std;
typedef void (*functor)();
vector<functor> functors;//任务集

//三个任务
void f1()
{
    cout << "进程[" << getpid() << "]正在执行,时间戳[" << (unsigned)time(nullptr) << "]\n\n";
}
void f2()
{
    cout << "父进程[" << getppid() << "]给"<<"["<<getpid()<< "]" << "的任务还在执行……\n\n";
}
void f3()
{
    cout << "["<<getpid()<<"]:It's impossible to not fall in love with you\n\n";
}

//任务集加载
void LoadFunction()
{
    functors.push_back(f1);
    functors.push_back(f2);
    functors.push_back(f3);
}
typedef pair<int32_t, int32_t> elem;//用来存放子进程的pid和对应管道的管道写端
int main()
{
    srand((unsigned)time(nullptr));
    LoadFunction();
    vector<elem> assginTable;
    vector<int> last;
    for (int i = 0; i < processNum; ++i)
    {
        int pipefd[2] = {0};

        //子进程创建前先打开管道
        if (pipe(pipefd) == -1)
        {
            cout << "管道建立失败" << endl;
            return 1;
        }

        //创建子进程
        pid_t id = fork();
        if (id == 0)//子进程只会执行if内部的代码,因为只要结束if语句,if内的exit就会使得子进程退出
        {
            for (auto &oldFd : last) close(oldFd); //子进程关闭从父进程继承下来的无用写端
            cout << "子进程的pid: " << getpid() << "对应的读端fd是: " << pipefd[0] << " 对应的写端是: " << pipefd[1] << endl;
            close(pipefd[1]); //子进程关闭写端
            
            //循环读取数据
            while (true)
            {
                //从管道中读取父进程写入的任务编号存在operatorCode中
                int32_t operatorCode = 0;
                ssize_t size = read(pipefd[0], &operatorCode, sizeof(int32_t)); //读取数据
                
                if (size == 0) //读取数据字节数为0,说明所有的读端关闭并且管道内没有数据,因此退出循环读取数据
                {
                    cout << "子进程: [" << getpid() << "]进程管道写端关闭,进程退出" << endl;
                    break;
                }

                //读到任务编号就调用任务集中的函数,执行任务
                if (operatorCode < functors.size())
                    functors[operatorCode](); //执行任务
            }
            close(pipefd[0]);
            exit(0); //子进程退出,保证子进程只会执行if语句内部的代码
        }
        //父进程
        close(pipefd[0]);          //父进程关闭读端
        last.push_back(pipefd[1]); //下次创建新的子进程,关闭从父进程继承的写端时会用到
        elem e(id, pipefd[1]);     //将本次开启的   子进程id    和    管道写入端    给存起来
        assginTable.push_back(e);  //父进程给各个子进程派送任务编号会用到
    }
    sleep(2);
    //进行五次随机任务的派发
    int cnt = 5;
    while (cnt--)
    {
        int32_t pick = rand() % assginTable.size(); //随机选取管道写入操作数
        int32_t task = rand() % functors.size();    //随机选取方法
        write(assginTable[pick].second, &task, sizeof(int32_t));
    }
    sleep(1);

    for (int i = 0; i < assginTable.size(); ++i)
    {
        close(assginTable[i].second);
        cout << "父进程: 已关闭子进程[" << assginTable[i].first << "]"
             << "对应管道,"
             << "对应的控制fd是: " << assginTable[i].second << endl;

        if (waitpid(assginTable[i].first, nullptr, 0) > 0)
        {
            cout << "父进程:子进程[" << assginTable[i].first << "]"
                 << "已被回收" << endl << endl;
        }
    }
    return 0;
}

💻:

image-20221111155311011

灰色代表当前打开的匿名管道的文件描述符关闭,绿色代表开启,虚线代表关闭对应的文件描述符。

🍃第一次创建子进程时的管道处理:

因为第一次没有从父进程那里继承之前打开的写端,故不需要额外关闭其他写端。

image-20221111165911053

🍃第二次创建子进程的管道处理:

由于父进程此时已经有了一个写端描述符,因此子进程继承之后得额外多关闭一个写端,这里也就是要额外关闭子进程的4。

image-20221111170450893

🍃第三次创建子进程的管道处理:

父进程又多了一个文件描述符,因此子进程继承之后得额外多关闭两个写端,这里也就是要额外关闭子进程的4、5。

image-20221111170630017

而且我们发现,由于每次父进程都关闭3,会导致再次开启管道时会使得3被重复开启,也就导致子进程每次都会以3为读端。

经过上面的处理,最后我们想要退出程序并关闭管道,回收子进程,只需要依次关闭父进程中记录好的文件写端,就会使所有的子进程读取数据为0退出进程,父进程的waitpid等待成功,子进程从僵尸状态转变为死亡状态,回收完成。

上面我们实现的其实是一个简单的进程池,假如将父进程派发任务的算法从随机指派改为某种高效利用多个进程的指派,那么就可以利用多进程完成某些特定的工作。虽然还没有学习到相关内容,但是已经可以展望一下之后关于管道的深度应用了。

🚩命名管道(FIFO)

上面我们提到匿名管道的是在有血缘关系的进程之间使用的,那么如果想要在没有血缘关系的进程之间使用,就没办法了。这个时候就只能使用所谓的命名管道了。

命名管道的实现方式和匿名管道的实现方式很相似,都是使得两个进程看到同一份资源,但是这次我们自己指定一份资源给两个进程通信使用,也就是指定一个文件作为管道文件。由于open函数创建的文件只能是普通文件,因此只能求助于系统提供的另一个函数mkfifo。

🍁mkfifo函数创建命名管道

image-20221111180108791

头文件:sys/types.h、sys/stat.h

参数:pathname–要创建的管道文件名、mode–文件权限设置(默认情况下,创建的FIFO的模式为0666(‘a+rw’)减去umask中设置的位)

返回值:如果文件创建成功,返回0,否则的话返回-1,并设置errno。

下面我们可以用命名管道实现一个有意思的操作:模拟 客户端与服务端的数据实时传输,两个完全没有关系的文件进行交流。

简单来说就是先启动服务端可执行文件,然后创建命名管道,之后以读的方式打开管道文件,进行数据的循环阻塞式读取。而客户端则是在此之后运行,不用创建管道文件了,直接打开已经被创建的管道文件,进行数据写入。

服务端只要拿到了数据可以进行想要的操作,这里我们就直接输出拿到的数据。

⌨:

//common.h
#pragma once
#include<iostream>
#include<unistd.h>
#include <sys/stat.h>
#include<sys/types.h>
#include<cstring>
#include<cerrno>
#include<fcntl.h>
using namespace std;
#define IPC_PATH "fifo"

//Serve.cpp
#include "common.h"
int main()
{
    cout << "This is server" << endl;
    umask(0);
    int fifo = mkfifo(IPC_PATH, 0600);
    if (fifo < 0)
    {
        cerr << strerror(errno) << endl;
        return 1;
    }
    int pipeFd = open(IPC_PATH, O_RDONLY);
    if (pipeFd < 0)
    {
        cerr << "open fifo error" << endl;
        return 2;
    }
    char str[1024];
    while (true)
    {
        ssize_t size = read(pipeFd, str, sizeof(str) - 1);
        if (size == 0)
        {
            break;
        }
        str[size] = '\0';
        cout << "server# " << (char*)str;
    }
    close(pipeFd);
    unlink(IPC_PATH);//服务端删除管道文件
    cout << "server# server exit" << endl;
    return 0;
}

//Client.cpp
#include "common.h"
int main()
{
    cout << "This is Client" << endl;
    int pipeFd = open(IPC_PATH, O_WRONLY);
    if (pipeFd < 0)
    {
        cerr << strerror(errno) << endl;
        return 1;
    }
    char line[1024];
    while (true)
    {
        cout << "client# ";
        fflush(stdout);
        if (fgets(line, sizeof(line), stdin) != nullptr)
        {
            line[strlen(line) - 1] = '\0';
            write(pipeFd, line, strlen(line));
        }
        else
        {
            break;
        }
    }
    close(pipeFd);
    cout << "client exit" << endl;
    return 0;

💻:

image-20221111195515227

🚩总结

管道就是文件。这句话才是最关键的,所有关于管道的操作都是围绕文件进行的。管道作为进程间通信方式的一种,算是比较基础的了。这算是我第一次接触进程间的通信方法吧,感觉之前学习的各种知识已经串起来了,继续努力😎!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

匿名管道、命名管道--Linux 的相关文章

  • 51单片机硬件介绍

    1 单片机是啥 单片机 xff0c 简称MCU xff0c 是微型计算机 xff0c 集成了一部计算机许多硬件功能 xff0c 有CPU 存储器 xff08 ROM RAM xff09 等 2 有了这样一个单片机芯片后 xff0c 怎么将程
  • matlab硬件支持包离线安装-(安装文件夹错误)

    dSupport Software Downloader MATLAB amp Simulinkhttps ww2 mathworks cn support install support software downloader html
  • 小结:卸载SolidWorks2018->重新安装系统->安装SolidWorks2020

    因为卸载SW2018卸载不干净 xff0c 所以在安装SW20版一直在出错 xff0c 错误如下 xff1a 这个错误解决后继续安装 xff0c 又发现没有出现原本序列号的那一界面 xff0c 然后还有异型孔向导安装不了 xff0c 最后还
  • FreeRTOS信号量 基于STM32

    目录 概述 一 信号量基本概念 1 二值信号量 2 计数信号量 3 互斥信号量 4 递归信号量 二 二值信号量运作机制 三 计数信号量运作机制 四 常用信号量函数接口讲解 1 创建二值信号量 xSemaphoreCreateBinary 2
  • FreeRTOS互斥量 基于STM32

    文章目录 一 互斥量基本概念 二 互斥量的优先级继承机制 三 互斥量应用场景 四 互斥量运作机制 五 互斥量函数接口讲解 1 互斥量创建函数 xSemaphoreCreateMutex 2 递归xSemaphoreCreateRecursi
  • FreeRTOS事件组 基于STM32

    概述 文章对事件组的 xff0c 应用场景 xff0c 运作机制 xff0c 以及事件的创建 xff0c 删除 xff0c 等待 xff0c 置位 xff0c 同步等操作 文章目录 概述 一 事件标志组简介 1 事件位 事件标志 2 事件组
  • FreeRTOS任务通知 基于STM32

    文章目录 一 任务通知简介 二 任务通知的运作机制 三 任务通知的函数接口讲解 1 xTaskGenericNotify 2 xTaskNotifyGive 3 vTaskNotifyGiveFromISR 4 xTaskNotify 5
  • FreeRTOS软件定时器 基于STM32

    文章目录 一 软件定时器的基本概念 二 软件定时器应用场景 三 软件定时器的精度 四 软件定时器的运作机制 五 软件定时器函数接口讲解 1 软件定时器创建函数 xTimerCreate 2 软件定时器启动函数 xTimerStart 3 软
  • FreeRTOS内存管理 基于STM32

    目录 一 内存管理的基本概念 二 内存管理的应用场景 三 heap 4 c 1 内存申请函数 pvPortMalloc 2 内存释放函数 vPortFree 四 内存管理的实验 五 内存管理的实验现象 一 内存管理的基本概念 在计算系统中
  • 关于ECSHOP模板架设的服务器php版本过高报错的解决方法集合

    1 admin index php admin sms url php ECSHOP模板 报错 xff1a Strict Standards mktime You should be using the time function inst
  • FreeRTOS中断管理 基于STM32

    文章目录 一 异常与中断的基本概念 二 中断的介绍 三 和中断相关的名词解释 四 中断管理的运作机制 五 中断延迟的概念 六 中断管理的应用场景 七 中断管理讲解 八 中断管理实验 九 中断管理实验现象 一 异常与中断的基本概念 异常是导致
  • 链表基础知识详解(非常详细简单易懂)

    概述 xff1a 链表作为 C 语言中一种基础的数据结构 xff0c 在平时写程序的时候用的并不多 xff0c 但在操作系统里面使用的非常多 不管是RTOS还是Linux等使用非常广泛 xff0c 所以必须要搞懂链表 xff0c 链表分为单
  • FreeRTOS临界段的保护

    什么是临界段 临界段用一句话概括就是一段在执行的时候不能被中断的代码段 在 FreeRTOS 里面 xff0c 这个临界段最常出现的就是对全局变量的操作 xff0c 全局变量就好像是一个枪把子 xff0c 谁都可以 对他开枪 xff0c 但
  • SPI通讯协议详解 基于STM32

    SPI 协议简介 SPI 协议是由摩托罗拉公司提出的通讯协议 Serial Peripheral Interface xff0c 即串行外围设备接口 xff0c 是 一种高速全双工的通信总线 它被广泛地使用在 ADC LCD 等设备与 MC
  • C语言编译过程

    C语言的编译过程 xff1a 预处理 编译 汇编 链接 gcc E hello c o hello i 1 预处理 gcc S hello i o hello s 2 编译 gcc c hello s o hello o 3 汇编 gcc
  • C语言数组详解

    目录 一 数组的概念 二 数组的分类 2 1 按元素的类型分类 2 2 按维数分类 三 数组的定义 3 1 一维数组的定义 格式 xff1a 3 2 二维数组的定义 四 定义并初始化 4 1 一维数组的初始化 4 2 二维数组的初始化 五
  • C语言动态分配内存

    文章目录 一 动态分配内存的概述 二 静态分配 动态分配 三 动态分配函数 3 1 malloc 3 2 free 3 3 calloc 3 4 realloc 四 内存泄漏 一 动态分配内存的概述 在数组一章中 xff0c 介绍过数组的长
  • 嵌入式C语言(入门必看)

    目录 STM32的数据类型 const关键字 static 关键字 volatile关键字 extern关键字 struct结构体 enum typedef define 回调函数 ifdef ifndef else if 嵌入式开发中既有
  • ESP32上手指南

    乐鑫的ESP32微控制器是一款集成有2 4 GHz Wi Fi和蓝牙4 0双模的物联网芯片方案 xff0c 采用台积电 TSMC 超低功耗的40纳米工艺代工 片上集成有天线开关 射频巴伦 功率放大器 接收低噪声放大器 滤波器 电源管理模块等
  • 基于STM32硬币识别检测

    本设计基于ARM内核的单片机STM32F4的高识别率硬币识别装置 xff0c 主要应用于各公共营业场所 xff0c 如各超市 xff0c 自动售货机 xff0c 公共交通等 它应该能完成一角 xff08 分新版旧版 xff09 xff0c

随机推荐

  • PHP多维数组排序

    User 61 M 39 User 39 Incomelog 61 M 39 incomelog 39 user 61 User gt select now date 61 39 2015 02 09 39 integral 61 arra
  • PH电极酸碱度检测

    最近做了一个项目是关于PH电极测酸碱度的一个仪器 简单地说 xff1a 玻璃电极是一种氢离子选择性电极 xff0c 相当于一个对玻璃膜两侧氢离子浓度差异能产生附加电势差的 盐桥 xff0c 一般的盐桥是为了消除浓差电势或者液体接触电势这种附
  • 关于调试RTC时钟出现的问题

    此次做一个项目出现了一个令我很不解的问题 xff0c 就是RTC时钟 xff0c 代码是提前写好的 xff0c 当时是用的STM32F103ZET6最小系统板 xff0c 所有功能都是没有问题的 但是最终我画好的PCB芯片用的是STM32F
  • vscode编写c/c++及自动配置c/c++环境

    目录 前言所需的工具链接一 vscode中文设置及c c 43 43 插件安装1 中文设置2 c c 43 43 插件安装 二 环境配置1 解压AutoVsCEnv WPF V1 993自动配置工具压缩包2 运行AutoVsCEnv WPF
  • 安装最新版keil5编译报错*** target ‘target 1‘ uses arm-compiler ‘default compiler version 5‘ which i,keil5.37版

    原因是 missing compiler version5 xff0c 缺少V5编译器 xff08 compiler version5 xff09 xff0c 因为打开的工程比较老 xff0c 是用v5的编译器写的 xff0c 而现在下的k
  • vector的理解以及模拟实现

    vector的理解以及模拟实现 vector介绍vector常见函数介绍vector模拟实现及迭代器失效讲解 vector介绍 vector文档 vector是表示可变大小数组的序列容器 就像数组一样 xff0c vector也采用的连续存
  • 《数据库的嵌套查询和统计查询》

    选择Study数据库 xff0c 用SQL语句进行以下查询操作 1 xff0e 嵌套查询 求选修了数据结构的学生学号和成绩 span class token keyword SELECT span Sno span class token
  • 由NP完全问题引出动态规划——状态压缩DP

    所有部分都应当在非强制的情况下组合回一起 要记住 xff0c 你重组的那部分原来就是你拆解的 因此 xff0c 如果你不能让它们组合回来的话 xff0c 那一定是有原因的 要想尽一切办法 xff0c 除了用锤头 IBM手册 1925 Par
  • IMU学习的一些记录(不含推导公式,仅做了解)

    IMU xff08 惯性测量元件 xff09 测量三个量 xff1a 1 加速度 2 角速度3地磁 xff08 具体内容不展开 xff09 原始数据采集 IMU芯片与单片机硬件享连 xff0c 通过程序处理数据 上位机 xff08 一般运行
  • STL简介

    STL主要包含了容器 迭代器 算法和string四部分 标准库算法对迭代器而不是容器进行操作 因此 xff0c 算法不能 xff08 直接 xff09 添加或删除元素 一 容器 容器为存储和管理数据对象的集合 xff0c 包含了三种容器 x
  • Linux开发工具(5)——git

    文章目录 git版本控制器git是什么git的操作clone仓库到本地上传本地文件到git git版本控制器 git是什么 标题也说了git就是一个版本控制器 xff0c 版本控制器是用来保存一个文件的历史版本 xff0c 如果有需要可以进
  • 微信支付的常见问题,invalide code

    这段时间在做微信 支付开发 xff0c 在公司的公众号审批下来后 xff0c 我这边的测试用例也已经开发完毕 xff0c 于是拿着具体的数据来调试了 xff0c 大段大段的代码就不贴了 xff0c demo里有 xff0c 这里就说说调试过
  • 记一次串口调试工具发指令无反应问题

    最近新采购一块板子 xff0c 需要连接Android端进行USB串口通讯 首先需要在Windows上用串口调试工具先调通来确认板子没问题 xff0c 调的时候发现 xff0c 咋发指令都不通 xff0c 换了几个调试工具都不行 xff0c
  • PyQt5 基本语法(七):布局管理

    文章目录 布局管理1 布局概念2 布局方式2 1 手动布局2 1 1 绝对布局2 1 2 方法重写 2 2 布局管理器 3 布局管理器概念4 使用演示5 详细使用5 1 QLayout5 1 1 作用5 1 2 功能描述5 1 2 1 构造
  • Qt 实现简单的tcp网络通信

    文章目录 成品效果图 xff1a 代码 xff1a 工具头文件tool hUI文件代码 ui widget h 窗口头文件 widget h xff1a 窗口源文件widget cpp 相关代码说明 xff1a Qt获取本机ip Qt 打开
  • strrchr函数

    lt string h gt 描述 C 库函数 char strrchr const char str int c 在参数 str 所指向的字符串中搜索最后一次出现字符 c xff08 一个无符号字符 xff09 的位置 声明 下面是 st
  • 独轮车串级pid初了解

    今晚满脑子都是如何调好独轮车 xff0c 应该用哪一套方案的时候 xff0c 我找到了一名博主 xff0c 他应该和我也一样大 xff0c 感觉他真的很值得我去学习 xff0c 所有东西几乎都是依靠自己手动制作 xff0c 也不凭借商业化的
  • 【归并排序】C++数据结构实现归并排序完整代码

    归并排序 C 43 43 数据结构实现归并排序完整代码 归并排序 xff08 Merging Sort xff09 定义 xff1a 把两个或者多个有序的序列合并为一个 递归调用方式实现方式实现代码 xff1a 一 归并排序函数入口 归并排
  • (Jetpack TX2)ubuntu上aarch64安装anaconda3

    1 在终端中输入sudo uname a可以查看ubuntu的信息 2 系统是aarch64 xff0c 它既不是64 Bit x86 也不是64 Bit Power8 and Power9 xff0c 所以使用anaconda官网 htt
  • 匿名管道、命名管道--Linux

    文章目录 x1f6a9 管道的理解 x1f6a9 匿名管道 x1f341 pipe函数创建匿名管道 x1f341 多个匿名管道的控制 x1f6a9 命名管道 xff08 FIFO xff09 x1f341 mkfifo函数创建命名管道 x1