Linux进程(1)

news/2024/4/19 21:36:01/文章来源:https://blog.csdn.net/wzh18907434168/article/details/130346058

在这里插入图片描述

目录

  • ⛹🏽进程简介
  • ⛹🏽查看进程
  • ⛹🏽系统调用
    • 🚴🏽获取进程标示符
    • 🚴🏽创建进程
  • ⛹🏽进程状态
    • 🚴🏽孤儿进程:
    • 🚴🏽进程优先级
  • ⛹🏽环境变量
    • 🚴🏽查看环境变量
    • 🚴🏽添加环境变量
    • 🚴🏽组织环境变量
    • 🚴🏽获取环境变量
    • 🚴🏽环境变量特性
  • ⛹🏽进程地址空间
  • ⛹🏽总结

⛹🏽进程简介

进程概念:程序的一个执行实例或者一个担当分配系统资源(CPU时间,内存)的实体。

进程描述:进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合,简称为PCB(process control block),在Linux操作系统下的PCB是: task_structtask_struct是PCB的一种,task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

task_ struct内容分类:

标示符: 描述本进程的唯一标示符,用来区别其他进程。

状态: 任务状态,退出代码,退出信号等。

优先级: 相对于其他进程的优先级。

程序计数器: 程序中即将被执行的下一条指令的地址。

内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

上下文数据: 进程执行时处理器的寄存器中的数据。

I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

其他信息

组织进程:在内核源代码里,所有运行在系统里的进程都以task_struct链表的形式存在内核里。

⛹🏽查看进程

进程的信息可以通过 /proc系统文件夹查看,如下图:

在这里插入图片描述

大多数进程信息同样可以使用ps这些用户级工具来获取,例如我们也可以写一个程序运行然后使用ps命令来查看该进程,如下代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{while(1){printf("正在运行\n");sleep(1);}return 0;
}

编译运行,然后使用命令ps axj | grep test1 | grep -v grep查看进程。

在这里插入图片描述

⛹🏽系统调用

🚴🏽获取进程标示符

进程的标识符id称为PID,进程的父进程标识符id成为PPID。

系统给我们分别提供了获取PID和PPID的函数——getpid()getppid()


通过查看手册可以看到,使用这两个函数我们需要包含sys/types.hunistd.h这两个头文件,并且getpid返回当前的进程id,getppid返回当前进程的父进程的id。

我们可以在自己的代码中使用系统提供给我们的两种系统调用函数来获取进程的PID和PPID,如下代码:

  1 #include <stdio.h>2 #include <unistd.h>3 #include <sys/types.h>4 int main()5 {6   while(1){7     printf("正在运行,我的PID为:%d,我的PPID为:%d\n", getpid(), getppid());                         8     sleep(1);9   }10   return 0;11 }

在这里插入图片描述

我们还可以用ps命令来查看进程,如下图:

在这里插入图片描述

🚴🏽创建进程

在系统接口中,还给我们提供了创建子进程的函数——fork()

查看手册可以看到,使用fork函数我们需要包含unistd.h这个头文件,当子进程成功创建时,将子进程的id返回给他的父亲,返回0给他自己,如果创建失败则返回-1给他的父亲。

如下代码:

 22 int main()23 {24     int ret = fork();25     if(ret > 0)26     {27       while(1)28       {29         printf("我是父进程,pid: %d, ppid: %d\n", getpid(), getppid());30         sleep(1);                 31       }32     }33     else if (ret == 0)34     { 35       while(1)36       {37          printf("我是子进程,pid:%d, ppid: %d\n", getpid(), getppid());38          sleep(1);39       }40     }41     else{42       printf("error\n");43     }44     return 0;45 }

在这里插入图片描述

由结果可以看到,子进程的PPID和父进程的PID一样。可能你会觉得这个结果很奇怪,为什么只有一个ret,但是却把if和else都执行了,这是因为我们使用fork创建子进程后,代码就是父子进程共享,数据各自开辟空间,私有一份,在返回时发生写时拷贝。

如下代码:

 21 int main()22 {23   printf("aaaaaaaaaaaaaaaaaa\n");24   fork();25   printf("bbbbbbbbbbbbbbbbb\n");26   return 0;   27 }

在这里插入图片描述

⛹🏽进程状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有如下几种状态:

  • R运行状态(running):并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

  • S睡眠状态(sleeping):意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。

  • D磁盘休眠状态(Disk sleep):有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。

  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

  • X死亡状态(dead):这个状态只是一个返回状态,在任务列表里看看不到这个状态。

  • Z(zombie)-僵尸进程(僵死状态(Zombies)):是一个比较特殊的状态。
    当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程,僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。

