【Linux】:进程控制(创建、终止、等待、替换)

news2024/9/22 21:18:08

目录

1.进程创建

2.进程终止(退出)

2.1 什么是进程终止

2.2 进程退出的场景(原因)

2.3 进程退出码

2.4 错误码errno

2.5 进程常见的退出方法

正常终止

从main函数返回

调用库函数exit

系统接口_exit

3.进程等待

3.1  进程等待的概念

3.2 僵尸进程为什么要被回收

3.3 wait和waitpid

wait

waitpid 

参数:status

宏WIFEXITED、WEXITSTATUS

3.4 等待失败

3.4 进程等待的原理 

3.5 非阻塞轮询

4.进程的程序替换

4.1 程序替换的概念

4.2 程序替换的原理

4.3 程序替换实例

 4.4 程序替换接口

​编辑4.4.1 execl

4.4.2 execlp

4.4.3 execv

4.4.4 execvp

4.4.5 execle

1.用make生成多个文件:​编辑

 2.在c程序中调用cpp程序

 3.修改环境变量

4.execle

4.4.6 execvpe 

4.4.7 总结


1.进程创建

在Linux系统中,我们一般用系统调用接口fork创建进程。

当前进程调用fork()创建一个子进程,当前进程则是该子进程的父进程。一个父进程可以有多个子进程,但一个子进程在同一时间段只能有一个父进程(父进程可以改变)。

更详细的信息请查看:浅析fork()和底层实现 - tp_16b - 博客园 (cnblogs.com)

注意:当系统中有太多的进程或实际用户的进程数超过了限制 ,fork调用可能会失败,这时需要释放进程资源。

2.进程终止(退出)

2.1 什么是进程终止

结束一个进程的生命可以分为两个步骤:进程退出和进程销毁。进程退出主要是释放进程的资源,使进程成为僵死(TASK_ZOMBIE)状态;进程销毁主要是通过父进程,释放僵死进程的各种信息。

当前进程被设为TASK_ZOMBIE僵死状态后会向其父进程发生SIGCHLD信号,父进程收到SIGCHLD信号后,才会销毁僵死状态的子进程,。

父进程通过调用wait()waitpid()函数等待SIGCHLD处理僵死进程

 进程退出和销毁过程分析 - 66Ring's Blog

终止进程的结果如下:

  • 进程中的任何剩余线程都标记为终止。
  • 将释放进程分配的任何资源。
  • 所有内核对象都已关闭。
  • 进程代码将从内存中删除。
  • 设置进程退出代码。
  • 进程对象已发出信号。

终止进程 - Win32 apps | Microsoft Learn

2.2 进程退出的场景(原因)

根据程序是否运行完毕,我们可以把进程划分为运行完毕正常退出运行异常非正常退出

对于正常退出的进程,我们又可以根据程序运行的结果,划分为结果正确和结果不正确。

那么我们是如何判断退出的结果正不正确呢?以及不正确有哪些原因呢?

2.3 进程退出码

任何进程退出时,都会留下退出码,操作系统根据退出码可以知道进程是否正常运行。退出码是0到N的整数(不同的操作系统N可能不同),通常0表示正常退出,其他数字表示不同的错误。

在Linux系统中,我们可以用“$?”获取上一个进程的退出码。我们编写一个程序,main函数的返回值设置为3,我们执行该源文件生成的可执行文件,可以用“echo”将“$?”的内容打印到终端。退出码是一个整数,不同的整数对应着不同的退出信息,我们可以用strerror打印退出码对于的退出信息。

CentOS7系统中一共有134个退出码。 

我们用ls列出一个当前目录下不存在的文件yls610,发现终端显示报错,报错信息刚好是退出码2对应的信息,我们用“echo $?”输出上一个进程返回的退出码也是2,

 再“echo $?”输出,结果是0,这是为什么呢?因为上一个“echo $?”是success退出,虽然echo是内建命令,不创建进程,但它还是会调用接口。

2.4 错误码errno

