有哪些网站做外贸的/百度快照是啥

有哪些网站做外贸的,百度快照是啥,福州做网站互联网公司,四川城市建设住建厅网站🔥本文专栏:Linux Linux实践项目 🌸博主主页:努力努力再努力wz 那么今天我们就要进入Linux的实践环节,那么我们之前学习了进程控制相关的几个知识点,比如进程的终止以及进程的等待和进程的替换,…

🔥本文专栏:Linux Linux实践项目
🌸博主主页:努力努力再努力wz

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

那么今天我们就要进入Linux的实践环节,那么我们之前学习了进程控制相关的几个知识点,比如进程的终止以及进程的等待和进程的替换,那么我们接下来就要结合前面所讲的进程控制相关的接口比如fork以及waitpid和execl等来自己实现一个命令行解释器,那么废话不多说,让我们进入正文

★★★ 本文前置知识
进程的替换
进程的终止与等待
进程的概念


shell实现的框架

那么在用c语言真正上手实操我们的shell外壳程序时,那么我们脑海里得有一个大体的实现框架,也就是所谓的一个整体思路,在有了整体思路后我们再去谈具体每个模块的细节,那么我们首先就先从shell本身的工作原理作为切入口入手

那么我们的shell也就是我们的命令行解释器,那么它的工作就是获取用户输入的指令,然后来执行用户输入的指令,那么我们知道用户输入的指令的本质上就是一个字符串,所以shell首先就得读取到用户输入的字符串,然后保存在一个字符数组中,读取到用户输入的字符串之后,那么紧接着下一步便是解析用户输入的字符串,那么我们用户输入的指令无非可以分成两大部分,分别是指令部分以及参数部分,那么这里我们就需要定义一个字符指针数组,那么数组的每一个元素就是一个指针,那么指针指向的就是一个字符串,那么我们用户输入的字符串的指令部分就保存在字符指针数组的第一个元素也就是下标为0的位置,那么参数部分则依次保存在之后的位置,比如我们用户输入的指令是ls -l -a,那么此时我们就要解析为三部分,分别是是指令部分的“ls”字符串以及两个参数部分的字符串"-l”和“-a”,将这三个字符串则是依次保存到我们的字符指针数组下标为0和1和2的位置当中

而具体的解析这三部分字符串则需要用到我们c语言的strtok函数,那么具体细节我们下文再说,那么这里我们讨论的是大的框架与思路,所以我们可以专门定义一个函数来完成这个字符串解析的模块,它的工作就是解析用户输入的字符串将其指令部分以及参数部分的各个字符串分别保存到字符指针数组不同位置中,并且返回命令行的个数,比如用户输入的是ls -l,那么将其保存在字符指针数组char* argv[]并返回的个数就是2,,而如果是pwd,将其保存在字符指针数组char* argv[]并返回的个数就是1

那么接下来解析完用户输入的字符串之后,那么我们就可以来执行用户输入的指令了,那么这里我们知道我们用户输入的各种指令本质上就是在特定路径下保存的一个可执行文件,那么指令的执行本质上就是创建一个进程,那么我们shell执行这些指令就得利用fork函数来创建一个子进程,然后我们利用fork函数的返回值,将父子进程分成不同的执行流,那么在子进程的执行流代码片段中,我们就可以利用进程的替换,那么将我们的子进程的内容替换为我们要执行指令所对应的进程的上下文,那么我们父进程的执行流代码片段则是等待我们子进程的退出结果,那么我们就需要用waitpid函数来获取子进程的退出码

最后获取完子进程的退出码,如果子进程没有正常终止,那么就得将情况返回给用户,也就是将错误信息打印到终端,如果子进程正常终止然后下一步就是重复我们之前上文的环节,那么重复也就意味着我们实现的时候最后这些逻辑的代码都要封装到一个死循环当中。

那么这就是我们实现shell外壳程序的一个大框将,那么我们可以简单将其分为几个模块,分别是获取用户输入->解析用户输入->创建子进程->子进程的替换->父进程等待获取子进程的退出情况->重复上述步骤

那么看到这些模块,想必你一定还有一些疑问,那么接下来我就会在下文补充每个模块的代码实现以及注意的一些细节,和其他的模块的补充,那么有了大框架之后,那么接下来就让我们具体实现每一个模块了
在这里插入图片描述

