进程相关的系统调用

news2024/9/21 23:11:42

文章目录

  • 进程
    • 进程相关的系统调用
      • wait函数
      • waitpid函数
        • 示例--使用wait fork函数创建子进程并使用宏验证子进程的退出状态信息
        • 示例--使用waitpid函数检测子进程是否进入暂停状态
    • exec族函数
        • 示例--exec族函数的使用
    • system函数
        • 示例--使用system函数执行外部指令
    • 进程状态切换

进程

进程相关的系统调用

wait函数

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);

//功能:等待子进程退出并回收,防止僵尸进程产生
//参数:指向status的指针,用于存储子进程的退出状态信息
//返回值:成功执行返回子进程ID,出错返回-1

waitpid函数

#include <sys/types.h>
#include <sys/wait.h>

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

//功能:等待指定子进程退出并回收,防止僵尸进程产生
//参数1:要等待的子进程的进程标识符
//参数2:指向status的指针,用于存储子进程的退出状态信息
//参数3:选项标志,用于控制waitpid的行为
//返回值:成功执行返回子进程的ID,出错返回-1

参数statusstatus包含了子进程的退出状态信息,在系统中有几个常用的宏来检查和提取特定的状态值。

  1. WIFEXITED(status): 检查子进程是否正常退出。如果子进程正常结束(通过调用 exit()或调用return),则此宏返回非零值。
  2. WEXITSTATUS(status): 如果子进程正常退出,此宏返回子进程的退出状态码。这是一个整数值,通常用于表示程序执行的结果(可以将exit(EXIT_SUCCESS)这里的宏解析出来)。
  3. WIFSIGNALED(status): 检查子进程是否因接收到信号而终止。如果子进程是由于接收到一个信号而被终止,则此宏返回非零值(例如除0操作会触发信号SIGFPE,造成段错误会触发信号SIGSEGV)。
  4. WTERMSIG(status): 如果子进程是由于接收到一个信号而被终止,此宏返回导致子进程终止的信号编号(会返回终止它的信号的编号,在Linux系统中可以使用kill -l列出当前系统中所有的信号来查看)。
  5. WIFSTOPPED(status): 检查子进程是否被暂停。如果子进程由于接收到一个暂停信号(如 SIGSTOP)而被暂停,则此宏返回非零值。
  6. WSTOPSIG(status): 如果子进程被暂停,此宏返回导致子进程暂停的信号编号(SIGSTOP信号的编号)。

参数options:用于控制waitpid的行为,主要支持WNOHANGWUNTRACED两个选项

  • WNOHANG:若由pid指定的子进程没有退出则立即返回,则waitpid不阻塞,此时返回值为0

  • WUNTRACED:若由pid指定的子进程进入暂停状态时立即返回,并且可以使用WIFSTOPPEDWSTOPSIG判断子进程是否进入到暂停状态并将使子进程进入暂停状态的信号编号解析出来。

  • waitwaitpid函数的区别

    • 在一个子进程终止前,wait使其调用者(父进程)阻塞,父进程会每隔一段时间检查子进程是否退出,如果退出就通知内核去释放子进程的资源
    • waitpid有一个选项,可使调用者不阻塞
    • waitpid等待一个指定的子进程,而wait等待所有的子进程,返回任一终止子进程的状态
示例–使用wait fork函数创建子进程并使用宏验证子进程的退出状态信息
#include "header.h"

void normal_exit_func()
{
	exit(EXIT_SUCCESS);
}

void abnormal_exit_func()
{
	char *p = NULL;
	*p = 'c';
	puts(p);	//对空指针操作肯定会造成段错误,由此引发SIGSEGV信号
}

void print_exit_mesg_func(int status)
{
	if(WIFEXITED(status))
		printf("child process exit normally and the status is %d\n",WEXITSTATUS(status));		//正常退出会将退出码打印出来
	else if(WIFSIGNALED(status))
		printf("child process exit abnormally and the signal number is %d\n",WTERMSIG(status));		//如果是异常导致信号退出的话,会将信号的编号打印出来
	else if(WIFSTOPPED(status))
		printf("child process stopped by the signal number is %d\n",WSTOPSIG(status));		//如果子进程在运行过程中发生过停止就会将使它停止的信号的编号打印出来
	else
		printf("unknown status\n");
}