C语言标准库errno.h中有个宏errno,该宏定义为一个int类型的左值, 存储最近一次函数调用产生的错误码,也就是最近一次返回的错误码。

在纯C语言的环境下,我们可以利用这个errno和strerror,用strerror读取错误码对应的错误信息,并将错误码作为进程的退出码,让父进程也知道错误信息。我们编写一个程序,验证一下,

我们讨论了程序运行完毕的情况。但是对于一个进程,我们一般先判断程序是否异常,再判断程序运行的结果是否正确,因为程序出现了异常,程序就无法正常运行,就难以运行完毕(不会产生错误码和退出码),这时我们又该怎么办呢?(两点:为什么发生异常,以及是什么样的异常)

我们知道,C语言中,我们不能对野指针解引用,如果访问野指针,可能抛异常,这是因为访问野指针,本质上是对虚拟地址的访问,但这个虚拟地址在页表中并没有建立映射关系,或者建立了映射关系,但权限为只读;C语言中我们不能对0整除,整除时会触发浮点异常,状态寄存器会发出对应的信号。

而这些异常问题,都是通过发出信号告诉操作系统使进程提前退出。 

状态寄存器_百度百科 (baidu.com)

浮点异常 - 活着的虫子 - 博客园 (cnblogs.com)

 如何证明异常是发送信号使进程退出的呢?

我们之前学习了用“kill -9”指令杀死一个进程,“kill -9”就相当于一个信号,信号8SIGFPE是“Floating point exception”,浮点数异常;信号11SIGSEGV是“Segmentation fault”,段错误,我们可以用信号8和信号11使一个进程退出。执行该文件生成的可执行文件,在root权限下用kill -8杀死该进程。

用kill -11杀死进程, 

2.5 进程常见的退出方法

正常终止

  • 从main函数返回
  • 调用库函数exit

在main函数中,exit中的参数status表示退出码;在进程中调用exit,表示退出该进程。

  • 系统接口_exit

参数:status 定义了进程的终止状态,父进程通过wait来获取该值。

说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255,

 exit和_exit都可以终止进程,他俩有什么区别呢?

其实exit最后会调用_exit, 但在调用_exit之前,还做了其他工作:

1. 执行用户通过 atexit或on_exit定义的清理函数。

2. 关闭所有打开的流,所有的缓存数据均被写入

3. 调用_exit 

由于_exit不刷新缓冲区,我们可以得出一个结论,缓冲区不在内核区,在用户区,因为操作系统不做浪费资源的事。

3.进程等待

3.1  进程等待的概念

狭义的进程等待是指,当子进程退出,进入僵尸状态,父进程会对子进程的状态进程检测并回收,如果子进程没有进入僵尸状态,父进程会等待子进程进入僵尸状态再回收。

3.2 僵尸进程为什么要被回收

基本概念: 

 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。僵尸进程无法被“kill -9”。

背景: 

父进程创建子进程,目的就是让子进程执行自己创建的任务,任务的执行情况最终需要父进程知道。 

unix提供了一种机制保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。

冲突:

但这样就导致了问题,如果父进程不调用wait / waitpid的话, 那么保留子进程的那段信息就不会释放,一直不释放,就会造成内存泄漏。 进程ID - 维基百科,自由的百科全书 (wikipedia.org)

所以,为什么要有进程等待?

一是因为父进程要识别子进程执行任务的最终情况;

二是因为不进行“进程等待”(调用系统接口wait或waitpid回收子进程)会造成”内存泄漏“。

3.3 wait和waitpid

wait

 pid_t wait(int*status);

返回值:

成功返回被等待进程pid,失败返回-1。

参数:

输出型参数,获取子进程退出状态,不关系可设置为NULL。

我们编写一个程序,

执行,

可以看到,子进程15580打印五次后退出,变为僵尸进程,最终被父进程调用wait成功回收。

waitpid 

pid_ t waitpid(pid_t pid, int *status, int options);