shell各个模块的实现

1.获取用户输入

那么我们的shell首先得获取用户输入的字符串,那么我们知道在c语言中,我们获取用户输入的字符串常见就是使用我们的scanf函数来获取用户的输入,但是scanf函数有一个缺陷就是一旦读取到空格的时候,那么scanf便停止读取输入,而我们用户在输入字符串的时候,会手动用空格隔开指令部分与参数部分,所以我们就不能采取scanf函数来获取输入,所以这里我们需要用fgets函数,那么fgets函数则是将从标准输入流中读取用户的输入,遇到换行符停止,那么我们可以指定其在输入流中读取的字符串的长度也就是fgets的第二个参数,那么将其保存到一个临时字符数组中,如果读取失败,那么fgets则会返回NULL,读取成功fgets则会返回保存数组的地址

  • fgets

    头文件:<string.h>

  • 功能:获取用户输入的字符串,末尾自动添加\0

    :函数原型

char *fgets(char *str, int n, FILE *stream);

而我们知道用户在输入之前,我们终端都会显示一个命令行提示符,会显示我们当前登录的用户名以及所处的工作目录和运行的主机名称,所以我们在获取用户输入之前,我们得先打印一个字符串也就是命令行提示符,而切记,我们的shell命令行解释器本质也是一个进程,所以这命令行提示符的每一个信息就保存在我们当前进程的环境变量中,我们需要通过我们的系统调用接口getenv来获取其中特定字段的环境变量,这里就需要获取到我们的USER以及HOSTNMAE以及PWD这三个字段,那么我们只需要向getenv函数传递这三个字符串的指针,那么他会依次匹配各个字段的名称所对应的字符串并返回对应的值,也就是字符串的起始地址

代码实现:

  printf("[%s@%s %s]$",getenv("USER"),getenv("HOSTNAME"),getenv("PWD"));if(fgets(temp,sizeof(temp),stdin)==NULL){perror("fgets");continue;}

2.解析命令行

那么现在我们获取了我们的用户输入的字符串之后,那么我们是将用户输入的字符串保存在一个临时字符数组里,那么接下来我们就要将这个字符串给分割,将其指令部分以及参数部分的各个字符串给分割保存到我们的字符指针数组当中,那么我们这里就专门可以定义一个函数来完成字符串解析模块,并返回命令行参数的个数,那么我们知道我们用户输入的字符串会手动以空格分割,那么这里我们就需要调用我们的字符串函数也就是strtok函数来分割我们的字符串按照空格作为分隔符。但是在分割之前,我们又得注意一个细节,也就是我们用户输入完一个字符串,那么它会敲一个回车键来表示输入的结束,而回车则是对应的一个换行符\n,他会被我们的fegts给读取到,那么意味着在我们的字符串的末尾可能会有一个回车换行符

而回车换行符并不是我们一个有效的字符信息,所以我们在解析之前得去掉这个换行符,所以我们就利用我们的strlen函数首先获取到我们这个字符串包括空格以及换行符的总长度,那么如果我判断用户输入的字符串的len-1位置处的字符处是换行符,那么我们就将len-1位置用\0来覆盖,而\0是标记字符串结尾的标志,那么这样我们就可以消去末尾的回车换行符,这里是其中一个关键的实现细节

那么第二个细节就是我们的strtok函数的使用,那么我们strtok函数第一次调用的时候要传递我们要分割的字符串的首元素的地址,那么strtok内部会访问到一个静态的全局变量,这个静态变量是用来保存下一次分割的位置,那么我们每次调用strtok函数的时候,会从分割的起始位置处往后扫描直到遇到分隔符,然后将分隔符的位置修改为\0,然后返回该分割起始位置的指针,而我们知道\0是标记字符串的结尾,所以返回分割起始位置的指针就达到了一个分割子串的一个效果
而下一次调用strtok函数的时候,那么我们就不用传要分割的字符串的首元素的地址,因为上文说过strtok内部能访问到一个记录下一次分割位置的全局变量,那么之后的调用我们只需要传递一个NULL即可,它内部会继续从这个全局变量记录的位置开始扫描到下一个分隔符,将其修改为\0,最后如果我们开始的分割的位置是\0,也就是字符串末尾,没有更多的子串来分割时候,那么strtok就返回一个NULL


  • strtok
    头文件:<string.h>
    -功能:分割字符串

    :函数原型

 char *strtok(char *str, const char *delim);

