【Linux】环境变量、进程替换、wait/waitpid

news2024/11/24 14:29:45

文章目录

  • 一、环境变量
    • 1. 查看环境变量的方法
      • 1.1 env
      • 1.2 echo $环境变量名
    • 2. 在代码中使用环境变量的方法
      • 2.1 命令行参数传参
      • 2.2 environ变量
      • 2.3 getenv( )函数
    • 3. export
  • 二、进程替换
    • 1. execl
    • 2. execlp
    • 3. execle
    • 4. execv
    • 5. execvp
    • 6. execvpe
    • 7. 补充
      • 7.1 命名理解
      • 7.2 返回值
  • 三、wait/waitpid
    • 1. wait
    • 2. waitpid
    • 3. 获取子进程的退出状态
      • 3.1 位运算获取退出状态
      • 3.2 内置宏获取退出状态


一、环境变量

1. 查看环境变量的方法

1.1 env

可以查看当前进程的所有环境变量.
在这里插入图片描述

1.2 echo $环境变量名

可以查看指定的环境变量.
在这里插入图片描述

2. 在代码中使用环境变量的方法

2.1 命令行参数传参

通过设置 main 函数的第三个形参 char* envp[ ], 接收环境变量.

#include <iostream>
using namespace std;

int main(int argc, char* argv[], char* envp[])
{
	for (int i = 0; envp[i]; ++i)
	{	
		cout << envp[i] << endl;
	}   
	return 0;
}

程序运行起来会是 bash 的子进程, 子进程会继承父进程的环境变量, envp 的最后一个位置会存储一个 NULL, 所以可以用作循环条件的判断.

2.2 environ变量

通过 extern 关键字引用外部变量, extern char** environ.

#include <iostream>
using namespace std;

extern char** environ;

int main()
{
	for (int i = 0; environ[i]; ++i)
	{	
		cout << environ[i] << endl;
	}   
	return 0;
}

environ 不包含在任何头文件中, 所以要使用 extern 引入.

2.3 getenv( )函数

通过 getenv( ) 可以获取指定的环境变量, 传递的参数是环境变量名.
头文件: #include <stdlib.h>
函数声明: char *getenv(const char *name);

#include <iostream>
#include <stdlib.h>
using namespace std;

int main()
{
	cout << getenv("USER") << endl;
	cout << getenv("HOME") << endl;
	return 0;
}

运行结果:
在这里插入图片描述

3. export

在 Linux 中, 像 ls, pwd 这样的指令可以直接敲了就运行, 而我们编写的代码在生成了可执行文件后要通过 ./xxx 的方式才可以运行, 这是为什么呢? 本质都是一个程序, 跑起来变成进程, 原因是 ls, pwd 这种指令的路径添加到了环境变量 PATH 中, 所以在运行时系统在环境变量 PATH 中可以找到, 而我们自己编写的程序的路径不存在环境变量 PATH 中, 所以要指定路径.

自己编写的程序, 如果不加 ./ 直接跑起来是这样的:
在这里插入图片描述

那么试着把自己编写的程序的所在路径添加到环境变量 PATH 中, 看看可不可以不需要 ./ 直接跑起来, 通过 export 添加到环境变量:

export PATH=$PATH:/root/EnvTest/Test/

$PATH 表示之前添加到 PATH 中的所有路径, 将自己编写的程序的所在路径写在冒号右边就好, 不要直接写成 PATH=/root/EnvTest/Test/, 这样写会把之前的路径都覆盖掉, 属于覆盖式写入.

运行结果:
在这里插入图片描述
可以看到可以不带 ./ 直接运行了, 再看看 PATH 中也存在了刚才添加的路径:
在这里插入图片描述

二、进程替换

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

下面介绍各种 exec 函数, 头文件均为: #include <unistd.h>

1. execl

函数声明: int execl(const char *path, const char *arg, …);

  • path: 替换程序的路径
  • arg/…: 可变参数列表
#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
	cout << "111" << endl;
	cout << "222" << endl;
	cout << "333" << endl;
	
	execl("/usr/bin/ls", "ls", "-l", "-a", NULL);
	
	cout << "444" << endl;
	cout << "555" << endl;
	cout << "666" << endl;
	return 0;
}

假设要替换的进程为 ls, 那么就把 ls 的路径写在 path 处, 在可变参数列表分别加上在终端如何执行 ls 的指令, 每个选项单独写, 最后必须以 NULL 结尾.