我们可以用代码来观察僵尸进程,如下代码:

  1 #include <stdio.h>2 #include <unistd.h>3 #include <sys/types.h>4 5 int main()6 {7   int ret = fork();8   if(ret == 0)9   {10     for(int i = 5; i > 0; i--)11     {12       printf("我是子进程%d,我的父进程%d,结束还有%d秒\n", getpid(), getppid(), i);13       sleep(1);14     }15   }16   else if (ret > 0)17   {18     for(int i = 10; i > 0; i--)19     {20       printf("我是父进程%d,我的子进程%d,结束还有%d秒\n", getpid(), getppid(), i);                                                        21       sleep(1);22     }23   }24   else{25     printf("eroor\n");26   }27   return 0;28 }

编译运行该程序后我们可以在xshell上运行ps命令来观察进程状态.

命令:while :; do ps axj | head -1 && ps axj | grep test1 | grep -v grep;sleep 1;echo "===============================";done

在这里插入图片描述

如上图我们可以看出,当子进程退出的时候,他的状态由s(sleep)转换为z(zombie)。

僵尸进程危害:

  • 进程的退出状态必须被维持下去,因为他要告诉它的父进程。可父进程如果一直不读取,那子进程就一直处于僵尸状态。

  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,也就是说,如果僵尸状态一直不退出,PCB一直都要维护!

  • 那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间! 就会造成内存泄漏。

🚴🏽孤儿进程:

当我们的父进程如果提前退出,那么子进程后退出,在子进程进入僵尸进程之后,那该如何处理呢?

我们称这种父进程先退出,子进程后退出,把这种子进程就称之为“孤儿进程”

这种孤儿进程通常是被1号init进程回收领养

我们也可以用一段代码来观察,如下代码:

  1 #include <stdio.h>2 #include <unistd.h>3 #include <sys/types.h>4 5 int main()                                                                                                 6 {7   int ret = fork();8   if(ret == 0)9   {10     for(int i = 10; i > 0; i--)11     {12       printf("我是子进程%d,我的父进程%d,结束还有%d秒\n", getpid(), getppid(), i);13       sleep(1);14     }15   }16   else if (ret > 0)17   {18     for(int i = 5; i > 0; i--)19     {20       printf("我是父进程%d,我的子进程%d,结束还有%d秒\n", getpid(), getppid(), i);21       sleep(1);22     }23   }24   else{25     printf("eroor\n");26   }27   return 0;28 }

用ps命令来查看进程状态

while :; do ps axj | head -1 && ps axj | grep test1 | grep -v grep;sleep 1;echo "===============================";done

在这里插入图片描述
如上图我们可以看到,当父进程退出后,我们的子进程就会更换自己的父进程。

🚴🏽进程优先级

cpu资源分配的先后顺序,就是指进程的优先权(priority),优先权高的进程有优先执行权利。

配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上。

这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

查看系统进程:

在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容

在这里插入图片描述

我们可以看到有如下几个重要信息

UID : 代表执行者的身份

PID : 代表这个进程的代号

PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号

PRI :代表这个进程可被执行的优先级,其值越小越早被执行

NI :代表这个进程的nice值

其中,PRI就是进程的优先级,通俗点说就是程序被CPU执行的先后顺序,PRI值越小进程的优先级别越高。

NI就是nice值,其表示进程可被执行的优先级的修正数值

PRI值越小越快被执行,在加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice

这样,当nice值为负值的时候,那么该程序的优先级值将变小,即其优先级会变高,则其越快被执行

所以,调整进程优先级,在Linux下,就是调整进程nice值,nice值取值范围是-20至19,一共40个级别。

需要注意的一点是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。

可以理解nice值是进程优先级的修正修正数据

查看进程优先级的命令:top

用top命令更改已存在进程的nice,进入top后按“r”然后输入进程PID再输入nice值。

在这里插入图片描述
如上图,当我们将test1的进程NI值修改为10之后,我们可以看到test1的PRI值变为30。

⛹🏽环境变量

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数

如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。

环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

常见环境变量:

PATH : 指定命令的搜索路径

HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)

SHELL : 当前Shell,它的值通常是/bin/bash。

🚴🏽查看环境变量

命令:echo $NAME

NAME为你的环境变量名称,例如echo $USER
在这里插入图片描述