返回值:

  • 当正常返回的时候waitpid返回收集到的子进程的进程ID;
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数:  

  • pid:        

       Pid=-1,等待任一个子进程。与wait等效。

       Pid>0,等待其进程ID与pid相等的子进程

  • status:        

       WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)        

        WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)  

  •  options:      

         WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID

那么wait和waitpid是如何获取子进程的退出信息呢?

 我们是通过输出型参数status获得进程退出的结果。

输入型参数是指这个参数的值已知,由函数外传给函数里使用.输入型参数是指这个参数的值未知,要通过函数传出来。

我们编写一个可执行文件,来检测一下,

 对于这个问题,我们必须认识一下status,

参数:status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。如果传递NULL,表示不关心子进程的退出状态信息。非空,操作系统会将子进程的退出信息用该参数反馈给父进程。

由于status返回的是进程退出信息,根据进程退出的场景,我们可以将退出信息分类两类,一类是异常退出,一类是运行完毕后退出。

由于进程中异常在先,一般程序执行中出现了异常,就不会运行完毕,而status是一个整形,有32位,采用位图的算法思想,不同的bit位代表不同的退出原因,其中后低7位表示进程异常的信息,低7位全为0,表示进程没有异常,第低8位是core dump标志;次低8位(15-8)表示程序正常执行完毕返回的错误码,如果没有出现异常(低8位全为0),且次低8位全为0,表示程序运行结果正确,上面子进程的错误码(退出码)为1,status的后16位为0000 0001 0000 0000,转换为十进制就是256。具体细节如下图(只研究status低16比特位)

核心转储 - 维基百科,自由的百科全书 (wikipedia.org)

关于上面的程序,有些同学可能有疑惑,为什么要用wait的参数status呢?我们直接将status定义成全局变量不行吗?exit返回全局变量不可行吗?

不可行,因为进程具有独立性,进程之间的数据、代码、PCB实例、内存空间等都是相对独立的,进程数据之间的传递一般都要通过调用系统接口实现。 

我们将上面代码中第二个if语句改为:

重新生成可执行文件并执行, 

退出码就是“1”了。

我们编写一个“对空指针”解引用,看看打印结果,

在子进程中整除0,

宏WIFEXITED、WEXITSTATUS

如果用"status&0x7F"表示是否异常,“(status>>8)&0x7F”表示错误码,一些用户可能看不懂,所以标准库中定义了两个宏WIFEXITED、WEXITSTATUS,分别代表这两个运算,

我们用这两个宏代替移位运算,

 执行,

结果无误。

3.4 等待失败

什么时候父进程会等待失败呢?(waitpid返回值为-1)

父进程等待到的进程不是它想等待的进程,父进程的儿子是张三等来的却是李四,父进程就会等待失败。我们看下执行结果,

3.4 进程等待的原理 

父进程通过且必须通过操作系统管理的调用接口wait或waitpid获取子进程的PCB实例中的成员exit_code和exit_signal。

上面我们演示的“进程等待”,全是阻塞式等待,父进程等等待期间,只能单一地等待子进程退出变为僵尸进程,不能进行或参与其他任何操作。这种等待效率低,“我等的时候啥事都不能干,手机都不能刷”,但实现简单。还有一种等待方式,就是隔一段时间询问一次子进程是否退出了,没退出父进程就去干自己的事,不会一直等待子进程退出。这种等待方式叫做非阻塞轮询。

3.5 非阻塞轮询

waitpid函数的第三个参数option,设置为0就表示“阻塞等待”,设置为“WNOHANG”就表示非阻塞等待。

夯住(Hang)是指程序仍在运行,卡在某个方法调用上,没有返回也没有异常抛出;卡住时间从几秒到几小时不等。

 

子进程和父进程在交叉打印。如果我们把WNOHANG改为0,就是阻塞等待, 这样父进程会在子进程运行完才会继续运行,

4.进程的程序替换

4.1 程序替换的概念

我们之前所创建的子进程,基本上都是继承了父进程的代码和数据。