运行结果:
在这里插入图片描述
可以看到前面三行代码正常输出, 进程替换后的代码不会输出, 因为在函数调用处后的代码会被替换.

2. execlp

函数声明: int execlp(const char *file, const char *arg, …);

  • file: 替换程序名
  • arg/…: 可变参数列表
#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
	cout << "111" << endl;
	cout << "222" << endl;
	cout << "333" << endl;
	
	execl("ls", "ls", "-l", "-a", NULL);
	
	cout << "444" << endl;
	cout << "555" << endl;
	cout << "666" << endl;
	return 0;
}

path 处不需要指明路径了, 直接写上替换程序的名称即可, 后面的可变参数列表处和 execl 一致.

运行结果:
在这里插入图片描述
需要注意的是, 如果替换的程序是你自己编写的, 那么需要把它所在的路径添加到 PATH 中, 否则不会替换.

3. execle

函数声明: int execle(const char *path, const char *arg, …, char * const envp[ ]);

  • path: 替换程序的路径
  • arg/…: 可变参数列表
  • envp: 环境变量列表

程序 myenv, 执行起来就是查看该进程的环境变量:
在这里插入图片描述
execle 示例代码:

#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
	cout << "111" << endl;
	cout << "222" << endl;
	cout << "333" << endl;
	
	char*const envp[] = {"MYENV=100", NULL};
	execle("/root/ExecTest/myenv", "myenv", NULL, envp);
	
	cout << "444" << endl;
	cout << "555" << endl;
	cout << "666" << endl;
	return 0;
}

传递环境变量需要自定义一个数组进行传递, 在数组中依然要以 NULL 结尾, 但是此时来看一看运行结果:
在这里插入图片描述
确实是把传递的环境变量给输出出来了, 但是之前的环境变量被覆盖了, 没错 execle 传递环境变量就是覆盖式传入, 那么如何做到不覆盖进程原本的环境变量, 又把新的环境变量传入呢? 先看一个函数 putenv( ):

头文件: #include <stdlib.h>
函数声明: int putenv(char *string);

该函数的功能很简单, 就是哪个进程调用该函数, 就把传递的环境变量形参设置到哪个进程的环境变量中.

结合该函数就可以实现不覆盖进程原本的环境变量, 又把新的环境变量传入, 如下:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;

extern char** environ;

int main()
{
	cout << "111" << endl;
	cout << "222" << endl;
	cout << "333" << endl;
	
	putenv("MYENV=100");
	execle("/root/ExecTest/myenv", "myenv", NULL, environ);
	
	cout << "444" << endl;
	cout << "555" << endl;
	cout << "666" << endl;
	return 0;
}

其实也很简单, 就是先把新的环境变量导入当前进程, 再把 environ 当作形参传递就好了.

运行结果:
在这里插入图片描述
可以看到, 不仅输出了原本的环境变量, 而且新添加的也输出了, 那么就成功做到了不覆盖进程原本的环境变量, 又把新的环境变量传入.

4. execv

函数声明: int execv(const char *path, char *const argv[]);

  • path: 替换程序的路径
  • argv: 执行该程序的指令数组
#include <iostream>
#include <unistd.h>
using namespace std;

int main()
{
	cout << "111" << endl;
	cout << "222" << endl;
	cout << "333" << endl;
	
	char*const argv[] = {"ls", "-l", "-a", NULL};
	execv("/usr/bin/ls", argv);
	
	cout << "444" << endl;
	cout << "555" << endl;
	cout << "666" << endl;
	return 0;
}

其实很简单, 和 execl 没什么区别, 无非是把替换程序的执行指令写在一个数组中, 再把数组作为参数传给 execv, 注意数组最后必须以 NULL 结尾即可.

运行结果:
在这里插入图片描述

5. execvp

函数声明: int execvp(const char *file, char *const argv[]);

  • file: 替换程序名
  • argv: 执行该程序的指令数组
#include <iostream>
#include <unistd.h>
using namespace std;

extern char** environ;

int main()
{
	cout << "111" << endl;
	cout << "222" << endl;
	cout << "333" << endl;
	
	char*const argv[] = {"ls", "-l", "-a", NULL};
	execv("ls", argv);
	
	cout << "444" << endl;
	cout << "555" << endl;
	cout << "666" << endl;
	return 0;
}

