【linux学习】linux系统调用编程

目录

一、任务、进程和线程

1.1任务

1.2进程

1.3线程

1.4线程和进程的关系

1.5 在linux系统下进程操作

二、Linux虚拟内存管理与stm32的真实物理内存区别

2.1 Linux虚拟内存管理

2.2 STM32的真实物理内存映射

2.3区别

三、 Linux系统调用函数 fork()、wait()、exec()

3.1 fork

3.2 wait

3.3 exec

四、在树莓派中,创建组员账号,完成练习

4.1 用户创建和配置:

4.2登录自己的树莓派账号练习

五、总结

一、任务、进程和线程

1.1任务

多任务系统指可以同一时间内运行多个应用程序的系统,每个应用程序被称作一个任务。

任务是一个逻辑概念,指由一个软件完成的任务,或者是一系列共同达到某一目的的操作。

任务的特点:

  • 在实时操作系统(RTOS)中,任务通常是独立的、无法返回的函数。

  • 任务的调度和管理依赖于任务控制块(TCB),它记录任务的状态、优先级等信息

1.2进程

进程是指一个具有独立功能的程序在某个数据集上的一次动态执行过程,它是系统进行资源分配和调度的最小单元。

通俗来说,进程就是程序的一次执行过程,程序是静态的,它作为系统中的一种资源是永远存在的。而进程是动态的,它是动态的产生,变化和消亡的,拥有其自己的生命周期。

举个例子:同时挂三个 QQ 号,它们就对应三个 QQ 进程,退出一个就会杀死一个对应的进程。但是,就算你把这三个 QQ 全都退出了,QQ 这个程序死亡了吗?显然没有。

进程不仅包含正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说 CPU、内存、网络资源等。很多小伙伴在回答进程的概念的时候,往往只会说它是一个运行的实体,而会忽略掉进程所占据的资源。比如说,同样一个程序,同一时刻被两次运行了,那么他们就是两个独立的进程。

  • 特点

    • 每个进程拥有独立的内存空间,包括代码段、数据段、堆和栈。

    • 进程之间相互隔离,一个进程的崩溃通常不会影响其他进程。

    • 进程是资源分配的最小单位,但不是CPU调度的最小单位。

1.3线程

线程是进程内独立的一条运行路线,是处理器调度的最小单元,也可以称为轻量级进程。线程——程序执行的最小单位。

  • 特点

    • 一个进程可以包含多个线程,线程共享所属进程的内存空间和资源。

    • 线程的切换开销较小,因为它只需要切换寄存器状态和栈信息。

    • 线程是程序执行的最小单位,多个线程可以并发执行。

1.4线程和进程的关系

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;

(2)资源分配给进程,同一进程内的所有线程共享该进程的所有资源;

(3)线程在执行过程中需要协作同步。不同进程中的线程之间要利用消息通信的方法实现同步;

(4)处理机分配给线程,即真正在处理机上运行的是线程;

(5)线程是进程的一个执行单元,也是进程内的可调用实体。

1.5 在linux系统下进程操作

操作:1) 用 ps -a 命令查看系统中各进程的编号pid ; 2) 用kill 命令终止一个进程pid。

ps -a
  • ps 是“process status”的缩写,用于显示当前系统中运行的进程信息。

  • 选项-a:表示显示当前终端(TTY)中所有用户启动的进程。

我可以通过用一个sleep也来弄一个进程方便我们将他kill,如下操作:

sleep 50&
  • sleep命令用于让当前进程暂停指定的时间(单位为秒)。这里让进程暂停50秒。

  • &符号:将命令放到后台执行。这样,用户可以在命令执行的同时继续在终端中输入其他命令。

kill 1317303
  • 向PID为1317303sleep进程发送终止信号,使其停止运行。

  • 再次用ps -a查看是否终止进程。

二、Linux虚拟内存管理与stm32的真实物理内存区别

2.1 Linux虚拟内存管理

Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间。