🚴🏽添加环境变量

我们都知道,当我们编译好一份代码文件时,如果我们想运行这个代码文件,我们需要在前面加上./,这个./是在当前路径下查找你所要执行的文件。

但是为什么有些指令可以直接执行,不需要带路径,而我们的二进制程序需要带路径才能执行?

因为这些指令在环境变量中存有他的路径,当我们执行这些指令时,会在环境变量所保存的路径中去查找该指令。

我们可以用命令echo $PATH来查看我们的环境变量,如下图:
在这里插入图片描述

我们可以将我们的程序所在路径加入环境变量PATH当中,然后查看与之前有什么不同。将程序路径加入到环境变量中我们可以使用命令:

export PATH=$PATH:[我们文件所在的路径]

例如当前有这么一个.c文件,如下代码:

  1 #include <stdio.h>2 3 int main()4 {5   for(int i = 0; i < 5; i++)6   {7     printf("hello world\n");8   }                                                                                                                                                                 9   return 0;10 }

我们当前所处路径为/home/wzh/linux/Test23_4_21

在这里插入图片描述

我们将该路径加入到PATH环境变量中去,执行命令export PATH=$PATH:/home/wzh/linux/Test23_4_21

在这里插入图片描述

之后我们可以发现,我们不需要带./也是可以运行我们的程序,如下图:
在这里插入图片描述

🚴🏽组织环境变量

每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以\0结尾的环境字符串

在这里插入图片描述

🚴🏽获取环境变量

我们可以通过代码来获取环境变量,例如我们可以通过main函数的第三个参数来获取,如下代码:

    1 #include <stdio.h>2 3 int main(int argc, char* argv[], char* env[])4 {5   for(int i = 0; env[i]; i++)6   {7     printf("%s\n", env[i]);                                                                                                                                       8   }9 10   return 0;11 }

在这里插入图片描述

除了参数获取,我们还可以通过第三方变量environ获取,如下代码

   16 #include <stdio.h>17 int main(int argc, char* argv[])18 {19   extern char** environ;20   for(int i = 0; environ[i]; i++)21     printf("%s\n", environ[i]);                                            22   return 0;23 }

在这里插入图片描述

libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明。

除了使用代码参数和变量查看我们的环境变量,我们还可以通过系统调用接口来获取环境变量,系统给我们提供了一个getenv()函数,如下图:

在这里插入图片描述

我们可以看出,这个函数是包含在stdlib.h这个头文件中,因此我们在使用这个函数的时候还得包这个头文件,如下代码:

 26 #include <stdio.h>27 #include <stdlib.h>28 int main()29 {30   printf("%s\n", getenv("PATH"));                                                           31   return 0;32 }

在这里插入图片描述

🚴🏽环境变量特性

环境变量通常是具有全局属性,可以被子进程继承下去。如下代码:

 26 #include <stdio.h>27 #include <stdlib.h>28 int main()29 {31   char* ret = getenv("testenv");                                                        32   if(ret)33     printf("%s\n", ret);34   return 0;35 }

当我们编译运行的时候发现什么都没有打印出来,说明不存在这个环境变量,但是当我们用命令export testenv="hello world"的时候再次运行会发现这个时候就打印出来hello world。

在这里插入图片描述

⛹🏽进程地址空间

在我们基于32位平台下,我们知道有如下图所示的地址空间分布

在这里插入图片描述

但是有这么一份奇怪的代码:

 40 #include <sys/types.h>41 #include <stdio.h>42 #include <unistd.h>43 #include <stdlib.h>44 int main()45 {46   int val = 0;47   pid_t ret = fork();48   if(ret == 0)49   {50     for(int i = 0; i < 5; i++)51     {52       val++;53       printf("child : %d childpid : %d address : %p\n", val, getpid(), &val);54       sleep(1);55     }56   }57   else if (ret > 0)58   {59     for(int i = 0; i < 5; i++)60     {61       val += 10;                                                                                                                                                    62       printf("father : %d fatherpid : %d address : %p\n", val, getpid(), &val);63       sleep(1);64     }65   }66   else{67     perror("fork");68   }69   return 0;70 }

在我们运行后发现结果如图所示
在这里插入图片描述

我们都知道进程与进程之间是独立的,所以子进程和父进程的val值修改互相并不影响,但是我们观察运行结果我们可以看到,子进程和父进程的val值地址都是一样的,如果它们地址是一样的那么父子进程的val就应该是同一个val,可是如果是同一个val那么他们修改又怎么会互不影响呢。所以我们大概就可以得出下面的结论:

变量内容不一样,所以父子进程输出的变量绝对不是同一个变量

该地址绝对不是物理地址

这样的地址在Linux下,我们把这种地址叫做虚拟地址,我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址用户一概看不到,由操作系统统一管理,操作系统必须负责将虚拟地址转化成物理地址 。因此我们所说的程序地址空间严格的说是不准确的,应该叫进程地址空间。

当我们的程序编译运行变为一个进程的时候,其中PCB(task_struct)中的某个指针就会指向一块虚拟地址空间,而虚拟地址空间又会由页表进行映射到物理内存上,如下图:
在这里插入图片描述

⛹🏽总结

本篇博客介绍了Linux的一些进程状态,环境变量,以及进程地址空间和一些系统调用接口,用代码来看现象。如果觉得本篇博客对你有帮助的话就三连吧!另外由于作者水平有限,如果文中有什么纰漏或者错误还请指正。谢谢大家。
在这里插入图片描述

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

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

相关文章

新一代边缘计算盒子,英码科技边缘计算盒子SY-E160

SY-E160 是英码科技推出的新一代智能工作站&#xff0c;内部集成了 4 核强悍处理器 A551.5 GHz&#xff0c;其内置的算力核拥有 16Tops 超强算力。SY-E160 工作站采用低功耗技术设计&#xff0c;支持 宽温度环境工作&#xff0c;可以灵活部署于各种 AI 场景中。 SY-E160 深元 A…

每日一个小技巧:1招教你提取伴奏怎么做

伴奏是指在演唱或演奏时&#xff0c;用来衬托或补充主唱或乐器的音乐声音。而伴奏提取是一种技术&#xff0c;它可以帮助我们从歌曲中将人声和乐器分离出来。当我们听到一些喜欢的歌曲时&#xff0c;往往会被它的旋律深深吸引&#xff0c;想要将其作为自己的演唱曲目&#xff0…

Domino的线程ID和操作系统的进程ID对应关系

大家好&#xff0c;才是真的好。 很多时候&#xff0c;在Domino中运行的任务出现一些错误提示&#xff0c;如果能够准确定位到和提示信息相关任务时&#xff0c;对我们排错有着巨大的帮助&#xff0c;也能节省很多时间。 例如&#xff0c;我们可能在Domino实时控制台上看到以…

InstructGPT原理讲解及ChatGPT类开源项目

InstructGPT原理讲解及ChatGPT类开源项目 Generative Pre-Trained Transformer&#xff08;GPT&#xff09; 是OpenAI的提出的生成式预训练语言模型&#xff0c;目前已经发布了GPT-1、GPT-2、GPT-3和GPT-4&#xff0c;未来也将发布GPT-5。 最近非常火的ChatGPT是基于Instruct…

想提高应用程序的用户满意度——APK体积包优化少不了

作者&#xff1a;子不语Any 前言 减少应用程序安装包的大小&#xff0c;不仅仅减少用户的网络数据流量&#xff0c;还减少了下载等待的时间。毋庸置疑&#xff0c;尽量减少程序安装包的大小是十分有必要的。 通常来说&#xff0c;减少程序安装包的大小有两条规律&#xff1a;…

Java_常用API

Java_常用API ​ API即Application Programming Interface&#xff0c;即应用程序接口。一般来说API就是软件组件之间信息交互的桥梁&#xff0c;通过它无需访问源码。API除了有应用程序接口的含义外&#xff0c;还特质API的说明文档&#xff0c;也称为帮助文档。 1.字符串的…

Linux服务使用宝塔面板搭建网站,并发布公网访问 - 内网穿透(1)

文章目录 前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面 转载自远程内网穿透的文章&#xff1a;Linux使用宝塔面板搭建网站&#xff0c;并内网穿透实现公网访问 前言 宝塔面板作为简单好用的服务器运维管理面板&#…

读书笔记---植物基因组学(樊龙江主编)

读书笔记---植物基因组学&#xff08;樊龙江主编&#xff09; 最近看了这本书&#xff0c;作者是樊龙江教授&#xff08;浙江大学&#xff09;&#xff0c;里面主要分为两个大部分&#xff1a; 总论&#xff1a;15章 各论&#xff1a;10章详细目录可以参看下面链接&#xff1a…

MySQL中使用批量插入,但需要校验每条数据是否重复且是否已经存在数据库中