当我们想要创建一个子进程去执行其他的任务,去完成独立于父进程的代码和数据的任务时,该怎么办呢? 

我们只要替换子进程的代码和数据即可。

那么如何替换进程的代码和数据呢?

只要将新的程序和数据从磁盘(或其他存储介质)中读取到内存中,然后用它来替换当前正在运行的进程的代码和数据。

4.2 程序替换的原理

 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。

4.3 程序替换实例

 我们可以看到,执行源文件生成的程序,就相当于执行指令“ls”。

 4.4 程序替换接口

4.4.1 execl

 l表示链表的意思,其中参数path表示要执行文件的绝对路径或相对路径,

4.4.2 execlp

p就表示环境变量PATH,execlp中第一个参数file表示要执行文件的文件名,这里不用写路径是因为execlp会自动到环境变量PATH中去寻找该文件所在路径,找到路径了,还要确定要执行的文件名,第二个“ls"就表示要执行的文件名。

实例演示:

4.4.3 execv

v表示vector,

path表示可执行文件的路径,argv是一个字符串指针数组,该数组第一个参数是要执行文件的名称,最后一个参数必须是NULL,中间是程序执行选项。

实例演示:

 注意:

ls是一个可执行文件,文件中有main函数,main函数有参数,谁传给它的参数?

答案是execv函数,execv会将argv数组中除了第一个参数外都传递给ls的main函数。

在命令行中运行程序,所创建的进程都是bash的子进程,这些进程都是通过调用exec系列的函数执行的。

4.4.4 execvp

同理,execvp会在环境变量PATH中寻找参数file(可以写文件名,也可以写文件所在的绝对路径),

实例演示:

4.4.5 execle

在学习execle之前,我们先了解三个知识点,

1.用make生成多个文件:
 2.在c程序中调用cpp程序

Linux系统中C++可执行文件的后缀可以写成“ .cc ”或“ .cxx ”。我们编写mycommand.c,在其main函数中通过execl调用otherExe.cpp生成的可执行程序otherExe,

 我们用C语言编写的程序去调用CPP编写的程序,同样,也可以用这两个语言编写的程序去调用其他语言编写的可执行程序。

运行结果对比, 那么,无论是我们可执行程序,还是脚本语言,为什么都能跨语言调用呢?

脚本语言(英语:Scripting language)是为了缩短传统的“编写、编译、链接、运行”(edit-compile-link-run)过程而创建的计算机编程语言。早期的脚本语言经常被称为批处理语言或作业控制语言。一个脚本通常是解释运行而非编译。脚本语言通常都有简单、易学、易用的特性,目的就是希望能让程序员快速完成程序的编写工作。而宏语言则可视为脚本语言的分支,两者也有实质上的相同之处。

虽然许多脚本语言都超越了计算机简单任务自动化的领域,比如JavaScript[1]、Perl[2]、PHP、Python、Ruby和Tcl,成熟到可以编写精巧的程序,但仍然还是被称为脚本。几乎所有计算机系统的各个层次都有一种脚本语言。包括操作系统层,如计算机游戏,网络应用程序,字处理文档,网络软件等。在许多方面,高级编程语言和脚本语言之间互相交叉,二者之间没有明确的界限。脚本语言 - 维基百科,自由的百科全书 (wikipedia.org)

 因为所有的语言编写的程序,运行起来,本质上都是进程,跨语言调用本质都是调用的进程。

 3.修改环境变量

当一个进程创建子进程时,环境变量会默认传递给子进程,我们在mycommand中调用otherExe,也就是说在进程替换中,默认环境变量不会改变,跟语言无关, 分别运行,

可见,程序替换不会修改环境变量。

那如果我们要修改环境变量,增加某个环境变量怎么办呢?

1.我们直接给bash进程添加环境变量,这样bash所有的子进程会继承这个添加的环境变量。

举例:在bash中添加环境变量val_env=1234,

 用unset可以取消这个环境变量,注意这个环境变量并没有保存在磁盘中在保存在内存中,进程运行结束这个环境变量会销毁。