在Linux中,虚拟内存管理的基本理念是“每个程序认为它拥有独立的内存”。虚拟内存通过以下方式实现:

  • 虚拟地址与物理地址:Linux使用虚拟地址来访问内存,这些虚拟地址通过内存管理单元(MMU)映射到物理地址。虚拟地址空间被分为用户空间和内核空间,每个进程都有自己的虚拟地址空间。

  • 分页机制:Linux将内存划分为固定大小的页面(通常是4KB),并根据需要将页面从磁盘交换到物理内存中。这种机制允许系统运行比物理内存更大的程序。

  • 内存保护与隔离:虚拟内存机制提供了内存保护,确保一个进程无法访问另一个进程的内存。内核空间的内存对用户空间进程不可见。

  • 动态内存分配:Linux内核使用懒惰分配(Lazy Allocation)技术,只有当进程实际访问分配的内存时,才会分配物理内存。

  • 交换空间(Swap):当物理内存不足时,Linux会将不常用的页面交换到磁盘上的交换空间。

用户感知:程序操作的是虚拟地址,物理地址对用户透明;通过malloc()分配内存时,实际可能仅在虚拟地址空间预留范围(brkmmap),直到访问时才触发缺页异常分配物理页。

2.2 STM32的真实物理内存映射

STM32是一种嵌入式微控制器,其内存管理相对简单,主要基于物理内存的直接访问。

  • 物理内存映射:STM32使用物理内存映射,将内存和外设分配到不同的地址范围。这种映射是固定的,没有虚拟地址的概念。

  • 内存保护缺失:STM32通常没有内存保护机制,用户空间代码可以直接访问内核空间的内存。这可能导致一个程序的错误操作影响整个系统。

  • 简单的内存管理:STM32的内存管理主要依赖于静态分配,程序在启动时分配所需的内存,并在运行时直接访问这些内存。

  • 无交换空间:STM32没有交换空间的概念,所有内存操作都直接在物理内存上进行。

开发模式:程序员需手动管理内存,避免溢出。外设操作通过指针直接访问寄存器,比如常见的:

// 直接操作STM32的GPIO寄存器
#define GPIOA_ODR (*(volatile uint32_t*)0x40020014)
GPIOA_ODR |= 0x00000001; // 设置PA0引脚为高电平

2.3区别

特性Linux虚拟内存STM32物理内存映射
地址空间虚拟地址(通过MMU转换)物理地址(直接访问)
硬件依赖必须支持MMU(如ARM Cortex-A系列)无MMU(如ARM Cortex-M系列)
内存隔离进程间隔离,防止非法访问无隔离,程序可直接修改任意内存/外设
动态分配支持按需分配和交换(malloc/mmap静态分配(链接脚本定义堆栈/全局变量)
访问权限控制通过页表实现读/写/执行权限无权限控制,依赖程序员自律
典型应用场景通用计算(多任务/复杂应用)实时嵌入式系统(确定性/低延迟)

三、 Linux系统调用函数 fork()、wait()、exec()

3.1 fork

在Linux 中创建一个新进程的唯一方法是使用fork()函数。fork()函数用于从已存在的一个进程中创建一个新的进程,新进程称为子进程,而原进程称为父进程。

  • 子进程特性

    • 子进程是父进程的复制品,继承父进程的地址空间,包括代码段、数据段、堆、栈等。

    • 子进程继承父进程的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录等。

    • 子进程拥有独立的进程号(PID)和资源使用信息。

  • 返回值

    • 在父进程中,fork()返回子进程的PID。

    • 在子进程中,fork()返回0。

 1.在练习文件夹下面利用nano创建函数fork_example.c文件(建议也可以参考下文中树莓派部分的fork函数,那个比较简洁)

#include <stdio.h>      // 标准输入输出库,用于printf等函数
#include <sys/types.h>  // 包含数据类型定义,如pid_t
#include <unistd.h>     // 包含fork、getpid、getppid等函数
#include <stdlib.h>     // 包含exit函数
#include <errno.h>      // 包含错误号定义
#include <sys/wait.h>   // 包含waitpid函数及相关宏int main() {pid_t pid;          // 定义一个pid_t类型的变量,用于存储fork返回的进程IDint ret = 1;        // 定义一个返回值变量,未在代码中使用int status;         // 定义一个变量,用于存储子进程退出状态pid = fork();       // 调用fork函数创建一个子进程if (pid == -1) {    // 如果fork返回-1,表示创建子进程失败printf("can't fork, error occured\n");  // 输出错误信息exit(EXIT_FAILURE);  // 退出程序,返回值为EXIT_FAILURE} else if (pid == 0) {  // 如果fork返回0,表示当前是子进程printf("child process, pid = %u\n", getpid());  // 输出子进程的PIDprintf("parent of child process, pid = %u\n", getppid());  // 输出子进程的父进程PIDchar *argv_list[] = {"ls", "-lart", "/home", NULL};  // 定义一个字符串数组,作为execv的参数// 调用execv替换当前进程映像为"ls"程序,并传递参数"-lart"和"/home"execv("ls", argv_list);  // 如果execv成功,控制权将转移到"ls"程序,不会返回到这里// 如果execv失败,返回-1,并继续执行下面的代码exit(0);  // 子进程退出} else {  // 如果fork返回一个正数,表示当前是父进程,返回值是子进程的PIDprintf("Parent of parent process, pid = %u\n", getppid());  // 输出父进程的父进程PIDprintf("parent process, pid = %u\n", getpid());  // 输出父进程的PID// 父进程调用waitpid等待子进程结束if (waitpid(pid, &status, 0) > 0) {  // 如果waitpid成功,返回子进程的PID// 检查子进程是否正常退出if (WIFEXITED(status) && !WEXITSTATUS(status))  // 如果子进程正常退出且返回值为0printf("program execution successful\n");else if (WIFEXITED(status) && WEXITSTATUS(status)) {  // 如果子进程正常退出但返回值非0if (WEXITSTATUS(status) == 127) {  // 如果返回值为127,表示execv失败printf("execv failed\n");} else  // 如果返回值非127,表示程序执行完成但返回了非零状态printf("program terminated normally, but returned a non-zero status\n");} else  // 如果子进程没有正常退出printf("program didn't terminate normally\n");} else {  // 如果waitpid失败printf("waitpid() failed\n");}exit(0);  // 父进程退出}return 0;  // 程序正常结束返回0
}