int main(int argc, char **argv)
{
	if(argc < 2)
	{
		fprintf(stderr,"usage: %s [normal | abnormal | stop]\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	pid_t pid;
	int status;

	pid = fork();
	if(pid < 0)
	{
		perror("fork error");
		exit(EXIT_FAILURE);
	}
	else if(pid == 0)
	{
		printf("child pid is %d and the parent pid is %d\n",getpid(),getppid());
		if(!(strcmp(argv[1],"normal")))		//根据外部传参来确定子进程的退出方式
			normal_exit_func();
		else if(!(strcmp(argv[1],"abnormal")))
			abnormal_exit_func();
		else if(!strcmp(argv[1],"stop"))
			pause();		//pause函数会让它一直卡着,作用和while加sleep函数一样
		else
			printf("input error,there is no such choice\n");	
	}
	else
	{
		wait(&status);		//将子进程的退出状态收集,然后写入到status这片空间里,然后使用宏对status进行判断和解析
		print_exit_mesg_func(status);
	}
	
	return 0;
}

image-20240906214058002

通过执行结果可以发现wait函数能通过status获取到子进程的退出状态,代码里正常退出使用的是EXIT_SUCCESS这个宏,经过比对发现正确解析出了宏。当传入abnormal参数的时候,子进程会去执行会造成段错误的代码,然后会收到信号处理机制的编号,这里段错误对应的信号编号是11,通过kill -l查询编号为11的就是SIGSEGV信号,执行正确。至于使用信号使子进程停止运行的功能,wait函数做不到,必须使用waitpid函数。

示例–使用waitpid函数检测子进程是否进入暂停状态
#include "header.h"

void normal_exit_func()
{
	exit(EXIT_SUCCESS);
}

void abnormal_exit_func()
{
	char *p = NULL;
	*p = 'c';
	puts(p);	//对空指针操作肯定会造成段错误,由此引发SIGSEGV信号
}

void print_exit_mesg_func(int status)
{
	if(WIFEXITED(status))
		printf("child process exit normally and the status is %d\n",WEXITSTATUS(status));		//正常退出会将退出码打印出来
	else if(WIFSIGNALED(status))
		printf("child process exit abnormally and the signal number is %d\n",WTERMSIG(status));		//如果是异常导致信号退出的话,会将信号的编号打印出来
	else if(WIFSTOPPED(status))
		printf("child process stopped by the signal number is %d\n",WSTOPSIG(status));		//如果子进程在运行过程中发生过停止就会将使它停止的信号的编号打印出来
	else
		printf("unknown status\n");
}

int main(int argc, char **argv)
{
	if(argc < 2)
	{
		fprintf(stderr,"usage: %s [normal | abnormal | stop]\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	pid_t pid;
	int status;

	pid = fork();
	if(pid < 0)
	{
		perror("fork error");
		exit(EXIT_FAILURE);
	}
	else if(pid == 0)
	{
		printf("child pid is %d and the parent pid is %d\n",getpid(),getppid());
		if(!(strcmp(argv[1],"normal")))		//根据外部传参来确定子进程的退出方式
			normal_exit_func();
		else if(!(strcmp(argv[1],"abnormal")))
			abnormal_exit_func();
		else if(!strcmp(argv[1],"stop"))
			pause();		//pause函数会让它一直卡着,作用和while加sleep函数一样
		else
			printf("input error,there is no such choice\n");	
	}
	else
	{
		waitpid(pid,&status,WUNTRACED);
		print_exit_mesg_func(status);
	}
	
	return 0;
}

image-20240907114658715

通过waitpid函数指定options选项为WUNTRACED,就可以使WIFSTOPPEDWSTOPSIG能够捕捉到SIGDTOP信号并且将使子进程暂停运行的信号的编号打印出来。如果想要指定父进程为非阻塞模式的话可以使用按位或的方式通过设置options来实现要求。

之前的僵尸进程通过Ctrl+C或者杀死父进程的方法来回收僵尸进程,可以使用wait或者waitpid函数来回收子进程,然后告诉内核将子进程的资源(进程表项)释放掉。

image-20240907162701733

这里的wait(NULL)就是父进程不关心子进程的退出方式,不接收来自子进程的退出状态码。

image-20240907162928612

通过观察可以发现并没有产生僵尸进程,僵尸进程被父进程回收掉了。

exec族函数

上边的父进程用fork函数创建子进程,在实际开发中一般是使用子进程实现和父进程不一样的功能,那么在Linux中有一个系统调用exec可以供用户去调用别的可执行程序。例如在实际开发中父进程去实现一个功能,然后使用子进程去调用别人已经实现的功能,增强自己代码的功能。exec函数一般在子进程中运行,因为如果要添加的功能不知一个,那么可以使用父进程创建多个子进程来实现多个功能,而如果在父进程中执行exec函数则不能实现这样的操作。

特点

  • 在用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序。
  • 当进程调用一种exec函数时,该进程完全由新程序替换,替换原有进程的代码段,而新程序则从其main函数开始执行。因为调用exec并不创建进程,所以前后的进程ID并未改变。exec知识用另一个新程序替换了当前进程的代码段、数据段、堆、栈。

exec族函数实现流程

exec族函数流程.drawio

exec族函数

#include <unistd.h>


int execl(const char *path, const char *arg0, ...);		//库函数
int execlp(const char *file, const char *arg0, ...);	//库函数
int execv(const char *path, char *const argv[]);	//库函数
int execvp(const char *file, char *const argv[]);	//库函数
int execle(const char *path, const char *arg0, ..., char *const envp[]);	//库函数
int execve(const char *path, char *const argv[], char *const envp[]);	//系统调用
int execvpe(const char *file, char *const argv[], char *const envp[]);		//库函数

//返回值:出错返回-1,成功不返回

参数

path参数是一个字符串,表示要执行的可执行文件的路径。
arg...参数是一个字符串,表示新程序的名称。
envp参数是一个字符串数组,每个字符串表示一个环境变量,以NULL作为结束标志。
file参数是一个字符串,表示要执行的可执行文件的名称。
argv参数是一个字符串数组,每个字符串表示一个命令行参数,以NULL作为结束标志。

exec族函数的后缀

l(list): 表示后面的参数列表是要传递给新程序的参数列表,参数列表的第一个参数必须是可执行程序的路径,最后一个参数必须是 NULL 结尾表示参数传递完毕。
v(vector): 表示参数通过一个指针数组传递,其实就是把上边的参数列表构建成一个指针数组然后传递给新程序的argv,第一个参数必须是可执行程序的路径,最后一个参数必须是 NULL 结尾表示参数传递完毕。
p(path): 表示在系统环境变量 $PATH 指定的目录中搜索可执行文件。
e(environment): 表示提供一份环境变量列表,这份列表是一个包含指向每个环境变量字符串的指针数组。

返回值

exec函数如果成功执行,会将子进程的代码段、数据段、堆、栈全部替换为新程序的。而且执行成功不返回,因为调用成功后之前子进程的代码不复存在,所以它不会返回。而如果exec函数执行出错,返回-1,可以继续执行之前的代码,但是执行出错以后一般都使用perror函数查看出错的原因,并且使用exit函数使子进程退出运行。

示例–exec族函数的使用
#include "header.h"

int main(void)
{
    char *cmd1 = "/bin/ls";
    char *cmd2 = "ls";
    char *argv1 = "/home/dx";
    char *argv2 = "/";

    pid_t pid;

    /**************execl函数******************/
    if((pid = fork()) < 0)
    {
        perror("Fork failed");
        exit(EXIT_FAILURE);
    }
    else if(pid == 0)
    {
        printf("this is the child process and the pid is %d,parent pid is %d\n",getpid(),getppid());
        if(execl(cmd1, cmd1, argv1, argv2, NULL) < 0)
        {
            perror("execl error");
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        printf("this is parent process and the pid is %d,the child process is %d\n",getpid(),pid);
        wait(NULL); //等待子进程退出,防止子进程如果execl失败变成一个僵尸进程
    }

    /**************execlp函数******************/
    if((pid = fork()) < 0)
    {
        perror("Fork failed");
        exit(EXIT_FAILURE);
    }
    else if(pid == 0)
    {
        printf("this is the child process and the pid is %d,parent pid is %d\n",getpid(),getppid());
        if(execlp(cmd2, cmd2, argv1, argv2, NULL) < 0)
        {
            perror("execlp error");
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        printf("this is parent process and the pid is %d,the child process is %d\n",getpid(),pid);
        wait(NULL); //等待子进程退出,防止子进程如果execl失败变成一个僵尸进程
    }

    /**************execv函数******************/
    char *argvs[] = {cmd1,argv1,argv2,NULL};
    if((pid = fork()) < 0)
    {
        perror("Fork failed");
        exit(EXIT_FAILURE);
    }
    else if(pid == 0)
    {
        printf("this is the child process and the pid is %d,parent pid is %d\n",getpid(),getppid());
        if(execv(cmd1, argvs) < 0)
        {
            perror("execv error");
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        printf("this is parent process and the pid is %d,the child process is %d\n",getpid(),pid);
        wait(NULL); //等待子进程退出,防止子进程如果execl失败变成一个僵尸进程
    }

    /**************execvp函数******************/
    char *argvp[] = {cmd2,argv1,argv2,NULL};
    if((pid = fork()) < 0)
    {
        perror("Fork failed");
        exit(EXIT_FAILURE);
    }
    else if(pid == 0)
    {
        printf("this is the child process and the pid is %d,parent pid is %d\n",getpid(),getppid());
        if(execvp(cmd2, argvp) < 0)
        {
            perror("execvp error");
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        printf("this is parent process and the pid is %d,the child process is %d\n",getpid(),pid);
        wait(NULL); //等待子进程退出,防止子进程如果execl失败变成一个僵尸进程
    }

    return 0;
}

image-20240907205351773

由编译结果可知execl函数和execlp函数它们的使用区别主要是在是否要指定可执行程序的路径。execl函数可以使用相对路径也可以使用绝对路径,具体使用哪一个主要是根据场景来决定的,相对路径是的解析是针对当前目录来进行查找可执行程序的位置,而绝对路径是从根目录/开始查找的。这样对比下来的话,绝对路径不会受当前工作目录的影响,相对路径如果切换了工作目录以后其指定的参数也需要改变。而execlp函数是根据环境变量来查找可执行程序的路径,可以使用echo $PATH来打印系统的环境变量。虽然说execlp函数使用起来比execl函数方便,但是execlp函数依赖于环境变量,如果环境变量中没有可执行程序的路径,那么当通过子进程执行exec族函数的时候就会出错显示找不到可执行程序的路径,没有此文件。它们的使用方法与execv函数和execvp函数的用法是一样的,execv函数需要指定可执行程序的路径,而execvp函数通过环境变量$PATH来查找可执行程序。

execlexecv函数它们使用的主要区别是execv将参数构建成了一个指针数组argv然后使用,而execl函数使用的是参数列表。这个用法与execlp函数和execvp函数的用法一样。都是将参数列表构建成一个指针数组argv然后传给调用的可执行程序里main函数的argv

system函数

通过上边的exec族函数可以在子进程中启动另外的可执行程序,方便增加程序的功能。但是在调用过程中发现使用exec族函数是有一点繁琐的。在Linux系统中提供了一个和exec族函数功能一样的一个库函数调用system,它的底层实现就是使用exec族函数,只不过系统已经帮我们封装好了,我们直接使用它即可。

#include <stdlib.h>

int system(const char *command);

//功能:执行系统命令,这些命令可以是内置命令、可执行文件或脚本。
//参数:一个命令字符串(可以是由用户自己构建的)
//返回值:如果命令执行成功,返回值是shell指令的退出码,如果调用失败,返回-1并设置errno
示例–使用system函数执行外部指令
#include "header.h"

void mysystem_func(char *cmd)
{
	pid_t pid;
	
	if((pid = fork()) < 0)
	{
		perror("fork error");
		exit(EXIT_FAILURE);
	}
	else if(pid == 0)
	{
		char *buffer[] = {"bash","-c",cmd,NULL};	//这里的参数bash表示使用bash来解析命令行,-c参数表示后面的字符串作为命令来执行

		if(execvp("bash",buffer) < 0)	//这里的execvp函数表明将后边的参数构建出一个指针数组,然后当作参数使用
										//由于这里使用的是p,所以会默认在系统环境变量里查找指定的可执行程序
		{
			perror("execlp error");
			exit(EXIT_FAILURE);
		}
	}
	else
	{
		wait(NULL);
	}
}

int main(void)
{
	char *cmd1 = "ls";
	char *cmd2 = "ls -l > ls.log";

	system(cmd1);
	mysystem_func(cmd2);

	return 0;
}

image-20240908113326596

通过编译执行,发现system函数执行的结果和exec组函数的结果是一样的。这里system函数执行的字符串可以向代码里这样直接写成一个整体,也可以通过后边使用sprintf函数将格式化的数据写入到字符串里然后调用system函数,两种效果是一样的。

进程状态切换

进程状态.drawio

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

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

相关文章

Vue2电商平台项目 (三) Search模块、面包屑(页面自己跳自己)、排序、分页器!

文章目录 一、Search模块1、Search模块的api2、Vuex保存数据3、组件获取vuex数据并渲染(1)、分析请求数据的数据结构(2)、getters简化数据、渲染页面 4、Search模块根据不同的参数获取数据(1)、 派发actions的操作封装为函数(2)、设置带给服务器的参数(3)、Object.assign整理参…

第十一章 【后端】商品分类管理微服务(11.1)——创建父工程

第十一章 【后端】商品分类管理微服务 11.1 创建父工程 项目名称:EasyTradeManagerSystem:Easy 表示简单易用,Trade 表示交易,Manager 表示管理,System 表示系统,强调系统在商品交易管理方面的便捷性,简称 etms。 新建工程 yumi-etms yumi-etms 作为所有模块的父工程,…

基于java 的医院排号管理系统设计与实现

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的可以…

1863. 找出所有子集的异或总和再求和

目录 一&#xff1a;题目&#xff1a; 二&#xff1a;代码&#xff1a; 三&#xff1a;结果&#xff1a; 一&#xff1a;题目&#xff1a; 一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果&#xff1b;如果数组为 空 &#xff0c;则异或总和为 0 。 例如&#x…

C++--类的实例化

一、实例化的概念 用类类型在屋里内存中创建对象的过程&#xff0c;称为类实例化出对象 类是对对象进行一种抽象描述&#xff0c;是一个模型一样的东西&#xff0c;限定了类有哪些成员变量&#xff0c;这些成员变量只是声明&#xff0c;没有分配空间&#xff0c;用类实例化出…

【C++前后缀分解 动态规划】2100. 适合野炊的日子|1702

本文涉及知道点 C前后缀分解 C动态规划 LeetCode2100. 适合野炊的日子 你和朋友们准备去野炊。给你一个下标从 0 开始的整数数组 security &#xff0c;其中 security[i] 是第 i 天的建议出行指数。日子从 0 开始编号。同时给你一个整数 time 。 如果第 i 天满足以下所有条件…

2024最新版MySQL详细学习教程

MySQL数据库提供了很多函数包括&#xff1a; 数学函数&#xff1b;字符串函数&#xff1b;日期和时间函数&#xff1b;条件判断函数&#xff1b;系统信息函数&#xff1b;加密函数&#xff1b;格式化函数&#xff1b; 一、数学函数 数学函数主要用于处理数字&#xff0c;包括…

基于paddleocr的批量图片缩放识别

说明 在进行ocr文字识别的时候&#xff0c;有时候我们需要使用批量测试的功能&#xff0c;但是有些图片会识别失败或者个别根本识别不出来&#xff0c;这时候我们可以通过对原图片进行缩放&#xff0c;提高图像的分辨率&#xff0c;然后再次识别&#xff0c;这样可以大大提高图…

Vue学习记录之一(介绍及脚手架的使用)

一、背景知识介绍 1、构建工具介绍 Vite, Webpack,Rollup, Parce 构建工具优点缺点Vite- 快速启动&#xff0c;秒级热更新&#xff0c;更快的构建速度&#xff0c;更好的开发体验&#xff1b;- 支持 Vue3 和 ES modules 的原生特性&#xff0c;轻松实现按需加载。- 对于单页…

项目实现:云备份②(文件操作、Json等工具类的实现)

云备份 前言文件操作实用工具类设计文件属性的获取文件的读写操作文件压缩与解压缩的实现文件目录操作 Json 实用工具类设计编译优化 前言 如果有老铁不知道当前项目实现的功能是什么的话&#xff0c;可以先移步这篇文章内容&#xff1a; 云备份项目的介绍 其中介绍了云备份项…

【吊打面试官系列-MySQL面试题】简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别?

大家好&#xff0c;我是锋哥。今天分享关于【简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别&#xff1f; MyISAM&#xff1a; 不支持事务&#xff0c;但是每次查…

跳出大厂圈子——普通程序员如何开启逆袭之路

时间&#xff1a;2024年09月16日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 音频&#xff1a;喜马拉雅 大家好&#xff0c;欢迎来到“小蒋聊技术”&#xff01;今天咱们聊点特别现实的事儿——普通程序员的出路。互联网时代…

化学实验室器具识别系统源码分享

化学实验室器具识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…

android设置实现广告倒计时功能

文章目录 CountDownTimer基本使用增加基础BaseActivity增加固定活动 在Android中&#xff0c;CountDownTimer 是一个用于计时的类&#xff0c;它允许你在指定的时间段内执行某些操作。通常用于倒计时功能&#xff0c;例如显示一个倒计时进度条或者在倒计时结束后执行某个动作。…

采用qt做一个命令行终端

qt做一个类似系统命令行终端的工具&#xff0c;方便集成到自己的软件里使用&#xff0c;这样能保证软件的整体性&#xff0c;而且是真正的做到和系统命令行终端一样的交互方式&#xff0c;而不是单独搞个编辑框的方式输入命令&#xff08;大部分博客都是做成这个样子&#xff0…

不善言辞的程序员适合做项目经理吗?

项目经理的角色需要承担多重任务&#xff0c;包括团队协调、资源调配、风险管理、沟通与汇报等。因此&#xff0c;很多人认为项目经理需要较强的沟通能力和外向性格。然而&#xff0c;不善言辞的程序员是否适合这一职位&#xff0c;实际上取决于多个因素。以下从不同角度进行分…

程序设计题(25-32)

第二十五题 题目 请编写函数fun&#xff0c;其功能是:在一个含有11个四位数的数组中&#xff0c;统计出这些数的奇数、偶数个数&#xff0c;然后计算出个数多的那些数的算数平均值并由函数返回&#xff0c;个数通过yy传回。 例如&#xff0c;若11个数据为:1101, 1202, 1303,…

AI+RPA 实战揭秘:DrissionPage 助力 CSDN 热榜数据抓取与 AI 结合

在前一篇文章《AIRPA&#xff1a;开启智能自动化新时代》 发布之后&#xff0c;今天我们将以实战的方式深入介绍数据分析中的RPA究竟是怎样抓取数据的&#xff0c;以及它与 AI 又是如何紧密结合的。首先&#xff0c;让我们来认识一个重要的工具包 ——DrissionPage。 一、Dris…

【例题】lanqiao301 实现基数排序

输入输出样例 输入 6 7 1 4 8 5 2输出 1 2 4 5 7 8解题思路 翻译&#xff1a;就是从个位到十位、……比较大小。 代码 nint(input()) alist(map(int,input().split())) a.sort() print( .join(map(str,a)))

Linux: network: delay and burst

最近遇到一个false-network的问题&#xff0c;抓到的网络包的特征是&#xff0c;有十几秒的延迟&#xff0c;然后来了一个burst。这个现象说明这个包肯定是缓存在了一个地方&#xff0c;但是具体是什么地方&#xff0c;就需要抓包确定。 这次的缓存的地方是虚拟机内部。由于一…