和 execv 差不多, 无非是函数的第一个参数直接传可执行程序名即可.

运行结果:
在这里插入图片描述

6. execvpe

函数声明: int execvpe(const char *file, char *const argv[], char *const envp[]);

  • file: 替换程序名
  • argv: 执行该程序的指令数组
  • envp: 环境变量列表
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;

extern char** environ;

int main()
{
	cout << "111" << endl;
	cout << "222" << endl;
	cout << "333" << endl;
	
	putenv("MYENV=100");
	char*const argv[] = {"myenv", NULL}; 
	execvpe("myenv", argv, environ);

	cout << "444" << endl;
	cout << "555" << endl;
	cout << "666" << endl;
	return 0;
}

无非就是第一个参数直接传递替换程序名, 第二个参数传递执行该程序的指令数组, 第三个参数传递环境变量数组, 和之前的大同小异
运行结果:
在这里插入图片描述

7. 补充

7.1 命名理解

exec 函数名中的规律:

  • 带 l 的: l = list, 表示传参直接传在函数参数列表中.
  • 带 v 的: v = vector, 表示一部分参数可以先写在数组中, 再把数组当作参数传递.
  • 带 p 的: p = path, 表示不需要传路径, 直接传递替换程序名即可.
  • 带 e 的: e = env, 表示自己维护环境变量.

7.2 返回值

exec( ) 函数仅在发生错误时返回, 返回值为 -1,并设置 errno 以指示错误, 其实不需要关心返回值, 因为如果成功程序会被替换, 失败不会被替换, 剩于的代码也会执行, 无论成功与否都可以很直观的判断出函数执行的结果.

三、wait/waitpid

子进程在退出后会变成僵尸进程, 通过 wait/waitpid 可以再获取了子进程的退出状态后将身为僵尸进程的子进程释放, 如果长期不释放会导致内存泄漏.

头文件均为:
#include <sys/types.h>
#include <sys/wait.h>

1. wait

函数原型: pid_t wait(int *status);
参数: 输出型参数, 可以获取进程的退出状态, 不需要获取退出状态设置为NULL.
返回值:

  • 成功: 返回被等待的进程的 pid.
  • 失败: 返回 -1, 这时errno会被设置成相应的值以指示错误所在.

示例代码:

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

int main()
{
	pid_t ret1 = fork(); //以此往后分化为两个进程执行
	//子进程
	if (ret1 == 0)
	{
		for (int i = 0; i < 5; ++i)
		{
			cout << "I am child, my pid:" << getpid() << endl;
			sleep(1);
		}
	}
	else
	{
		pid_t ret2 = wait(NULL);
		cout << "wait success, ret2:" << ret2 << endl;
	}
	return 0;
}

子进程调用 5 次 cout << “I am child, my pid:” << getpid() << endl; 输出信息, 最后由父进程成功 wait 并且输出 wait 的返回值.

运行结果:
在这里插入图片描述
通过图片无法看出, 但是以上代码是两个进程再跑, 但是情况是这样的, 父进程一直 wait 子进程退出, 而这种等待是阻塞式等待, 所以其结论就是父进程在使用 wait 时是处于阻塞式等待的, 需要子进程退出后并且父进程 wait 结束后才会往下执行父进程剩于的代码.

2. waitpid

函数原型: pid_t waitpid(pid_t pid, int *status, int options);
参数:

  • pid: 设置为 -1 表示等待任一子进程, 设置为 >0 表示等待指定 pid 的子进程.
  • status: 输出型参数, 可以获取进程的退出状态, 不需要获取退出状态设置为NULL.
  • options: 设置为 0 表示阻塞式等待, 设置为 WNOHANG 表示非阻塞式等待.

返回值:

  • 正常返回时, 返回等待的子进程的 pid.
  • 如果 options 设置了选项WNOHANG, 而调用中 waitpid 发现没有已退出的子进程可等待, 则返回0.
  • 失败返回 -1, 这时errno会被设置成相应的值以指示错误所在.

示例代码:

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

int main()
{
	pid_t ret1 = fork(); //以此往后分化为两个进程执行
	//子进程
	if (ret1 == 0)
	{
		for (int i = 0; i < 5; ++i)
		{
			cout << "I am child, my pid:" << getpid() << endl;
			sleep(1);
		}
	}
	else
	{
		int status = 0;
		pid_t ret2 = 0;
		do
		{
			ret2 = waitpid(ret1, &status, WNOHANG);
			cout << "father is running." << endl;
			sleep(1);
		}while(ret2 == 0)
		cout << "waitpid success, ret2:" << ret2 << endl;
	}
	return 0;
}