2.使用cmake编译fork_example.c在根目录下创建(想着用不同的方法来编译,掌握多种编译办法。也可以直接采用下文的gcc方式来编译)

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)  # 最低 CMake 版本要求
project(Fork_demo)                     # 项目名称# 添加可执行文件
add_executable(fork_demo fork_example.c)# 设置 C 标准(可选)
set(CMAKE_C_STANDARD 11)

3.构建目录生成文件

mkdir build && cd build

创建构建目录并生成 Makefile

cmake --build build
./build/fork_demo

运行即可,结果如图:

3.2 wait

功能:wait()函数用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接收到了一个指定的信号为止。如果该父进程没有子进程或者它的子进程已经结束,则wait()函数就会立即返回。

  • 作用

    • 确保父进程在子进程结束后再继续执行。

    • 防止子进程变成僵尸进程(zombie process)。

  • 返回值

    • 如果父进程没有子进程或者子进程已经结束,wait()会立即返回。

    • 返回值是子进程的PID。

 wait调用:

1.创建一个 fork_wait.c 文件,内容如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>  // 必须包含此头文件以使用 wait()int main() {pid_t pid = fork();if (pid < 0) {perror("fork 失败");return 1;} else if (pid == 0) {// 子进程执行任务printf("子进程 PID = %d\n", getpid());sleep(2);  // 模拟耗时操作printf("子进程结束\n");} else {// 父进程等待子进程结束printf("父进程 PID = %d,等待子进程 %d...\n", getpid(), pid);int status;wait(&status);  // 阻塞等待子进程结束printf("子进程退出状态: %d\n", WEXITSTATUS(status));}return 0;
}

同样的创建Makefile,

# 定义编译器和编译选项
CC = gcc
CFLAGS = -Wall -Wextra -std=c11# 定义目标可执行文件名和源文件
TARGET = fork_wait_demo
SRC = fork_wait.c# 默认目标
all: $(TARGET)# 编译规则
$(TARGET): $(SRC)$(CC) $(CFLAGS) -o $@ $^# 清理生成的文件
clean:rm -f $(TARGET)# 伪目标声明(避免与同名文件冲突)
.PHONY: all clean

