linux系统编程-进程

news2024/11/27 16:38:21

目录

1 程序和进程

1.1 并发

1.3 多道程序设计

1.4 CPU 和 MMU

1.5 进程控制块 PCB 

1.6 进程状态

1.7 环境变量

1.8 setenv 函数

2. 进程控制

2.1 fork 函数

2.2 循环创建 n 个子进程

2.3getpid 函数

2.4 getppid 函数

2.5 getuid 函数

2.6 getgid 函数

2.7 进程共享

2.8 exec 函数族

2.9 execlp 函数

2.10 execl 函数

2.11 execvp 函数

2.12 exec 函数族一般规律

2.13 回收子进程

2.14 僵尸进程

2.15 wait 函数

2.16 waitpid 函数


1 程序和进程

        

程序,是指编译好的二进制文件,在磁盘上,不占用系统资源 (cpu 、内存、打开的文件、设备、锁....)
进程,是一个抽象的概念,与操作系统原理联系紧密。进程是活跃的程序,占用系统资源。在内存中执行。( 程序运行起来,产生一个进程 )
程序 → 剧本 ( )
进程 → 戏 ( 舞台、演员、灯光、道具 ...)
同一个剧本可以在多个舞台同时上演。同样,同一个程序也可以加载为不同的进程 ( 彼此之间互不影响)
如:同时开两个终端。各自都有一个 bash 但彼此 ID 不同。

1.1 并发

        

 并发,在操作系统中,一个时间段中有多个进程都处于已启动运行到运行完毕之间的状态。但,任一个时刻点上仍只有一个进程在运行。
例如,当下,我们使用计算机时可以边听音乐边聊天边上网。 若笼统的将他们均看做一个进程的话,为什么可以同时运行呢,因为并发。

1.2 单道程序设计 

        所有进程一个一个排对执行。若 A 阻塞,B 只能等待,即使 CPU 处于空闲状态。而在人机交互时阻塞的出现时必然的。所有这种模型在系统资源利用上及其不合理,在计算机发展历史上存在不久,大部分便被淘汰了。

1.3 多道程序设计

在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之下,相互穿插的运行。多道程序设计必须有硬件基础作为保证。时钟中断 即为多道程序设计模型的理论基础。 并发时,任意进程在执行期间都不希望放弃 cpu 。因此系统需要一种强制让进程让出 cpu 资源的手段。时钟中断有硬件基础作为保障,对进程而言不可抗拒。 操作系统中的中断处理函数,来负责调度程序执行。在多道程序设计模型中,多个进程轮流使用 CPU (分时复用 CPU 资源 ) 。而当下常见 CPU 为纳秒级,1 秒可以执行大约 10 亿条指令。由于人眼的反应速度是毫秒级,所以看似同时在运行。
1s = 1000ms, 1ms = 1000us, 1us = 1000ns 1000000000.实质上,并发是宏观并行,微观串行! -----推动了计算机蓬勃发展,将人类引入了多媒体时代。

1.4 CPU 和 MMU

 

1.5 进程控制块 PCB 

我们知道,每个进程在内核中都有一个进程控制块( PCB )来维护进程相关的信息, Linux内核的进程控制块是 task_struct 结构体。/usr/src/linux-headers-3.16.0-30/include/linux/sched.h 文件中可以查看 struct task_struct结构体定义。其内部成员有很多,我们重点掌握以下部分即可:* 进程 id 。系统中每个进程有唯一的 id ,在 C 语言中用 pid_t 类型表示,其实就是一个非负整数。
* 进程的状态,有就绪、运行、挂起、停止等状态。
* 进程切换时需要保存和恢复的一些 CPU 寄存器。
* 描述虚拟地址空间的信息。
* 描述控制终端的信息。
* 当前工作目录( Current Working Directory )。
* umask 掩码。
* 文件描述符表,包含很多指向 file 结构体的指针。
* 和信号相关的信息。
* 用户 id 和组 id
* 会话( Session )和进程组。
* 进程可以使用的资源上限( Resource Limit )。

1.6 进程状态

进程基本的状态有 5 种。分别为初始态,就绪态,运行态,挂起态与终止态。其中初始态为进程准备阶段,常与就绪态结合来看

1.7 环境变量