在这个函数中我们就定义一个int类型的argc变量来跟踪命令行的个数,初始化为0,而我们将分割的字符串保存在对应的字符数组的下标就是argc的值,保存之后接着递增argc,最后返回的该argc就是我们的命令行的个数

代码实现:

int getString(char temp[],char* argv[])
{int len=strlen(temp);if(len>0&&temp[len-1]=='\n'){temp[len-1]='\0';len--;}int argc=0;char* toke=strtok(temp," ");while(toke!=NULL&&argc<length-1){argv[argc++]=toke;toke=strtok(NULL," ");}argv[argc]=NULL;return argc;
}

3.指令判断

那么这里在我们上文介绍实现我们的shell外壳程序的框架的时候模块的时候,其实我们故意漏了一个模块,那就是指令的判断,那么想必你一定会有所疑问,那么就是我们获取解析完用户输入的指令之后,我们为什么还要进行指令的判断呢?直接通通交给子进程去执行不就完了吗,我们父进程也就是shell外壳程序的本职工作不就是获取用户的输入吗

那么这里我们就要注意的就是,我们用户其中输入的指令,比如cd指令,也就是更改我们进程所处的工作目录,那么它针对的对象其实是我们的父进程也就是我们的shell外壳程序,那么如果我们把这个指令交给了子进程去完成,将子进程替换为cd指令所对应的上下文,那么子进程的执行是不会影响父进程的,那么子进程执行结束退出之后,我们shell进程所处的工作目录没有进行更改,那么所以我们对于有些指令,也就是针对当前父进程shell的运行环境的指令,比如cd,比如PWD指令,那么它就不能交给子进程来执行,而是得交给父进程来自己完成,那么这些指令也就是我们的内置指令

那么内置指令那么就不再是一个编写好的可执行文件,那么它是通常是一个实现好的库函数或者直接嵌套在我们的shell进程所对应的代码中,所以我们自己用c语言实现的时候,那么我们就首先准备定义一个全局属性的字符指针数组,然后该数组里面记录了我们所有的内置命令所对应的字符串,那么当解析完用户的指令之后,解析完保存的字符指针数组的第一个位置就是对应用户输入的指令部分的字符串,所以下一步我们依次匹配保存的所有内置命令对应的字符串,如果匹配成功,那么意味着是内置指令,就直接交给我们父进程执行,匹配失败则说明该指令不是内置命令,就交给子进程来执行,那么我们匹配的过程以及内置命令的执行的过程都可以定义两个函数来分别实现这两个模块,那么其中字符串的匹配就需要用到strcmp函数来实现

而所谓的内置命令,他的底层实现的时候本质其实就是依赖用c编写的库函数或者系统调用,比如cd内置命令,那么它就是用chdir库函数来实现的,那么这个库函数的作用就是能够访问到当前进程的环境变量中的工作目录字段,然后修改当前所处的工作目录,而pwd内置命令的本质其实也就是依赖getcwd库函数,那么该库函数会访问到该进程中环境变量记录当前所处也就是工作目录的字段PWD,将其值记录保存到一个数组当中,并且返回指向该数组的指针

  • chdir
    头文件:<unistd.h>

    :函数原型

  int chdir(const char *path);
  • getcwd
    头文件:<unistd.h>

    :函数原型

    char *getcwd(char *buf, size_t size);

那么这里我在实现的时候,就只判断了cd以及pwd这两种内置命令,那么我们可以下来直接去添加更多的内置命令,然后查询对应实现所依赖的库函数或者系统调用接口

代码实现:

bool check(char* argv[])//指令的判断
{for(int i=0;order[i]!=NULL;i++){if(strcmp(argv[0],order[i])==0)//如果该指令是内置命令就返回true{return true;}}return false;
}
void ordercomplete(int argc,char* argv[])//内置命令的执行
{if(strcmp(argv[0],"cd")==0){if(argc==2)//cd指令最多只能两个参数,其中第二个参数就是跳转的工作目录{if (chdir(argv[1]) != 0) {perror("chdir");}}else{printf("error: expected argument for 'cd'\n");}}if(strcmp(argv[0],"pwd")==0){char cwd[length];  // 定义一个字符数组错误的来保存我们的当前所处的工作目录if (getcwd(cwd, sizeof(cwd)) != NULL) {printf("Current working directory: %s\n", cwd);} else {perror("getcwd failed");  // 输出错误信息}}
}

5.子进程执行指令

那么剩下几个模块的细节和实现就很简单了,接下来这个模块就调用fork函数来创建一个子进程,然后利用fork函数的返回值,让父子进程有着不同的执行流,然后我们在子进程对应的执行流代码片段中,调用进程的替换的系统接口,而这里我们调用的exec族函数,一定是不能带有l的比如execl以及execlp等,因为我们不知道用户输入的命令行个数,所以不能用可变参数列表的进程替换接口,这里要注意
而我们用户输入的字符串都解析在了一个字符指针数组中,所以我们传的参数肯定就是一个数组,所以这里我们选择进程替换的函数就是execvp,那么它可以默认在环境变量的PATH中去匹配我们用户输入的指令所对应的可执行文件
那么我们用execvp函数来将子进程替换为指令所对应的进程的上下文,但是我们知道我们进程替换会出现调用失败的情况,那么调用失败的结果则是会执行进程替换接口之后的代码,那么我们就在execvp函数后面打印一个错误信息并且返回一个特殊的退出码

6.父进程的等待

那么我们父进程对应的执行流代码片段则是等待我们子进程的退出情况,所以我们需要调用waitpid函数来获取子进程的退出码,那么waitpid我们的等待方式则是设置为阻塞式等待,那么它的返回值就分别对应两种情况,要么等待成功并且获取到子进程的退出码,对应的返回值就是子进程的pid,而等待失败则是返回-1,我们对于等待失败则是要打印错误信息以及子进程的退出码

完整实现

那么将我们上面的6个模块所对应代码融合就是我们的shell的外壳程序,那么其实我们在实现shell外壳程序的时候,其实shell的整体实现难度不大,主要考察你对shell的工作原理的理解程度和几个系统调用接口的熟悉程度,shell实现的真正的难点其实在它各个模块实现的细节上,很容易出错,其中就考察我们对于一些c语言的库函数的掌握情况,那么接下来我就给出完成的shell的c语言代码的实现

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<stdbool.h>
#define length 1000
#define EXIT_FAIL 40
const char* order[]={"cd","pwd",NULL};int getString(char temp[],char* argv[])
{int len=strlen(temp);if(len>0&&temp[len-1]=='\n'){temp[len-1]='\0';len--;}int argc=0;char* toke=strtok(temp," ");while(toke!=NULL&&argc<length-1){argv[argc++]=toke;toke=strtok(NULL," ");}argv[argc]=NULL;return argc;
}
bool check(char* argv[])
{for(int i=0;order[i]!=NULL;i++){if(strcmp(argv[0],order[i])==0){return true;}}return false;
}
void ordercomplete(int argc,char* argv[])
{if(strcmp(argv[0],"cd")==0){if(argc==2){if (chdir(argv[1]) != 0) {perror("chdir");}}else{printf("error: expected argument for 'cd'\n");}}if(strcmp(argv[0],"pwd")==0){char cwd[length];  // 定义一个足够大的缓冲区来存储路径if (getcwd(cwd, sizeof(cwd)) != NULL) {printf("Current working directory: %s\n", cwd);} else {perror("getcwd failed");  // 输出错误信息}}
}
int main()
{int argc;char* argv[length];char temp[length];while(1){printf("[%s@%s %s]$",getenv("USER"),getenv("HOSTNAME"),getenv("PWD"));if(fgets(temp,sizeof(temp),stdin)==NULL){perror("fgets");continue;}argc=getString(temp,argv);if(argc==0){continue;}if(check(argv)){ordercomplete(argc,argv);continue;}int id=fork();if(id==0){execvp(argv[0],argv);perror("execvp");exit(EXIT_FAIL);}else{int status;int m=waitpid(id,&status,0);if(m<0){perror("waitpid");}else{if(WIFEXITED(status)){if(WEXITSTATUS(status)==40){printf("error\n");}}}}}return 0;
}

在Linux上的运行截图:
在这里插入图片描述

结语

那么这就是用c语言实现shell外壳程序的所有内容啦,那么它也是我第一个学习Linux所完成的一个小项目,那么它这个小项目的教学价值以及学习意义其实非常高,因为它不仅可以帮组你了解shell外壳程序的工作原理,更重要的是帮组你更能熟练掌握运用那几个关于进程控制十分重要的系统调用接口其中比如fork以及waitpid等,那么我的下一篇Linux文章就正式进入文件系统啦,我会持续更新,希望大家多多关注,那么如果本篇文章对你有所帮组的话,那么还请多多三连加关注哦,你的支持就是我最大的动力!

在这里插入图片描述

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

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

相关文章

⭐算法OJ⭐N-皇后问题 II【回溯剪枝】(C++实现)N-Queens II

⭐算法OJ⭐N-皇后问题【回溯剪枝】&#xff08;C实现&#xff09;N-Queens 问题描述 The n-queens puzzle is the problem of placing n n n queens on an n n n \times n nn chessboard such that no two queens attack each other. Given an integer n, return the num…

03.06 QT

一、使用QSlider设计一个进度条&#xff0c;并让其通过线程自己动起来 程序代码&#xff1a; <1> Widget.h: #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QThread> #include "mythread.h"QT_BEGIN_NAMESPACE namespace Ui {…

地下井室可燃气体监测装置:守护地下安全,防患于未“燃”!

在城市的地下&#xff0c;隐藏着无数的燃气管道和井室&#xff0c;它们是城市基础设施建设的重要部分&#xff0c;燃气的使用&#xff0c;给大家的生活提供了极大的便利。在便利生活的背后&#xff0c;也存在潜在的城市安全隐患。 近年来&#xff0c;地下井室可燃气体泄漏事故…

【使用hexo模板创建个人博客网站】

使用hexo模板创建个人博客网站 环境准备node安装hexo安装ssh配置 使用hexo命令搭建个人博客网站hexo命令 部署到github创建仓库修改_config.yml文件 编写博客主题扩展 环境准备 node安装 进入node官网安装node.js 使用node -v检查是否安装成功 安装成功后应该出现如上界面 …

C# OPC DA获取DCS数据(提前配置DCOM)

OPC DA配置操作手册 配置完成后&#xff0c;访问远程ip&#xff0c;就能获取到服务 C#使用Interop.OPCAutomation采集OPC DA数据&#xff0c;支持订阅&#xff08;数据变化&#xff09;、单个读取、单个写入、断线重连

发行思考:全球热销榜的频繁变动

几点杂感&#xff1a; 1、单机游戏销量与在线人数的衰退是剧烈的&#xff0c;有明显的周期性&#xff0c;而在线游戏则稳定很多。 如去年的某明星游戏&#xff0c;最高200多万在线&#xff0c;如今在线人数是48名&#xff0c;3万多。 而近期热门的是MH&#xff0c;在线人数8…

Unity自定义区域UI滑动事件

自定义区域UI滑动事件 介绍制作1.创建一个Image2.创建脚本 总结 介绍 一提到滑动事件联想到有太多的插件了比如EastTouchBundle&#xff0c;今天想单纯通过UI去做一个滑动事件而不是基于Box2d或者Box去做滑动事件。 制作 1.创建一个Image 2.创建脚本 using UnityEngine; us…

taosd 写入与查询场景下压缩解压及加密解密的 CPU 占用分析

在当今大数据时代&#xff0c;时序数据库的应用越来越广泛&#xff0c;尤其是在物联网、工业监控、金融分析等领域。TDengine 作为一款高性能的时序数据库&#xff0c;凭借独特的存储架构和高效的压缩算法&#xff0c;在存储和查询效率上表现出色。然而&#xff0c;随着数据规模…

《UE5_C++多人TPS完整教程》学习笔记34 ——《P35 网络角色(Network Role)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P35 网络角色&#xff08;Network Role&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; Stephe…

微信小程序引入vant-weapp组件教程

本章教程,介绍如何在微信小程序中引入vant-weapp。 vant-weapp文档:https://vant-ui.github.io/vant-weapp/#/button 一、新建一个小程序 二、npm初始化 npm init三、安装 Vant Weapp‘ npm i @vant/weapp -

C++ 作业 DAY5

作业 代码 Widtget.h class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);~Widget();private:Ui::Widget *ui;/************************ 起始终止坐标 ************************/QPoint end;QPoint start;QVector<QPoint> per_start_lis…

【js逆向】iwencai国内某金融网站实战

地址&#xff1a;aHR0cHM6Ly93d3cuaXdlbmNhaS5jb20vdW5pZmllZHdhcC9ob21lL2luZGV4 在搜索框中随便输入关键词 查看请求标头&#xff0c;请求头中有一个特殊的 Hexin-V,它是加密过的&#xff1b;响应数据包中全是明文。搞清楚Hexin-V的值是怎么生成的&#xff0c;这个值和cooki…

使用Node.js从零搭建DeepSeek本地部署(Express框架、Ollama)

目录 1.安装Node.js和npm2.初始化项目3.安装Ollama4.下载DeepSeek模型5.创建Node.js服务器6.运行服务器7.Web UI对话-Chrome插件-Page Assist 1.安装Node.js和npm 首先确保我们机器上已经安装了Node.js和npm。如果未安装&#xff0c;可以通过以下链接下载并安装适合我们操作系…

BUUCTF——[GYCTF2020]FlaskApp1 SSTI模板注入/PIN学习

目录 一、网页功能探索 二、SSTI注入 三、方法一 四、方法二 使用PIN码 &#xff08;1&#xff09;服务器运行flask登录所需的用户名 &#xff08;2&#xff09;modename &#xff08;3&#xff09;flask库下app.py的绝对路径 &#xff08;4&#xff09;当前网络的mac地…

FPGA学习篇——Verilog学习3(关键字+注释方法+程序基本框架)

1 Verilog常用关键字 大概知道以下哪些是关键字就好&#xff0c;如何使用还是得在编写代码中来学习。 2 Verilog注释方法 Verilog有两种注释方式&#xff1a; 2.1 “ // ” 单行。 2.2 “ /* ... */ ” 可扩展多行。 3 Verilog程序基本框架 Verilog 的基本设计单元是“…

FPGA之USB通信实战:基于FX2芯片的Slave FIFO回环测试详解

FPGA之Usb数据传输 Usb 通信 你也许会有疑问&#xff0c;明明有这么多通信方式和数据传输&#xff08;SPI、I2C、UART、以太网&#xff09;为什么偏偏使用USB呢? 原因有很多&#xff0c;如下&#xff1a; 1. 高速数据传输能力 高带宽&#xff1a;USB接口提供了较高的数据传…

深入理解与配置 Nginx TCP 日志输出

一、背景介绍 在现代网络架构中&#xff0c;Nginx 作为一款高性能的 Web 服务器和反向代理服务器&#xff0c;广泛应用于各种场景。除了对 HTTP/HTTPS 协议的出色支持&#xff0c;Nginx 从 1.9.0 版本开始引入了对 TCP 和 UDP 协议的代理功能&#xff0c;这使得它在处理数据库…

selenium库

一、什么是selenium库&#xff1f; selenim是一个用于Web应用程序自动化测试工具&#xff0c;selenium测试直接运行在浏览器中 像真正的用户在操作一样&#xff0c;驱动浏览器执行特定的动作&#xff0c;如点击&#xff0c;下拉等操作 二、selenium在爬虫中的应用 获取动态…

十七、从0开始卷出一个新项目之瑞萨RZN2L定时器(GPT)+DMA生成PWM的运动控制

一、概述 嵌入式科普(34)通过对比看透DMA的本质 分享瑞萨RZN2L使用DMA生成PWM的运动控制的例程源码 rzn2l必要的外设资源&#xff1a; rzn2l拥有32-bit timer General PWM Timer (GPT) with 18 channels CPU、GPT最高频率400Mhz DMAC0 and DMAC1 8 channels 8 channels 还…

MR的环形缓冲区(底层)

MapReduce的大致流程&#xff1a; 1、HDFS读取数据&#xff1b; 2、按照规则进行分片&#xff0c;形成若干个spilt&#xff1b; 3、进行Map 4、打上分区标签&#xff08;patition&#xff09; 5、数据入环形缓冲区&#xff08;KVbuffer&#xff09; 6、原地排序&#xff…