运行结果:
在这里插入图片描述
可以看到在子进程没有退出时, 父进程还是有在往后执行的, 而且在子进程退出时, 父进程也能成功的等待到子进程.

3. 获取子进程的退出状态

在 wait/waitpid 中都有 status, status 是 int 类型, 那么共有 32 位, status 是当作一个位图使用, 在其中存储着退出状态信息, 如下图:
在这里插入图片描述
其中 status 的高 16 位不用, 只使用低 16 位, 而次低 8 位用于存储退出码, 就是 return, exit 携带的数字, 低 7 位存储终止信号, 如果终止信号为 0 表示正常退出, 接着可以获取其退出码, 判断结果是否正确, 如果终止信号不为 0, 表示进程是被信号所终止退出的, 此时为异常退出, 异常退出退出码没有意义, 所以未被设置, 直接获取终止信号判断分析异常退出原因即可.

3.1 位运算获取退出状态

获取退出码: ((status >> 8) & (0xff))
获取终止信号: (status & (0x7f))

退出码示例代码:

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

int main()
{
	pid_t ret1 = fork(); //以此往后分化为两个进程执行
	//子进程
	if (ret1 == 0)
	{
		for (int i = 0; i < 5; ++i)
		{
			cout << "I am child, my pid:" << getpid() << endl;
			sleep(1);
		}
	}
	else
	{
		int status = 0;
		pid_t ret2 = 0;
		do
		{
			ret2 = waitpid(ret1, &status, WNOHANG);
			cout << "father is running." << endl;
			sleep(1);
		}while(ret2 == 0)
		cout << "waitpid success, ret2:" << ret2 << ", exit code:" << ((status >> 8) & (0xff)) << ", signal:" << (status & (0x7f)) << endl;
	}
	return 0;
}

运行结果:
在这里插入图片描述
为了直观的看到退出码, 改变一下代码:

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

int main()
{
	pid_t ret1 = fork(); //以此往后分化为两个进程执行
	//子进程
	if (ret1 == 0)
	{
		for (int i = 0; i < 5; ++i)
		{
			cout << "I am child, my pid:" << getpid() << endl;
			sleep(1);
		}
		return 111; //为了更直观的看到退出码
	}
	else
	{
		int status = 0;
		pid_t ret2 = 0;
		do
		{
			ret2 = waitpid(ret1, &status, WNOHANG);
			cout << "father is running." << endl;
			sleep(1);
		}while(ret2 == 0)
		cout << "waitpid success, ret2:" << ret2 << ", exit code:" << ((status >> 8) & (0xff)) << ", signal:" << (status & (0x7f)) << endl;
	}
	return 0;
}

运行结果:
在这里插入图片描述

终止信号示例代码:

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

int main()
{
	pid_t ret1 = fork(); //以此往后分化为两个进程执行
	//子进程
	if (ret1 == 0)
	{
		for (int i = 0; i < 5; ++i)
		{
			cout << "I am child, my pid:" << getpid() << endl;
			sleep(1);
		}
		return 111; //为了更直观的看到退出码
	}
	else
	{
		int status = 0;
		pid_t ret2 = 0;
		do
		{
			ret2 = waitpid(ret1, &status, WNOHANG);
			cout << "father is running." << endl;
			sleep(1);
		}while(ret2 == 0)
		if((status & (0x7f)))
		{
			cout << "waitpid success, ret2:" << ret2 << ", signal:" << (status & (0x7f)) << endl;                                                                       
		} 
	}
	return 0;
}

如果终止信号不为 0, 表示异常退出, 此时就没必要获取退出码了, 因为此时的退出码不具有参考性, 没有获取的必要.

发送 9 号信号 kill 掉进程:

kill -9 20266

运行结果:
在这里插入图片描述

3.2 内置宏获取退出状态

