当前位置: 首页 > article >正文

Linux进程替换与自定义shell详解

引言

进程替换和shell编程是Linux系统中极其重要的概念,它们不仅是系统内部工作的基础机制,也是系统管理员和开发者必备的技能。本文将深入探讨Linux中的进程替换原理、系统调用实现以及如何创建自定义shell,帮助你全面理解这些重要概念并掌握相关技术。

目录

1. [进程替换基础](#进程替换基础)

2. [进程创建与替换系统调用](#进程创建与替换系统调用)

3. [进程控制与状态管理](#进程控制与状态管理)

4. [Shell原理与设计](#Shell原理与设计)

5. [自定义Shell实现](#自定义Shell实现)

6. [进程间通信与管道](#进程间通信与管道)

7. [信号处理与作业控制](#信号处理与作业控制)

8. [环境变量与执行环境](#环境变量与执行环境)

9. [实用Shell技巧与调试](#实用Shell技巧与调试)

10. [总结与实践建议](#总结与实践建议)

1.进程替换基础

进程替换是指一个进程通过特定的系统调用,将自己的内存空间、代码段和数据段替换为另一个程序的过程。

进程模型概述

- 进程是程序的一次执行实例

- 每个进程拥有独立的地址空间、资源和状态

- Linux使用轻量级进程模型,进程创建和调度高效

进程状态转换

- 就绪态:等待CPU资源执行

- 运行态:正在CPU上执行

- 阻塞态:等待某个事件(如I/O操作)完成

- 终止态:进程执行完毕或异常退出

进程替换的作用

- 允许在不创建新进程的情况下执行新程序

- 保留原进程的PID、打开文件和权限等

- 实现命令解释器(shell)的核心功能

- 优化系统资源使用,减少进程创建开销

2.进程创建与替换系统调用

Linux提供了一系列系统调用,用于创建、替换和控制进程。

fork()系统调用

- 创建当前进程的副本(子进程)

- 父子进程具有相同的代码和数据(写时复制)

- 返回值区分父子进程:父进程获得子进程PID,子进程获得0

- 完全复制父进程的地址空间和资源

pid_t child_pid = fork();if (child_pid == 0) {// 子进程代码printf("我是子进程,PID: %d\n", getpid());} else if (child_pid > 0) {// 父进程代码printf("我是父进程,子进程PID: %d\n", child_pid);} else {// 错误处理perror("fork failed");}

exec系列系统调用

- 用新的程序替换当前进程的内存映像

- 进程ID保持不变,但运行的程序完全替换

- 不会自动返回到调用程序,除非执行失败

主要的exec函数变体:

- execl():使用可变参数列表指定命令行参数

- execv():使用参数数组

- execle():可以指定环境变量

- execve():内核级系统调用,其他exec函数都基于它实现

- execlp()/execvp():会在PATH中查找可执行文件

// 使用execl替换进程execl("/bin/ls", "ls", "-l", NULL);// 如果执行到这里,说明exec调用失败perror("execl failed");

wait()/waitpid()系统调用

- 等待子进程终止

- 回收子进程资源,防止僵尸进程

- 获取子进程退出状态

int status;pid_t pid = wait(&status);if (WIFEXITED(status)) {printf("子进程 %d 正常退出,退出码: %d\n", pid, WEXITSTATUS(status));}

system()函数

- 结合fork()、exec()和wait()的高级封装

- 创建shell执行命令并等待完成

- 简单但不够灵活,不适用于复杂情景

int ret = system("ls -l /tmp");printf("命令执行结果: %d\n", ret);

3.进程控制与状态管理

父子进程关系

- 子进程继承父进程的大部分属性

- 不继承的属性:进程ID、父进程ID、内存锁等

- 文件描述符可选择性地继承(通过close-on-exec标志控制)

进程组与会话

- 进程组:相关进程的集合,共享一个进程组ID

- 会话:一个或多个进程组的集合,通常对应一个终端会话

- 前台进程组:可接收终端输入的进程组

- 后台进程组:在终端中被挂起的进程组

孤儿进程与僵尸进程

- 孤儿进程:父进程先于子进程结束,子进程被init进程(PID 1)收养

- 僵尸进程:已终止但未被父进程回收的进程,仅存留在进程表中

- 大量僵尸进程会耗尽系统资源,应当避免

进程资源限制

- 使用ulimit命令或setrlimit()系统调用设置

- 可限制进程的文件大小、CPU时间、内存使用等

- 防止单个进程消耗过多系统资源

struct rlimit limit;limit.rlim_cur = 1024 * 1024; // 1MBlimit.rlim_max = 2 * 1024 * 1024; // 2MBsetrlimit(RLIMIT_DATA, &limit); // 限制数据段大小

4.Shell原理与设计

Shell的核心功能

- 命令解析与执行

- 环境变量管理

- 作业控制

- 脚本执行

- 提供用户界面

Shell执行流程

1. 读取命令(从终端或脚本)

2. 解析命令(分词、展开变量等)

3. 查找命令路径(内置命令或外部命令)

4. 创建进程执行命令

5. 等待命令完成

6. 返回提示符等待下一个命令

常见Shell类型

- Bourne Shell (sh):最初的Unix shell

- Bash (Bourne-Again Shell):Linux系统默认shell

- C Shell (csh):语法类似C语言

- Z Shell (zsh):功能强大的交互式shell

- Fish:用户友好的交互式shell

Shell内置命令

- 不需要创建新进程执行的命令(如cd、pwd、export等)

- 需要修改shell自身状态的命令

- 实现为shell程序代码的一部分,而非外部可执行文件

5.自定义Shell实现

基本Shell框架

- 命令读取循环(REPL - Read, Evaluate, Print, Loop)

- 命令解析器

- 命令执行器

- 内置命令处理器

// 一个简单shell的主循环void run_shell() {char line[1024];char *args[64];while (1) {printf("myshell> ");if (!fgets(line, sizeof(line), stdin)) break;if (parse_line(line, args) == 0) continue; // 空命令if (handle_builtin(args)) continue; // 处理内置命令execute_command(args); // 执行外部命令}}

命令解析实现

- 将输入字符串分割为命令和参数

- 处理引号、转义字符等特殊情况

- 变量替换和通配符展开

int parse_line(char *line, char **args) {int i = 0;char *token = strtok(line, " \t\n");while (token != NULL && i < 63) {args[i++] = token;token = strtok(NULL, " \t\n");}args[i] = NULL; // 参数列表以NULL结尾return i; // 返回参数数量}

命令执行实现

- 使用fork()创建子进程

- 子进程使用exec系列函数执行命令

- 父进程使用wait/waitpid等待子进程完成

void execute_command(char **args) {pid_t pid = fork();if (pid < 0) {perror("fork failed");} else if (pid == 0) {// 子进程执行命令execvp(args[0], args);perror("execvp failed"); // 如果执行到这里,说明exec失败exit(1);} else {// 父进程等待子进程完成int status;waitpid(pid, &status, 0);}}

内置命令处理

- 如cd、exit、help等需要由shell直接处理的命令

- 通常通过函数表或switch语句实现

int handle_builtin(char **args) {if (strcmp(args[0], "cd") == 0) {if (args[1] == NULL) {// 无参数,切换到主目录chdir(getenv("HOME"));} else {if (chdir(args[1]) != 0) {perror("cd failed");}}return 1; // 已处理内置命令}if (strcmp(args[0], "exit") == 0) {exit(0);}return 0; // 不是内置命令}

6.进程间通信与管道

管道原理

- 管道是进程间通信的基本机制

- 单向数据流,一端写入,另一端读取

- 在shell中用"|"符号连接命令

管道创建与使用

- pipe()系统调用创建匿名管道

- 管道有读端和写端两个文件描述符

- 通常配合fork()使用,实现父子进程通信

int pipefd[2];pipe(pipefd); // 创建管道pid_t pid = fork();if (pid == 0) {// 子进程:关闭读端,将标准输出重定向到管道写端close(pipefd[0]);dup2(pipefd[1], STDOUT_FILENO);close(pipefd[1]);execlp("ls", "ls", "-l", NULL); // 执行第一个命令} else {// 父进程:关闭写端,将标准输入重定向到管道读端close(pipefd[1]);dup2(pipefd[0], STDIN_FILENO);close(pipefd[0]);execlp("grep", "grep", "^d", NULL); // 执行第二个命令}

重定向实现

- 使用dup/dup2系统调用重定向文件描述符

- 实现输入重定向(<)、输出重定向(>)、追加重定向(>>)

// 输出重定向实现void redirect_output(char *filename) {int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);if (fd < 0) {perror("open failed");return;}dup2(fd, STDOUT_FILENO); // 将标准输出重定向到文件close(fd);}

实现多重管道

- 需要创建多个管道并合理设置文件描述符

- 使用多个子进程,每个子进程执行一个命令

- 需要仔细管理进程关系和文件描述符继承

7.信号处理与作业控制

信号基础

- 信号是进程间通信的一种简单方式

- 用于通知进程发生了某个事件

- 每个信号有默认处理方式,进程可以自定义处理

常见信号

- SIGINT (Ctrl+C):中断进程

- SIGTERM:终止进程

- SIGKILL:强制终止进程(不可捕获)

- SIGCHLD:子进程状态变化

- SIGSEGV:段错误

- SIGSTOP/SIGTSTP (Ctrl+Z):停止进程

- SIGCONT:继续执行已停止进程

信号处理设置

- 使用signal()或更现代的sigaction()设置信号处理函数

- 可以忽略、使用默认处理或自定义处理函数

void handle_sigint(int sig) {printf("\n捕获到SIGINT信号,但不退出\n");}// 设置SIGINT信号处理函数signal(SIGINT, handle_sigint);

作业控制

- 允许用户在一个终端中管理多个进程

- 包括前台/后台运行、挂起/恢复进程等

- 使用进程组和终端控制实现

作业控制命令

- jobs:列出当前shell的作业

- fg:将作业带到前台

- bg:在后台继续执行作业

- Ctrl+Z:暂停前台作业

- 命令后加&:在后台启动命令

Shell中实现作业控制

- 维护作业表,记录进程组信息

- 适当处理SIGCHLD信号,更新作业状态

- 实现fg/bg等内置命令

// 在shell中实现后台执行int is_background = 0;// 检查命令行是否以&结尾if (args[arg_count-1][0] == '&') {is_background = 1;args[arg_count-1] = NULL; // 移除&符号}pid_t pid = fork();if (pid == 0) {// 子进程执行命令execvp(args[0], args);perror("execvp failed");exit(1);} else {// 父进程if (!is_background) {// 前台执行,等待子进程完成waitpid(pid, &status, 0);} else {// 后台执行,不等待printf("[%d] %s &\n", pid, args[0]);}}

8.环境变量与执行环境

环境变量基础

- 环境变量是进程的全局设置

- 子进程继承父进程的环境变量

- 用于配置程序行为和提供系统信息

重要环境变量

- PATH:可执行文件搜索路径

- HOME:用户主目录

- USER/LOGNAME:当前用户名

- SHELL:当前shell路径

- PWD:当前工作目录

- LANG/LC_*:语言和区域设置

- LD_LIBRARY_PATH:动态库搜索路径

环境变量操作

- getenv():获取环境变量值

- setenv()/putenv():设置环境变量

- unsetenv():删除环境变量

// 获取环境变量char *path = getenv("PATH");if (path) {printf("当前PATH: %s\n", path);}// 设置环境变量setenv("MY_VAR", "my_value", 1); // 1表示覆盖已有值// 删除环境变量unsetenv("MY_VAR");

Shell中实现环境变量

- 解析和替换命令中的环境变量引用($VAR)

- 实现export内置命令设置环境变量

- 管理子进程的环境变量传递

// 实现export命令if (strcmp(args[0], "export") == 0) {if (args[1]) {char *name = strtok(args[1], "=");char *value = strtok(NULL, "");if (name && value) {setenv(name, value, 1);}}return 1; // 命令已处理}

执行环境查找

- 如何在PATH中查找可执行文件

- 实现类似execvp()的功能

// 在PATH中查找可执行文件char *find_executable(char *cmd) {static char path[1024];char *PATH = getenv("PATH");if (!PATH) return NULL;char *path_copy = strdup(PATH);char *dir = strtok(path_copy, ":");while (dir) {snprintf(path, sizeof(path), "%s/%s", dir, cmd);if (access(path, X_OK) == 0) {free(path_copy);return path;}dir = strtok(NULL, ":");}free(path_copy);return NULL;}

9.实用Shell技巧与调试

调试自定义Shell

- 使用打印语句跟踪程序执行

- 检查返回值和错误信息

- 隔离测试各个功能模块

- 使用strace追踪系统调用

# 使用strace查看shell执行过程中的系统调用strace -f ./myshell

错误处理技巧

- 始终检查系统调用返回值

- 使用perror()或strerror()获取详细错误信息

- 实现错误日志功能

pid_t pid = fork();if (pid < 0) {perror("fork failed");// 错误处理}

功能扩展方向

- 命令历史记录

- 命令补全

- 别名支持

- 脚本解析器

- 更复杂的作业控制

// 实现命令历史#define HISTORY_MAX 100char *history[HISTORY_MAX];int history_count = 0;void add_to_history(char *cmd) {if (history_count < HISTORY_MAX) {history[history_count++] = strdup(cmd);} else {free(history[0]);for (int i = 0; i < HISTORY_MAX - 1; i++) {history[i] = history[i+1];}history[HISTORY_MAX-1] = strdup(cmd);}}

安全注意事项

- 避免缓冲区溢出

- 正确处理用户输入

- 检查文件和命令权限

- 防止命令注入攻击

10.总结与实践建议

核心概念回顾

- 进程替换是shell和其他程序的基础机制

- fork/exec模型支撑了Unix/Linux的设计理念

- 自定义shell是理解Linux系统工作原理的绝佳练习

实践项目建议

- 从最小可行的shell开始,逐步添加功能

- 先实现基本命令执行,再添加管道和重定向

- 后续可扩展更高级功能(作业控制、历史记录等)

- 尝试实现一些独特功能,如内置脚本语言

学习资源推荐

- 《Advanced Programming in the UNIX Environment》by W. Richard Stevens

- 《The Linux Programming Interface》by Michael Kerrisk

- Linux系统调用手册(man pages)

- 开源shell项目源码(bash、zsh等)

常用命令与工具

- ps:查看进程状态

- top/htop:监控系统进程

- strace:跟踪系统调用

- ltrace:跟踪库调用

- gdb:调试工具

- valgrind:内存检查工具

进程替换和自定义shell是理解Linux系统工作机制的关键。通过深入学习这些概念和实现自己的shell,能更好地帮你理解Linux系统的内部工作原理,还能提升系统编程能力和问题解决能力。希望本文能够帮助你扎实掌握这些重要概念,为你的Linux系统编程之旅提供有力支持。

相关文章:

Linux进程替换与自定义shell详解

引言 进程替换和shell编程是Linux系统中极其重要的概念&#xff0c;它们不仅是系统内部工作的基础机制&#xff0c;也是系统管理员和开发者必备的技能。本文将深入探讨Linux中的进程替换原理、系统调用实现以及如何创建自定义shell&#xff0c;帮助你全面理解这些重要概念并掌…...

【数据结构_4下篇】链表

一、链表的概念 链表&#xff0c;不要求在连续的内存空间&#xff0c;链表是一个离散的结构。 链表的元素和元素之间&#xff0c;内存是不连续的&#xff0c;而且这些元素的空间之间也没有什么规律&#xff1a; 1.顺序上没有规律 2.内存空间上也没有规律 *如何知道链表中包…...

Mybatis的简单介绍

文章目录 MyBatis 简介 1. MyBatis 核心特点2. MyBatis 核心组件3. MyBatis 基本使用示例(1) 依赖引入&#xff08;Maven&#xff09;(2) 定义 Mapper 接口(3) 定义实体类(4) 在 Service 层调用 4. MyBatis 与 JPA/Hibernate 对比 MyBatis 简介 MyBatis 是一款优秀的 持久层框…...

JavaScript 性能优化实战:深入探讨 JavaScript 性能瓶颈,分享优化技巧与最佳实践

在当今 Web 应用日益复杂的时代&#xff0c;JavaScript 性能对于用户体验起着决定性作用。缓慢的脚本执行会导致页面加载延迟、交互卡顿&#xff0c;严重影响用户留存率。本文将深入剖析 JavaScript 性能瓶颈&#xff0c;并分享一系列实用的优化技巧与最佳实践&#xff0c;助你…...

1g内存电脑sqlite能支持多少并发

1. SQLite的并发机制 写操作&#xff1a;默认使用串行锁&#xff0c;同一时间仅允许一个写操作&#xff08;其他写/读需等待&#xff09;。读操作&#xff1a;支持多并发读取&#xff0c;但受内存、磁盘I/O和配置限制。 2. 关键限制因素 &#xff08;1&#xff09;内存资源 …...

jetpack之jetpack的概括和其中组件的简单使用

注意⚠&#xff1a;此篇文章由deepseek大力支持&#xff01;&#xff01;&#xff01;(╹ڡ╹ ) 主要是对不知道学什么&#xff0c;对各个组件一头雾水的jetpack新手准备的文章 不知道jetpack学什么&#xff0c;就看这篇文章&#xff01;&#xff01; 1. DataBinding&#xff…...

音视频 五 看书的笔记 MediaCodec

MediaCodec 用于访问底层媒体编解码器框架&#xff0c;编解码组件。通常与MediaExtractor(解封装,例如Mp4文件分解成 video和audio)、MediaSync、MediaMuxer(封装 例如音视频合成Mp4文件)、MediaCrypto、Image(cameraX 回调的ImageReader对象可以获取到Image帧图像,可转换成YU…...

物联网|无人自助台球厅源码|哪些框架支持多设备连接?

在无人自助台球厅的智能化管理中&#xff0c;物联网&#xff08;IoT&#xff09;技术是核心支撑。如何实现不同设备&#xff08;如智能门锁、环境传感器、支付终端、灯光控制系统等&#xff09;的高效连接与协同工作&#xff0c;是系统开发的关键挑战。本文将带大家探讨支持多设…...

Python中NumPy的统计运算

在数据分析和科学计算领域&#xff0c;Python凭借其丰富的库生态系统成为首选工具之一&#xff0c;而NumPy作为Python数值计算的核心库&#xff0c;凭借其高效的数组操作和强大的统计运算功能&#xff0c;广泛应用于机器学习、信号处理、统计分析等场景。本文将系统介绍NumPy在…...

uniapp实现H5页面麦克风权限获取与录音功能

1.权限配置 在uni-app开发H5页面时&#xff0c;需要在manifest.json文件中添加录音权限的配置。具体如下&#xff1a; {"h5": {"permissions": {"scope.record": {"desc": "请授权使用录音功能"}}} }这段配置代码是用于向…...

两个树莓派如何通过wifi direct传输视频并显示

这里写自定义目录标题 在两台设备上安装必要软件Wi-Fi Direct接收端IP&#xff08;自动发现或静态设置&#xff09;设置摄像头参数显示初始化网络设置 系统架构概述 发送端树莓派&#xff1a;捕获视频&#xff08;摄像头或视频文件&#xff09;→ 编码 → 通过Wi-Fi Direct传输…...

ubuntu 系统安装Mysql

安装 mysql sudo apt update sudo apt install mysql-server 启动服务 sudo systemctl start mysql 设置为开机自启 sudo systemctl enable mysql 查看服务状态 &#xff08;看到类似“active (running)”的状态信息代表成功&#xff09; sudo systemctl status mysql …...

selenium快速入门

一、操作浏览器 from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By# 设置选项 q1 Options() q1.add_argument("--no-sandbo…...

Redis:线程模型

单线程模型 Redis 自诞生以来&#xff0c;一直以高性能著称。很多人好奇&#xff0c;Redis 为什么早期采用单线程模型&#xff0c;它真的比多线程还快吗&#xff1f; 其实&#xff0c;Redis 的“快”并不在于并发线程&#xff0c;而在于其整体架构设计极致简单高效&#xff0c;…...

Transformer模型解析与实例:搭建一个自己的预测语言模型

目录 1. 前言 2. Transformer 的核心结构 2.1 编码器&#xff08;Encoder&#xff09; 2.2 解码器&#xff08;Decoder&#xff09; 2.3 位置编码&#xff08;Positional Encoding&#xff09; 3. 使用 PyTorch 构建 Transformer 3.1 导入所需的模块&#xff1a; 3.2 定…...

Java常用安全编码的规范整理及工具

对Java安全编码的主要规范及要求的一些整理&#xff1a; 一、输入验证与数据校验 ‌外部输入校验‌ 对所有外部输入&#xff08;如用户输入、文件、网络数据&#xff09;进行合法性校验&#xff0c;采用白名单机制限制输入内容范围 ‌。校验前对输入数据做归一化处理&#xff…...

重返JAVA之路——面向对象

目录 面向对象 1.什么是面向对象&#xff1f; 2.面向对象的特点有哪些&#xff1f; 3.什么是对象&#xff1f; 4.什么是类&#xff1f; 5.什么是构造方法? 6.构造方法的特性有哪些&#xff1f; 封装 1.什么是封装&#xff1f; 2.封装有哪些特点&#xff1f; 数据隐…...

Java设计模式全解析(共 23 种)

一、设计模式全解&#xff1a; Java 中的设计模式是为了解决在软件开发中常见问题的一些“最佳实践”总结。设计模式分为三大类&#xff0c;共 23 种经典模式&#xff1a; 1. 创建型模式&#xff08;5 种&#xff09; 用于对象的创建&#xff0c;解决对象实例化过程中的问题。…...

Python10天突击--Day 2: 实现观察者模式

以下是 Python 实现观察者模式的完整方案&#xff0c;包含同步/异步支持、类型注解、线程安全等特性&#xff1a; 1. 经典观察者模式实现 from abc import ABC, abstractmethod from typing import List, Anyclass Observer(ABC):"""观察者抽象基类""…...

springboot框架集成websocket依赖实现物联网设备、前端网页实时通信!

需求&#xff1a; 最近在对接一个物联网里设备&#xff0c;他的通信方式是 websocket 。所以我需要在 springboot框架中集成websocket 依赖&#xff0c;从而实现与设备实时通信&#xff01; 框架&#xff1a;springboot2.7 java版本&#xff1a;java8 好了&#xff0c;还是直接…...

【玩泰山派】5、点灯,驱动led-(2)ubuntu18.04 升级python3.6到python3.7,安装pip3

文章目录 前言升级python3.71、安装 software-properties-common 包2、添加 deadsnakes PPA 源3、安装 Python 3.71. 安装 Python 3.72. 安装 Python 3.7 的开发包和虚拟环境支持&#xff08;可选但推荐&#xff09;3. 设置 Python 3.7 为默认版本4. 验证 Python 版本注意事项 …...

ES6学习03-字符串扩展(unicode、for...of、字符串模板)和新方法()

一、字符串扩展 1. eg: 2.for...of eg: 3. eg: 二。字符串新增方法 1. 2. 3. 4. 5....

c++中的this

在 C 中&#xff0c;this 是一个指向当前对象实例的指针&#xff0c;它隐式地存在于类的非静态成员函数中。以下是 this 的详细用法和常见场景&#xff1a; 1. 常见场景 明确成员归属&#xff1a;当成员变量与局部变量同名时&#xff0c;用 this-> 显式访问成员。当成员变量…...

目前状况下,计算机和人工智能是什么关系?

目录 一、计算机和人工智能的关系 &#xff08;一&#xff09;从学科发展角度看 计算机是基础 人工智能是计算机的延伸和拓展 &#xff08;二&#xff09;从技术应用角度看 二、计算机系学生对人工智能的了解程度 &#xff08;一&#xff09;基础层面的了解 必备知识 …...

Flutter 2025 Roadmap

2025 这个路线图是有抱负的。它主要代表了我们这些在谷歌工作的人收集的内容。到目前为止&#xff0c;非Google贡献者的数量超过了谷歌雇佣的贡献者&#xff0c;所以这并不是一个详尽的列表&#xff0c;列出了我们希望今年Flutter能够出现的所有令人兴奋的新事物&#xff01;在…...

[数据结构]排序 --2

目录 8、快速排序 8.1、Hoare版 8.2、挖坑法 8.3、前后指针法 9、快速排序优化 9.1、三数取中法 9.2、采用插入排序 10、快速排序非递归 11、归并排序 12、归并排序非递归 13、排序类算法总结 14、计数排序 15、其他排序 15.1、基数排序 15.2、桶排序 8、快速排…...

第16届蓝桥杯c++省赛c组个人题解

偷偷吐槽&#xff1a; c组没人写题解吗&#xff0c;找不到题解啊 P12162 [蓝桥杯 2025 省 C/研究生组] 数位倍数 题目背景 本站蓝桥杯 2025 省赛测试数据均为洛谷自造&#xff0c;与官方数据可能存在差异&#xff0c;仅供学习参考。 题目描述 请问在 1 至 202504&#xff…...

记一次InternVL3- 2B 8B的部署测验日志

1、模型下载魔搭社区 2、运行环境&#xff1a; 1、硬件 RTX 3090*1 云主机[普通性能] 8核15G 200G 免费 32 Mbps付费68Mbps ubuntu22.04 cuda12.4 2、软件&#xff1a; flash_attn&#xff08;好像不用装 忘记了&#xff09; numpy Pillow10.3.0 Requests2.31.0 transfo…...

Android PowerManager功能接口详解

PowerManager 是 Android 系统中用于管理设备电源状态的核心服务&#xff0c;开发者可以通过它控制设备的唤醒、休眠、屏幕亮灭等行为。以下是对 PowerManager 核心功能接口的详细说明&#xff0c;包含使用场景、注意事项和代码示例。 1. 获取 PowerManager 实例 通过 Context…...

使用SSH解决在IDEA中Push出现403的问题

错误截图&#xff1a; 控制台日志&#xff1a; 12:15:34.649: [xxx] git -c core.quotepathfalse -c log.showSignaturefalse push --progress --porcelain master refs/heads/master:master fatal: unable to access https://github.com/xxx.git/: The requested URL return…...