但是,当我们不想在bash中添加环境变量,只想在父进程中添加环境变量,该怎么办呢?

2.调用putenv

putenv:在当前进程中添加环境变量。

举例:

4.execle

在Linux中,environ是一个全局的外部变量,其类型为char**,存储着系统的环境变量。 除了使用主函数中的第三个参数外,我们也可使用environ函数直接访问系统的环境变量。

彻底替换环境变量信息, 

4.4.6 execvpe 

同execle一样,envp表示环境变量信息表,传递全局的外部变量environ,就是不改变环境变量,自定义环境变量信息表传参,会覆盖旧程序的环境变量。 

4.4.7 总结

  • exec函数族如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • exec函数族只有出错的返回值而没有成功的返回值

上面的6个函数,都是对系统调用接口execve的封装,

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

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

相关文章

【Linux】简易线程池项目

线程池是一个可以巩固一些线程相关接口 && 加强理解的一个小项目。 注意:这里的线程池使用的线程并不是Linux原生接口,而是经过封装的,具体请看线程封装,为什么不使用原生接口? 因为原生接口一旦进行pthread…

2024最新easyrecovery 14中文破解版图文教程

使用EasyRecovery易恢复进行数据恢复非常简单。首先,用户需要选择需要恢复的数据类型,如文档、图片、视频等。然后,软件会对选定的存储设备进行全面扫描,以寻找可恢复的数据。在扫描过程中,用户可以预览部分已找到的文…

成化瓷器“制字衣横少越刀”--还有例外

孙瀛洲先生关于成化款瓷器的名言非常经典,但是,凡事总有以外。 图1,本人收藏成化斗彩鸡缸杯底款,制字的衣横越过双勾刀。 下面是两件台北故宫成化瓷器底款,制字下面的衣横也是越过刀了。 所以,凡事总有例外…

mysql5.7安装

1.创建一个software文件 2.先下载mysql的repo源 wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm 3安装源包 rpm -ivh mysql-community-release-el7-5.noarch.rpm 可能会报错 改成命令 rpm -ivh mysql-community-release-el7-5.noarch.rpm --nodeps…

优化图像处理:从旋转与缩放到水印添加

1. 旋转与缩放的仿射变换 在 OpenCV 中,cv2.getRotationMatrix2D() 函数可以生成旋转矩阵,该矩阵用于对图像进行旋转和缩放变换。旋转矩阵的主要参数是: Center:旋转中心点的坐标 (x, y)。 Angle:旋转角度&#xff0…

数据结构与算法--图的应用

文章目录 回顾提要连通图生成树最小生成树构造最小生成树的算法普里姆(Prim)算法克鲁斯卡尔(Kruskal)算法 最短路径狄杰斯特拉 (Dijkstra) 算法当前最短路径的更新拓扑排序拓扑排序方法拓扑排序示例总结 回顾 图的遍历方法: 深度优先遍历 (DFS):从任意…

在centos7安装mysql

1.卸载旧环境 ps axj | grep mysql ps axj | grep mariabd 如果是这样就什么都不需要做。 如果不是 2.检查并卸载系统安装包 //检查安装包 rpm -qa | grep mysql//卸载安装包 rpm -qa | grep mysql | xargs yum -y remove 3.安装官方yum源 先查看系统的版本 比如我是7.9版…

力扣高频SQL 50题(基础版)第四十题之1164. 指定日期的产品价格