WIFEXITED(status): 若为正常终止子进程返回的状态, 则为真.(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零, 提取子进程退出码.(查看进程的退出码)

示例代码:

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

int main()
{
	pid_t ret1 = fork(); //以此往后分化为两个进程执行
	//子进程
	if (ret1 == 0)
	{
		for (int i = 0; i < 5; ++i)
		{
			cout << "I am child, my pid:" << getpid() << endl;
			sleep(1);
		}
		return 111; //为了更直观的看到退出码
	}
	else
	{
		int status = 0;
		pid_t ret2 = 0;
		do
		{
			ret2 = waitpid(ret1, &status, WNOHANG);
			cout << "father is running." << endl;
			sleep(1);
		}while(ret2 == 0)
		//正常退出,查看退出码
		if(WIFEXITED(status))
		{
			cout << "waitpid success, ret2:" << ret2 << ", exit code:" << WEXITSTATUS(status) << endl;
		}
		else
		{
			cout << "waitpid success, ret2:" << ret2 << ", signal:" << (status & (0x7f)) << endl;
		}
	}
	return 0;
}

因为没有提供查看终止信号的宏, 所以查看终止信号仍需要用位运算的方法.

运行结果:
在这里插入图片描述

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

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

相关文章

数据结构可视化(适合考研党)

废话不多说传送门 还在疑惑平衡二叉树、红黑树、B树、B树怎么插入构建的吗&#xff0c;不要慌张&#xff0c;这个网站会一步一步来演示.&#xff0c;听了咸鱼的课还不够&#xff0c;需要自己动手模拟一下各种数据结构的CRUD&#xff01;&#xff01;

中华科技控股集团:人工智能标准化引领者与数字化服务新航程的启航者

4月30日, 矗立于时代科技潮头的中华科技控股集团&#xff0c;自2010年在香港这片国际金融沃土上诞生以来&#xff0c;便以其独特的国资背景与全球化视野&#xff0c;肩负起推动中国科技进步与产业升级的重任。作为国资委麾下的重要一员&#xff0c;中华科技始终坚持创新驱动发展…

数据结构--栈与队列【您的关注是我创作的动力!】

文章目录 栈什么是栈&#xff1f;栈的具体实现 队列什么是队列&#xff1f;队列的实现 栈 什么是栈&#xff1f; 栈也是顺序表的一种&#xff0c;栈的逻辑实现是先进后出&#xff08;后进先出&#xff09;就跟子弹夹一样。 具体逻辑就是它只允许在固定的一端进行数据的插入与…

Spring Boot中使用Redis和Lua脚本实现延时队列

码到三十五 &#xff1a; 个人主页 延时队列是一种常见的需求。延时队列允许我们延迟处理某些任务&#xff0c;这在处理需要等待一段时间后才能执行的操作时特别有用&#xff0c;如发送提醒、定时任务等。文中&#xff0c;将介绍如何在Spring Boot环境下使用Redis和Lua脚本来实…

【JAVA基础之反射】反射详解

&#x1f525;作者主页&#xff1a;小林同学的学习笔录 &#x1f525;mysql专栏&#xff1a;小林同学的专栏 1.反射 1.1 概述 是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b; 对于任意一个对象&#xff0c;都能够调用它…

Edge 浏览器键入时如何关闭显示搜索和站点建议

Edge 浏览器键入时如何关闭显示搜索和站点建议

ssm104园区停车管理系统+jsp

园区停车管理系统的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管…

基于SSM SpringBoot vue宾馆网上预订综合业务服务系统

基于SSM SpringBoot vue宾馆网上预订综合业务服务系统 系统功能 首页 图片轮播 宾馆信息 饮食美食 休闲娱乐 新闻资讯 论坛 留言板 登录注册 个人中心 后台管理 登录注册 个人中心 用户管理 客房登记管理 客房调整管理 休闲娱乐管理 类型信息管理 论坛管理 系统管理 新闻资讯…

【VueUse】超越基本功能的高级 Vue 元素操作

在vue开发中我们经常需要操作DOM元素&#xff0c;从简单的添加类到动态创建元素&#xff0c;这些操作都是不可避免的。而在VueUse库中&#xff0c;Elements相关API函数为我们提供了一系列强大而灵活的工具&#xff0c;帮助我们更轻松地处理DOM元素。无论是优雅地处理元素、动态…

25计算机考研院校数据分析 | 哈尔滨工业大学

哈尔滨工业大学&#xff08;Harbin Institute of Technology&#xff09;&#xff0c;简称哈工大&#xff0c; 校本部位于黑龙江省哈尔滨市&#xff0c;是由工业和信息化部直属的全国重点大学&#xff0c;位列国家“双一流”、“985工程”、“211工程”&#xff0c;九校联盟 、…

数据结构与算法之经典排序算法

一、简单排序 在我们的程序中&#xff0c;排序是非常常见的一种需求&#xff0c;提供一些数据元素&#xff0c;把这些数据元素按照一定的规则进行排序。比如查询一些订单按照订单的日期进行排序&#xff0c;再比如查询一些商品&#xff0c;按照商品的价格进行排序等等。所以&a…

ServiceNow 研究:通过RAG减少结构化输出中的幻觉

论文地址&#xff1a;https://arxiv.org/pdf/2404.08189 原文地址&#xff1a;rag-hallucination-structure-research-by-servicenow 在灾难性遗忘和模型漂移中&#xff0c;幻觉仍然是一个挑战。 2024 年 4 月 18 日 灾难性遗忘&#xff1a; 这是在序列学习或连续学习环境中出现…

Costas-Barker序列模糊函数仿真

文章目录 前言一、Costas 序列二、Barker 码三、Costas-Barker 序列模糊函数仿真1、MATLAB 核心代码2、仿真结果①、Costas-Barker 模糊函数图②、Costas-Barker 距离模糊函数图③、Costas-Barker 速度模糊函数图 四、资源自取 前言 Costas 码是一种用于载波同步的频率调制序列…

20232810 2023-2024-2 《网络攻防实践》实验七

一、实践内容 &#xff08;1&#xff09;使用Metasploit进行Linux远程渗透攻击 任务&#xff1a;使用Metasploit渗透测试软件&#xff0c;攻击Linux靶机上的Samba服务Usermap_script安全漏洞&#xff0c;获取目标Linux靶机的主机访问权限。实践步骤如下&#xff1a; ①启动Met…

字节跳动发起AI战争 寻找下一个TikTok

现如今在字节跳动&#xff0c;已近乎隐退的张一鸣&#xff0c;只重点关注两件事&#xff1a;其一&#xff0c;是风暴中的TikTok&#xff1b;其二&#xff0c;就是字节跳动正在全力追赶的AI战略业务。 提及字节的AI战略远望,多个接近字节的人士均认为,以Flow部门出品最为“正统…

缩小COCO数据集

在运行YOLOS模型的过程中&#xff0c;需要使用到COCO2017这个数据集&#xff0c;但从实验运行来看&#xff0c;其所需时间无疑是相当漫长&#xff0c;预计可能需要近几十天才能完成&#xff0c;因此便考虑缩小COCO数据集大小&#xff0c;即尽可能在遵循其分布的情况下&#xff…

Unity开发一个FPS游戏之四

在前面的系列中&#xff0c;我已介绍了如何实现一个基本的FPS游戏&#xff0c;这里将继续进行完善&#xff0c;主要是增加更换武器以及更多动作动画的功能。 之前我是采用了网上一个免费的3D模型来构建角色&#xff0c;这个模型自带了一把AR自动步枪&#xff0c;并且自带了一些…

Unity开发微信小游戏(2)分享

目录 1.概述 2.代码 3.示例 4.个人作品 1.概述 这里我们能做有两件事&#xff1a; 1&#xff09;主动发起分享 2&#xff09;监听右上角分享&#xff08;...按钮&#xff0c;发朋友圈也在这里&#xff09; API&#xff1a;官方文档 2.代码 1&#xff09;主动发起分享&…

DHCPv4_CLIENT_ALLOCATING_01: 在其本地物理子网上广播DHCPDISCOVER消息

测试目的&#xff1a; 确保客户端能够在其本地物理子网上广播DHCPDISCOVER消息。 描述&#xff1a; 该测试用例旨在验证DHCP客户端是否能够正确地在其本地物理子网上广播DHCPDISCOVER消息&#xff0c;以便进行IP地址的自动分配。 测试拓扑&#xff1a; 测试步骤&#xff1a…

生产看板:最直观的车间管理方式之一,是马是马户牵出来溜溜。

可视化生产看板在组织工业生产中扮演着重要的角色&#xff0c;它可以提供实时的信息和可视化的数据&#xff0c;帮助团队和管理层更好地监控和管理生产过程。 以下是可视化生产看板在组织工业生产中的作用&#xff1a; 实时监控&#xff1a;可视化生产看板可以显示实时的生产数…