编译运行效果:

make
./fork_wait_demo

结果演示:

3.3 exec

在Linux 中使用exec函数族主要有两种情况:
1.当进程认为自己不能再为系统和用户做出任何贡献时,他就可以发挥最后一点余热,调用任何一个exec,让自己以新的面貌重生;
2.如果一个进程想执行另一个程序,那么它就可以调用fork() 函数新建一个进程,然后调用exec 函数族中的任意一个函数,这样看起来就像通过执行应用程序而产生了一个新进程(这种情况非常普遍)。

exec调用:

可以先建一个文件夹哈(这样方便看到保存每个函数的那个记录,我上面两个函数忘记创建了,之后做实验最好一个实验一个文件夹,这样清爽一些)

mkdir exec && cd exec

1.创建 fork_exec.c 文件,参考代码:

#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程执行 ls -lexecl("/bin/ls", "ls", "-l", NULL);perror("exec failed"); // 若 exec 失败才会执行return 1;}return 0;
}

2.可以用gcc或者make或者cmake等方式来进行编译,我们这里就用常规简单一点的gcc(cmake和make的方式上面两个函数都有代码,修改一下名称就可以用啦)

gcc fork_exec.c -o fork_demo
./fork_demo

结果演示:

四、在树莓派中,创建组员账号,完成练习

4.1 用户创建和配置:

提前进入到主要的账号里边给各个用户加上相关的权限,操作如下(putty和Xterminal都可以)

1.使用adduser命令创建用户

sudo adduser zsc
  • 执行后会提示设置密码及用户信息(非必填项可直接回车跳过)

  • 默认自动生成同名主目录 /home/username

2.配置用户权限

1.将用户加入sudo组

sudo usermod -aG sudo zsc

2.加入常用硬件访问组

sudo usermod -aG adm,dialout,plugdev zsc

3. 验证用户权限

id zsc  # 查看用户所属组
groups zsc  # 列出用户所有附加组

4.2登录自己的树莓派账号练习

在Xterminal中利用ssh连接登陆上自己的树莓派账号。(连接过程和之前的博客步骤一样的,通过电脑移动热点查询物理地址,账号密码确认后即可登录)

我们还可以查看树莓派下面的其他用户

compgen -u

进入到自己的树莓派环境中

配置一下安装一下环境:

在树莓派Ubuntu系统中,默认可能未安装GCC,安装GCC及编译所需的工具链(如makeg++等)

sudo apt update
sudo apt install build-essential

1.创建并打开目录

mkdir test0404 && cd test0404

2.编写fork.c程序(选用gcc的方式来编译啦,这样快捷一些)

nano fork_gcc.c

简单的编写一个代码:

#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid < 0) {fprintf(stderr, "Fork失败\n");return 1;} else if (pid == 0) {printf("子进程ID: %d\n", getpid());} else {printf("父进程ID: %d,子进程ID: %d\n", getpid(), pid);}return 0;
}

3.编译并运行:

gcc fork_gcc.c -o fork_gcc//编译需要卡一会./fork_gcc

五、总结

通过本次让我收获很大很大,本文主要阐述了任务、进程、线程的定义,区别和联系,参考文献1中有更加直白易懂的说法(感兴趣的可以去看看),同时通过查阅资料阐述了虚拟机内存管理和STM32的物理内存中的一些差别,进一步了解虚拟机和存储的方式;最后也是本次最重要的实践环节,学习调用了fork、wait、exec函数,深入理解了每个函数在树莓派的中是如何使用的,同时每个函数的调用用了不同的编译方法(cmake、make、gcc)(个人建议设计内容少的函数可以直接用gcc是最方便的),加深了对Ubuntu的运用和理解,同时通过反反复复的敲代码和命令,对整体的编程水平还是上升了不少,感兴趣的朋友也可以自己试试手敲,收获会很大的。

树莓派的操作也越来越熟悉了,在XTerminal很好用,建议用这个,很不错。

本文中原理部分有些图片来源于网络,如有侵权请及时与我联系删除,本人才疏学浅,如有描述不准确或出错的地方还请海涵,感谢您的阅读!