问题 批量插入一组数据&#xff0c;数据库中name和age字段组合起来不能有重复&#xff0c;如果出现重复&#xff0c;则直接跳过不插入。 name和age字段组合起来不重复&#xff0c;要求如下&#xff1a; 解决方法 建立name和age的复合索引&#xff0c;并设置为唯一索引 场景…

数列分段 马蹄集

数列分段 难度&#xff1a;黄金 0时间限制&#xff1a;1秒 巴占用内存&#xff1a;128M 对于给定的一个长度为N的正整数数列A,,现要将其分成连续的若干段&#xff0c; 并且每段和不超过M(可以等于M),问最少能将其分成多少段使得满足 要求。 格式 输入格式&#xff1a;第一行包含…

基于Java开发的分布式在线教育系统,支持考试、直播、问答

一、开源项目简介 知道学习平台是一个基于 Java 开发的分布式在线教育系统项目采用前后端分离的企业级微服务架构引入组件化的思想实现高内聚低耦合&#xff0c;项目代码简洁注释丰富上手容易注重代码规范&#xff0c;严格控制包依赖可以帮助个人、企业或机构快速搭建一个在线…

刷爆朋友圈!前百度总裁陆奇最新AI重磅演讲:我的大模型世界观

文 / 高扬 陆奇的演讲刷屏了&#xff0c;我认真看了他的演讲稿&#xff0c;收获颇丰。 陆奇提到&#xff0c;人类社会的发展&#xff0c;大致可分为&#xff1a;农业化、工业化和数字化三个阶段。 在三个递进的阶段中&#xff0c;人类一直在探索如何减少烦琐且消耗能量的体力和…

案例1:Java超市管理系统设计与实现开题报告

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Java文件IO操作基础

目录 前言 java.io.File 1. 构造方法 2. 方法 get类方法 文件的创建和删除 目录的创建与删除 输入输出流 InputStream FileInputStream 概述 代码实例1 代码实例2 字符集问题? Scanner 读取 OutputStream Java输入输出流的使用案例 创作不易, 多多支持&#x1f636;‍&…

漫画 | Linux之父:财务自由以后,我失眠了!

前言&#xff1a;今年是Linux诞生的30周年&#xff01; 1991年的8月&#xff0c; Linus在新闻组中公布了他正在开发的一个免费的操作系统&#xff0c;这也是以后风靡世界的Linux操作系统的雏形。 今天翻到这篇漫画&#xff0c;看到Linux的诞生过程&#xff0c;很是感慨&#x…

Unlimited “使用GPT-4 ”!它来了!

备注本文末尾附录注册登录过程 平台介绍: 聊天机器Chat.fore人front.ai是一为款基于人主工智能的题聊天的机器人主平台,旨在帮菜助企,业提可以高客户服务质是量和一效率。该平款台利用自然语精言处理技术和机器致学习的算法,能够自牛动排回答客,户的问题,提供个性化的服…

《string的模拟实现》

本文主要介绍库里面string类的模拟实现 文章目录 前言一、string的构造函数①无参的构造函数②带参的构造函数③修改构造函数 二、析构函数三、拷贝构造四、赋值重载五、返回size 、capacity和empty六、[]的运算符重载七、迭代器① 正向迭代器。② 正向const迭代器 八、string比…

接口自动化测试数据处理:技术人员必备的一项技能

目录 前言&#xff1a; 1.定义测试数据结构 2.从文件中加载测试数据 3.使用faker库生成随机测试数据 4.在测试用例中使用测试数据 总结&#xff1a; 前言&#xff1a; 在进行接口自动化测试时&#xff0c;测试数据的准备和处理是至关重要的一环。测试数据的准确性和完整性…

SAP 已根据规则拒绝服务器触发的操作 报错处理

SAP GUI在读取和写入文件的时候&#xff0c;询问是否给与权限&#xff0c;误操作点了否&#xff0c;导致报错如下 解决步骤如下&#xff1a; 点击选项 在下面的界面中依次点击安全配置---》已定制--》打开安全配置 在下面的界面找到你拒绝的条目&#xff0c;双击 将下图下拉框…

python海龟库教学

海龟库&#xff1a; 海龟绘图 “小海龟”turtle是Python语言中一个很流行的绘制图像的函数库&#xff0c;想象一个小乌龟&#xff0c;在一个横轴为x、纵轴为y的坐标系原点&#xff0c;(0,0)位置开始&#xff0c;它根据一组函数指令的控制&#xff0c;在这个平面坐标系中移动&…