文章目录 力扣高频SQL 50题(基础版)第四十题1164. 指定日期的产品价格题目说明实现过程准备数据实现方式结果截图总结FIRST_VALUE()函数LAST_VALUE()函数NTH_VALUE()函数 LAST_VALUE()函数NTH_VALUE()函数 力扣高频SQL 50题(基础版&#xff0…

YJ0043定制版抖音电商卷抢购系统带回收商城抖音电商优惠卷投资理财系统

系统是基于逍遥商城二开的系统,pc手机端都新增了邀请码验证 手机端重新定制的UI,前端产品不至于抖音卷也可以自行更改其他产品 用户前端下单,后台订单可以直接回收,后台支持设置默认邀请码和抢卷时间限制

动手学深度学习(pytorch)学习记录10-从零开始实现softmax回归[学习记录]

注:本代码在jupyter notebook上运行 封面图片来源 导包 import torch from IPython import display import torchvision from torchvision import transforms from torch.utils import data设置加载数据的线程数 def get_dataloader_workers(): ""&qu…

《学会 SpringBoot 系列 · spring.factories 详解》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

机器人阻抗控制之设计方法

机器人阻抗控制的设计方法主要围绕调整机器人与环境之间的动态关系,使其等效为由弹簧-阻尼-质量组成的二阶系统。这一控制策略不是直接控制机器人的运动或其与外界的接触力,而是控制这二者之间的动态关系。以下是机器人阻抗控制设计方法的详细阐述&#…

Centos7系统上安装docker

centos7安装docker 安装之前,一定查看是否安装docker,如果有,卸载老版本 我是虚拟机装的Centos7,linux 3.10 内核,docker官方说至少3.8以上,建议3.10以上(ubuntu下要linux内核3.8以上&#xff…

LVS详细配置

目录 LVS简介 LVS集群体系结构 LVS相关术语 lvs集群的类型 1、NAT模式 NAT简介 NAT模式数据逻辑 2、DR模式 DR模式简介 DR模式数据逻辑 DR模式的特点 3、TUN模式 TUN模式简介 TUN模式数据传输过程 TUN模式特点 4、fullnet模式 LVS模式总结 LVS调度算法 LVS静…

python从入门到精通:函数

目录 1、函数介绍 2、函数的定义 3、函数的传入参数 4、函数的返回值 5、函数说明文档 6、函数的嵌套调用 7、变量的作用域 1、函数介绍 函数是组织好的,可重复使用的,用来实现特定功能的代码段。 name "zhangsan"; length len(nam…

机器学习(1)--数据可视化

文章目录 数据可视化作用可视化方法实现可视化 总结 数据可视化 数据可视化是将数据以图形、图像、动画等视觉形式表示出来,以便人们能够更直观地理解、分析和交流数据中的信息。 作用 一个整理的好好的数据,我们为什么要将其可视化呢?将它…

深入理解指针

前言&#xff1a;对于指针我们已经有了初步的了解&#xff0c;并已能够简单使用。今天我们来深入理解指针。让我们的指针功力更上一层楼。 1 使用指针访问数组 有了指针的知识&#xff0c;再结合数组的特点&#xff0c;我们就可以使用指针来访问数组了。 #include<stdio.…

线程的进阶学习

线程结束方式: 1.pthread_exit //pthread_join 2.从线程执行函数中return //此时效果等价于pthread_exit 3.pthread_cancel //线程可以被取消 4.任何一个线程调用了exit 或者 主线程 (main函数) return都会造成 进程结束 线程资源回收 ---pthread_join int pthread_ca…

汤姆·克鲁斯对妮可·基德曼经常对粉丝提起他们以前的事感到恼火

妮可基德曼最近回忆了她与前夫汤姆克鲁斯和导演斯坦利库布里克在 1999 年的电影《大开眼戒》中合作的时光。这似乎是对她职业生涯中某个时刻的无伤大雅的回顾&#xff0c;但据报道&#xff0c;有一个人对她在纪念该电影上映 25 周年时的谈话感到不满。 据报道&#xff0c;克鲁…

Polars简明基础教程十四:可视化(四)

数据帧交换协议 数据帧互换协议&#xff08;Dataframe Interchange Protocol&#xff09;&#xff0c;是为了提高不同数据帧库之间的互操作性而设计的。 想象一下&#xff0c;你有很多不同类型的储物箱&#xff08;在这里比喻为不同的数据帧库&#xff0c;如 Pandas、Polars、…