参考文献:

https://zhuanlan.zhihu.com/p/391496775

https://zhuanlan.zhihu.com/p/403313422

Linux系统调用编程-CSDN博客

Linux Ubuntu 入门基本命令整理_linux ubuntu入门基本命令整理-CSDN博客

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

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

相关文章

react redux的学习,多个reducer

redux系列文章目录 第一章 简单学习redux,单个reducer 前言 前面我们学习到的是单reducer的使用&#xff1b;要知道redux是个很强大的状态存储库&#xff0c;可以支持多个reducer的使用。 combineReducers ‌combineReducers‌是Redux中的一个辅助函数&#xff0c;主要用于…

Oracle数据库数据编程SQL<3.5 PL/SQL 存储过程(Procedure)>

存储过程(Stored Procedure)是 Oracle 数据库中一组预编译的 PL/SQL 语句集合,存储在数据库中并可通过名称调用执行。它们是企业级数据库应用开发的核心组件。 目录 一、存储过程基础 1. 存储过程特点 2. 创建基本语法 3. 存储过程优点 4. 简单示例 二、没有参数的存储…

手撕AVL树

引入&#xff1a;为何要有AVL树&#xff0c;二次搜索树有什么不足&#xff1f; 二叉搜索树有其自身的缺陷&#xff0c;假如往树中插入的元素有序或者接近有序&#xff0c;二叉搜索树就会退化成单支树&#xff0c;时间复杂度会退化成O(N)&#xff0c;因此产生了AVL树&#xff0c…

《 C语言中的变长数组:灵活而强大的特性》

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言 &#x1f30d;文章目入 一、变长数组的定义二、变长数组的优势三、变长数组的使用示例示例1&#xff1a;动态输入数组大小示例2&#xff1a;变长数组在函数中的应用 四、变长数组的…

【微服务】基础概念

1.什么是微服务 微服务其实就是一种架构风格&#xff0c;他提倡我们在开发的时候&#xff0c;一个应用应该是一组小型服务而组成的&#xff0c;每一个服务都运行在自己的进程中&#xff0c;每一个小服务都通过HTTP的方式进行互通。他更加强调服务的彻底拆分。他并不是仅局限于…

Linux make与makefile 项目自动化构建工具

本文章将对make与makefile进行一些基础的讲解。 假设我们要建造一座房子&#xff0c;建造过程涉及很多步骤&#xff0c;比如打地基、砌墙、安装门窗、粉刷墙壁等。每个步骤都有先后顺序&#xff0c;并且有些步骤可能依赖于其他步骤的完成。比如&#xff0c;你必须先打好地基才…

如何判断多个点组成的3维面不是平的,如果不是平的,如何拆分成多个平面

判断和拆分三维非平面为多个平面 要判断多个三维点组成的面是否为平面&#xff0c;以及如何将非平面拆分为多个平面&#xff0c;可以按照以下步骤进行&#xff1a; 判断是否为平面 平面方程法&#xff1a; 选择三个不共线的点计算平面方程&#xff1a;Ax By Cz D 0检查其…

多layout 布局适配

安卓多布局文件适配方案操作流程 以下为通过多套布局文件适配不同屏幕尺寸/密度的详细步骤&#xff0c;结合主流适配策略及最佳实践总结&#xff1a; 一、‌创建多套布局资源目录‌ ‌按屏幕尺寸划分‌ 在 res 目录下创建以下文件夹&#xff08;根据设备特性自动匹配&#xff…

Java 大视界 -- Java 大数据在智能农业无人机植保作业路径规划与药效评估中的应用(165)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

美关税加征下,Odoo免费开源ERP如何助企业破局?

近期&#xff0c;美国特朗普政府推行的关税政策对全球供应链和进出口企业造成巨大冲击&#xff0c;尤其是依赖中美贸易的企业面临成本激增、利润压缩和合规风险。在此背景下&#xff0c;如何通过数字化转型优化管理效率、降低运营成本成为企业生存的关键。本文以免费开源ERP系统…

go游戏后端开发25:红中麻将规则介绍