环境变量,是指在操作系统中用来指定操作系统运行环境的一些参数。通常具备以下特征:
① 字符串 ( 本质 ) ② 有统一的格式:名 = [: ] ③ 值用来描述进程环境信息。
存储形式:与命令行参数类似。 char *[] 数组,数组名 environ ,内部存储字符串, NULL 作为哨兵结尾。
使用形式:与命令行参数类似。加载位置:与命令行参数类似。位于用户区,高于 stack 的起始位置。引入环境变量表:须声明环境变量。extern char ** environ;
练习:打印当前进程的所有环境变量。
常见环境变量
按照惯例,环境变量字符串都是 name=value 这样的形式,大多数 name 由大写字母加下划线组成,一般把 name 的部分叫做环境变量, value 的部分则是环境变量的值。环境变量定义了进程的运行环境,一些比较重要的环境变量的含义如下:
PATH
可执行文件的搜索路径。 ls 命令也是一个程序,执行它不需要提供完整的路径名 /bin/ls ,然而通常我们执行当前目录下的程序 a.out 却需要提供完整的路径名 ./a.out ,这是因为 PATH环境变量的值里面包含了 ls 命令所在的目录 /bin ,却不包含 a.out 所在的目录。 PATH 环境变量的值可以包含多个目录,用: 号隔开。在 Shell 中用 echo 命令可以查看这个环境变量的值:
$ echo $PATH
SHELL
当前 Shell ,它的值通常是 /bin/bash
TERM
当前终端类型,在图形界面终端下它的值通常是 xterm ,终端类型决定了一些程序的输出显示方式,比如图形界面终端可以显示汉字,而字符终端一般不行。
LANG
语言和 locale ,决定了字符编码以及时间、货币等信息的显示格式。
HOME
当前用户主目录的路径,很多程序需要在主目录下保存配置文件,使得每个用户在运行该程序时都有自己的一套配置。
getenv 函数
获取环境变量值
char *getenv(const char *name); 成功:返回环境变量的值;失败: NULL (name 不存在)
练习:编程实现 getenv 函数。
getenv.c

1.8 setenv 函数

设置环境变量的值
int setenv(const char *name, const char *value, int overwrite); 成功:0 ;失败:-1
参数 overwrite 取值: 1 :覆盖原环境变量
0 :不覆盖。 ( 该参数常用于设置新环境变量,如: ABC = haha-day-night)
unsetenv 函数
删除环境变量 name 的定义  int unsetenv(const char *name); 成功: 0 ;失败: -1
注意事项: name 不存在仍返回 0( 成功 ) ,当 name 命名为 "ABC=" 时则会出错。

2. 进程控制

2.1 fork 函数

创建一个子进程。
pid_t fork(void);
失败返回 -1 ;成功返回:① 父进程返回子进程的 ID( 非负 ) ② 子进程返回 0 .pid_t 类型表示进程 ID ,但为了表示 -1 ,它是有符号整型。 (0 不是有效进程 ID ,init 最小,为 1) 注意返回值,不是 fork 函数能返回两个值,而是 fork 后, fork 函数变为两个,父子需【各自】返回一个。

2.2 循环创建 n 个子进程

一次 fork 函数调用可以创建一个子进程。那么创建 N 个子进程应该怎样实现呢?
简单想, for(i = 0; i < n; i++) { fork() } 即可。但这样创建的是 N 个子进程吗?
从上图我们可以很清晰的看到,当 n 3 时候,循环创建了 (2^n)-1 个子进程,而不是 N的子程。需要在循环的过程,保证子进程不再执行 fork ,因此当 (fork() == 0) 时,子进程应该立即 break; 才正确。
练习:通过命令行参数指定创建进程的个数,每个进程休眠 1S 打印自己是第几个被创建的进程。如:第 1 个子进程休眠 0 秒打印:“我是第 1 个子进程”;第 2 个进程休眠 1 秒打印:“我是第 2 个子进程”;第 3 个进程休眠 2 秒打印:“我是第 3 个子进程”。
fork1.c
通过该练习 掌握框架 :循环创建 n 个子进程,使用循环因子 i 对创建的子进程加以区分。

2.3getpid 函数

获取当前进程 ID
pid_t getpid(void);

2.4 getppid 函数

获取当前进程的父进程 ID
pid_t getppid(void);
区分一个函数是“系统函数”还是“库函数”依据:
是否访问内核数据结构
② 是否访问外部硬件资源
二者有任一 → 系统函数;二者均无 → 库函数

2.5 getuid 函数

获取当前进程实际用户 ID
uid_t getuid(void);
获取当前进程有效用户 ID
uid_t geteuid(void);

2.6 getgid 函数

获取当前进程使用用户组 ID
gid_t getgid(void);
获取当前进程有效用户组 ID
gid_t getegid(void);

2.7 进程共享

