-
概念 环境变量:一般指的是在操作系统中用来指定操作系统运行环境的一些参数。 就比如说我们编写的C/C++代码在连接时从来不知道我们所连接的动静态库在哪里,但是照样可以连接成功。其原因就是有环境变量帮助编译器进行查找。 环境变量通常具有某些特殊用途,其在系统中还具有全局特性。命令行参数 我们知道中Linux命令中许多命令都是有对应的选项的,不同的选项对应不同的功能。那这个操作是如何实现的呢?main函数参数 首先,让我们先来讲讲main函数。mian函数可以说是我们接触编程的第一步,但许多人可以对main函数并不了解。 main函数其实是可以有参数的。int main(int argc,char* argv[])AI生成项目 第一个参数,argc:表示命名行参数的数量(包括程序名本身) 第二个参数,argv:是一个字符指针数组,储存了命令行中的每个参数验证:#include<stdio.h> int main(int argc,char* argv[]){ for(int i=0;i<argc;i++) { printf("%s\n",argv[i]); }}AI生成项目 实现指令选项#include<stdio.h>#include<string.h> int main(int argc,char* argv[]){ if(argc==2) { char* x=argv[1]; if(strcmp(x,"-a")==0) printf("执行-a功能\n"); if(strcmp(x,"-b")==0) printf("执行-b功能\n"); if(strcmp(x,"-c")==0) printf("执行-c功能\n"); } else printf("请使用:-a/-b/-c\n");}AI生成项目 进程中有一张表:argv表(命令行参数表),利用此表则可以实现命令的选项的功能。认识环境变量首先我们要执行一个程序,必须先找到它而环境变量可以帮助我们找到其位置使用env指令我们可以查看系统中所有的环境变量,也可以使用echo $名称,打印对应的环境变量环境变量的格式为:名称=内容 PATH 我们先来认识一下PATH环境变量。 PATH环境变量表示查找指令位置的默认路径,比如当我们执行指令:ls、cd等等指令时,bash就会使用PATH环境变量去找到指令对应的程序并执行。 我们知道系统指令的实现也是C语言,但为什么系统自己的指令可以直接执行,而我们自己写的代码却必须要 “./” 作为前缀才可以执行,PATH环境变量就是关键。理解两个问题:1.环境变量是如何存储的呢? bash下会形成一个环境变量表,存放各种各样的环境变量。(上面我们也讲到了bash中有一个命令行参数表,也是就是bash下有两张表)2.环境变量一开始是从哪里来的? bash下的环境变量表是重系统配置文件中来的(以下便是配置文件)。 认识更多环境变量HOME HOME环境变量,记录的是当前用户的家目录路径root@hcss-ecs-4ce7:~# echo $HOME/rootAI生成项目SHELL 表示当前使用的shell版本root@hcss-ecs-4ce7:~# echo $SHELL/bin/bashAI生成项目 USER 表示当前用户是谁root@hcss-ecs-4ce7:~# echo $USERrootAI生成项目 HISTSIZE 表示Linux中记录历史命令的最大数量(既Linux的历史命令最多记录HISTSIZE条)root@hcss-ecs-4ce7:~# echo $HISTSIZE1000AI生成项目HOSTNAME 表示当前主机的主机名root@hcss-ecs-4ce7:~# echo $HOSTNAMEhcss-ecs-4ce7AI生成项目PWD 记录当前所在的工作路径hyc@hcss-ecs-4ce7:~/linux/progress$ echo $PWD/home/hyc/linux/progressAI生成项目OLDPWD 记录上一次的工作路径hyc@hcss-ecs-4ce7:~/linux/progress$ echo $OLDPWD/home/hyc/linuxAI生成项目环境变量的获取方式指令env:查看全部的环境变量echo $xxx:查看某个环境变量expot:添加环境变量unset:删除环境变量 代码调用系统调用:getenv获取环境变量头文件为:#include<stdlib.h>返回值:获取成功返回指针,失败返回NULL#include<stdio.h>#include <stdlib.h> int main(){ char* str=getenv("PATH"); if(!str) printf("获取失败"); else printf("%s\n",str);}AI生成项目 环境变量特性1.环境变量具有全局性,可以被子进程继承,同一个bash下的进程都可以访问 。2.存在本地变量,只能在bash内部使用。如下:i 就是本地变量。 3.export添加环境变量,是添加在bash中的,再子进程继承达到全局可用。 export命令与其他命令不同:export命令是内键命令:既不需要创建子进程,bash自己调用函数执行(或系统调用完成)———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/huangyuchi/article/details/149715773
-
links:https://lm-studio.cn/docs/apiLM Studio 可以像 Ollama 一样通过暴露本地端口实现局域网内客户端调用。以下是具体实现方式和技术细节:1. 核心功能支持LM Studio 的 开发者选项卡 提供了完整的本地 API 服务器功能,支持以下配置:端口暴露:默认使用 1234 端口,可自定义修改(需注意端口冲突问题)3局域网访问:勾选 “在局域网内提供服务” 选项后,服务器会监听所有网络接口(0.0.0.0),允许其他设备通过 IP 地址访问34跨域支持:启用 CORS 后,可对接网页应用或其他客户端工具32. 配置步骤实现局域网调用的具体操作流程:启动服务端:在 LM Studio 的 “开发者” 选项卡中选择模型勾选 “在局域网内提供服务” 并设置端口(如 1234)3点击 “启动服务” 按钮,控制台会显示运行日志和访问地址(如 http://<本机IP>:1234)3客户端调用:其他设备可通过以下方式访问:# 示例:使用 curl 调用聊天接口curl http://<本机IP>:1234/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"messages": [{"role": "user", "content": "你好"}], "model": "<模型名称>"}'AI生成项目bash1234支持 OpenAI 兼容的 API 格式(包括 /v1/chat/completions、/v1/completions 等端点)13防火墙配置(Windows 系统):需在防火墙入站/出站规则中开放对应端口(如 1234)53. 与 Ollama 的对比特性 LM Studio Ollama协议兼容性 OpenAI API 兼容 自有 API 格式界面操作 图形界面一键配置 需命令行操作多模型支持 需手动加载不同模型 支持 Modelfile 动态切换日志管理 内置日志记录(可记录提示与响应)3 需自行配置日志工具4. 典型应用场景多人协作开发:团队成员通过局域网共享同一模型服务2跨设备调用:手机/平板通过浏览器或 Open WebUI 访问本地模型2自动化流程:对接 VS Code 插件或其他 AI 工具链1注意:如果遇到连接问题,需检查以下三点:LM Studio 服务端是否已勾选局域网选项防火墙是否开放对应端口客户端是否使用正确的 IP 地址(可通过 ipconfig/ifconfig 查看本机局域网 IP)35———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/a772304419/article/details/145800588
-
一· 认识HTTPSHTTPS 也是一个应用层协议. 是在 HTTP 协议的基础上引入了一个加密层,即可以理解成SSL+TLS+HTTP构成!二· 为何要加密所以:因为 http的内容是明文传输的,明文数据会经过路由器、wifi热点、通信服务运营商、代理服务器等多个物理节点,如果信息在传输过程中被劫持,传输的内容就完全暴露了。劫持者还可以篡改传输的信息且不被双方察觉,这就是中间人攻击,所以我们才需要对信息进行加密。总之,早期的http在网络中明文传递,是暴露的,能被修改!!!其中:加密就是把明文(要传输的信息)进行一系列变换,生成密文!解密就是把密文再进行一系列变换,还原成明文!在这个加密和解密的过程中,往往需要一个或者多个中间的数据,辅助进行这个过程,这样的数据称为密钥(正确发音yue 四声,不过大家平时都读作yao四声)!因此:HTTPS 就是在 HTTP 的基础上进行了加密,进一步的来保证用户的信息安全!三. 加密方式3.1 对称加密采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密常见对称加密算法(了解):DES、3DES、AES、TDEA、Blowfish、RC2等特点:算法公开、计算量小、加密速度快、加密效率高!因此:我们只需要记住:加密秘钥和解密秘钥是同一把,这在某种意义上成为了它的缺点!一个简单的对称加密:按位异或:假设明文a= 1234,密钥key = 8888则加密a ^ key得到的密文b 为9834,然后针对密文9834再次进行运算b ^key,得到的就是原来的明文1234.(对于字符串的对称加密也是同理,每一个字符都可以表示成一个数字)当然,按位异或只是最简单的对称加密.HTTPS中并不是使用按位异或.3.2 非对称加密需要两个密钥来进行加密和解密,这两个密钥是公开密钥(public key,简称公钥)和私有密钥(private key,简称私钥)。常见非对称加密算法:RSA,DSA,ECDSA。特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使得加密解密速度没有对称加密解密的速度快。最大的缺点就是运算速度非常慢,比对称加密要慢很多。因此:这里只需要记住公钥加密只有私钥解开,私钥加密只有公钥解开!3.3 数据摘要与数据指纹数字指纹(数据摘要),其基本原理是利用单向散列函数(Hash函数)对信息进行运算,生成一串固定长度的数字摘要。数字指纹并不是一种加密机制,但可以用来判断数据有没有被篡改。摘要常见算法:有 MD5、SHA1、SHA256、SHA512等,算法把无限的映射成有限,因此可能会有碰撞(两个不同的信息,算出的摘要相同,但是概率非常低)。摘要特征:和加密算法的区别是,摘要严格意义不是加密,因为没有解密,只不过从摘要很难反推原信息,通常用来进行数据对比,比如数据库管理使用摘要。四. HTTPS加密方式4.1 只使用对称加密也就是我们只使用一把秘钥进行通信:理想:实际:这种理想上被中间人截取的也只能是密文,无利用价值,但是服务端和每个客户端都要有个秘钥来维护,这样就比较麻烦,因此一般采取的是先发送秘钥进行协商,但是这样就被中间人获取秘钥了,因此这种方案是不可行的!因此就导致了这样:4.2 双方使用一个非对称加密服务端发送信息前提是先要和浏览器协商(把公钥发给它)然后用私钥加密,给浏览器,这样信息就暴露了(服务器到浏览器的信息暴露),但是浏览器拿到后,进行发送用公钥加密(只能私钥解密),此时无法暴露,服务器就能正常收到信息!比如:4.3 双方都使用非对称加密交换公钥,公钥加密,私钥解密!它们俩互相交换了公钥(用于加密),有没有可能中间人也用公钥进行加密然后比如从客户端发给服务端,这样服务端也就分不清了或者中间人用自己的公钥给客户端,然后进行信息窃取!总结:因为非对称加密:效率低,而且还有安全问题!4.4 非对称加密+对称加密也就是服务端先把公钥给客户端,客户端用它进行对称密钥加密然后给服务端(中间人无私钥,无法解密),最后他俩就用这个对称公钥进行加密!过程如图所示:这样看似是没有毛病的,但是有没有可能中间人把自己的公钥m给client,然后不就得到了这个被m加密的x了,然后再用s进行加密给服务端这个x,之后s与c用x通信不就被中间人掌握了吗!结局就成了这样:因此:关键问题还是防止中间人攻击也就是如何确保client收到的公钥来自server而不是中间人呢?4.5 两次非对称加密与一次对称加密引入证书这种方式的前两次非对称加密都是为了保证最后一次对称加密的安全性!证书=签名+明文CA认证:服务端在使用 HTTPS 前,需要向 CA 机构申领一份数字证书,数字证书里含有证书申请者信息、公钥信息等。服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了,证书就如身份证,证明服务端公钥的权威性!下面看一下申请证书过程图:这里需要记住:签名只有CA机构能做(因为只有它才有对应私钥)。签名和解签名的秘钥和服务端的秘钥不是同一种。CA的私钥由CA使用进行签名,CA的公钥由浏览器自备进行解签名验证。服务端和客户端通信的时候,客户端会先请求,此时服务端就去CA机构申请一个证书,这个证书就是保证client拿到的一定是server的公钥!证书有没有可能被篡改那么此时浏览器拿到证书如何核对有没有在server与client传播过程中被中间人修改呢?CA机构在形成证书的时候会拿着私钥对明文信息散列后的摘要进行加密作为签名!然后当客户端收到证书后会拿着CA公钥对签名解密拿到的数据和明文信息散列后进行对比;如果相同就证明没有被修改,拿着公钥进行传递对称秘钥!!!被修改了就警告! !! —>保证了client一定拿到server的公钥!!!如图:但是如果中间人修改server发给client的证书的明文/签名/或者整体替换呢(中间人如果要想成功传递自己的秘钥必须修改签名)?修改明文:如果修改了的话,当client拿到证书后会发现不匹配的。修改签名:中间人没有ca的私钥无法对前面修改。整体替换:这里整体替换就必须要ca的严格审查,中间人只能按照要求申请新的证书,但是还有域名等等一些信息使得客户端可以识别到是不是对应服务端!中间人动机:要么把明文里的公钥换成自己的。要么换成自己的后然后改签名让客户能够获取公钥。但是,均行不通!永远记住:中间人没有CA私钥,所以对任何证书都无法进行合法修改,包括自己的。因此总结:中间人无法修改证书和伪造新的证书!非对称加密+对称加密+证书认证因此,它来了!这里可以理解成:首先进证书申请+客户端验证证书(第一次非对称加密以及解密);接着就是client拿到对应的server的公钥,然后进行传递接下来进行非对称加密的秘钥,服务端然后拿着自己的私钥进行解密拿到(第二次非对称加密以及解密);接着就是用这个非对称秘钥进行通信了(非对称加密及解密)!也就是这样:注意:这里的易混点是签名和解签名只能用CA的私钥和公钥;而不是服务器的的私钥公钥(过程中服务器会泄露公钥但私钥不会泄露)!! !下面看张更加详细的图:详细步骤:第一次非对称加密:用于校验证书是否被篡改.服务器持有私钥(私钥在形成CSR文件与申请证书时获得),客户端持有公钥(操作系统包含了可信任的CA认证机构有哪些,同时持有对应的公钥).服务器在客户端请求时,返回携带签名的证书.客户端通过这个公钥进行证书验证,保证证书的合法性,进一步保证证书中携带的服务端公钥权威性。第二次非对称加密:用于协商生成对称加密的密钥.客户端用收到的CA证书中的公钥(是可被信任的)给随机生成的对称加密的密钥加密,传输给服务器,服务器通过私钥解密获取到对称加密密钥。对称加密:客户端和服务器后续传输的数据都通过这个对称密钥加密解密。超详细大白话叙述过程:首先服务器拿着相关信息(域名,csr,明文信息等),然后生产对应的私钥(自己保存),公钥(交给CA)然后向CA申请证书,ca检查严格审核后,拿着自己的专属私钥进行对明文散列后数据签名,接着交给server,最后server把证书给client,client拿着自己内置信任的CA机构的公钥容纳后进行解密签名看是否合法(明文散列是否相同,域名等是否匹配),不合格就发出警告(上报,或者浏览器阻止用户访问等),合格的话就拿到对应服务器的公钥,自己产生对称秘钥,然后发给服务端(中间人稚嫩获得密文,无法解密),然后服务端解密后,他俩就利用对称秘钥进行通信了!总结:这一切的目的就是保证这个对称秘钥通信的时候是安全的 !疑问点为什么要进行签名加密:如果不签名的话就是直接证书上的就是明文,那么在客户端核对前是有可能被中间人修改的,因此是不安全的。为什么明文不直接加密而是先生成摘要呢:缩小签名密文的长度,加快数字签名的验证签名的运算速度。如何成为中间人:ARP欺骗:在局域网中,hacker经过收到ARP Request广播包,能够偷听到其它节点的(IP,MAC)地址。例,黑客收到两个主机A,B的地址,告诉B(受害者),自己是A,使得B在发送给A的数据包都被黑客截取。ICMP攻击: 由于ICMP协议中有重定向的报文类型,那么我们就可以伪造一个ICMP信息然后发送给局域网中的客户端,并伪装自己是一个更好的路由通路。从而导致目标所有的上网流量都会发送到我们指定的接口上,达到和ARP欺骗同样的效果。查看浏览器的受信任证书相关信息首先点击浏览器对应设置:接下来点击安全选项:可以看到有很多信任的证书,点击可以看到公钥等信息:五. 本篇小结通过本篇对HTTPS的基础认识,建立了大致的了解,知道了它背后的原理等,此次学习虽然是大致的,但是也看到了HTTPS背后的基本原理,为以后对它更加深入的学习奠定了基础,冲冲冲!———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/2401_82648291/article/details/147658666
-
一、编译的四个核心阶段从源代码到可执行文件,gcc/g++的工作流程分为四个关键阶段,每个阶段都有明确的输入、输出和任务目标。四个核心阶段输出输出输出输出**预处理 (gcc -E)**▸ 宏替换▸ 删除注释▸ 头文件展开▸ 条件编译处理**编译 (gcc -S)**▸ 语法分析▸ 语义检查▸ 生成汇编指令**汇编 (gcc -c)**▸ 转换汇编为机器码▸ 生成目标文件▸ 未解决外部引用**链接 (gcc *.o)**▸ 合并目标文件▸ 解析函数引用▸ 链接库文件▸ 地址重定位源文件*.c/*.cpp.i文件.s文件.o文件可执行文件1. 预处理阶段:代码的初步加工预处理是编译的第一个环节,主要负责对源代码进行“前期处理”,包括:宏定义的替换(如#define指令)注释的删除(避免注释干扰编译过程)条件编译的处理(如#if、#ifdef等指令)头文件的展开(将#include指令引入的头文件内容插入到当前文件中)预处理阶段的输入是.c(或.cpp)源文件,输出是.i文件。在Linux中,可通过以下命令单独执行预处理:gcc -E code.c -o code.i # 对C文件进行预处理,生成code.ig++ -E code.cpp -o code.i # 对C++文件进行预处理其中,-E参数表示“仅执行预处理阶段”,-o用于指定输出文件的名称。预处理后的.i文件仍是文本格式,可通过cat命令查看内容。Linux系统的标准头文件(如stdio.h、stdlib.h)默认存储在/usr/include/目录下,预处理时编译器会自动到该路径搜索头文件。2. 编译阶段:从高级语言到汇编语言编译阶段的任务是将预处理后的.i文件转化为汇编语言代码。编译器会对代码进行语法检查、语义分析,并最终生成与硬件架构相关的汇编指令。该阶段的输入是.i文件,输出是.s汇编文件。执行命令如下:gcc -S code.i -o code.s # 生成汇编文件code.s-S参数表示“仅执行到编译阶段”,生成的.s文件包含汇编指令,例如mov、add等操作。汇编语言是介于高级语言和机器语言之间的中间形式,仍可被人类阅读。3. 汇编阶段:从汇编到二进制目标文件汇编阶段将汇编语言转化为机器可识别的二进制指令,生成目标文件(.o文件,也称为obj文件)。输入为.s汇编文件,输出为.o二进制文件,命令如下:gcc -c code.s -o code.o # 汇编生成目标文件-c参数表示“仅执行到汇编阶段”。.o文件是二进制格式,无法直接通过文本编辑器查看,但它已经包含了程序的基本指令。需要注意的是,此时的.o文件尚未解决函数调用的依赖关系(如调用其他文件中的函数),因此还不能直接执行。4. 链接阶段:整合目标文件与库链接是编译的最后阶段,其核心任务是将多个.o目标文件与系统库(或第三方库)整合,生成最终的可执行文件。链接阶段主要解决以下问题:函数调用的地址绑定(确定被调用函数在内存中的具体位置)全局变量的引用解析库文件的关联(如C标准库中的printf、scanf等函数)执行命令如下:gcc code.o -o code # 将code.o链接为可执行文件code此时生成的code文件即可直接运行(通过./code命令)。如果程序依赖多个目标文件,可同时传入链接命令:gcc a.o b.o c.o -o app # 链接多个目标文件生成app二、一次性编译与常用扩展选项在实际开发中,我们通常不需要手动执行四个阶段,而是直接通过一条命令完成从源码到可执行文件的全过程:gcc code.c -o code # 直接编译C文件生成可执行文件codeg++ code.cpp -o app # 直接编译C++文件生成可执行文件app这条命令会自动依次执行预处理、编译、汇编和链接四个阶段,最终生成指定的可执行文件。除了基础命令,gcc/g++还提供了许多实用选项,用于优化编译过程或控制输出:选项 功能描述-Wall 显示更多警告信息(如未使用的变量、类型不匹配等),帮助排查潜在问题-O(或-O1、-O2、-O3) 开启编译优化,-O3为最高级别优化,可提升程序运行效率(但编译时间更长)-g 生成调试信息,用于gdb调试工具(如gcc -g code.c -o code)-std=c99(或-std=c++11) 指定编译标准(如C99、C++11),确保代码兼容性-I(大写i) 指定头文件搜索路径(如gcc -I./include code.c -o code)-L 指定库文件搜索路径(如gcc code.c -L./lib -o code)-l(小写L) 链接指定的库(如-lm表示链接数学库libm.so)三、多文件编译实践在大型项目中,程序往往被拆分为多个源文件(如main.c、func1.c、func2.c),此时需要通过多文件编译来管理代码。假设项目结构如下:project/├── main.c # 主函数├── func1.c # 功能函数1├── func2.c # 功能函数2└── include/ ├── func1.h # func1.c的头文件 └── func2.h # func2.c的头文件编译命令如下:# 方法1:分步编译gcc -c main.c -o main.o -I./include # 编译main.c,指定头文件路径gcc -c func1.c -o func1.o -I./includegcc -c func2.c -o func2.o -I./includegcc main.o func1.o func2.o -o app # 链接所有目标文件# 方法2:一次性编译gcc main.c func1.c func2.c -o app -I./include通过多文件编译,不仅可以提高代码的可维护性,还能减少重复编译的时间(修改单个文件后,只需重新编译该文件的.o目标文件,再重新链接即可)。四、总结gcc/g++编译器是Linux开发中不可或缺的工具,其工作流程涵盖预处理、编译、汇编和链接四个阶段,每个阶段都有明确的目标和输出。掌握编译器的使用方法,不仅能帮助我们生成可执行程序,还能通过调试选项、优化参数等提升开发效率和程序性能。在实际开发中,我们可以根据需求选择分步编译(用于调试或分析中间过程)或一次性编译(用于快速生成可执行文件)。同时,合理使用多文件编译和库链接,能更好地管理大型项目的代码结构。理解这些基础原理和操作,是深入Linux开发的重要一步。———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/2302_78391795/article/details/149369002
-
Linux进程信号是一种进程间通信的机制,它允许一个进程通知另一个进程某个事件已经发生。以下是关于Linux进程信号的详细介绍: 1. 信号的概念 信号就像是一个突然的电话铃声,它会打断正在进行的程序并引起其注意。在Linux系统中,信号是一种软件中断,它通常是异步发生的,用来通知进程某个事件已经发生。每个信号都有一个唯一的编号和一个宏定义名称,这些宏定义可以在signal.h中找到。 使用kill -l命令查看信号编号: 查看信号宏定义: 2. 信号的分类 在Linux中,信号被分为标准信号(也称为传统或不可靠信号)和实时信号。它们的主要区别在于编号范围、处理方式以及特性。 1) 标准信号 (Traditional/Standard Signals)这些信号是早期Unix系统定义的,编号通常从1到31(尽管某些系统可能会有所不同)。以下是一些常见的标准信号: SIGHUP (1): 终端挂起或控制进程结束。1号信号,当用户退出终端时,由该终端开启的所有进程都会接收到这个信号,默认动作为终止进程。但也可以捕获这个信号,比如wget能捕获SIGHUP信号并忽略它,以便在退出登录后继续下载。 SIGINT (2): 中断信号,通常是Ctrl+C产生的。2号信号,程序终止(interrupt)信号,在用户键入INTR字符(通常是Ctrl+C)时发出,用于通知前台进程组终止进程。 SIGQUIT (3): 退出信号,产生核心转储。3号信号,和SIGINT类似,但由QUIT字符(通常是Ctrl+\)来控制。进程在因收到SIGQUIT退出时会产生core文件,在这个意义上类似于一个程序错误信号。 SIGILL (4): 非法指令。SIGTRAP (5): 跟踪陷阱(由调试器使用)。SIGABRT (6): 调用abort()函数生成的信号。SIGBUS (7): 总线错误。SIGFPE (8): 浮点异常。SIGKILL (9): 强制终止信号(不可被捕获、阻塞或忽略)。9号信号,用来立即结束程序的运行。本信号不能被阻塞、处理和忽略。 SIGSEGV (11): 段违例。SIGPIPE (13): 管道破裂。SIGALRM (14): 定时器到期。SIGTERM (15): 终止请求。15号信号,程序结束(terminate)信号,与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,允许进程做一些必要的清理工作后退出。 2) 实时信号 (Real-time Signals)实时信号是在POSIX.1b标准中引入的,用于提供更可靠的信号机制。它们的编号范围从SIGRTMIN到SIGRTMAX,具体数值取决于操作系统实现。一般情况下,这个范围是从34开始直到系统的最大信号数。例如,在许多Linux系统上,SIGRTMIN对应的是34,而SIGRTMAX可以达到64或者更高。 实时信号的特点包括但不限于: 不会丢失:如果多个相同的实时信号发送给同一个进程,所有信号都会被接收。支持排队:每个类型的实时信号可以有一个队列来存储未处理的信号实例。有序性:实时信号按照发送顺序处理。可携带数据:可以通过sigqueue()发送附加的数据(一个整数或指针)。请注意,实际的信号编号可能根据不同的系统架构和版本有所变化。此外,对于实时信号,应当使用SIGRTMIN + n和SIGRTMAX - n这样的形式来引用,而不是直接使用具体的数字值,以确保兼容性和正确性。 本章只讨论编号31以下的信号,不讨论实时信号。 3. 信号的处理 在Linux中,信号处理是进程对特定事件响应的一种机制。信号处理有三种方式: ✨方式一:执⾏该信号的默认处理动作 使用命令man 7 signal查看信号在什么条件下产⽣,默认的处理动作是什么: ✨方式二:忽略此信号 可以通过设置信号处理器(也就是信号处理函数来实现): 信号处理器(Signal Handler)信号处理器是一个函数,它在进程接收到指定信号时被调用。你可以为每个信号设置一个自定义的处理器,函数如下:#include <signal.h> void (*signal(int signum, void (*handler)(int)))(int); 参数signum表示要设置的信号编号,参数handler表示要设置的信号处理函数。signal函数会返回上一个信号处理函数的指针,如果出错则返回SIG_ERR。 忽略信号可以将信号处理器设置为 SIG_IGN 来忽略某些信号。但是,不能忽略像 SIGKILL 和 SIGSTOP 这样的不可捕获信号。代码如下:#include<iostream>#include<unistd.h>#include<signal.h>int main(){ signal(SIGINT,SIG_IGN);//将2号信号忽略 while(true) { std::cout<<"PID:"<<getpid()<<" I am waiting a signal."<<std::endl; sleep(1); } return 0;} 2号信号的默认处理动作是程序终止(interrupt),但是由于我们使用信号处理器忽略了该信号,所以输入Ctrl+c没有终止程序。 ✨情况三:设置自定义处理方式 和忽略信号相同我们也是使用信号处理器来实现,实例代码如下: #include<iostream>#include<unistd.h>#include<signal.h>void Handler(int signo){ std::cout<<"PID:"<<getpid()<<" Get a signal:"<<signo<<std::endl;//当接受到SIGINT也就是2号信号会执行打印动作,而非终止进程}int main(){ signal(SIGINT,Handler);//对2号信号设置自定义处理动作 while(true) { std::cout<<"PID:"<<getpid()<<" I am waiting a signal."<<std::endl; sleep(1); } return 0;} 对于信号默认处理动作,我们也可以使用信号处理器来实现:signal(SIGINT/*2*/, SIG_DFL);使用SIG_DFL即可选择默认处理动作,结果如下: 注意:signal方法只需要设置一次,那么在这个进程中,只要接受到被设置的信号就会执行Handler方法;此外如果没有产生被设置的信号,Handler方法就永远不会被执行。也就是说signal方法类似的设置了一种机制,当有对应的信号产生时就触发,没有就不触发。 4. 理解信号处理 对于信号的处理我们可以分别通过软件和硬件这两个视角来理解,由于硬件比较麻烦,设计操作系统运行原理、时钟中断、死循环等,所以这里不做解释。 软件我们以发送2号信号——从键盘输入Ctrl+c为例,当键盘将输入的Ctrl+c信息交给操作系统后,操作系统就会将信号发送给对应的进程,然后进程就会在合适的时候处理信号。 注意,进程处理信号不是立即处理,而是在合适的时机,因为当前进程可能在处理自己的事。 当进程接收到信号时,是如何记录是哪个信号从而执行相应的信号处理任务呢? 我们发现本次学习的信号是1 ~ 31,连续的数字,那么只需选择位图即可使用最小的空间记录下完整的1 ~ 31个数字;只需在进程PCB中定义一个无符号整型,使用32个比特位记录31个信号,比特位的位置代表信号的编号,比特位设为1代表收到该位置的信号,为0代表没有。也就是说操作系统给进程发送信号就是将进程PCB中记录信号的位图对应位置的信号比特位由0置1,然后进程在合适的时候发现自己收到了信号,执行对应处理动作。 接下来为了保证条理,将采⽤如下思路来进⾏阐述: 5. 信号产生 在Linux系统中,信号(signal)是一种异步通知机制,用于通知进程发生了某些事件。进程可以接收到多种类型的信号,并且可以对这些信号进行处理或者忽略。以下是几种产生信号的常见方法: 1) 键盘输入Ctrl+C:发送SIGINT给前台进程,通常用于终止一个进程。Ctrl+\:发送SIGQUIT给前台进程,类似于SIGINT但会生成核心转储文件。Ctrl+Z:发送SIGTSTP给前台进程,暂停进程的执行。2) kill命令使用kill命令可以向指定的进程发送信号,默认情况下是发送SIGTERM信号请求进程正常终止。可以通过-l选项列出所有可用的信号,通过-s或直接跟信号编号来指定发送的信号类型。kill -9 <pid> # 发送SIGKILL信号,强制终止进程kill -15 <pid> # 发送SIGTERM信号,请求进程正常终止AI写代码123) kill函数及系统调用kill()函数: 可以直接从程序内部发送信号给其他进程。#include <sys/types.h>#include <signal.h> int kill(pid_t pid, int sig); 参数说明: pid:要发送信号的进程IDsig:要发送的信号编号该函数的返回值为0表示成功,返回-1表示失败。 所以我们可以根据kill系统调用来封装一个自己的kill命令,代码如下: #include<iostream>#include <sys/types.h>#include <signal.h> int main(int argc,char* argv[]){ if(argc!=3) { std::cout<<"you should print: ./mykill -signo -pid"<<std::endl;//提示输入格式 return 1; } int signo = std::stoi(argv[1]);//字符串转整型 pid_t pid = std::stoi(argv[2]); int n = ::kill(pid,signo); if(n < 0) { perror("kill"); return 2; } return 0;} 所以其实kill命令本质也是通过系统调用来实现的。 raise函数:用于向调用它的进程自身发送信号。int raise(int sig);AI写代码cpp运行1参数sig为要生成的信号编号。常见的信号编号包括SIGINT(中断信号,通常由终端键盘输入产生)、SIGABRT(终止信号,由abort函数产生)等。 abort函数:abort函数用于异常终止程序。当调用该函数时,程序会立即退出,并生成SIGABRT信号。void abort(void); 4) 软件条件当特定的软件条件发生时,比如子进程结束(SIGCHLD),文件描述符准备就绪(SIGIO),可能会触发信号的产生。例如alarm定时器:用于设置一个定时器,可以在指定的时间后产生一个SIGALRM信号。#include <unistd.h> unsigned int alarm(unsigned int seconds); 参数seconds指定了定时器的时间间隔,单位为秒。该函数返回当前的闹钟定时器的剩余秒数。如果没有闹钟定时器正在运行,则返回0。使用代码如下: #include<iostream>#include<unistd.h>#include<signal.h>int main(){ ::alarm(1);//设置1s的定时器 int count = 0; while(true) { count++;//统计服务器1s可以将计数器累加到多少 printf("count:%d\n",count); } return 0;} 我们发现服务器1s也就计算4万多次,有点慢,这其实是因为打印count数值时,进行了IO交互,会影响速度;所以我们可以通过signal信号处理器来优化,代码如下: #include<iostream>#include<unistd.h>#include<signal.h> int gcount = 0; void Handler(int signo){ printf("count:%d\n",gcount);//只在1s后打印总的计算次数 exit(1);//直接退出}int main(){ signal(SIGALRM,Handler); ::alarm(1); while(true) { gcount++; } return 0;} 结果如下: 可以看到服务器1s大概计算了6亿多次 闹钟返回值:返回当前的闹钟定时器的剩余秒数。如果没有闹钟定时器正在运行,则返回0。测试代码如下:#include<iostream>#include<unistd.h>#include<signal.h> int main(){ int n = ::alarm(4);//在此之前没有闹钟在运行,返回0 std::cout<<n<<std::endl; sleep(1); int m = ::alarm(0);//0:表示取消闹钟,m表示剩余秒数 std::cout<<m<<std::endl; return 0;} 结果如下: 闹钟原理: 其实本质是OS必须自身具有定时功能,并能让用户设置这种定时功能,才可能实现闹钟这样的技术。操作系统需要对定时器进行管理,使用结构体对它先描述再组织,其结构体大概包含: struct timer{int who;task_struct *t;//表示哪个进程设置的闹钟uint64_t expired;//过期时间struct timer *next;//定时器可以有多个,通过链表连接起来func_t f;//到期执行的函数//...} 当定时器到期后,操作系统就会根据它的func_t f函数,执行对应的方法,比如给目标进程发送SIGALRM信号。此外我们可以理解定时器在操作系统中连接的数据结构为小堆,每次都是堆顶元素先到期,这样操作系统就不需要每次都遍历定时器查找是否有过期的定时器。 也就是说定时器到期时可以理解为软件条件就绪,操作系统就会给对应进程发送信号,所以软件条件当作信号产生的方式之一。 使用闹钟完成定时器功能: 根据闹钟定时作用,我们可以使用信号处理器使得闹钟响起时进程自动去完成某些任务,代码如下: #include<iostream>#include<unistd.h>#include<signal.h>#include<stdlib.h>#include<vector>#include<functional> using func_t = std::function<void()>;int gcount = 0;std::vector<func_t> gfuncs;//使用vector管理相关任务 void Handler(int signo){ for(auto& f : gfuncs)//遍历任务执行 f(); std::cout<<"gcount:"<<++gcount<<std::endl;//记录执行次数 alarm(1);}int main(){ gfuncs.push_back([](){std::cout<<"我是一个内核刷新操作..."<<std::endl;}); gfuncs.push_back([](){std::cout<<"我是一个检测进程时间片的操作..."<<std::endl;}); gfuncs.push_back([](){std::cout<<"我是一个检测进程时间片的操作..."<<std::endl;}); signal(SIGALRM,Handler);//信号处理器自定义设置闹钟响起时执行的任务 ::alarm(1);//设置1s后执行任务 while(true)//防止进程退出 { pause();//暂停等待信号到来 } return 0;} 注意闹钟设置是一次性的,所以在Handler执行方法中要再设置闹钟,不然就会暂停在那。 5) 硬件异常 硬件异常被硬件以某种⽅式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执⾏了除以0的指令, CPU的运算单元会产⽣异常, 内核将这个异常解释为SIGFPE信号发送给进程。再⽐如当前进程访问了⾮法内存地址, MMU会产⽣异常,内核将这个异常解释为SIGSEGV信号发送给进程。 除0异常:#include <stdio.h>#include <signal.h> void handler(int sig){ printf("catch a sig : %d\n", sig);} int main(){ signal(SIGFPE, handler); // 8) SIGFPE sleep(1); int a = 10; a /= 0; while (1) ; return 0;} 野指针异常:#include <stdio.h>#include <signal.h> void handler(int sig){ printf("catch a sig : %d\n", sig);}int main(){ signal(SIGSEGV, handler); sleep(1); int *p = NULL; *p = 100; while (1) ; 由此可以确认,我们在C/C++当中除零,内存越界等异常,在系统层⾯上,是被当成信号处理的。 但是我们发现⼀直有8/11号信号产⽣被我们捕获,这是为什么呢?上⾯我们只提到CPU运算异常后,如何处理后续的流程,实际上 OS 会检查应⽤程序的异常情况,其实在CPU中有⼀些控制和状态寄存器,主要⽤于控制处理器的操作,通常由操作系统代码使用。状态寄存器可以简单理解为⼀个位图,对应着⼀些状态标记位、溢出标记位。OS 会检测是否存在异常状态,有异常存在就会调⽤对应的异常处理⽅法。除零异常后,我们并没有清理内存,关闭进程打开的⽂件,切换进程等操作,所以CPU中还保留上下⽂数据以及寄存器内容,除零异常会⼀直存在,就有了我们看到的⼀直发出异常信号的现象。访问⾮法内存其实也是如此,⼤家可以⾃⾏实验。 以上就是信号产生的五种方法,包括键盘输入、命令行产生、kill函数及系统调用、软件条件及硬件异常;但是无论产生信号是何种方式,发送信号的永远是操作系统!!!这是因为操作系统是进程的管理者。 6. 信号保存 6.1 信号其他相关常见概念信号递达(Delivery):实际执⾏信号的处理动作称为信号递达信号未决(Pending):信号从产⽣到递达之间的状态,称为信号未决阻塞:进程可以选择阻塞 (Block )某个信号。被阻塞的信号产⽣时将保持在未决状态,直到进程解除对此信号的阻塞,才执⾏递达的动作注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,⽽忽略是在递达之后可选的⼀种处理动作。 6.2 信号在内核中的表示 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有⼀个函数指针表示处理动作。如图所示: 信号产⽣时,内核在进程控制块中设置该信号的未决标志,表示接收到信号,直到信号递达(也就是说信号处理完成)才清除该标志。 在上图的例⼦中,SIGHUP信号未产生(pending表SIGHUP信号标志位为0),也未阻塞(block表SIGHUP信号标志位为0)。SIGINT信号产⽣过,但正在被阻塞,所以暂时不能递达。SIGQUIT信号未产⽣过,⼀旦产⽣SIGQUIT信号将被阻塞,无法执行它的处理动作(也就是无法递达)。 如果SIGHUP信号产生时,操作系统会将SIGHUP信号发送给进程,进程pending表SIGHUP标志位就会由0置为1,进程在将自己的事情处理完后查看信号表,发现收到了SIGHUP信号而且没被阻塞,进而执行SIGHUP的handler方法——SIG_DEF表示默认处理方法。 如果SIGINT解除阻塞,那么进程在查看信号时就会执行SIGINT的处理方法——SIG_IGN(忽略)。 如果在进程解除对某信号的阻塞之前这种信号产⽣过多次,将如何处理? POSIX.1允许系统递送该信号⼀次或多次。Linux是这样实现的:常规信号在递达之前产⽣多次只计⼀次,⽽实时信号在递达之前产⽣多次可以依次放在⼀个队列⾥。本章不讨论实时信号。6.3 sigset_t 从上图来看,pending表中每个信号只有⼀个bit的未决标志, ⾮0即1, 不记录该信号产⽣了多少次;阻塞标志(block表)也是这样表示的。因此, 未决和阻塞标志可以⽤相同的数据类型sigset_t来存储, sigset_t称为信号集,这个类型可以表⽰每个信号的“有效”或“⽆效”状态。 在阻塞信号集中“有效”和“⽆效”的含义是该信号是否被阻塞, ⽽在未决信号集中“有 效”和“⽆效”的含义是该信号是否处于未决状态。 sigset_t的底层实现是一个整数类型,使用位操作来设置和获取各个信号的状态。阻塞信号集也叫做当前进程的信号屏蔽字这⾥的“屏蔽”应该理解为阻塞⽽不是忽略。 6.4 信号集操作函数 有了信号的操作类型——信号集sigset_t之后,我们就可以通过它对pending表和block表进行操作,函数如下: 1) 初始化sigset_t信号集函数int sigemptyset(sigset_t *set);int sigfillset(sigset_t *set);int sigaddset(sigset_t *set, int signo);int sigdelset(sigset_t *set, int signo);int sigismember(const sigset_t *set, int signo); 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置为1,表示该信号集的有效信号包括系 统⽀持的所有信号。注意,在使⽤sigset_ t类型的变量之前,⼀定要调⽤sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调⽤sigaddset和sigdelset在该信号集中添加或删除某种有效信号。这四个函数都是成功返回0,出错返回-1。sigismember是⼀个布尔函数,⽤于判断⼀个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。有了上述函数,我们就可以定义完一个信号集之后进行添加或删除等初始化操作。 2) 读取或更改未决信号集(pending)#include <signal.h>int sigpending(sigset_t *set); 读取当前进程的未决信号集(pending),通过set参数传出。调用成功则返回0,出错则返回-13) 读取或更改阻塞信号集函数(block)#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 返回值:若成功则为0,若出错则为-1如果oset是非空指针,则读取进程的当前信号屏蔽字(阻塞信号集)通过oset参数传出如果set是非空指针,则 更改进程的信号屏蔽字参数how指⽰如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how可选参数及其含义:how参数 参数使用说明SIG_BLOCK set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|setSIG_UNBLOCK set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~setSIG_SETMASK 设置当前信号屏蔽字为set所指向的值,相当于mask=set如果调⽤sigprocmask解除了对当前若⼲个未决信号的阻塞,则在sigprocmask返回前,⾄少将其中⼀个信号递达。 下⾯是使用上述函数的示例代码: #include <iostream>#include <unistd.h>#include <cstdio>#include <sys/types.h>#include <sys/wait.h>//打印pending表void PrintPending(sigset_t &pending){ std::cout << "cur process[" << getpid() << "]pending: "; for (int signo = 31; signo >= 1; signo--) { if (sigismember(&pending, signo))//如果pending表中有signo信号返回1 std::cout << 1; else std::cout << 0; } std::cout << "\n";} void handler(int signo){ std::cout << signo << " 号信号被递达!!!" << std::endl; std::cout << "-------------------------------" << std::endl; sigset_t pending; sigpending(&pending);//获取当前pending表 PrintPending(pending);//打印pending表 std::cout << "-------------------------------" << std::endl;}int main(){ signal(2, handler); // 0.⾃定义捕捉2号信号 // 1. 屏蔽2号信号 sigset_t block_set, old_set; sigemptyset(&block_set);//将自定义block_set设为0 sigemptyset(&old_set);//将自定义old_set设为0 sigaddset(&block_set, SIGINT); // 我们有没有修改当前进⾏的内核block表呢???1 0 // 1.1 设置进⼊进程的Block表中 sigprocmask(SIG_BLOCK, &block_set, &old_set); //SIG_BLOCK->block_set = mask|oldset 真正的修改当前进⾏的内核block表,完成了对2号信号的屏蔽! int cnt = 10; while (true) { // 2. 获取当前进程的pending信号集 sigset_t pending; sigpending(&pending); // 3. 打印pending信号集 PrintPending(pending); cnt--; // 4. 解除对2号信号的屏蔽 if (cnt == 0) { std::cout << "解除对2号信号的屏蔽!!!" << std::endl; sigprocmask(SIG_SETMASK, &old_set, &block_set); } sleep(1); }} 没有看懂的同学,可以回顾一下信号未决与阻塞的概念以及相关操作函数。 7. 信号捕捉 如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。 7.1 信号捕捉流程 由于信号处理函数的代码是在用户空间的,处理过程⽐较复杂,举例如下: 用户程序注册了 SIGQUIT 信号的处理函数 sighandler 。当前正在执行main 函数,这时发生中断或异常切换到内核态。在中断处理完毕后要返回用户态的 main 函数之前检查到有信号 SIGQUIT 递达。内核决定返回用户态后不是恢复 main 函数的上下⽂继续执行,而是执行 sighandler 函数, sighandler 和 main 函数使用不同的堆栈空间,它们之间不存在调⽤和被调⽤的关系,是两个独⽴的控制流程。sighandler 函数返回后自动执行特殊的系统调用 sigreturn 再次进⼊内核态。如果没有新的信号要递达,这次再返回用户态就是恢复 main 函数的上下文继续执行了。我们之前说过信号处理不是立即的,而是选择合适的时间,所以进程在进行信号处理的时间段如下图: 也就是说当接收到信号会被存储在pending表中,此时不会立即处理,等到进程从用户态与内核态切换时再检查pending表看是否有信号要处理。 我们可以简单理解: 用户态:执行我自己写的代码内核态:执行操作系统的代码7.2 sigaction函数#include <signal.h>int sigaction(int signo, const struct sigaction *act, struct sigaction *oact); sigaction函数可以读取和修改与指定信号相关联的处理动作。调⽤成功则返回0,出错则返回- 1。 signo:指定要操作的信号编号,例如 SIGINT、SIGTERM 等。 act:指向 sigaction 结构体的指针,根据act修改该信号的处理动作。如果不需要改变当前的信号处理方式,则可以设置为 NULL。 oact:指向 sigaction 结构体的指针,通过oact传出该信号原来的处理动作。如果不需要保存当前的信号处理方式,则可以设置为 NULL。 sigaction 结构体: struct sigaction { void (*sa_handler)(int); // 指定信号处理函数或特殊值 SIG_IGN 或 SIG_DFL void (*sa_sigaction)(int, siginfo_t *, void *); // 用于替代 sa_handler 的扩展信号处理器 sigset_t sa_mask; // 额外的信号屏蔽字,在执行信号处理器期间阻止其他信号 int sa_flags; // 特殊标志,影响信号传递行为 void (*sa_restorer)(void); // 不再使用,应设为 NULL}; 将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号;赋值为常数SIG_DFL表⽰执⾏系统默认动作;赋值为⼀个函数指针表示用自定义函数捕捉信号,或者说向内核注册了⼀个信号处理函数,该函数返回值为void,可以带⼀个int参数——通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是⼀个回调函数,不是被main函数调用,而是被系统所调用。代码示例如下: #include<iostream>#include<signal.h>void handler(int signo)//自定义处理动作{ std::cout<<"get a signal:"<<signo<<std::endl;}int main(){ struct sigaction act,oact; act.sa_handler=handler;//将sa_handler赋值为一个函数指针,自定义2号信号处理动作 ::sigaction(2,&act,&oact);//oact保存的是2号信号原来的处理动作 while(true) { ::pause();//等待 } return 0;} 其实和信号处理器功能一样,只是sigaction函数提供了对信号处理更精确的控制,相比于 signal 函数来说更为安全和灵活。 8. 结语 我们从信号定义、分类、处理谈到信号产生、信号保存最后到信号捕捉,关键在于信号处理的理解、相关的信号处理函数、信号保存的三张表——pending表、block表和handler表以及信号捕捉的理解与运用。总之,Linux进程信号是一种强大且灵活的进程间通信机制。通过合理地使用信号,可以实现进程间的异步通知、同步和通信等功能。以上就是Linux进程信号有关的内容啦~ 完结撒花~ 🥳🎉🎉———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/Renswc/article/details/144488673
-
一、文件与目录复制:cp命令1. 基础复制:文件与单目录复制cp(copy的缩写)是Linux中最常用的复制命令 ,支持文件与目录的复制操作:# 复制文件到指定目录cp 源文件 目标目录# 示例:将config.ini复制到backup目录cp config.ini backup/# 复制并重命名文件cp 旧文件名 新文件名# 示例:复制并备份配置文件cp config.ini config.ini.bak# 复制目录(需加-r参数)cp -r 源目录 目标目录# 示例:复制整个项目目录cp -r project/ project_backup/核心参数:-r(recursive):递归复制目录及其内容,是复制目录的必备参数-f(force):强制复制,覆盖已存在的目标文件,不询问-i(interactive):交互式复制,覆盖前询问用户-v(verbose):显示复制过程,输出详细信息2. 高级复制:多文件与属性保留# 复制多个文件到同一目录cp 文件1 文件2 文件3 目标目录# 示例:复制所有脚本到bin目录cp *.sh bin/# 保留文件属性复制(如权限、时间戳)cp -a 源文件 目标文件# 示例:完整备份配置文件cp -a /etc/httpd/conf/httpd.conf /backup/httpd.conf# 仅复制目录结构(不复制文件内容)cp -dR --no-dereference 源目录 目标目录参数详解:-a(archive):相当于-r -p -d的组合,保留所有文件属性-p(preserve):保留文件的权限、所有者、时间戳等属性-d:复制符号链接本身,而非链接指向的文件--no-dereference:不跟随符号链接,复制链接文件本身3. 复制场景实战(1)系统配置备份# 备份整个/etc目录(重要!需root权限)cp -a /etc /backup/etc_bak# 备份单个配置文件并添加时间戳cp -a /etc/nginx/nginx.conf /backup/nginx.conf.$(date +%Y%m%d)(2)项目文件复制# 复制src目录下所有.java文件到build目录cp src/**/*.java build/ -t build/# 排除特定文件复制cp -r project/ project_backup/ --exclude "*.log"(3)跨设备复制# 复制文件到U盘(假设U盘挂载在/mnt/usb)cp important_file.txt /mnt/usb/# 复制目录到移动硬盘cp -r project/ /mnt/hdd/project_backup/4. 复制性能优化(1)大文件复制# 使用pv显示复制进度pv large_file.iso | cp > /backup/large_file.iso# 示例输出:1.2GB 0:02:10 [9.8MB/s] [====> ] 11% ETA 0:17:20(2)断点续传(需第三方工具)# 安装rsync(系统默认已安装)yum install -y rsync# 断点续传复制rsync -avP source/ destination/(3)压缩复制# 压缩并复制目录tar -czf - source_dir/ | (cd destination_dir/; tar -xzf -)二、移动与重命名:mv命令1. 文件重命名# 重命名文件mv 旧文件名 新文件名# 示例:重命名脚本文件mv install.sh setup.sh# 批量重命名(配合通配符)mv *.txt *.md# 示例:将所有.txt文件重命名为.mdmv *.txt *.md重命名规则:目标文件名不能与当前目录下的已有文件重名支持使用通配符批量重命名重命名操作不改变文件内容,仅修改文件名2. 目录与文件移动# 移动文件到指定目录mv 文件名 目标目录# 示例:将日志文件移动到归档目录mv access.log archive/# 移动目录(无需额外参数)mv 源目录 目标目录# 示例:将项目移动到发布目录mv project/ release/# 跨磁盘分区移动(自动复制后删除源文件)mv large_file.iso /other_disk/移动原理:在同一文件系统内移动:仅修改文件指针,速度极快跨文件系统移动:本质是先复制后删除,速度受磁盘I/O影响移动目录时自动递归处理所有内容,无需-r参数3. 重命名与移动的组合应用(1)目录结构重构# 将src下的java目录重命名为java_src并移动到根目录mv src/java/ /java_src/# 调整项目目录结构mv project/{docs,documentation}/(2)批量文件整理# 按类型移动文件到对应目录mkdir -p images docs scriptsmv *.jpg *.png images/mv *.doc *.pdf docs/mv *.sh scripts/(3)临时文件清理# 将临时文件移动到垃圾桶mv temp_* trash/# 定期清理过期文件find . -name "*.log" -mtime +30 -exec mv {} old_logs/ \;4. 危险操作与安全提示(1)误操作风险场景: 误将目录移动到文件上(目标为已存在文件时,会覆盖该文件)示例错误: mv important_dir/ important_file后果: important_file被删除,important_dir被重命名为important_file(2)安全操作建议移动前先用ls -la确认源和目标对重要文件先备份:cp -a 源文件 源文件.bak && mv 源文件 目标使用-i参数开启交互模式:mv -i 文件名 目标复杂移动操作分步骤执行,避免一次性操作三、文件查找工具1. which:命令路径快速查找which命令用于查找可执行命令的路径,本质是在系统PATH变量指定的目录中搜索:# 查找命令路径which 命令名# 示例:查找ls命令的路径which ls# 输出示例:/bin/ls# 查找多个命令which ls cp mv工作原理:读取系统环境变量PATH的内容(如/usr/bin:/bin:/usr/sbin:/sbin)按顺序在每个目录中查找具有可执行权限的命令文件找到第一个匹配项后立即返回,不再继续搜索应用场景:确认命令是否存在及位置查看系统默认命令与自定义命令的优先级排查命令执行问题(如找不到命令时检查PATH设置)2. whereis:文件全方位定位whereis命令比which更强大,可查找命令、源代码和帮助文档的位置:# 查找文件位置whereis 文件名# 示例:查找ssh相关文件whereis ssh# 输出示例:# ssh: /usr/bin/ssh /usr/libexec/ssh /etc/ssh /usr/share/man/man1/ssh.1.gz /usr/share/man/man8/ssh.8.gz# 仅查找可执行文件whereis -b 文件名# 仅查找帮助文档whereis -m 文件名参数说明:-b:binary,仅查找可执行文件-s:source,仅查找源代码文件-m:manual,仅查找帮助文档-u:unfound,查找没有匹配项的文件3. find:全能型文件搜索工具find命令是Linux中最强大的文件搜索工具,支持按名称、类型、时间、大小等多种条件搜索(如果展开来讲,单独一个find命令都要写一篇文章):# 基本语法find 搜索路径 搜索条件 操作# 示例1:按名称搜索文件find /usr -name "ls"# 示例2:搜索当前目录下所有.txt文件find . -name "*.txt"# 示例3:搜索一天内修改过的文件find /var/log -mtime -1# 示例4:搜索大于100MB的文件find / -size +100M -type f核心搜索条件:名称相关:-name "模式":按文件名匹配(支持通配符)-iname "模式":不区分大小写匹配-regex "正则表达式":按正则表达式匹配时间相关:-mtime n:修改时间为n天前的文件(n为正整数)-mtime -n:n天内修改过的文件-mtime +n:n天前修改过的文件大小相关:-size n:精确大小为n的文件(b=字节,k=KB,M=MB,G=GB)-size +n:大于n的文件-size -n:小于n的文件类型相关:-type f:普通文件-type d:目录-type l:符号链接-type b:块设备-type c:字符设备4. find高级用法与操作(1)组合条件搜索# 搜索同时满足两个条件的文件find /path -条件1 -and -条件2# 示例:搜索/etc目录下大于1MB且修改时间在1周内的文件find /etc -size +1M -and -mtime -7# 搜索满足任意条件的文件find /path -条件1 -or -条件2# 示例:搜索.java或.py文件find src/ -name "*.java" -or -name "*.py"(2)对搜索结果执行操作# 对搜索结果执行命令(谨慎使用!)find 路径 条件 -exec 命令 {} \;# 示例1:删除10天前的临时文件find /tmp -name "temp_*" -mtime +10 -exec rm {} \;# 示例2:为所有.sh文件添加执行权限find . -name "*.sh" -exec chmod +x {} \;# 更安全的替代方案(使用xargs)find . -name "*.log" | xargs ls -la(3)深度限制与排除目录# 限制搜索深度(-maxdepth n)find /usr -maxdepth 2 -name "*.conf"# 排除指定目录(-path "模式" -prune)find /project -path "/project/test" -prune -o -name "*.java"四、通配符与命令路径:底层逻辑与高级技巧1. 通配符:批量操作的效率神器(1)基础通配符*:匹配任意数量的任意字符(包括0个)# 匹配所有以.txt结尾的文件ls *.txt# 匹配所有以a开头、.log结尾的文件rm a*.log?:匹配任意单个字符# 匹配文件名长度为5,且第3个字符为x的文件ls ??x??.txt# 重命名文件,将中间字符替换为amv file? .txt filea.txt[]:匹配括号内的任意一个字符# 匹配文件名以a、b或c开头的文件cp [abc]*.sh scripts/# 匹配扩展名是jpg、png或gif的图片mv image.[jpgpnggif] images/(2)扩展通配符(需启用bash扩展)# 启用扩展通配符shopt -s extglob# 示例1:匹配一个或多个数字ls *+([0-9]).txt# 示例2:匹配不是以.txt结尾的文件rm !(*.txt)# 示例3:匹配以a到f开头的文件ls @(a|b|c|d|e|f)*.sh2. 命令路径的底层逻辑(1)命令执行原理命令本质是可执行程序,存储在特定目录中(比如系统基础指令存放在 /bin路径下)系统通过PATH环境变量指定搜索命令的目录顺序执行命令时,系统按PATH顺序查找第一个匹配的可执行文件(2)查看与修改PATH# 查看当前PATHecho $PATH# 输出示例:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin# 临时添加目录到PATH(仅当前会话有效)PATH=$PATH:/new/directory# 永久添加目录到PATH(修改配置文件)echo 'export PATH=$PATH:/new/directory' >> ~/.bashrcsource ~/.bashrc(3)自定义命令优先级将自定义命令放在PATH靠前的目录(如~/bin)同名命令优先执行PATH中靠前的版本用which确认命令执行的实际路径3. 文件管理实战案例案例1:系统日志归档# 1. 查找一周前的日志文件find /var/log -name "*.log" -mtime +7 -type f# 2. 压缩并移动到归档目录find /var/log -name "*.log" -mtime +7 -type f -exec gzip {} \;find /var/log -name "*.log.gz" -mtime +7 -type f -exec mv {} /archive/logs/ \;案例2:项目文件清理# 1. 查找并删除临时文件find project/ -name "*.tmp" -exec rm {} \;# 2. 备份并删除大文件find project/ -size +100M -type f -exec cp {} backup/ \;find project/ -size +100M -type f -exec rm {} \;案例3:跨服务器文件同步# 使用rsync同步文件(增量更新,仅传输变化部分)rsync -avzP /local/source/ user@remote:/destination/# 选项说明:# -a:归档模式,保留所有属性# -v:详细输出# -z:传输时压缩# -P:显示进度并支持断点续传五、扩展知识:文件管理的高级工具与最佳实践1. rsync:高效的文件同步工具# 基本同步语法rsync [选项] 源路径 目标路径# 示例1:同步目录并显示进度rsync -avhP /data/ /backup/# 示例2:排除指定文件同步rsync -avhP /data/ /backup/ --exclude "*.log"# 示例3:远程同步(通过ssh)rsync -avzP /local/ user@remote:/remote/核心优势:增量同步:仅传输变化的部分,大幅减少数据量支持断点续传:中断后可继续传输保持文件属性:与cp -a效果一致远程同步:内置ssh支持,无需额外配置2. ln:链接文件的创建与管理(1)软链接(符号链接)# 创建软链接ln -s 源文件 链接文件# 示例:创建指向配置文件的软链接ln -s /etc/nginx/nginx.conf /usr/local/nginx/conf/nginx.conf(2)硬链接# 创建硬链接ln 源文件 链接文件# 示例:创建数据文件的硬链接ln data.txt data_hardlink.txt链接区别:软链接:类似Windows快捷方式,可跨文件系统,源文件删除后链接失效硬链接:共享同一个inode,不能跨文件系统,源文件删除后仍可通过硬链接访问3. 文件管理最佳实践(1)目录结构规范项目目录遵循src/(源代码)、docs/(文档)、bin/(可执行文件)的标准结构配置文件统一放在conf/目录,日志文件放在logs/目录临时文件使用tmp/目录,并定期清理(2)备份策略重要数据至少保留3份副本(本地、异地、云端)采用增量备份方式,减少存储占用定期验证备份数据的完整性和可恢复性(3)权限管理遵循"最小权限"原则,文件所有者为创建者,重要文件属主为root定期检查关键目录的权限(如/etc、/usr/bin)使用chmod和chown规范文件权限,避免使用777权限结语通过掌握文件复制、移动与查找的高级操作,读者将能够在Linux系统中高效管理文件资源,从简单的日常操作进阶到复杂的系统维护与数据管理。无论是cp的属性保留复制,mv的智能重命名,还是find的精准搜索,这些工具的灵活运用将成为Linux高手的必备技能。在实际应用中,建议结合rsync、ln等高级工具,建立规范的文件管理流程,确保系统数据的安全性与可维护性。———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/2302_78391795/article/details/148311368
-
在 grep 中,要输出匹配行及其前后若干行内容,可以使用以下选项:1. 输出匹配行及其后 N 行(-A)grep -A 10 "pattern" file.txt-A 10 表示显示匹配行 及之后 10 行。2. 输出匹配行及其前 N 行(-B)grep -B 10 "pattern" file.txt-B 10 表示显示匹配行 及之前 10 行。3. 输出匹配行及其前后 N 行(-C)grep -C 10 "pattern" file.txt-C 10 表示显示匹配行 及前后各 10 行(相当于 -A 10 -B 10)。示例说明假设 file.txt 内容如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 搜索数字 5 并显示前后 2 行:grep -C 2 "5" file.txt输出:3 4 5 6 7 其他实用选项显示文件名(多文件搜索时):grep -C 10 "pattern" *.txt -H忽略大小写:grep -i -C 10 "pattern" file.txt仅显示匹配部分(而非整行):grep -o -C 10 "pattern" file.txt注意事项如果匹配结果相邻,grep 会用 -- 分隔不同匹配块的输出(避免内容混淆)。若需更复杂的上下文控制(如正则匹配上下文),可结合 awk 或 sed 实现。
-
Linux主机互信配置指南:SSH密钥认证实战教程引言在分布式系统和自动化运维场景中,主机间的无密码互信访问是提升效率的关键。通过SSH密钥认证替代传统密码登录,不仅能增强安全性(避免密码泄露风险),还能为Ansible、Jenkins等工具提供无缝集成的基础。本文将详细介绍Linux主机间配置互信的完整流程,涵盖单台配置、批量部署及常见问题排查。一、SSH互信原理1. 非对称加密机制公钥(Public Key):可自由分发,用于加密数据或验证签名私钥(Private Key):必须严格保密,用于解密数据或生成签名认证过程:客户端用私钥对服务器发送的随机数签名服务器用客户端公钥验证签名验证通过即建立信任2. 与密码认证对比特性SSH密钥认证密码认证安全性无需传输密码,抗暴力破解易受字典攻击/中间人攻击便捷性一次配置永久免密每次需输入密码自动化支持完美支持需额外工具(如sshpass)二、单台主机互信配置1. 生成SSH密钥对(若不存在)# 生成RSA密钥(默认2048位) ssh-keygen -t rsa -b 4096 -C "admin@example.com" # 生成Ed25519密钥(推荐,更安全且性能更好) ssh-keygen -t ed25519 -C "admin@example.com" 参数说明:-t:指定算法(rsa/dsa/ecdsa/ed25519)-b:RSA密钥位数(至少2048)-C:添加注释(通常为用户名或用途)生成文件:~/.ssh/id_rsa(私钥)~/.ssh/id_rsa.pub(公钥)2. 配置目标主机授权# 将公钥上传到目标主机(假设用户为deploy) ssh-copy-id -i ~/.ssh/id_rsa.pub deploy@192.168.1.100 # 手动配置方式(若ssh-copy-id不可用) cat ~/.ssh/id_rsa.pub | ssh deploy@192.168.1.100 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" 3. 验证互信连接ssh -o PreferredAuthentications=publickey deploy@192.168.1.100 "hostname; uptime" 成功标志:直接进入目标主机命令行,无需输入密码三、批量主机互信配置方案方案1:使用循环脚本(适用于少量主机)#!/bin/bash USER="deploy" KEY_FILE="$HOME/.ssh/id_rsa.pub" HOSTS=("192.168.1.100" "192.168.1.101" "192.168.1.102") for host in "${HOSTS[@]}"; do echo "Configuring trust for $host..." ssh-copy-id -i "$KEY_FILE" "$USER@$host" if [ $? -eq 0 ]; then echo "✅ Success: $host" else echo "❌ Failed: $host" fi done 方案2:使用Ansible(推荐企业级方案)# playbooks/setup_ssh_trust.yml --- - name: Configure SSH Trust Between Hosts hosts: all gather_facts: no vars: ssh_user: deploy ssh_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" tasks: - name: Ensure .ssh directory exists file: path: "/home/{{ ssh_user }}/.ssh" state: directory mode: 0700 owner: "{{ ssh_user }}" group: "{{ ssh_user }}" - name: Add public key to authorized_keys lineinfile: path: "/home/{{ ssh_user }}/.ssh/authorized_keys" line: "{{ ssh_key }}" create: yes mode: 0600 owner: "{{ ssh_user }}" group: "{{ ssh_user }}" state: present - name: Validate SSH connectivity command: "ssh -o BatchMode=yes -o ConnectTimeout=5 {{ ssh_user }}@{{ inventory_hostname }} echo 'Test successful'" delegate_to: localhost ignore_errors: yes register: ssh_test changed_when: false 执行命令:ansible-playbook -i inventory.ini playbooks/setup_ssh_trust.yml -K方案3:使用Puppet/Chef(适合已有配置管理平台的环境)# Puppet示例 file { '/home/deploy/.ssh/authorized_keys': ensure => file, owner => 'deploy', group => 'deploy', mode => '0600', content => epp('ssh_keys/authorized_keys.epp'), } 四、高级配置与安全加固1. 禁用密码认证(强制密钥登录)编辑/etc/ssh/sshd_config:PasswordAuthentication no ChallengeResponseAuthentication no UsePAM no PubkeyAuthentication yes生效命令:sudo systemctl restart sshd2. 使用SSH代理转发(跳板机场景)# 配置本地SSH配置文件 cat >> ~/.ssh/config <<EOF Host jumpbox HostName 192.168.1.1 User jumpuser Host backend* ProxyJump jumpbox User deploy EOF # 测试连接 ssh backend01 "hostname" 3. 限制特定密钥功能在~/.ssh/authorized_keys中添加选项:from="192.168.1.0/24",command="/usr/bin/rsync --server" ssh-rsa AAAAB3NzaC1... 常用限制选项:from="IP/CIDR":限制来源IPcommand="CMD":强制执行特定命令permitopen="host:port":限制端口转发no-agent-forwarding:禁用代理转发五、常见问题排查1. 连接被拒绝现象:ssh: connect to host port 22: Connection refused原因:SSH服务未运行或防火墙拦截解决方案:sudo systemctl status sshd sudo ufw allow 22/tcp # Ubuntu sudo firewall-cmd --add-service=ssh --permanent # CentOS 2. 权限错误现象:Permissions 0644 for '/home/user/.ssh/id_rsa' are too open原因:SSH对密钥文件权限要求严格解决方案:chmod 600 ~/.ssh/id_rsa chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys3. 密钥认证失败现象:User deploy not allowed because account is locked排查步骤:检查目标用户是否被锁定:sudo passwd -S deploy验证authorized_keys文件格式是否正确(每行一个公钥)查看SSH日志:journalctl -u sshd -n 50 --no-pager4. 已知主机变更问题现象:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!原因:目标主机重装系统或IP被复用解决方案:ssh-keygen -R 192.168.1.100 # 删除旧记录 # 或手动编辑 ~/.ssh/known_hosts 删除对应行 六、最佳实践建议密钥管理:使用密码保护私钥(ssh-keygen -p可修改密码)考虑使用硬件安全模块(HSM)或云KMS存储高敏感密钥轮换策略:每6-12个月更换密钥对使用ssh-keygen -f old_key -e > new_key.pub生成新密钥审计监控:记录所有SSH登录事件:/var/log/auth.log使用工具如fail2ban防范暴力破解兼容性考虑:旧系统可能需要添加-oKexAlgorithms=diffie-hellman-group-exchange-sha256参数Windows主机需配置Win32-OpenSSH或使用PuTTY格式密钥总结通过SSH密钥认证实现主机互信,是构建安全自动化运维体系的基础步骤。本文从原理到实践,涵盖了从单台配置到批量部署的全流程,并提供了企业级安全加固方案。建议在实际环境中先在小规模测试,再逐步推广。掌握这些技能后,您将能轻松实现:自动化脚本的无密码执行集群节点的免密管理云原生环境的无缝集成立即开始配置您的主机互信环境,体验真正的"一键登录"吧!
-
Ansible 入门教程:从零开始掌握自动化运维利器引言在云计算和容器化时代,运维自动化已成为企业IT管理的标配。Ansible凭借其无代理架构、YAML语法和幂等性设计,迅速成为最受欢迎的自动化工具之一。本文将通过实战案例,带您快速掌握Ansible的核心概念与基础操作,实现从“手动运维”到“自动化管理”的跨越。一、Ansible 核心优势与适用场景1. 为什么选择 Ansible?无代理(Agentless):通过SSH或WinRM通信,无需在目标节点安装客户端声明式语言:使用YAML编写Playbook,直观描述系统状态幂等性:重复执行不会导致意外变更模块化设计:内置5000+模块(如yum、file、service)2. 典型应用场景批量服务器配置管理应用部署(如Nginx、Docker)持续交付(CI/CD流水线集成)云资源编排(AWS/Azure/GCP)安全合规检查二、环境准备与安装1. 控制节点要求Python 3.8+(推荐Python 3.10)目标节点需开启SSH服务(Linux)或WinRM(Windows)2. 安装方式(以Ubuntu为例)# 方法1:通过APT安装(推荐) sudo apt update sudo apt install ansible # 方法2:通过pip安装(获取最新版) pip install --user ansible # 验证安装 ansible --version3. 目录结构规划/ansible-demo/ ├── inventory/ # 主机清单目录 │ ├── dev.ini # 开发环境清单 │ └── prod.ini # 生产环境清单 ├── playbooks/ # Playbook目录 │ ├── web_setup.yml # Web服务器配置 │ └── db_backup.yml # 数据库备份 └── roles/ # 角色目录(可选)三、核心概念解析1. Inventory(主机清单)定义管理的主机和分组,支持INI或YAML格式:# inventory/dev.ini [webservers] web1 ansible_host=192.168.1.10 ansible_user=ubuntu web2 ansible_host=192.168.1.11 [dbservers] db1 ansible_host=192.168.1.20 [all:vars] ansible_ssh_private_key_file=~/.ssh/id_rsa关键参数:ansible_host:主机IP或域名ansible_user:SSH用户名ansible_port:SSH端口(默认22)ansible_ssh_pass:SSH密码(不推荐明文存储)2. Ad-Hoc 命令(临时命令)快速执行单条任务,格式:ansible <主机组> -m <模块> -a "<参数>" 实战示例:# 检查主机连通性 ansible all -m ping # 在webservers组安装Nginx ansible webservers -m apt -a "name=nginx state=present" --become # 查看Nginx服务状态 ansible webservers -m service -a "name=nginx state=started" --become3. Playbook(剧本)YAML格式的自动化脚本,定义一系列任务:# playbooks/web_setup.yml --- - name: Configure Web Servers hosts: webservers become: yes # 以root权限执行 vars: nginx_port: 8080 tasks: - name: Install Nginx apt: name: nginx state: present - name: Configure Nginx Port template: src: templates/nginx.conf.j2 dest: /etc/nginx/sites-available/default notify: Restart Nginx - name: Ensure Nginx is running service: name: nginx state: started enabled: yes handlers: - name: Restart Nginx service: name: nginx state: restarted关键元素:name:任务描述(必填)hosts:目标主机组tasks:任务列表handlers:触发器(由notify调用)vars:变量定义四、实战案例:部署WordPress网站1. 环境准备2台Ubuntu 22.04服务器(web1/web2)1台MySQL服务器(db1)2. 完整Playbook# playbooks/wordpress.yml --- - name: Setup Database Server hosts: dbservers become: yes vars: mysql_root_password: "SecurePass123!" mysql_db: wordpress mysql_user: wpuser mysql_password: "WpPass456!" tasks: - name: Install MySQL apt: name: ["mysql-server", "python3-pymysql"] state: present - name: Start MySQL Service service: name: mysql state: started enabled: yes - name: Create WordPress Database mysql_db: name: "{{ mysql_db }}" state: present login_user: root login_password: "{{ mysql_root_password }}" - name: Create MySQL User mysql_user: name: "{{ mysql_user }}" password: "{{ mysql_password }}" priv: "{{ mysql_db }}.*:ALL" state: present login_user: root login_password: "{{ mysql_root_password }}" - name: Setup Web Servers hosts: webservers become: yes vars: wp_version: 6.4.1 wp_checksum: "sha256:12345..." # 实际值需替换 tasks: - name: Install Dependencies apt: name: ["nginx", "php-fpm", "php-mysql", "wget", "unzip"] state: present - name: Download WordPress get_url: url: "wordpress.org/wordpress-{{ wp_version }}.zip" dest: /tmp/wordpress.zip checksum: "{{ wp_checksum }}" - name: Extract WordPress unarchive: src: /tmp/wordpress.zip dest: /var/www/html remote_src: yes owner: www-data group: www-data - name: Configure Nginx template: src: templates/nginx_wp.conf.j2 dest: /etc/nginx/sites-available/wordpress notify: Restart Nginx handlers: - name: Restart Nginx service: name: nginx state: restarted3. 执行Playbookansible-playbook -i inventory/prod.ini playbooks/wordpress.yml五、进阶技巧1. 变量管理Inventory变量:在group_vars/或host_vars/目录中定义Playbook变量:通过vars:或vars_files:引入注册变量:捕获命令输出- name: Get Uptime command: uptime register: uptime_result - debug: var=uptime_result.stdout2. 条件判断- name: Install Apache (if not present) apt: name: apache2 state: present when: ansible_os_family == "Debian"3. 循环结构- name: Install Multiple Packages apt: name: "{{ item }}" state: present loop: - curl - vim - htop4. 角色(Roles)将复杂任务拆分为可复用组件:roles/ ├── nginx/ │ ├── tasks/ │ │ └── main.yml │ ├── templates/ │ └── vars/ └── mysql/ 六、常见问题排查1. 连接失败错误:SSH connection failed检查目标主机SSH服务状态验证ansible_host和ansible_user配置使用-vvvv参数查看详细日志2. 权限问题错误:Failed to change ownership确保控制节点用户有目标目录的sudo权限在Playbook中添加become: yes3. 模块报错错误:module stderr: E: Could not open lock file检查目标主机包管理器是否被占用添加retries和delay参数重试七、总结与学习资源1. 核心收获掌握Inventory配置与分组管理熟练编写Ad-Hoc命令和Playbook理解变量、条件、循环等编程概念在Ansible中的应用2. 推荐学习路径官方文档:[Ansible Documentation]实战项目:使用Ansible部署Kubernetes集群编写自定义模块(Python)认证考试:Red Hat Certified Engineer (RHCE)Ansible Certified Specialist3. 工具生态Ansible Tower:企业级Web控制台(现改名为AWX开源版)Molecule:Playbook测试框架Ansible Lint:代码风格检查工具
-
炎热的天气不能影响学习技术的热情,7月份干货合集来了 ,希望可以帮到大家。1.详解MySQL中乐观锁与悲观锁的实现机制及应用场景https://bbs.huaweicloud.com/forum/thread-0232188895402649026-1-1.html2.C++寻位映射的究极密码:哈希扩展-转载https://bbs.huaweicloud.com/forum/thread-0288188731723925024-1-1.html3.【初阶数据结构】双向链表-转载https://bbs.huaweicloud.com/forum/thread-0278188731637851025-1-1.html4. 通俗易懂->哈希表详解-转载https://bbs.huaweicloud.com/forum/thread-02127188731515830029-1-1.html5.数组去重性能优化:为什么Set和Object哈希表的效率最高 -转载https://bbs.huaweicloud.com/forum/thread-0228188731443765029-1-1.html6.【数据结构】时间复杂度和空间复杂度-转载https://bbs.huaweicloud.com/forum/thread-0232188731388663024-1-1.html7.【HarmonyOS Next之旅】DevEco Studio使用指南(三十五) -> 配置构建(二)-转载https://bbs.huaweicloud.com/forum/thread-0278188730852978024-1-1.html8.在【k8s】中部署Jenkins的实践指南-转载https://bbs.huaweicloud.com/forum/thread-0255188730734591030-1-1.html9.Linux下CUDA安装全攻略 -转载https://bbs.huaweicloud.com/forum/thread-0228188730644737028-1-1.html10.Spring Boot 中的默认异常处理机制及执行流程【转载】https://bbs.huaweicloud.com/forum/thread-0228188730601126027-1-1.html11.MyBatis-Plus 自动赋值实体字段最佳实践指南【转载】https://bbs.huaweicloud.com/forum/thread-0278188730544024023-1-1.html12.【Linux】网络基础-转载https://bbs.huaweicloud.com/forum/thread-02127188730499410027-1-1.html13.一文详解php、jsp、asp和aspx的区别(小科普)【转载】https://bbs.huaweicloud.com/forum/thread-0278188730481700022-1-1.html14.如何使用Ajax完成与后台服务器的数据交互详解【转载】https://bbs.huaweicloud.com/forum/thread-0201188730427989026-1-1.html15.【Linux指南】Linux系统 -权限全面解析 -转载https://bbs.huaweicloud.com/forum/thread-0223188730394113017-1-1.html
-
案例介绍本案例基于 Versatile AI原生应用引擎的搭建《系统编程》智能助教。1 概述1.1 案例介绍Versatile AI原生应用引擎提供了丰富的AI Agent的支撑功能。本案例基于DeepSeek模型、知识库,搭建了一个简单的Agent,用于承担深圳大学《系统编程》课程助教的角色。其中:DeepSeek模型:利用出色的上下文推理能力和深度思考功能,解答学生的问题。知识库:存储了《系统编程》课程的讲义、示例代码和参考资料,用于增强输出内容的专业性、与课堂知识和案例的相关性,并缓解模型幻觉问题。1.2 适用对象高校师生社区开发者1.3 案例时间本案例总时长预计60分钟。1.4 案例流程用户提出问题;Agent向知识库请求相关的知识;Agent获取相关知识后,启动DeepSeek的深度思考;Agent向用户正式输出回答。1.5 资源总览本案例预计花费0元。资源名称规格单价(元)时长(分钟)开发者空间 AI AgentDeepSeek-R1060 2 知识库配置2.1 进入Versatile AI原生应用引擎界面,点击知识库配置《系统编程》课程所需要的资料。 2.2 点击创建知识库。 2.3 点击创建知识库,并按照如下方法创建配置知识库基础配置 数据接入 数据加工 创建索引配置注意:此处有vectorRAG和GraphRAG两种类型,分别使用向量数据库和知识图谱存储知识。笔者选择了向量数据库的方式。将文本转换成向量,不可避免要用到embedding模型(向量化模型),而平台已经为我们准备好了。 至此,知识库已经做好。 3 Agent开发3.1 返回Versatile AI原生应用引擎界面,点击右上角创建Agent。 3.2 返回Versatile AI原生应用引擎界面,点击右上角创建Agent,并按照下图,设置名称、描述、模型选择、角色设定、知识库、记忆、开场白、推荐问题等选项。然后保存并发布。 3.3 选择发布渠道和设置在发布密钥选择发布渠道:渠道有二:1)以API的形式调用;2)以Web URL的形式访问。本案例选择后者,并以此URL向学生用户开放。设置发布秘钥:请参考文档获取。最后,点击右上角发布。 发布需要等待一小会: 获得如下情况,即可说明成功发布! 4 实际效果智能助教曹老板,在用户提问后,首先查阅资料,然后深度思考,最后给出解答。 我是华为开发者布道师学生曹弈轩,正在参加【案例共创】第5期 开发者空间 AI Agent 开发 cid:link_0
-
引言在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛。当我们需要备份海量数据以节省存储资源时,tar与zip等压缩工具能将文件体积大幅缩减;当需要在不同服务器间传输文件时,scp和rz/sz提供了安全高效的传输方案;而top、sar等系统工具则能实时洞悉服务器的运行状态,帮助管理员提前发现性能瓶颈。本文将系统解析压缩解压的核心技术、跨设备文件传输的最佳实践,以及系统资源监控的全流程方案,帮助读者构建从数据压缩到远程管理的完整技能体系。一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作(1)zip压缩命令# 压缩单个文件zip 压缩包名.zip 文件名# 示例:压缩配置文件zip config.zip config.ini# 压缩目录(需加-r参数)zip -r 压缩包名.zip 目录名# 示例:压缩整个项目目录zip -r project.zip project/# 压缩时排除指定文件zip -r project.zip project/ -x "project/temp/*"# 压缩并显示进度zip -rq project.zip project/ &> /dev/null && echo "压缩完成"核心参数:-r(recursive):递归压缩目录及其内容-q(quiet):静默压缩,不显示过程-x(exclude):排除指定文件或目录-z:添加压缩说明-m:压缩后删除源文件(2)unzip解压命令# 解压到当前目录unzip 压缩包名.zip# 示例:解压配置文件包unzip config.zip# 解压到指定目录(-d参数)unzip 压缩包名.zip -d 目标目录# 示例:解压到backup目录unzip project.zip -d backup/# 查看压缩包内容(不解压)unzip -l 压缩包名.zip# 解压时覆盖文件不询问unzip -o 压缩包名.zip实用技巧:批量解压多个zip包:for f in *.zip; do unzip $f; done选择性解压:unzip project.zip "src/*.java"密码保护压缩包:zip -r -P password secure.zip sensitive_data/(注:zip密码保护强度较低,不建议用于敏感数据)2. tar:专业备份工具的终极方案注意:日常生活中我们一般都是说打包压缩,事实上,打包和压缩是两码事而对于tar来说,就将此区分开来,打包的文件后缀为.tar,压缩后的文件为.gz打包压缩后的文件为 .tar.gz 一般也简写为.tgz(1)tar打包压缩语法# 基本语法:tar [选项] 归档文件名 源文件/目录# 示例1:打包目录(不压缩)tar -cvf project.tar project/# 示例2:打包并压缩为tgz格式(gzip压缩)tar -czvf project.tgz project/# 示例3:打包并压缩为tbz2格式(bzip2压缩,压缩率更高)tar -cjvf project.tbz2 project/# 示例4:打包并压缩为txz格式(xz压缩,压缩率最高)tar -cJvf project.txz project/(2)tar解压命令# 解压tgz文件到当前目录tar -xvzf project.tgz# 解压到指定目录(-C参数)tar -xvzf project.tgz -C /backup/# 查看归档文件内容tar -tvf project.tar# 解压时排除指定文件tar -xvzf project.tgz --exclude "project/logs/*"(3)核心选项解析打包选项:-c(create):创建归档文件-v(verbose):显示详细过程-f(file):指定归档文件名(必须放在选项最后)压缩选项:-z:使用gzip压缩(.tar.gz或.tgz)-j:使用bzip2压缩(.tar.bz2)-J:使用xz压缩(.tar.xz)解压选项:-x(extract):解压归档文件-C(directory):指定解压目录--strip n:解压时移除n层目录结构3. 压缩工具对比与场景选择工具 压缩格式 压缩率 速度 适用场景zip .zip 中 快 通用文件压缩、跨平台传输gzip .gz 中 快 单文件压缩、日志归档bzip2 .bz2 高 中 备份文件、长期存储xz .xz 极高 慢 归档压缩、节省存储空间tar .tar 无 极快 仅打包不压缩的场景4. 压缩实战:日志归档与备份策略(1)按日期归档日志# 归档今天的日志并压缩tar -czvf access_log_$(date +%Y%m%d).tgz /var/log/access.log# 保留7天日志,自动删除过期归档find /backup/logs -name "access_log_*.tgz" -mtime +7 -exec rm {} \;(2)增量备份方案# 首次全量备份tar -czvf full_backup.tgz /data/# 后续增量备份(仅备份变化文件)rsync -avz --delete /data/ /data_backup/tar -czvf incremental_$(date +%Y%m%d).tgz /data_backup/(3)跨服务器压缩传输# 压缩并远程传输(一行命令完成)tar -czf - /data/ | ssh user@remote "cat > /backup/data_$(date +%Y%m%d).tgz"AI写代码bash12二、网络传输:跨设备文件交互的桥梁1. scp:安全拷贝的跨服务器传输(1)基本传输语法# 从本地复制到远程scp 本地文件/目录 user@远程IP:远程路径# 示例:上传文件到远程服务器scp config.ini root@192.168.1.100:/etc/# 从远程复制到本地scp user@远程IP:远程文件/目录 本地路径# 示例:下载远程日志文件scp root@192.168.1.100:/var/log/access.log ./# 递归复制目录(-r参数)scp -r 本地目录 user@远程IP:远程目录(2)高级传输选项# 指定SSH端口(-P参数,注意大写)scp -P 2222 config.ini root@192.168.1.100:/etc/# 显示传输进度(-v参数)scp -v big_file.iso user@remote:/backup/# 压缩传输减少带宽占用(-C参数)scp -C large_directory/ user@remote:/backup/8(3)免密传输配置# 1. 本地生成密钥对ssh-keygen -t rsa# 2. 将公钥复制到远程服务器ssh-copy-id user@remoteIP# 3. 免密传输示例scp config.ini user@remote:/etc/ # 无需输入密码2. rz/sz:交互式文件传输(1)rz:上传文件到服务器# 在终端中执行rz命令,弹出文件选择对话框rz# 示例:上传代码包到当前目录rz project.zip# 指定上传目录rz -e -y -p /data/(2)sz:从服务器下载文件# 下载单个文件sz 文件名# 示例:下载配置文件到本地sz /etc/httpd.conf# 下载多个文件sz file1.txt file2.log# 递归下载目录(需配合tar)tar -czf - directory/ | sz -(3)工具安装与配置# CentOS/RHEL安装rz/szyum install -y lrzsz# 配置rz/sz默认路径(修改~/.bashrc)export RZ_SZ_PATH="/downloads"source ~/.bashrc3. 传输工具对比与场景选择工具 协议 交互性 跨平台 大文件支持 适用场景scp SSH 非交互 是 好 脚本自动化传输、安全传输rz/sz Zmodem 交互 部分 一般 终端交互式上传下载rsync SSH 非交互 是 极好 增量同步、远程备份ftp/sftp FTP/SFTP 交互 是 好 传统文件服务器、多用户共享4. 网络传输安全与性能优化(1)安全传输实践避免使用明文传输协议(如ftp),优先使用scp/sftp定期更新服务器SSH密钥,防止中间人攻击对敏感数据先加密再传输:gpg -c sensitive_data.txt && scp sensitive_data.txt.gpg remote:(2)大文件传输优化使用rsync替代scp进行大文件增量传输:rsync -avzP --progress big_file.iso user@remote:/backup/分割文件传输后合并:# 分割文件(每个1GB)split -b 1G big_file.iso part_# 传输分割文件scp part_* user@remote:/backup/# 远程合并文件cat /backup/part_* > /backup/big_file.iso利用多线程传输工具(如aria2):aria2c -x 16 scp://user@remote/path/to/big_file.isoAI写代码bash1三、系统状态监控:服务器健康的全方位诊断1. top:实时系统监控(1)基础监控界面# 启动top进入实时监控top(2)界面解析系统概况行:top - 15:30:22 up 1 day, 23:50, 2 users, load average: 0.15, 0.20, 0.25分别为:时间、运行时间、用户数、1/5/15分钟平均负载CPU使用行:Cpu(s): 0.5%us, 0.3%sy, 0.0%ni, 99.2%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%stus:用户空间CPU,sy:内核空间,id:空闲,wa:I/O等待内存使用区:Mem: 16384M total, 15232M used, 1152M free, 768M buffersSwap: 32768M total, 0M used, 32768M free, 8960M cached进程列表区:PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND1 root 20 0 4880 340 280 S 0.0 0.0 0:01.20 systemd(3)交互操作P:按CPU占用排序M:按内存占用排序N:按PID排序k:输入PID终止进程f:自定义显示字段t:切换到CPU/内存/进程树视图2. sar:系统活动的历史记录仪(1)CPU性能监控# 查看当前CPU利用率(每1秒采样,共5次)sar -u 1 5# 查看历史CPU数据(昨天10点到12点)sar -u -f /var/log/sa/sa28 -s 10:00 -e 12:00(2)内存与swap监控# 查看内存使用情况sar -r 2 3# 查看swap使用情况sar -W 2 3(3)磁盘I/O监控# 查看磁盘读写速率sar -d 3 2# 查看top 5繁忙磁盘sar -d -o disk_sar 3 100sar -d -f disk_sar | sort -k 5 -nr | head -n 53. 系统资源查看:free、df、netstat(1)内存查看:free# 查看内存使用(默认单位KB)free# 以MB为单位显示free -m# 显示详细内存统计free -h -t(2)磁盘查看:df# 查看磁盘空间使用df# 以人类可读格式显示df -h# 查看指定目录的磁盘占用df -h /data/(3)网络查看:netstat# 查看所有网络连接netstat -an# 查看TCP连接状态netstat -ant | grep ESTABLISHED# 查看监听端口netstat -tuln4. 性能分析实战:定位系统瓶颈(1)CPU高负载排查# 1. 用top定位高CPU进程top# 2. 查看进程详细信息ps -ef | grep 高CPU进程PID# 3. 分析进程资源占用pmap 进程PID# 4. 查看进程线程状态top -H -p 进程PID(2)内存泄漏检测# 1. 监控内存变化趋势watch -n 5 'free -m | grep Mem'# 2. 查看内存占用前10的进程ps -eo pid,ppid,user,%mem,%cpu,comm | sort -k4 -nr | head -n 10# 3. 使用valgrind分析程序内存泄漏valgrind --leak-check=full ./program(3)网络拥堵分析# 1. 查看网络接口流量iftop -i eth0# 2. 分析TCP连接状态netstat -ant | awk '{print $6}' | sort | uniq -c | sort -nr# 3. 追踪网络延迟tracepath www.example.com四、系统管理工具:服务器控制的终极手段1. uname:系统信息的全景扫描(1)基本系统信息# 查看所有系统信息uname -a# 输出示例:# Linux localhost.localdomain 5.4.0-124-generic #140-Ubuntu SMP Fri Mar 24 14:29:49 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux(2)分项信息查看# 查看内核版本uname -r# 查看系统架构uname -m# 查看主机名uname -n# 查看操作系统类型uname -s(3)扩展系统信息# 查看Linux发行版信息cat /etc/os-release# 查看CPU信息lscpu# 查看硬件信息dmidecode2. shutdown:系统开关机的安全控制注意:Linux系统作为服务器使用,如非必要,不建议关机(1)关机操作# 立即关机(root权限)shutdown -h now# 10分钟后关机shutdown -h +10 "系统即将关机,请保存工作"# 取消关机shutdown -c(2)重启操作# 立即重启shutdown -r now# 定时重启shutdown -r 22:00 "夜间例行重启"(3)其他关机命令# 立即关机(简化命令)halt# 立即重启(简化命令)reboot# 关闭系统但不切断电源poweroff3. bc:命令行计算器的科学计算(1)基本计算# 启动bc计算器bc# 示例计算:10 + 2030 * 4100 / 35 ^ 2 # 5的平方sqrt(16) # 平方根(2)表达式计算# 直接计算表达式(无需进入交互模式)echo "100 * 3.14" | bc# 计算分数结果echo "scale=2; 1/3" | bc # scale指定小数位数# 计算复杂表达式echo "((2+3)*4-5)/2" | bc3)高级数学函数# 启用数学库(-l参数)bc -l# 示例函数:sin(1) # 正弦函数cos(1) # 余弦函数log(10) # 自然对数exp(1) # 指数函数五、扩展知识:压缩与系统工具的深度实践1. 压缩原理与算法选择(1)压缩核心概念无损压缩:还原后数据与原始完全一致(如zip、tar.gz)有损压缩:还原后有数据损失(如jpg、mp3,不用于系统文件)压缩率与速度权衡:xz > bzip2 > gzip > zip,速度反之(2)算法特点对比gzip:平衡压缩率与速度,适用于日志归档、临时压缩bzip2:较高压缩率,适用于备份文件、长期存储xz:最高压缩率,适合归档极少访问的历史数据zip:跨平台支持好,适合通用文件压缩传输2. 系统监控最佳实践(1)监控指标阈值设置CPU利用率:长期超过80%需关注内存空闲:低于10%需排查内存泄漏磁盘空间:剩余空间低于20%需清理网络带宽:利用率超过70%需扩容(2)自动化监控脚本# 定时监控系统状态并发送告警#!/bin/bash# 监控指标cpu_usage=$(top -bn1 | grep "Cpu" | awk '{print $2}' | cut -d'%' -f1)mem_free=$(free -m | grep Mem | awk '{print $4}')disk_free=$(df -h | grep / | awk '{print $4}' | cut -d% -f1)# 告警阈值cpu_warn=80mem_warn=100disk_warn=20# 发送告警邮件if [ $(echo "$cpu_usage > $cpu_warn" | bc -l) -eq 1 ]; then echo "CPU高负载: $cpu_usage%" | mail -s "系统告警" admin@example.comfi# 更多指标监控...3. 实战案例:服务器完整备份与迁移案例:服务器数据迁移方案# 1. 压缩源服务器数据tar -czvf data_backup.tgz /data/ /config/# 2. 传输到目标服务器scp data_backup.tgz root@new_server:/backup/# 3. 解压并验证ssh root@new_server "tar -xvzf /backup/data_backup.tgz -C /"# 4. 同步系统配置rsync -avzP --exclude "data/" root@old_server:/etc/ /etc/# 5. 启动服务并检查systemctl restart all-servicestop -15结语通过掌握压缩、网络传输与系统监控的核心工具,读者将能够高效管理服务器数据生命周期,从本地压缩归档到跨服务器安全传输,再到系统运行状态的实时监控与性能优化。无论是tar的专业备份方案,scp的安全远程传输,还是top与sar的多维性能分析,这些工具的灵活运用将成为Linux系统管理员的核心竞争力。在实际运维中,建议结合自动化脚本与监控平台,建立完善的数据备份策略与系统健康检查机制,确保服务器稳定高效运行。———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/2302_78391795/article/details/148311922
-
在当今的高性能计算和人工智能领域,CUDA(Compute Unified Device Architecture)已经成为不可或缺的技术。它是由NVIDIA推出的并行计算平台和编程模型,能够充分利用NVIDIA GPU的强大计算能力,为各种计算密集型任务提供加速。本文将详细介绍在Linux系统下安装CUDA的全过程,包括概念讲解、安装步骤、代码示例、应用场景、注意事项以及相关架构图和流程图,帮助读者快速上手并掌握CUDA的使用。一、CUDA简介(一)什么是CUDACUDA是一种并行计算平台和编程模型,它允许开发者使用C、C++、Fortran等常见的编程语言编写程序,并将这些程序运行在NVIDIA的GPU上。通过CUDA,开发者可以充分利用GPU的多核架构,实现大规模并行计算,从而显著提高计算效率。(二)CUDA的优势强大的并行计算能力:GPU拥有成千上万个核心,能够同时处理大量任务,这使得CUDA在处理大规模数据和复杂计算时具有显著优势。与NVIDIA硬件紧密结合:CUDA专为NVIDIA的GPU设计,能够充分利用硬件的特性,提供高效的计算性能。广泛的生态系统:CUDA拥有丰富的库和工具,如cuBLAS、cuFFT等,为开发者提供了强大的支持。跨平台支持:CUDA不仅支持Linux系统,还支持Windows和macOS,开发者可以在不同的操作系统上开发和运行CUDA程序。(三)CUDA的架构CUDA的架构主要由以下几个部分组成:主机(Host):主机是指运行CUDA程序的CPU,它负责管理程序的执行流程和内存分配。设备(Device):设备是指NVIDIA的GPU,它负责执行并行计算任务。CUDA内核(Kernel):CUDA内核是运行在GPU上的函数,它由主机调用,并在设备上执行。内存空间:CUDA支持多种内存空间,包括全局内存、共享内存和寄存器等,开发者可以根据需要选择合适的内存类型。二、CUDA安装前的准备在安装CUDA之前,需要确保系统满足以下条件:(一)硬件要求NVIDIA GPU:CUDA仅支持NVIDIA的GPU,因此需要确保系统中安装了支持CUDA的NVIDIA显卡。可以通过访问NVIDIA官方网站查询支持CUDA的GPU型号。 在 https://developer.nvidia.com/cuda-gpus 查看 GPU 是否支持CUDA。兼容的主板和电源:确保主板支持NVIDIA显卡,并且电源能够提供足够的功率以满足显卡的需求。(二)软件要求Linux操作系统:CUDA支持多种Linux发行版,如Ubuntu、CentOS等。本文将以Ubuntu为例进行说明。uname -m && cat /etc/*releasex86_64DISTRIB_ID=UbuntuDISTRIB_RELEASE=22.07AI写代码gcc --versiongcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0AI写代码驱动程序:需要安装最新版本的NVIDIA驱动程序,驱动程序版本需要与CUDA版本兼容。可以通过访问NVIDIA官方网站下载并安装驱动程序。 https://developer.nvidia.com/cuda-gpus 注意选择正确版本如果您之前安装过 CUDA(例如为12.1版本),需要先使用 sudo /usr/local/cuda-12.1/bin/cuda-uninstaller 卸载。如果该命令无法运行,可以直接:sudo rm -r /usr/local/cuda-12.1/sudo apt clean && sudo apt autocleanAI写代码卸载完成后运行以下命令并根据提示继续安装:wget https://developer.download.nvidia.com/compute/cuda/12.2.0/local_installers/cuda_12.2.0_535.54.03_linux.runsudo sh cuda_12.2.0_535.54.03_linux.runAI写代码注意:在确定 CUDA 自带驱动版本与 GPU 是否兼容之前,建议取消 Driver 的安装。完成后输入 nvcc -V 检查是否出现对应的版本号,若出现则安装完成。开发工具:需要安装C/C++编译器(如gcc)和make工具,这些工具用于编译CUDA程序。(三)安装步骤更新系统:在安装CUDA之前,建议先更新系统,以确保系统中的软件包是最新的。可以使用以下命令更新系统sudo apt updatesudo apt upgradeAI写代码安装NVIDIA驱动程序:可以通过访问NVIDIA官方网站下载驱动程序安装包,并按照说明进行安装。安装完成后,可以通过以下命令验证驱动程序是否安装成功nvidia-smiAI写代码如果安装成功,将显示驱动程序版本和GPU状态。三、CUDA安装过程(一)下载CUDA安装包访问NVIDIA官方网站的CUDA下载页面,选择适合的CUDA版本和操作系统版本。CUDA版本需要与NVIDIA驱动程序版本兼容,否则可能会出现兼容性问题。下载CUDA安装包,安装包通常是一个.run文件或一个.deb文件。(二)安装CUDA安装.run文件:如果下载的是.run文件,可以通过以下命令安装CUDAsudo chmod +x cuda_installer.runsudo ./cuda_installer.runAI写代码在安装过程中,需要根据提示进行操作,包括接受许可协议、选择安装路径等。安装.deb文件:如果下载的是.deb文件,可以通过以下命令安装CUDAsudo dpkg -i cuda-repo-<distro>_<version>_amd64.debsudo apt updatesudo apt install cudaAI写代码其中<distro>表示Linux发行版,<version>表示CUDA版本。(三)配置环境变量安装完成后,需要配置环境变量,以便能够正确使用CUDA。可以将以下内容添加到~/.bashrc文件中:export PATH=/usr/local/cuda/bin:$PATHexport LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATHAI写代码然后执行以下命令使环境变量生效:source ~/.bashrcAI写代码(四)验证CUDA安装可以通过运行以下命令验证CUDA是否安装成功:nvcc --versionAI写代码如果安装成功,将显示CUDA版本信息。四、CUDA开发环境搭建(一)安装CUDA ToolkitCUDA Toolkit是CUDA开发的核心工具,它提供了编译器、调试器、性能分析工具等。可以通过以下命令安装CUDA Toolkit:sudo apt install cuda-toolkitAI写代码(二)安装CUDA驱动程序CUDA驱动程序是CUDA运行的基础,它负责管理GPU的硬件资源。可以通过以下命令安装CUDA驱动程序:sudo apt install nvidia-cuda-toolkitAI写代码(三)安装CUDA运行时库CUDA运行时库是CUDA程序运行时所需的库文件,可以通过以下命令安装CUDA运行时库:sudo apt install libcudart7AI写代码(四)安装CUDA示例程序CUDA示例程序可以帮助开发者快速了解CUDA的编程模型和开发流程。可以通过以下命令安装CUDA示例程序:sudo apt install cuda-samplesAI写代码五、CUDA编程基础(一)CUDA程序结构CUDA程序通常由主机代码和设备代码组成。主机代码运行在CPU上,负责管理程序的执行流程和内存分配;设备代码运行在GPU上,负责执行并行计算任务。(二)CUDA内核函数CUDA内核函数是运行在GPU上的函数,它由主机调用,并在设备上执行。内核函数的定义方式与普通C函数类似,但需要使用__global__关键字修饰。(三)CUDA内存管理CUDA提供了多种内存管理方式,包括全局内存、共享内存和寄存器等。开发者可以根据需要选择合适的内存类型,以提高程序的性能。(四)代码示例以下是一个简单的CUDA程序示例,它实现了向量加法运算:#include <stdio.h> __global__ void vector_add(int *a, int *b, int *c, int n) { int index = threadIdx.x + blockIdx.x * blockDim.x; if (index < n) { c[index] = a[index] + b[index]; }} int main() { int n = 1024; int *a, *b, *c; int *d_a, *d_b, *d_c; // 分配主机内存 a = (int *)malloc(n * sizeof(int)); b = (int *)malloc(n * sizeof(int)); c = (int *)malloc(n * sizeof(int)); // 初始化主机内存 for (int i = 0; i < n; i++) { a[i] = i; b[i] = i; } // 分配设备内存 cudaMalloc((void **)&d_a, n * sizeof(int)); cudaMalloc((void **)&d_b, n * sizeof(int)); cudaMalloc((void **)&d_c, n * sizeof(int)); // 将数据从主机内存复制到设备内存 cudaMemcpy(d_a, a, n * sizeof(int), cudaMemcpyHostToDevice); cudaMemcpy(d_b, b, n * sizeof(int), cudaMemcpyHostToDevice); // 调用CUDA内核函数 int blockSize = 256; int numBlocks = (n + blockSize - 1) / blockSize; vector_add<<<numBlocks, blockSize>>>(d_a, d_b, d_c, n); // 将结果从设备内存复制到主机内存 cudaMemcpy(c, d_c, n * sizeof(int), cudaMemcpyDeviceToHost); // 输出结果 for (int i = 0; i < n; i++) { printf("%d + %d = %d\n", a[i], b[i], c[i]); } // 释放内存 free(a); free(b); free(c); cudaFree(d_a); cudaFree(d_b); cudaFree(d_c); return 0;}AI写代码该程序首先在主机上分配内存并初始化数据,然后将数据从主机内存复制到设备内存,接着调用CUDA内核函数在设备上执行向量加法运算,最后将结果从设备内存复制回主机内存并输出。六、CUDA应用场景(一)深度学习CUDA在深度学习领域得到了广泛应用,许多深度学习框架如TensorFlow、PyTorch等都支持CUDA加速。通过CUDA,可以显著提高神经网络的训练和推理速度,从而加速深度学习模型的开发和部署。(二)科学计算CUDA在科学计算领域也具有重要应用,例如在物理模拟、化学计算、生物信息学等领域。通过CUDA,可以利用GPU的强大计算能力,快速处理大规模数据和复杂计算任务,提高科学研究的效率。(三)图像处理CUDA在图像处理领域也有广泛应用,例如在图像识别、图像增强、图像分割等方面。通过CUDA,可以实现高效的图像处理算法,提高图像处理的速度和质量。(四)金融计算CUDA在金融计算领域也具有重要应用,例如在风险评估、投资组合优化、金融市场模拟等方面。通过CUDA,可以快速处理大量金融数据,提高金融计算的效率和准确性。七、CUDA安装注意事项(一)驱动程序版本兼容性CUDA版本需要与NVIDIA驱动程序版本兼容,否则可能会出现兼容性问题。在安装CUDA之前,需要确保驱动程序版本与CUDA版本兼容。(二)安装路径选择在安装CUDA时,可以选择默认的安装路径(通常是/usr/local/cuda),也可以选择自定义安装路径。如果选择自定义安装路径,需要确保环境变量配置正确。(三)依赖包安装在安装CUDA时,需要确保系统中安装了所有必要的依赖包,否则可能会出现安装失败或运行错误。可以通过运行以下命令安装依赖包:sudo apt install build-essentialAI写代码(四)CUDA版本选择在安装CUDA时,需要根据实际需求选择合适的CUDA版本。不同版本的CUDA在功能和性能上可能存在差异,需要根据实际需求进行选择。八、总结本文详细介绍了在Linux系统下安装CUDA的全过程,包括概念讲解、安装步骤、代码示例、应用场景、注意事项以及相关架构图和流程图。通过本文的介绍,读者可以快速掌握CUDA的安装和使用方法,为后续的并行计算开发打下坚实的基础。CUDA作为一种强大的并行计算平台,具有广泛的应用前景,希望本文能够对读者有所帮助。———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/csdn122345/article/details/147722895
-
一、TCP/IP五层模型为什么要有TCP/IP?单个计算机内部,硬件和硬件之间通过线路直接连接完成通信,那两台相隔长距离的计算机之间也会需要通信。而长距离通信的数据传输,又会有以下问题:靠什么传播?传播到哪里(目的地)?传到目的地之间要经过哪里?传播过程中出现丢包(错误)?传到了以后,怎么处理?TCP/IP就是针对上述问题的解决长距离传输问题的解决方案!TCP/IP五层模型是网络通信的框架(所有OS都要遵守,是标准),它将复杂的网络通信过程分为五个层次,每一层都有特定的功能。 下面用通俗易懂的方式介绍每一层的作用,并举例说明:1. 物理层(Physical Layer)作用:负责实际的比特流(0和1)传输,通过物理介质(如电缆、光纤、无线电波)传递信号。例子:就像快递员搬运包裹时使用的“交通工具”(卡车、飞机、自行车),物理层就是网络数据的“车轮”,负责把数据从A点搬到B点。比如你家的Wi-Fi信号、网线的电流、光纤里的光脉冲都属于这一层。2. 数据链路层(Data Link Layer)作用:将比特流组织成“帧”(Frame),并检查传输错误(如丢包、干扰)。通过MAC地址(如00-1A-2B-3C-4D)在局域网内直接通信。例子:就像快递员在小区内送货时,根据“门牌号”(MAC地址)把包裹投递到具体的家庭。比如你家的路由器通过Wi-Fi将数据传给手机时,就是通过MAC地址找到你的设备。3. 网络层(Network Layer)作用:通过IP地址(如192.168.1.1)在不同网络之间路由数据包。核心协议是IP(Internet Protocol)。例子:就像快递公司的“分拣中心”,决定包裹从北京到上海要走哪条高速公路(路由选择)。比如你访问百度时,数据包会经过多个路由器跳转,最终到达百度的服务器。4. 传输层(Transport Layer)作用:确保数据可靠传输(如TCP协议会重传丢失的数据包)或快速传输(如UDP协议)。通过端口号(如80用于网页)区分不同应用程序。例子:就像快递员把包裹送到你家后,还要确认是给“爸爸”(端口443)还是“孩子”(端口80)的。比如你刷视频(UDP)和网上付款(TCP)对可靠性的要求不同,传输层会分别处理。5. 应用层(Application Layer)作用:直接面向用户,提供具体的网络服务(如网页、邮件、文件传输)。例子:就像你使用的“快递服务内容”:网购(HTTP)、发邮件(SMTP)、视频通话(WebRTC)。比如你打开浏览器访问网页时,用的就是HTTP协议(属于应用层)。6. 总结:网购类比物理层:快递卡车在路上跑。数据链路层:卡车司机按“门牌号”(MAC地址)把包裹送到你家小区。网络层:快递公司决定包裹从杭州到北京走哪条路线(IP地址)。传输层:确保包裹完整送到,如果丢了就重发(TCP),或直接扔给你不管是否完整(UDP)。应用层:你拆开包裹,看到买的是一本书(HTTP)或一件衣服(FTP)。这样分层的好处是:每一层只需专注自己的任务,其他层的变化不会影响整体。7. 每层所处的位置 协议所谓协议,就是通信双方都认识的结构化的数据类型(共识)比如一个int占4个字节,因为我们知道它占4个字节,所以我们&拿到最低字节的地址也能访问整个int,这就是一种协议!假设在传输层定义了一个协议(结构体),传输的时候,两台主机都按这个协议标准,则 B 收到 A 发来的结构体,自然就知道里面是什么内容。对于不同的操作系统,他们的网络协议栈一定是一样的8. 广域网和局域网从主机 A 传输到主机 B ,整个过程,靠的是广域网,遵循的是TCP/IP。但是中途,从一个路由器到另一个路由器(或者从一个主机到一个路由器),靠的是局域网(局域网的标准不只一个),广域网可连接多个局域网。二,局域网网络传输流程(以太网为例)1. 认识MAC地址对于每一个主机,在出厂的时候,每个网卡上都会有一个标识唯一的地址——MAC地址(不能修改)MAC地址的特点用来识别数据链路层中相连的节点长度为 48 位, 及 6 个字节. 一般用 16 进制数字加上冒号的形式来表示(例如:08:00:27:03:fb:19)2. 局域网通信当多个主机连接同一个以太网时,一个主机往网络里面发信息,所有主机都能收到。但是,信息中会标识接收主机的mac地址,每个主机将自己的mac地址与之比较。如果相同则处理信息,不相同则丢弃信息。 以太网有以下规定:以太网中,任何时刻,只允许一台机器向网络中发送数据(类似:线程互斥)如果有多台同时发送,会发生数据干扰,我们称之为数据碰撞所有发送数据的主机要进行碰撞检测和碰撞避免比如,碰撞检测发现碰撞了,就sleep等一下发(碰撞避免)没有交换机的情况下,一个以太网就是一个碰撞域局域网通信的过程中,主机对收到的报文确认是否是发给自己的,是通过目标mac地址判定其他局域网无限LAN的原理和以太网相同,不过是无线。令牌环网的原理:只允许拿着令牌的主机发(互斥锁)3. 以两台主机通信为例当两台主机在以太网内通信,实际上,发送信息的是协议栈的应用层。在网络传输的过程中,数据不是直接发送给对方主机的,而是先要自定向下将数据交付给下层协议,最后由底层发送,然后由对方主机的底层来进行接受,在自底向上进行向上交付数据要通过协议栈发送到网络上,(自顶向下时)每层协议都要加上一个数据首部(本协议层的报头),我们称为封装首部信息中包含了一些类似于首部有多长, 载荷(payload)有多长, 上层协议是什么等信息数据到达目标主机以后,(自底向上时)每层协议再剥掉相应的首部(解包),根据首部中的 “上层协议字段” 将数据交给对应的上层协议处理(分用)除了报头,剩下的叫做有效载荷报文 = 报头 + 有效载荷任何一层都只关注自己层的报头(都认为自己在和对方同层的协议直接通信,实际上是经过了自顶向下的封包和自底向上的解包和分用的)数据封装数据解包三,跨网络传输流程1. 认识IP地址IP地址是分配给连接到计算机网络的设备的唯一标识符比如两个主机连接了同一个WIFI,则会被分配对应的IP地址(内网IP),它是设备在网络中的 “门牌号”IP 协议有两个版本, IPv4 和 IPv6(默认指IPv4)IPv4 来说, IP 地址是一个 4 字节, 32 位的整数。例如 192.168.0.1,用点分割的每一个数字表示一个字节, 范围是 0 - 255我们可以把IP地址看做是“最终地址”(逻辑地址),而MAC地址是“下一站地址”(物理地址)IP地址网络层有效,MAC地址数据链路层有效2. 跨网络传输流程跨网段的主机的数据传输。数据从一台计算机到另一台计算机传输过程中要经过一个或多个路由器。以下图为例,A 的数据要通过以太网和令牌网两个不同的网才能传到B。首先,用户 A 的数据经过层层封包到了网络层,网络层给数据加上 IP 头部src(源IP):192.168.2.2(用户 A 的局域网 IP)dst(目标IP):172.168.2.2(用户 B 的 IP,属于另一个网段,通过前缀判断)然后,主机 A 网络层对比目标 IP 和自身所在的网段,发现:数据不是发给本网段主机的,必须通过路由器转发到其他网段如果发现和自身所在网段相同,那其实就是往局域网里发所以,网络层数据需要向下传递到数据链路层,封装MAC头部源MAC:主机A的目标MAC:路由器的局域网侧 MAC接着,数据帧会通过 网卡 从主机 A 发送到 路由器(通过 MAC 地址)路由器收到数据帧以后,会解包向上传,在网络层检查目标 IP,发现网段对应的出口是路由器的另一块网卡于是,又会把数据往下传递到数据链路层,重新封装MAC头部源MAC:路由器外网侧网卡的 MAC目标MAC:主机 B 的最后,封装后的数据帧通过路由器外网侧网卡发送,最终到达用户 B 的网卡我们可以发现:目的 IP 是一种长远目标,Mac 是下一阶段目标,目的 IP 是路径选择的重要依据,mac 地址是局域网转发的重要依据———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/tan_run/article/details/148472524
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签