一、游戏基础规则介绍 在开发红中麻将游戏之前&#xff0c;我们需要先了解其基础规则。红中麻将的牌面由 a、b、c、d 四种花色组成&#xff0c;其中 a、b、c 分别代表万、条、筒&#xff0c;每种花色都有 1 - 9 的九种牌&#xff0c;每种牌各有四张&#xff0c;总计 36 张 4 …

Unity:平滑输入(Input.GetAxis)

目录 1.为什么需要Input.GetAxis&#xff1f; 2. Input.GetAxis的基本功能 3. Input.GetAxis的工作原理 4. 常用参数和设置 5. 代码示例&#xff1a;用GetAxis控制角色移动 6. 与Input.GetAxisRaw的区别 7.如何优化GetAxis&#xff1f; 1.为什么需要Input.GetAxis&…

OpenCV:计算机视觉的强大开源库

文章目录 引言一、什么是OpenCV&#xff1f;1.OpenCV的核心特点 二、OpenCV的主要功能模块1. 核心功能&#xff08;Core Functionality&#xff09;2. 图像处理&#xff08;Image Processing&#xff09;3. 特征检测与描述&#xff08;Features2D&#xff09;4. 目标检测&#…

AI浪潮下的IT职业转型:医药流通行业传统IT顾问的深度思考

AI浪潮下的IT职业转型&#xff1a;医药流通行业传统IT顾问的深度思考 一、AI重构IT行业的技术逻辑与实践路径 1.1 医药流通领域的智能办公革命 在医药批发企业的日常运营中&#xff0c;传统IT工具正经历颠覆性变革。以订单处理系统为例&#xff0c;某医药集团引入AI智能客服…

Qt进阶开发:QFileSystemModel的使用

文章目录 一、QFileSystemModel的基本介绍二、QFileSystemModel的基本使用2.1 在 QTreeView 中使用2.2 在 QListView 中使用2.3 在 QTableView 中使用 三、QFileSystemModel的常用API3.1 设置根目录3.2 过滤文件3.2.1 仅显示文件3.2.2 只显示特定后缀的文件3.2.3 只显示目录 四…

KAPC的前世今生--(下)下RPCRT4!NMP_SyncSendRecv函数分析

第一部分&#xff1a;nt!KiDeliverApc函数调用nt!IopCompleteRequest函数后准备返回 1: kd> kv # ChildEBP RetAddr Args to Child 00 ba3eec18 80a3c83b 896e4e40 ba3eec64 ba3eec58 nt!IopCompleteRequest0x3a3 (FPO: [Non-Fpo]) (CONV: stdcall) [d:\srv…

深入理解C++引用:从基础到现代编程实践

一、引用的本质与基本特性 1.1 引用定义 引用是为现有变量创建的别名&#xff0c;通过&符号声明。其核心特点&#xff1a; 必须初始化且不能重新绑定 与被引用变量共享内存地址 无独立存储空间&#xff08;编译器实现&#xff09; 类型必须严格匹配 int value 42; in…

嵌入式Linux开发环境搭建,三种方式:虚拟机、物理机、WSL

目录 总结写前面一、Linux虚拟机1 安装VMware、ubuntu18.042 换源3 改中文4 中文输入法5 永不息屏6 设置 root 密码7 安装 terminator8 安装 htop&#xff08;升级版top&#xff09;9 安装 Vim10 静态IP-虚拟机ubuntu11 安装 ssh12 安装 MobaXterm &#xff08;SSH&#xff09;…

软件工程面试题(二十七)

1、j a v a 对象初始化顺序 1.类的初始化(initialization class & interface) 2.对象的创建(creation of new class instances) 顺序:应为类的加载肯定是第一步的,所以类的初始化在前。大体的初始化顺序是: 类初始化 -> 子类构造函数 -> 父类构造函数 -&g…

《AI大模型开发笔记》MCP快速入门实战(一)

目录 1. MCP入门介绍 2. Function calling技术回顾 3. 大模型Agent开发技术体系回顾 二、 MCP客户端Client开发流程 1. uv工具入门使用指南 1.1 uv入门介绍 1.2 uv安装流程 1.3 uv的基本用法介绍 2.MCP极简客户端搭建流程 2.1 创建 MCP 客户端项目 2.2 创建MCP客户端…