父子进程之间在 fork 后。有哪些相同,那些相异之处呢?刚 fork 之后:
父子相同处 : 全局变量、 .data .text 、栈、堆、环境变量、用户 ID 、宿主目录、进程工作目录、信号处理方式...
父子不同处 : 1. 进程 ID 2.fork 返回值 3. 父进程 ID 4. 进程运行时间 5. 闹钟 ( 定时器) 6. 未决信号集似乎,子进程复制了父进程 0-3G 用户空间内容,以及父进程的 PCB ,但 pid 不同。真的每 fork 一个子进程都要将父进程的 0-3G 地址空间完全拷贝一份,然后在映射至物理内存吗?
当然不是!父子进程间遵循 读时共享写时复制 的原则。这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销练习:编写程序测试,父子进程是否共享全局变。
fork_shared.c
重点注意!躲避父子进程共享全局变量的知识误区!
【重点】:父子进程共享: 1. 文件描述符 ( 打开文件的结构体 ) 2. mmap 建立的映射区 (进程间通信详解 )
特别的, fork 之后父进程先执行还是子进程先执行不确定。取决于内核所使用的调度算

2.8 exec 函数族

fork 创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子
进程往往要调用一种 exec 函数以执行另一个程序。当进程调用一种 exec 函数时,该进程的
用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec 并不创
建新进程,所以调用 exec 前后该进程的 id 并未改变。
将当前进程的 .text .data 替换为所要加载的程序的 .text .data ,然后让进程从新的 .text
第一条指令开始执行,但进程 ID 不变,换核不换壳。
其实有六种以 exec 开头的函数,统称 exec 函数:
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

2.9 execlp 函数

加载一个进程,借助 PATH 环境变量 int execlp(const char *file, const char *arg, ...);
成功:无返回;失败: -1
参数 1 :要加载的程序的名字。该函数需要配合 PATH 环境变量来使用,当 PATH 中所有目录搜索后没有参数 1 则出错返回。 该函数通常用来调用系统程序。如:ls date cp cat 等命令。

 

2.10 execl 函数

加载一个进程, 通过 路径 + 程序名 来加载。int execl(const char *path, const char *arg, ...);
成功:无返回;失败: -1
对比 execlp ,如加载 "ls" 命令带有 -l -F 参数
execlp("ls", "ls", "-l", "-F", NULL); 使用程序名在 PATH 中搜索。
execl("/bin/ls", "ls", "-l", "-F", NULL); 使用参数 1 给出的绝对路径搜索。

2.11 execvp 函数

加载一个进程,使用自定义环境变量 env
int execvp(const char *file, const char *argv[]);
变参形式: ① ... argv[] (main 函数也是变参函数,形式上等同于 int main(int argc, char *argv0,...))
变参终止条件:① NULL 结尾 ② 固参指定
execvp execlp 参数形式不同,原理一致。
练习:将当前系统中的进程信息,打印到文件中。
exec_ps.c

2.12 exec 函数族一般规律

exec 函数一旦调用成功即执行新的程序,不返回。 只有失败才返回,错误值 -1 。所以通常我们直接在 exec 函数调用后直接调用 perror() exit() ,无需 if 判断。
l (list)
命令行参数列表
p (path)
搜素 file 时使用 path 变量
v (vector)
使用命令行参数数组
e (environment)
使用环境变量数组 , 不使用进程原有的环境变量,设置新加载程序运行的环境变量。事实上,只有 execve 是真正的系统调用,其它五个函数最终都调用 execve ,所以 execve在 man 手册第 2 节,其它函数在 man 手册第 3 节。这些函数之间的关系如下图所示。

 

2.13 回收子进程

孤儿进程
孤儿进程 : 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为 init 进程,称为 init 进程领养孤儿进程。
orphan.c

2.14 僵尸进程

僵尸进程 : 进程终止,父进程尚未回收,子进程残留资源( PCB )存放于内核中,变成僵尸(Zombie )进程。
特别注意,僵尸进程是不能使用 kill 命令清除掉的。因为 kill 命令只是用来终止进程的,
而僵尸进程已经终止。思考!用什么办法可清除掉僵尸进程呢?

2.15 wait 函数

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的 PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用 wait waitpid 获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在 Shell 中用特殊变量$? 查看,因为 Shell 是它的父进程,当它终止时 Shell 调用 wait waitpid 得到它的退出
状态同时彻底清除掉这个进程。
父进程调用 wait 函数可以回收子进程终止信息。该函数有三个功能:
① 阻塞等待子进程退出
② 回收子进程残留资源
③ 获取子进程结束状态 ( 退出原因 )
pid_t wait(int *status); 成功:清理掉的子进程 ID ;失败: -1 ( 没有子进程 )
当进程终止时,操作系统的隐式回收机制会: 1. 关闭所有文件描述符 2. 释放用户空间
分配的内存。内核的 PCB 仍存在。其中保存该进程的退出状态。 ( 正常终止→退出值;异常
终止→终止信号 )
可使用 wait 函数传出参数 status 来保存进程的退出状态。借助宏函数来进一步判断进
程终止的具体原因。宏函数可分为如下三组:
1. WIFEXITED(status) 为非 0 → 进程正常结束
WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit 的参数 )
2. WIFSIGNALED(status) 为非 0 → 进程异常终止
WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
*3. WIFSTOPPED(status) 为非 0 → 进程处于暂停状态
WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

2.16 waitpid 函数

作用同 wait ,但可指定 pid 进程清理,可以不阻塞。
pid_t waitpid(pid_t pid, int *status, in options); 成功:返回清理掉的子进程 ID ;失
败: -1( 无子进程 )
特殊参数和返回情况:
参数 pid
> 0 回收指定 ID 的子进程
-1 回收任意子进程(相当于 wait
0 回收和当前调用 waitpid 一个组的所有子进程
< -1 回收指定进程组内的任意子进程
返回 0 :参 3 WNOHANG ,且子进程正在运行。
注意: 一次 wait waitpid 调用只能清理一个子进程 ,清理多个子进程应使用循环。
waitpid.c
作业:父进程 fork 3 个子进程,三个子进程一个调用 ps 命令, 一个调用自定义程序
1( 正常 ) ,一个调用自定义程序 2( 会出段错误 ) 。父进程使用 waitpid 对其子进程进行回收。

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

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

相关文章

分表后mybatis-plus删除操作失效等问题处理

因为重构dao层&#xff0c;问题太多了&#xff0c;于是想着另起一个章节。 3 mybatis封装对象的问题 直接使用mybatis自身的对象&#xff0c;会有问题&#xff0c;他的列表对象会设置一个id&#xff0c;导致后续的工程会出问题 <select id"getStatementDefaultEquation…

基于SpringBoot+vue的银行OA系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

使用Electron包装ruoyi-ui/ruoyi-vue实践总结

背景&#xff1a;最近公司新起的项目&#xff0c;由于工期、资源等原因&#xff0c;采用ruoyi框架快速实现开发&#xff0c;由于需要构建客户端&#xff0c;所以借助electron来实现。 electron 是使用javascript html css来构建跨平台的桌面应用程序。 官网地址&#xff1a;简介…

ModelWhale 基于 AI for Science 助力航遥中心基础设施建设

2023 年 3 月&#xff0c;科技部会同自然科学基金委启动“人工智能驱动的科学研究&#xff08;AI for Science&#xff09;”专项部署工作。科技部新一代人工智能发展研究中心主任赵志耘认为&#xff0c;AI for Science 将突破传统科学研究能力瓶颈&#xff0c;成为全球科研新范…

【2023-07-18】jsvmp逆向profileData

加密逻辑 注册gid需要用到profileData&#xff0c;profileData的加密逻辑是将一个46对键值对的json经过bas64编码后进行des加密&#xff0c;这46对键值对都是环境和设备相关的一些参数&#xff0c;要完整还原profileData还需要将这些参数的生成逻辑进行还原。 变量名说明key…

Python开发项目基于卷积神经网络的车牌识别仿真软件设计与实现

博主介绍&#xff1a;擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例…

ICASSP2023论文解读|如何检测会议记录中的待办事项?

得益于语音识别技术的发展&#xff0c;人工智能可以帮助人们记录会议&#xff0c;自动检测与会后行动项关联的会议内容&#xff0c;并进行总结。 行动项识别对于管理会后待办任务至关重要。 针对对于行动项识别任务&#xff0c;相关数据集稀缺且规模小。因此&#xff0c;达摩…

酷炫无敌!10分钟学会制作3D园区大屏,职场新人也能秒变大神!

近年来随着大数据的飞速发展&#xff0c;各大行业都进行了一定的产业革新&#xff0c;智慧园区也逐渐进入企业视野并成为主流&#xff0c;不论大小企业&#xff0c;领导老板都要求员工制作出智慧园区的酷炫大屏&#xff0c;不顾及其中的技术难度&#xff0c;只想看到最终成果&a…

2023隐私计算大会亮点前瞻:《隐私计算应用 面向政务场景的应用要求》标准解读预告

7月26日&#xff0c;2023隐私计算大会将于青岛正式扬帆&#xff0c;本次大会将齐聚业内专家大咖共论行业最新进展&#xff0c;洞察未来发展趋势&#xff0c;共同推进隐私计算行业蓬勃发展。 本次大会将正式公开发布《隐私计算应用研究报告&#xff08;2023&#xff09;》、“隐…

J2EEJSP自定义标签库01out标签if标签

目录 一.什么是标签 二.JSP自定义标签库 2.1 JSP标签库是什么 2.2 处理流程 2.3 如何自定义标签 2.4 标签类型 三.开发示例 3.1 out标签 1.创建助手类 2.编写tld&#xff08;标签库的描述&#xff09;文件&#xff0c;&#xff08;必须放在WEB-INF目录或其目录下&a…

LCD—STM32液晶显示(3.NOR FLASH时序结构体)

目录 LCD结构体配置 NOR FLASH时序结构体 FSMC的NOR FLASH初始化结构体 LCD结构体配置 NOR FLASH时序结构体 与控制SRAM时一样&#xff0c;控制FSMC使用NOR FLASH存储器时主要是配置时序寄存器以及控制寄存器&#xff0c;利用ST标准库的时序结构体以及初始化结构体可以很方便地…

第六章:string类

系列文章目录 文章目录 系列文章目录前言为什么学习string类C语言中的字符串ASCIIUnicode**UTF-8**UTF-16UTF-32 GBK 标准库中的string类string类总结 string类的常用接口说明1. string类对象的常见构造2. string类对象的容量操作3. string类对象的访问及遍历操作4. string类对…

亚马逊云科技十年出海经验,为中国企业提供跨越式发展

对于一座优秀的港口而言&#xff0c;不仅要求其所在的地理位置得天独厚以及拥有庞大的货运船舶吞吐量&#xff0c;能否为地区带来良好的发展生态&#xff0c;提供和创造新的就业机会也是重要的考量。对于很多中小企业而言&#xff0c;他们不具备大型企业的体量&#xff0c;在海…

vscode(Better Comments插件)在vue文件中不显示相对应的颜色

解决办法&#xff1a; 1、在.vscode文件下找到 aaron-bond.better-comments-3.0.2 &#xff08;我的路径&#xff1a;C:\Users\cown\.vscode\extensions\aaron-bond.better-comments-3.0.2&#xff09;&#xff0c;后面版本不唯一&#xff0c;根据自身情况辨别 2、进入文件路…

HTTPS概念wireshark分析

目录 密钥&加密算法CA & 数字证书https流程连接流程springboot项目启动httpswireshark分析https流程 密钥&加密算法 密钥&#xff1a;通常是一个字符串或数字&#xff0c;进行加密/解密算法时使用。公钥和私钥都是密钥&#xff0c;只不过一般公钥是对外开放的&…

如何在 Endless OS 上安装 ONLYOFFICE 桌面编辑器

ONLYOFFICE 桌面编辑器是一款基于依据 AGPL v.3 许可进行分发的开源办公套件。使用这款应用&#xff0c;您无需保持网络连接状态即可处理存储在计算机上的文档。本指南会向您介绍&#xff0c;如何在 Endless OS 上安装 ONLYOFFICE 桌面编辑器。 ONLYOFFICE 桌面版是什么 ONLYO…

Sencha ExtJS v1.0.4 扩展不能“转到定义“的解决办法

VSCode 的 Sencha 扩展自动更新到了 1.0.4&#xff0c;去除激活提示的方法和以前一样。 但是有扩展bug&#xff0c;就是无法转到定义&#xff0c;打开 VSCode 的开发者工具&#xff0c;由一堆报错。 解决办法&#xff1a;要修改下文件 C:\Users\xxx\.vscode\extensions\sench…

谷歌黑客语法与漏洞寻找

谷歌黑客语法与漏洞寻找 一、常见的搜索引擎二、Google部分语法三、通配符四、FOFA五、Shodan六、例子&#xff1a;常见的后台地址 一、常见的搜索引擎 浏览器 浏览器是用来检索、展示以及传递Web信息资源的应用程序。 搜索引擎 所谓搜索引擎&#xff0c;就是根据用户需求与一…

惊艳!AI创意字体真绝了;6道AIGC高频面试题与解答;为啥语音助手还没集成LLM;深度学习算法红宝书 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 惊艳&#xff01;手把手教你使用 Stable Diffusion 制作AI创意字体 最近AI创意字体刷爆了 ShowMeAI 社群&#xff01;伙伴们纷纷惊叹这…

OJ练习第139题——模拟行走机器人

模拟行走机器人 力扣链接&#xff1a;874. 模拟行走机器人 题目描述 机器人在一个无限大小的 XY 网格平面上行走&#xff0c;从点 (0, 0) 处开始出发&#xff0c;面向北方。该机器人可以接收以下三种类型的命令 commands &#xff1a; -2 &#xff1a;向左转 90 度 -1 &…