[{"content":"1.分支和循环（下) 一.随机数生成 (1).rand (2).srand (3)time (4)设置随机数的范围 二.猜数字游戏实现 分支和循环（下) 在本节内容当中，主要讨论了关于随机函数相关的知识点并做一些应用，也就是说讨论关于猜数字的游戏。\n一.随机数生成 随机函数主要有rand,srand,函数，我们将会讨论什么是随机数种子，为何说rand函数不是我们的随机函数，以及用time函数实现srand的随机种子，进而实现了对于随机数的求解。\n(1).rand C语言提供了求解随机数的方法：\nint rand(void); 但是呢，由于rand函数是为随机函数是通过某种算法生成的随机数，不是真正的随机数，下面就为大家试一下:\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int main() { printf(\u0026#34;%d\\n\u0026#34;, rand()); printf(\u0026#34;%d\\n\u0026#34;, rand()); printf(\u0026#34;%d\\n\u0026#34;, rand()); printf(\u0026#34;%d\\n\u0026#34;, rand()); printf(\u0026#34;%d\\n\u0026#34;, rand()); return 0; )\n由此可以知道的，是rand两次所生成的结果是一样的，那么可以说，rand不是一个正常的生成随机数的函数，因为他默认的随机种子数是1，如果要生成不同的随机数，就要让种子是变化的。\n(2).srand 下面我就介绍关于srand函数，只要在使用rand之前，使用srand函数，通过 srand函数的参数seed来设置rand函数生成随机数的时候的种子，只要种子在变化，每次生成的随机数序列也就变化起来了, 那也就是说给srand的种子是如果是随机的，rand就能生成随机数；在生成随机数的时候又需要一个随 机数，这就矛盾了.\n(3)time 因为时间是经常变化的，在c当中有一个函数是time,因此我们可以使用它时间作为随机数种子。在C当中的定义如下：\ntime_t time (time_t* timer); 如果 timer 是NULL，就只返回这个时间的差值。time函数返回的这个时间差也被叫做：时间戳。 使用time函数的时候，需要包含头文件：time.h 书写生成随机函数的代码如下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; int main() { //使用time函数的返回值设置种子 //因为srand的参数是unsigned int类型，我们将time函数的返回值强制类型转换 srand((unsigned int)time(NULL)); printf(\u0026#34;%d\\n\u0026#34;, rand()); printf(\u0026#34;%d\\n\u0026#34;, rand()); printf(\u0026#34;%d\\n\u0026#34;, rand()); printf(\u0026#34;%d\\n\u0026#34;, rand()); printf(\u0026#34;%d\\n\u0026#34;, rand()); return 0; } 得到的结果是： 可以知道的是，两次得到的随机数并不像第一次那样—二次得到的，是一样的，这个函数可以成功的实现对于随机数的求解，这就是我们需要的函数。\n(4)设置随机数的范围 在这里我为大家介绍关于如何精确调整随机数的取值范围：\n比如说：从0到99之间的随机值就是:\nrand()%100; 那么显而易见的是，从1到100之间的随机数取值就是这个：\nrand()%100+1; 显而易见的是，我们可以得到这样一个规律，从100到200随机数就是100+rand（）%200-100。 那么我们可以用一个公式表达: 假如从a到b之间的随机值范围那么可以用以上经验做出解答，是a+rand()%b-a,这个公式.\n我们可以用这个公式，对猜数字进行解答，下面就是详细的解答。\n二.猜数字游戏实现 我们，写出了这些代码，可以实现猜数字的功能：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; void game() { int r = rand()%100+1; int guess= 0; while(1) { printf(\u0026#34;请猜数字\u0026gt;:\u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;guess); if(guess \u0026lt; r) { printf(\u0026#34;猜小了\\n\u0026#34;); } else if(guess \u0026gt; r) { printf(\u0026#34;猜大了\\n\u0026#34;); } else { printf(\u0026#34;恭喜你，猜对了\\n\u0026#34;); break; } } } void menu() { printf(\u0026#34;***********************\\n\u0026#34;); printf(\u0026#34;****** 1. play ******\\n\u0026#34;); printf(\u0026#34;****** 0. exit ******\\n\u0026#34;); printf(\u0026#34;***********************\\n\u0026#34;); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf(\u0026#34;请选择:\u0026gt;\u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;input); switch(input) { case 1: game(); break; case 0: printf(\u0026#34;游戏结束\\n\u0026#34;); break; default: printf(\u0026#34;选择错误，重新选择\\n\u0026#34;); break; } }while(input); return 0; } 我们的到了运行结果如下： 我们对这段代码进行优化，可以假设玩游戏时，只允许5次机会，那么可以书写如下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; void game() { int r = rand() % 100 + 1; int guess = 0; int count = 5; while (count) { printf(\u0026#34;\\n你还有%d次机会\\n\u0026#34;, count); count--; printf(\u0026#34;请猜数字\u0026gt;:\u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;guess); if (guess \u0026lt; r) { printf(\u0026#34;猜小了\\n\u0026#34;); } else if (guess \u0026gt; r) { printf(\u0026#34;猜大了\\n\u0026#34;); } else { printf(\u0026#34;恭喜你，猜对了\\n\u0026#34;); break; } } if (count == 0) { printf(\u0026#34;你失败了，正确值是:%d\\n\u0026#34;, r); } } void menu() { printf(\u0026#34;***********************\\n\u0026#34;); printf(\u0026#34;****** 1. play ******\\n\u0026#34;); printf(\u0026#34;****** 0. exit ******\\n\u0026#34;); printf(\u0026#34;***********************\\n\u0026#34;); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf(\u0026#34;请选择:\u0026gt;\u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;input); switch (input) { case 1: game(); break; case 0: printf(\u0026#34;游戏结束\\n\u0026#34;); break; default: printf(\u0026#34;选择错误，重新选择\\n\u0026#34;); break; } } while (input); return 0; } 同时呢，大家可用这个网上的编译器，它挺好用的：,大家可以点击一下。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E5%88%86%E6%94%AF%E5%92%8C%E5%BE%AA%E7%8E%AF%E4%B8%8B/","summary":"\u003ch2 id=\"1分支和循环下\"\u003e1.分支和循环（下)\u003c/h2\u003e\n\u003ch3 id=\"一随机数生成\"\u003e一.随机数生成\u003c/h3\u003e\n\u003ch4 id=\"1rand\"\u003e(1).rand\u003c/h4\u003e\n\u003ch4 id=\"2srand\"\u003e(2).srand\u003c/h4\u003e\n\u003ch4 id=\"3time\"\u003e(3)time\u003c/h4\u003e\n\u003ch4 id=\"4设置随机数的范围\"\u003e(4)设置随机数的范围\u003c/h4\u003e\n\u003ch3 id=\"二猜数字游戏实现\"\u003e二.猜数字游戏实现\u003c/h3\u003e\n\u003ch2 id=\"分支和循环下\"\u003e\u003cstrong\u003e分支和循环（下)\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e\u003cstrong\u003e在本节内容当中，主要讨论了关于随机函数相关的知识点并做一些应用，也就是说讨论关于猜数字的游戏\u003c/strong\u003e\u003c/strong\u003e。\u003c/p\u003e\n\u003ch3 id=\"一随机数生成-1\"\u003e\u003ccode\u003e 一.随机数生成\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cem\u003e随机函数主要有rand,srand,函数，我们将会讨论什么是随机数种子，为何说rand函数不是我们的随机函数，以及用time函数实现srand的随机种子，进而实现了对于随机数的求解\u003c/em\u003e。\u003c/p\u003e\n\u003ch4 id=\"1rand-1\"\u003e(1).rand\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003eC语言提供了求解随机数的方法：\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eint rand(void);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e但是呢，由于rand函数是为随机函数是通过某种算法生成的随机数，不是真正的随机数，下面就为大家试一下:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include \u0026lt;stdio.h\u0026gt;\n#include \u0026lt;stdlib.h\u0026gt;\nint main()\n{\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nreturn 0;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%88%86%E6%94%AF%E5%92%8C%E5%BE%AA%E7%8E%AF%E4%B8%8B/images/0d91b1a346e3767ef41cbdc09424ff4c.png\"\u003e)\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%88%86%E6%94%AF%E5%92%8C%E5%BE%AA%E7%8E%AF%E4%B8%8B/images/a09ac5388163d77441b0a368dbdc7331.png\"\u003e\n由此可以知道的，是rand两次所生成的结果是一样的，那么可以说，\u003ccode\u003erand\u003c/code\u003e不是一个正常的生成随机数的函数，因为他默认的随机种子数是1，如果要生成不同的随机数，就要让种子是变化的。\u003c/p\u003e\n\u003ch4 id=\"2srand-1\"\u003e(2).srand\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003e下面我就介绍关于\u003ccode\u003esrand\u003c/code\u003e函数\u003c/strong\u003e，只要在使用\u003ccode\u003erand\u003c/code\u003e之前，使用\u003ccode\u003esrand\u003c/code\u003e函数，通过 \u003ccode\u003esrand\u003c/code\u003e函数的参数\u003ccode\u003eseed\u003c/code\u003e来设置\u003ccode\u003erand\u003c/code\u003e函数生成随机数的时候的种子，只要种子在变化，每次生成的随机数序列也就变化起来了,\n那也就是说给\u003ccode\u003esrand\u003c/code\u003e的种子是如果是随机的，\u003ccode\u003erand\u003c/code\u003e就能生成随机数；在生成随机数的时候又需要一个随\n机数，这就矛盾了.\u003c/p\u003e\n\u003ch4 id=\"3time-1\"\u003e(3)time\u003c/h4\u003e\n\u003cp\u003e因为时间是经常变化的，在\u003ccode\u003ec\u003c/code\u003e当中有一个函数是\u003ccode\u003etime\u003c/code\u003e,因此我们可以使用它时间作为随机数种子。在\u003ccode\u003eC\u003c/code\u003e当中的定义如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etime_t time (time_t* timer);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003e如果 timer 是NULL，就只返回这个时间的差值。time函数返回的这个时间差也被叫做：时间戳。\n使用time函数的时候，需要包含头文件：time.h\u003c/strong\u003e\n书写生成随机函数的代码如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include \u0026lt;stdio.h\u0026gt;\n#include \u0026lt;stdlib.h\u0026gt;\n#include \u0026lt;time.h\u0026gt;\nint main()\n{\n//使用time函数的返回值设置种子\n//因为srand的参数是unsigned int类型，我们将time函数的返回值强制类型转换\nsrand((unsigned int)time(NULL));\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nprintf(\u0026#34;%d\\n\u0026#34;, rand());\nreturn 0;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e得到的结果是：\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%88%86%E6%94%AF%E5%92%8C%E5%BE%AA%E7%8E%AF%E4%B8%8B/images/f9cdebfc5425f8c1103f26e10c95c9f9.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%88%86%E6%94%AF%E5%92%8C%E5%BE%AA%E7%8E%AF%E4%B8%8B/images/1616a81838c5a7b9d4957404d50ffa74.png\"\u003e\n可以知道的是，两次得到的随机数并不像第一次那样—二次得到的，是一样的，这个函数可以成功的实现对于随机数的求解，这就是我们需要的函数。\u003c/p\u003e","title":"【分支和循环（下）】"},{"content":"【实战】DNS建设案例: 0.需求\n1.准备工作\n2.进行标准化的服务器配置\n1.修改ip\n2.修改主机名\n3.修改hosts文件\n4.免密配置\n3.实操流程\n4.在node1和node2中安装相关服务\n1.node1上面安装相关软件\n1.先在node1上面安装jdk软件包\n1.环境变量配置\n2.任意目录测试一下\n2.最后安装tomcat服务器\n1,从官网下载阿帕奇的tar的包\n2.解压这个包\n3.运行这个软件，并在浏览器查看\n2.在node2安装相关软件\n1.安装NFS\n1.开启服务\n2.开放端口\n3.创建共享目录\n4.修改配置文件\n5.验证是否成功共享\n6.挂载使用\n7.验证\n3安装ftp软件\n1.创建文件夹\n2.安装软件\n3.修改配置\n4.启动并配置 vsftpd 服务\n5.配置防⽕墙\n6.连接FTP服务器\n5.在node3中配置dns服务器\n1.修改named.conf这个文件\n2.修改vi /etc/named.rfc1912.zones\n3.修改/var/named/itcast.local.db\n4.修改/var/named/192.168.149.rev文件\n5.检测\n6.启动服务以及开放端口\n6.测试操作\n6.总结\n0.需求 目前我们在node2节点中已经有FTP服务器、NFS服务器， 同时大家需要在node1中部署一个tomcat服务器。 请对三个服务设置对应得域名，并基于DNS进行内网解析，从而实现基于域名访问，要求DNS服务器架设至node3， 配置后， 不管是在node1还是node2都可以基于此DNS进行访问\nNFS服务器域名： nfs.itcast.local FTP服务器域名： ftp.itcast.local tomcat服务器域名： www.itcast.local FTP服务器要求: 1- 禁锢FTP目录： /export/ftp 2- 禁止匿名用户访问，可以使用系统中任意存在用户访问即可 3- 不管在服务器还是在windows中， 都可以直接使用域名的方式访问到ftp服务器 windows中，建议使用filezilla.exe 软件来连接 NFS服务器： 共享目录： /export/nfs 要求 node1在挂载这个目录的时候，请使用域名方式进行挂载即可（测试 临时挂载即可） 1.准备工作 我们在进行这个综合案例的时候，需要准备3台主机，分别是node1,node2,node3 而node3的话，我们需要对node1克隆就可以得到了,开启这三台Linux主机就可以进行下一个步骤了\n2.进行标准化的服务器配置 因为node1和node2的之前配置过了免密ssh登录，以及用主机名可以ping通，因此我们只需要对node3进行修改就可以了\nip： 192.168.149.129 主机名： node3.itcast.cn hosts文件: 增加对node3的解析 免密配置：增加对node3的免密 1.修改ip 我们需要打开 vim /etc/NetworkManager/system-connections/ens33.nmconnection这个文件，然后把主机修改为129网段就可以了，随后，我们重启这个服务，并把网卡也重启一下，是配置生效 这个是重启这个服务项 这个就是重启网卡这个服务了 最后我们查询一下，是否成功了呢 发现这修改是生效的\n2.修改主机名 hostnamectl set-hostname node3 3.修改hosts文件 需要打开/etc/hosts文件然后输入以下内容\nvim /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.149.128 node1 node1.itcast.cn 192.168.149.129 node3 node3.itcast.cn 192.168.149.130 node2 node2.itcast.cn 我们发现，这个也是显然成功的，hosts也是修改成功的·\n4.免密配置 1.生成公钥和私钥 ssh-keygen -f ~/.ssh/id_rsa -P \u0026#39;\u0026#39; -q 2.分发公钥 ssh-copy-id node1 ssh-copy-id node2 ssh-copy-id node3 node3测试一下 写到这一步，其实node3的相关配置就已经搞定了\n3.实操流程 node1: 部署tomcat node2：NFS 和 FTP node3：DNS 新克隆的node3节点： ip： 192.168.88.103 主机名： node3.itcast.cn hosts文件: 增加对node3的解析 免密配置：增加对node3的免密 可选： 安装vim 安装EPEL库 4.在node1和node2中安装相关服务 1.node1上面安装相关软件 1.先在node1上面安装jdk软件包 tar -xzf jdk-26_linux-x64_bin.tar.gz -C /opt/ 1.环境变量配置 打开/etc/profile文件,然后在里面追加以下内容\nexport JAVA_HOME=/opt/jdk-26.0.2 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=$JAVA_HOME/lib:$CLASSPATH 然后我们保存文件，就可以\n2.任意目录测试一下 输入以下内容进行测试\njava -version javac 我发现这个也是配置ok的，任意目录下都是可以正常访问的\n2.最后安装tomcat服务器 1,从官网下载阿帕奇的tar的包 从下载的地方并把他上传到指定路径并解压 2.解压这个包 tar -xzf apache-tomcat-9.0.97.tar.gz -C /export/software 3.运行这个软件，并在浏览器查看 cd /export/software/bin ./startup.sh 然后浏览器输入node1对应的ip地址看一下是否ok 出现这个页面，显然是阿帕奇正常运行了\n2.在node2安装相关软件 1.安装NFS 服务端： dnf install nfs-utils 客户端： dnf install nfs-utils 1.开启服务 systemctl enable nfs-server systemctl start nfs-server 2.开放端口 执行开放端口: firewall-cmd --add-service=nfs --permanent # 核心, 有了这个, NFS即可使用共享操作 firewall-cmd --reload 3.创建共享目录 mkdir /export/nfs #这个需要在node1和node2中同时创建 4.修改配置文件 vi /etc/exports #文件内需要填写的内容 /export/nfs *(rw,sync,no_root_squash) #填完之后，我们保存文件，并重启这个nfs服务即可 5.验证是否成功共享 #输入以下内容即可查看是否成功共享 服务端执行: 查看服务端NFS共享信息 showmount -e （早期版本兼容） exportfs 6.挂载使用 mount -t nfs 192.168.149.130:/export/software /export/software 7.验证 #经过验证是可以共享文件的 3安装ftp软件 1.创建文件夹 mkdir /etc/ftp #客户端与服务端都要创建这个文件夹 2.安装软件 服务端的软件安装 dnf install vsftpd -y 客户端软件安装 dns install lftp -y 3.修改配置 vi /etc/vsftpd/vsftpd.conf chroot_local_user=YES local_root=/export/ftp #修改完配置文件，之后我们重启服务即可了 4.启动并配置 vsftpd 服务 systemctl start vsftpd systemctl enable vsftpd 5.配置防⽕墙 firewall-cmd --permanent --add-service=ftp firewall-cmd --reload 6.连接FTP服务器 #在node2上面新增一个账户 useradd ftp_user passwd ftp_user lftp ftp://ftp_user:123456@192.168.149.130 我们做到这里，其实node1和node2都已经搞定了，只剩下给node3配dns了，并且我们给node1和node2配的都是没有任何问题的，可以正常的使用\n5.在node3中配置dns服务器 注意这里其实和上一个博客内容重复了，我只把不一样的写出来 首先呢，下载方式一样，操作流程一样，只不过是文件内容修改的不一样 前提是我们需要的根域名是itcast.local,其下的域名是www.itcast.local;nfs.itcast.local;ftp.itcast.local; 因此我们在正向文件，反向文件声明都要修改，以及正方向文件内容也要改\n1.修改named.conf这个文件 2.修改vi /etc/named.rfc1912.zones 向文件尾部追加俩条记录\nzone \u0026#34;itcast.local\u0026#34; IN { type master; file \u0026#34;/var/named/itcast.local.db\u0026#34;; allow-update { none; }; }; zone \u0026#34;149.168.192.in-addr.arpa\u0026#34; IN { type master; file \u0026#34;/var/named/192.168.149.rev\u0026#34;; allow-update { none; }; }; 3.修改/var/named/itcast.local.db $TTL 86400 @ IN SOA ns1.itcast.local. admin.itcast.local. ( 2024011701 ; Serial 3600 ; Refresh 1800 ; Retry 1209600 ; Expire 86400 ) ; Minimum TTL IN NS ns1.itcast.local. ns1 IN A 192.168.149.129 nfs IN A 192.168.149.130 ftp IN A 192.168.149.130 www IN A 192.168.149.128 @ IN A 192.168.149.128 4.修改/var/named/192.168.149.rev文件 $TTL 86400 @ IN SOA ns1.itcast.local. admin.itcast.local. ( 2024011701 ; Serial 3600 ; Refresh 1800 ; Retry 1209600 ; Expire 86400 ) ; Minimum TTL IN NS ns1.itcast.local. 129 IN PTR dns 130 IN PTR nfs 130 IN PTR ftp 128 IN PTR www 128 IN PTR @ 5.检测 # 配置文件语法检查 named-checkconf /etc/named.conf 如果报了错误， 一般都是由于配置文件丢失内容导致语法结构不对， 请检查配置文件 /etc/named.conf 【大概率是该文件的问题】 /etc/named.rfc1912.zones # 区域文件语法检查 named-checkzone internal.local /var/named/internal.local.db named-checkzone 88.168.192.in-addr.arpa /var/named/192.168.88.rev 6.启动服务以及开放端口 systemctl start named -- 立即启动 systemctl enable named -- 开启自动启动 systemctl status named -- 查看状态 firewall-cmd --zone=public --add-port=53/udp --permanent firewall-cmd --zone=public --add-port=53/tcp --permanent firewall-cmd --reload 或者： firewall-cmd --zone=public --add-service=dns --permanent firewall-cmd --reload # 查看规则信息： firewall-cmd --list-all 6.测试操作 把node1和node2的dns记录第一条改成node3的IP地址就可以了 6.总结 这个案例综合性的把之前学到的各种服务器都运用到了，综合度确实高，把之前学到的nfs,ftp,dns都用上了，同时也加深了对于这些内容的认识，其实我觉得，这些东西要多练习，即使会，但是还是要练习，有时候确实会多练习几遍明白一些东西，比如这次，其实我没有想到只有域名解析功能的dns可以如此组合，把node1和node2的ip放在node3里面，这样就可以通过访问其设定的域名来访问对应的服务了. 在 node3 部署 BIND 做内网 DNS，统一解析 Tomcat (www)、NFS 和 FTP (nfs/ftp)，实现了全集群基于 itcast.local 域名的服务访问，打通了 Web、存储与文件传输三大基础服务。 核心注意点： 1. NFS 挂载要用域名 (nfs.itcast.local)，别只用 IP。 2. FTP 禁锢目录需配 allow_writeable_chroot=YES。 3. DNS 指向要写进网卡配置，防止重启失效。 4. JDK/FTP 仅限内网实验，生产环境建议换 LTS JDK 和 SFTP。 ","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E5%AE%9E%E6%88%98dns%E5%BB%BA%E8%AE%BE%E6%A1%88%E4%BE%8B_/","summary":"\u003ch4 id=\"实战dns建设案例\"\u003e【实战】DNS建设案例:\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#0_1\"\u003e0.需求\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_21\"\u003e1.准备工作\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_24\"\u003e2.进行标准化的服务器配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1ip_32\"\u003e1.修改ip\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_42\"\u003e2.修改主机名\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3hosts_49\"\u003e3.修改hosts文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_66\"\u003e4.免密配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_89\"\u003e3.实操流程\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4node1node2_109\"\u003e4.在node1和node2中安装相关服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1node1_111\"\u003e1.node1上面安装相关软件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1node1jdk_112\"\u003e1.先在node1上面安装jdk软件包\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_120\"\u003e1.环境变量配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_130\"\u003e2.任意目录测试一下\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2tomcat_139\"\u003e2.最后安装tomcat服务器\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1tar_140\"\u003e1,从官网下载阿帕奇的tar的包\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_144\"\u003e2.解压这个包\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_149\"\u003e3.运行这个软件，并在浏览器查看\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2node2_159\"\u003e2.在node2安装相关软件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1NFS_160\"\u003e1.安装NFS\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_174\"\u003e1.开启服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_180\"\u003e2.开放端口\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_187\"\u003e3.创建共享目录\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_191\"\u003e4.修改配置文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_205\"\u003e5.验证是否成功共享\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#6_216\"\u003e6.挂载使用\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#7_221\"\u003e7.验证\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3ftp_228\"\u003e3安装ftp软件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_230\"\u003e1.创建文件夹\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_238\"\u003e2.安装软件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_252\"\u003e3.修改配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_vsftpd__264\"\u003e4.启动并配置 vsftpd 服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_272\"\u003e5.配置防⽕墙\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#6FTP_281\"\u003e6.连接FTP服务器\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5node3dns_305\"\u003e5.在node3中配置dns服务器\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1namedconf_310\"\u003e1.修改named.conf这个文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2vi_etcnamedrfc1912zones_314\"\u003e2.修改vi /etc/named.rfc1912.zones\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3varnameditcastlocaldb_329\"\u003e3.修改/var/named/itcast.local.db\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4varnamed192168149rev_348\"\u003e4.修改/var/named/192.168.149.rev文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_367\"\u003e5.检测\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#6_386\"\u003e6.启动服务以及开放端口\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#6_408\"\u003e6.测试操作\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#6_420\"\u003e6.总结\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"0需求\"\u003e0.需求\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e目前我们在node2节点中已经有FTP服务器、NFS服务器， 同时大家需要在node1中部署一个tomcat服务器。 请对三个服务设置对应得域名，并基于DNS进行内网解析，从而实现基于域名访问，要求DNS服务器架设至node3， 配置后， 不管是在node1还是node2都可以基于此DNS进行访问\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eNFS服务器域名： nfs.itcast.local\nFTP服务器域名： ftp.itcast.local\ntomcat服务器域名： www.itcast.local\n\n\nFTP服务器要求:\n    1- 禁锢FTP目录： /export/ftp\n    2- 禁止匿名用户访问，可以使用系统中任意存在用户访问即可\n    3- 不管在服务器还是在windows中， 都可以直接使用域名的方式访问到ftp服务器\n            windows中，建议使用filezilla.exe  软件来连接\n\nNFS服务器：\n    共享目录： /export/nfs\n\n    要求 node1在挂载这个目录的时候，请使用域名方式进行挂载即可（测试 临时挂载即可）\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"1准备工作\"\u003e1.准备工作\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e我们在进行这个综合案例的时候，需要准备3台主机，分别是node1,node2,node3\u003c/code\u003e\n而node3的话，我们需要对node1克隆就可以得到了,开启这三台Linux主机就可以进行下一个步骤了\u003c/p\u003e","title":"【实战】DNS建设案例_"},{"content":"CSDN 博客本地化工具 用途 CSDN 导出的 Markdown 文件通常只包含图床链接，例如：\n![图片](https://img-blog.csdnimg.cn/xxxx.png) 如果图床失效或文章被删除，图片就找不回来了。\n这个工具会把 Markdown 里的远程图片全部下载到本地，并把链接改成相对路径：\n![图片](images/xxxx.png) 这样博客的正文和图片就能一起保存、迁移、备份。\n使用方法 1. 准备 本机需要安装 Python 3.11 或更高版本。\n检查：\npython --version 2. 下载图片 python download_images.py \u0026#34;D:\\博客\\文章.md\u0026#34; 执行后会在文章同目录生成：\nD:\\博客\\ ├─ 文章.md └─ images\\ └─ xxxx.png 文章.md 里的图片链接会自动改写为：\n![图片](images/xxxx.png) 3. 可选参数 指定图片目录名：\npython download_images.py \u0026#34;D:\\博客\\文章.md\u0026#34; --out 图片 只预览，不实际下载：\npython download_images.py \u0026#34;D:\\博客\\文章.md\u0026#34; --dry-run 注意事项 脚本只处理 Markdown 的 ![alt](url) 图片语法 下载失败时会保留原图床链接，并在结果里显示 FAILED 已存在且大小不为 0 的图片会自动跳过，不会重复下载 执行前会先把原 Markdown 备份为 文章.md.bak 下载的图片文件会按 URL 文件名保存，重名时自动加编号 建议 下载完成后，把整个博客文件夹放进 Teldrive 备份目录，这样正文和图片都会一起自动备份，不再依赖 CSDN 图床。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/csdn%E5%8D%9A%E5%AE%A2%E6%9C%AC%E5%9C%B0%E5%8C%96%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E/","summary":"\u003ch1 id=\"csdn-博客本地化工具\"\u003eCSDN 博客本地化工具\u003c/h1\u003e\n\u003ch2 id=\"用途\"\u003e用途\u003c/h2\u003e\n\u003cp\u003eCSDN 导出的 Markdown 文件通常只包含图床链接，例如：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e![\u003cspan style=\"color:#f92672\"\u003e图片\u003c/span\u003e](\u003cspan style=\"color:#a6e22e\"\u003ehttps://img-blog.csdnimg.cn/xxxx.png\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e如果图床失效或文章被删除，图片就找不回来了。\u003c/p\u003e\n\u003cp\u003e这个工具会把 Markdown 里的远程图片全部下载到本地，并把链接改成相对路径：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e![\u003cspan style=\"color:#f92672\"\u003e图片\u003c/span\u003e](\u003cspan style=\"color:#a6e22e\"\u003eimages/xxxx.png\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这样博客的正文和图片就能一起保存、迁移、备份。\u003c/p\u003e\n\u003ch2 id=\"使用方法\"\u003e使用方法\u003c/h2\u003e\n\u003ch3 id=\"1-准备\"\u003e1. 准备\u003c/h3\u003e\n\u003cp\u003e本机需要安装 Python 3.11 或更高版本。\u003c/p\u003e\n\u003cp\u003e检查：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython --version\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"2-下载图片\"\u003e2. 下载图片\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython download_images.py \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;D:\\博客\\文章.md\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e执行后会在文章同目录生成：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eD:\\博客\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e├─ 文章.md\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e└─ images\\\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e   └─ xxxx.png\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003e文章.md\u003c/code\u003e 里的图片链接会自动改写为：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e![\u003cspan style=\"color:#f92672\"\u003e图片\u003c/span\u003e](\u003cspan style=\"color:#a6e22e\"\u003eimages/xxxx.png\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"3-可选参数\"\u003e3. 可选参数\u003c/h3\u003e\n\u003cp\u003e指定图片目录名：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython download_images.py \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;D:\\博客\\文章.md\u0026#34;\u003c/span\u003e --out 图片\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e只预览，不实际下载：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epython download_images.py \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;D:\\博客\\文章.md\u0026#34;\u003c/span\u003e --dry-run\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"注意事项\"\u003e注意事项\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e脚本只处理 Markdown 的 \u003ccode\u003e![alt](url)\u003c/code\u003e 图片语法\u003c/li\u003e\n\u003cli\u003e下载失败时会保留原图床链接，并在结果里显示 \u003ccode\u003eFAILED\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e已存在且大小不为 0 的图片会自动跳过，不会重复下载\u003c/li\u003e\n\u003cli\u003e执行前会先把原 Markdown 备份为 \u003ccode\u003e文章.md.bak\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e下载的图片文件会按 URL 文件名保存，重名时自动加编号\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"建议\"\u003e建议\u003c/h2\u003e\n\u003cp\u003e下载完成后，把整个博客文件夹放进 Teldrive 备份目录，这样正文和图片都会一起自动备份，不再依赖 CSDN 图床。\u003c/p\u003e","title":"CSDN 博客本地化工具使用说明"},{"content":"if语句 在if语句中他的语法是if(表达式)\n语句\n表达式成立那么语句执行，不成立那么语句不执行。\n在if语句中只要是true就执行，false不执行。\n那么true通常是1，而false则是0，比如nun%2==1，它执行的是判断num是否是奇数，其实这样写也可以，但是不是更很直观\n没有前者更加直观一些。\n在if语句中if语句只可以跟着一条语句，但是如果过于多的话可以调整为大括号\n由此我们可以知道这样的道理，else是在一些情况可以省略的，不必再写像\nif(num%2)\nprintf(\u0026quot;%d 是奇数\u0026quot;，num);\nprintf(\u0026quot;%d 是偶数\u0026quot;,num);\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E7%9A%84%E5%88%86%E6%94%AF%E4%B8%8E%E5%BE%AA%E7%8E%AF%E4%B8%8A/","summary":"\u003ch2 id=\"if语句\"\u003eif语句\u003c/h2\u003e\n\u003cp\u003e在if语句中他的语法是if(表达式)\u003c/p\u003e\n\u003cp\u003e                                       语句\u003c/p\u003e\n\u003cp\u003e表达式成立那么语句执行，不成立那么语句不执行。\u003c/p\u003e\n\u003cp\u003e在if语句中只要是true就执行，false不执行。\u003c/p\u003e\n\u003cp\u003e那么true通常是1，而false则是0，比如nun%2==1，它执行的是判断num是否是奇数，其实这样写也可以，但是不是更很直观\u003c/p\u003e\n\u003cp\u003e没有前者更加直观一些。\u003c/p\u003e\n\u003cp\u003e在if语句中if语句只可以跟着一条语句，但是如果过于多的话可以调整为大括号\u003c/p\u003e\n\u003cp\u003e由此我们可以知道这样的道理，else是在一些情况可以省略的，不必再写像\u003c/p\u003e\n\u003cp\u003eif(num%2)\u003c/p\u003e\n\u003cp\u003e   printf(\u0026quot;%d 是奇数\u0026quot;，num);\u003c/p\u003e\n\u003cp\u003eprintf(\u0026quot;%d 是偶数\u0026quot;,num);\u003c/p\u003e\n\u003cp\u003e \u003c/p\u003e\n\u003cp\u003e \u003c/p\u003e\n\u003cp\u003e \u003c/p\u003e\n\u003cp\u003e \u003c/p\u003e\n\u003ch2\u003e\u003c/h2\u003e","title":"C语言的分支与循环上"},{"content":"C语言之操作符（上） 一.二进制，八进制，十六进制\n1.二进制\n2.八进制与十六进制\n二.原码，反码，补码\n1.原码\n2.反码\n3.补码\n三.移位操作符\n1.左移操作符\n2.右移操作符\n1.逻辑右移\n2.算术右移\n四.位操作符\n1.按位与\n2.按位或\n3.按位异或\n4.运用\n五.逗号表达式\n在C语言之中，操作符也是极其重要的，不不仅仅C语言会用到，同时最为关键的是学会操作符，会对于我们学习单片机起到奠基石的作用，对于单片机的学习起到引领的作用，会非常有助于我们学习单片机的，会让我们对于单片机的一些基础操作可以看的懂。\n一.二进制，八进制，十六进制 在计算机当中信息是以二进制的形式存储的，而我们在日常生活当中使用的最为多的，其实就是十进制，然而我们对于十进制的转化可以说是手到擒来。但是对于二进制相信一些伙伴，连听都没有听过，更不要说二进制之间的转化，不同进制之间的转化。由于二进制表达的数字太过于庞大了，我们不习惯使用二进制，因此我们需要学习八进制，十六进制，在接下来的讲解中我将为大家娓娓道来，并力求做到，看过我写的便学会，懂得了。\n1.二进制 1.首先，需要注意的是，二进制是什么。二进制是是计算机在存储信息之中时以1，0对信息加以存储和表达，满2进1对的。 2.其次我们需要注意的是，二进制的转化： 1.二进制同十进制之间的转化：首先需要明确一下的是权重，求值，以十进制数字：123为例。\n10进制的位 1 2 3 权重 10^2 10^1 10^0 权重值 100 10 1 求值 1*100 2*10 3*1 = 123 1.由此可以看出，权重，权重值,的关系，对这个概念可以运用到二进制·之上，对于二进制数如：110.\n二进制的位 1 1 0 权重 2^2 2^1 2^0 权重值 4 2 1 求值 4*1 2*1 0*1= 6 2.这样我们就完成了对二进制与十进制数相互进行转化。而对于十进制数转化为二进制数字，比如说一个十进制数字125将他转化成二进制数字，可以采用短除法，具体操作如下：\n3.因此可以知道，十进制数125转化为二进制数是0111 1101。虽然我们可以采用短除法求值，但是在一般情况下不需要调用短除法，我们可以用一种估值的方法计算,比如说十进制数字125,可以用64+32+16+8+4+1=12 4 ,而2的64,32,16,8,8,4,1分别是2的,6,5,4,3,2,0 次方，这样估计值似乎比使用短除法更加迅速，遇到稍微大一些的数字可以采用电脑上的计算器。\n2.八进制与十六进制 通过以上叙述，我们对二进制的数字有了新的认识，更加深刻的明白了，二进制数字该如何转化十进制，十进制数字该如何转化为成为二进制数字，同时通过我的叙述想必各位一定知道了，为何在日常之中短除法不能成为主要的方法，下面我就为大家详尽的介绍八进制，十六进制同二进制之间的巧妙的关系。 2.二进制与八进制，十六进制的关系: 1.由于十进制8是2的3三次方，16是2的4次方，因此可以这样说，每一位八进制数字可以拆分为3个二进制位，而每一位的十六进制可以1拆分为4个二进制位。 2.举一个例子，如下：十六进制数字3f,可以看作是3 f 而3的二进制位是11，f的二进制位是1111，因此他们的十进制数字是111111。 2.再举一个例子：又如二进制数字0111111，转化为十六进制如下，011 1111,011转化为十进制的数字则是6,1111转为十进制的数字是15，十六进制则是f,因此答案就是6f. 3.八进制的转化类似，在此不再做叙述了，有需要的小伙伴，可以观看B站鹏哥视频，那里会有讲解。 4.写到最后，我们通常把前置如0X写作十六进制。\n二.原码，反码，补码 在初学计算机时，我们必须明白计算机存储的实质，其实是用二进制数字的补码加以存储的，下面我就为大家介绍一下三者之间的各种关系。\n1.原码 首先呢，原码是二进制数原来的存储方式，但是需要注意一点的是，正数的原码最高位，其实就是符号位是0；而负数的原码的最高位则是1 如：int c=1;它的原码是32个比特位，也就是说：0000 0000 0000 0000 0000 0000 0000 0001； 而：\nint a=-1; 它的原码则是，1000 0000 0000 0000 0000 0000 0000 0001\n2.反码 1.正数的反码，补码依旧是其本身，不做任何变化，因此在关于后面讨论反码，补码时不再做讨论。然而，负数的反码则是，符号位不变而，其他的位取反.，后面的讨论会详尽的讨论关于负数的，如：\nint a=-1; 它的反码则是，32个比特位：1111 1111 1111 1111 1111 1111 1111 1110\n3.补码 在负数中，它的补码是其反码+1；就可以了。 如:\nint a=-1 这个数的补码是32个比特位：1111 1111 1111 1111 1111 1111 1111 1111，因此-1的补码是全为1。\n三.移位操作符 移位操作符分为，左移操作和右移操作。 左移操作符记作：\u0026laquo; 右移操作符记作：\u0026raquo;\n1.左移操作符 它是最为容易的，指的是二进制的数字整体向左移动，空缺的位用0补齐，下面以第8个位记作符号位。 如下：3\u0026laquo;1 记作 ：0000 0011\u0026laquo; 1 0000 0110 而0000 0110=6，左移操作就是这样操做的。\n2.右移操作符 右移操作就显得稍微麻烦一些，具体的话分为，逻辑右移和算术右移。\n1.逻辑右移 这种右移操作，大部分编译器是不支持的。其实逻辑右移就有一些简单粗暴了，是整体右移，最高位也就是说是符号位，用0补齐，最低位则自动舍弃。这样肯定会造成一些误差的。比如说：5\u0026raquo;1 按照逻辑右移，那么整体右移：0000 0101\u0026raquo;1 =0000 0010\n2.算术右移 他其实同逻辑右移差不多，只不过是做高位按照符号位进行补齐，其他就都一样，比如说-1\u0026raquo;11111 1111 \u0026raquo;1=1111 1111,也就是说同原来是一样的还是-1.，基本原理还是同逻辑右移的实现是一样的。\n四.位操作符 位操作符有\u0026amp; ，^, |.\n1.按位与 他的意思是说，有0就0，都是1则为1那么对于像如下： 3\u0026amp;2=011\u0026amp;010=2\n2.按位或 他的意思是说，有1就为1，都是0则为0，像以下如：3|2=011|010=011=3\n3.按位异或 按位异或的意思是，相同则为0，不同则为1. 像如下，如：3^2=11 ^ 10=11=3\n4.运用 在不创建临时变量时对于数字进行交换 比如说，数字1 2进行交换可以这样操作，1=1^2,2=1 ^2, 1=1 ^ 2,也就是说异或可以进行交换像a ^ b ^ a= a ^ a ^ b=0 ^ b =b,但是这样的运算有着局限性，不可以对数组，等进行交换。\n五.逗号表达式 他可以起到，简化代码的作用，我们通常以逗号表达式的最后一个作为他的值，如下：\nint c = (a\u0026gt;b, a=b+10, a, b=a+1); c的值就是取最后一个逗号表达式的值，是b之值。\na = get_val(); count_val(a); 如果使用逗号表达式，改写： while (a\u0026gt;0) { a = get_val(); count_val(a); } 可以看出，这样写比较冗余，但是写成逗号表达式的话就比较简洁一些。记作：\nwhile (a = get_val(), count_val(a),a\u0026gt;0) { a = get_val(); count_val(a); } 结束。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%93%8D%E4%BD%9C%E7%AC%A6%E4%B8%8A/","summary":"\u003ch4 id=\"c语言之操作符上\"\u003eC语言之操作符（上）\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_3\"\u003e一.二进制，八进制，十六进制\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_5\"\u003e\u003ccode\u003e1.二进制\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_27\"\u003e\u003ccode\u003e2.八进制与十六进制\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_35\"\u003e二.原码，反码，补码\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_37\"\u003e\u003ccode\u003e1.原码\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_45\"\u003e\u003ccode\u003e2.反码\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_53\"\u003e\u003ccode\u003e3.补码\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_60\"\u003e三.移位操作符\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_64\"\u003e\u003ccode\u003e1.左移操作符\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_69\"\u003e\u003ccode\u003e2.右移操作符\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_71\"\u003e1.逻辑右移\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_73\"\u003e2.算术右移\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_75\"\u003e四.位操作符\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_78\"\u003e\u003ccode\u003e1.按位与\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_82\"\u003e\u003ccode\u003e2.按位或\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_84\"\u003e\u003ccode\u003e3.按位异或\u003c/code\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_89\"\u003e4.运用\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_92\"\u003e五.逗号表达式\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e在C语言之中，操作符也是极其重要的，不不仅仅C语言会用到，同时最为关键的是学会操作符，会对于我们学习单片机起到奠基石的作用，对于单片机的学习起到引领的作用，会非常有助于我们学习单片机的，会让我们对于单片机的一些基础操作可以看的懂。\u003c/p\u003e\n\u003ch2 id=\"一二进制八进制十六进制\"\u003e一.二进制，八进制，十六进制\u003c/h2\u003e\n\u003cp\u003e在计算机当中信息是以二进制的形式存储的，而我们在日常生活当中使用的最为多的，其实就是十进制，然而我们对于十进制的转化可以说是手到擒来。但是对于二进制相信一些伙伴，连听都没有听过，更不要说二进制之间的转化，不同进制之间的转化。由于二进制表达的数字太过于庞大了，我们不习惯使用二进制，因此我们需要学习八进制，十六进制，在接下来的讲解中我将为大家娓娓道来，并力求做到，看过我写的便学会，懂得了。\u003c/p\u003e\n\u003ch3 id=\"1二进制\"\u003e\u003ccode\u003e1.二进制\u003c/code\u003e\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1.首先，需要注意的是，二进制是什么。二进制是是计算机在存储信息之中时以1，0对信息加以存储和表达，满2进1对的。\n2.其次我们需要注意的是，二进制的转化：\n\u003ccode\u003e1.二进制同十进制之间的转化：\u003c/code\u003e首先需要明确一下的是权重，求值，以十进制数字：123为例。\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e10进制的位\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e1\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e2\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e3\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e权重\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e10^2\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e10^1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e10^0\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e权重值\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e100\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e10\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e求值\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e1*100\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e2*10\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e3*1 =\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e123\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1.由此可以看出，权重，权重值,的关系，对这个概念可以运用到二进制·之上，对于二进制数如：110.\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e二进制的位\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e1\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e1\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e0\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e权重\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e2^2\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e2^1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e2^0\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e权重值\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e4\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e2\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e求值\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e4*1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e2*1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e0*1=\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e6\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e2.这样我们就完成了对二进制与十进制数相互进行转化。而对于十进制数转化为二进制数字，比如说一个十进制数字125将他转化成二进制数字，可以采用短除法，具体操作如下：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%93%8D%E4%BD%9C%E7%AC%A6%E4%B8%8A/images/9072ce77b60a39dcb414d5023bb22df6.png\"\u003e\u003c/p\u003e","title":"C语言之操作符（上）"},{"content":"C语言之操作符【下】 一.下标访问[],函数调用()\n1.下标访问[]\n1.什么是下标访问[]\n2.下标访问操作符[]的应用与举例\n2.函数调用()\n二.操作符的属性：优先级、结合性\n1.优先级\n2.结合性\n3.俩者之间的应用\n三.整型提升\n1.整型提升\n2.整形提升的意义\n3.如何进行整型提升\n例子如下：\n四.算术转换\n五.表达式求值\n在上一节中，我们已经学习了操作符的一些相关知识，我们对C语言的操作符有了新的认识与理解，那么在本节C语言中，我们将继续上一次的讲解，为大家呈现更加有趣并且深刻的讲解，来开启我们的操作符之旅吧！\n一.下标访问[],函数调用() 1.下标访问[] 1.什么是下标访问[] 1.在C语言学习中，我们会经常用到下标访问操作符，那么相信在屏幕前的你们一定很想知道什么是下标访问操作符吧？下面我就为大家详细的道来：其实下标访问操作符，是数组里面的一个知识点，下标访问操作符是数组遍历当中经常使用的，数组在遍历[]里面会有一些数字，而那些数字其实就可以表示数组的内容，因此就可以对数组进行遍历了。 2.形式是[]里面的数字从数字0开始，到最后的数字结束。\n2.下标访问操作符[]的应用与举例 1.下标访问操作符的应用：下标访问操作符，应用到数组当中，对于数组内容的访问有着极其重要的意义。 2.下标访问操作符的举例：如在下面的函数当中，我们就用到了下标访问操作符。\n#include\u0026lt;stdio.h\u0026gt; int main() { int a[5]={1,2,3,4,5}; for(int i=0; i\u0026lt;5;i++) printf(\u0026#34;%d\u0026#34;,a[i]); return 0; } 3.在上例当中，a[i]里面的i其实就是数组里面的内容，它的意思是说数组里面的的i-1个元素，因此我们通过这个方法对其进行遍历。\n2.函数调用() 1.函数调用操作符是小括号。 2.形式如下：\nint add(int x,int y); 在上例之中，小括号之前的是，函数名；在小括号之后的是形参，可以看出该函数有两个形参：一个是int x，另一个是int y，该函数的返回类型是int形。\n二.操作符的属性：优先级、结合性 1.优先级 1.每种同类型的运算符都有内部的运算符优先级，不同类型的运算符之间也有相应的优先级顺序。一个表达式中既可以包括相同类型的运算符，也可以包括不同类型的运算符或者函数。当多种运算符出现在同一个表达式中时，应该先按照不同类型运算符间的优先级进行运算。各种运算符间的优先级如下：数值运算符、字符串运算符、关系运算符、逻辑运算符。可以用括号改变优先级顺序，使得括号内的运算优先于括号外的运算。对于多重括号，总是由内到外强制表达式的某些部分优先运行。括号内的运算总是最优先计算。 2.操作符的优先级如下： 2.结合性 结合性是，对于多个优先级是一样的操作符的一个表达式，此时他们的就需要用到，结合性，如下：\n2*3/5 此时他们的优先级是一样的，因此我们选择使用结合性，因为，乘法和除法的结合性是做结合的因此该表达式是从左到右计算的。\n3.俩者之间的应用 因为在上文的优先级表格中，小括号的优先级是最高的，使用小括号可以提高算术的优先级。因此呢，当有一些表达式的优先级是一样的时候，我们就可以使用小括号，这样小括号括起来的地方，优先级是比较高的，可以达到我们想要的结果\n三.整型提升 1.整型提升 C语言中整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度，表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。\n2.整形提升的意义 整型提升的意义是在表达式中，各种长度可能小于int长度的整型值，都必须先转换为int或unsigned int，然后才能送入CPU去执行运算12。C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度，表达式中的字符和短整型操作数在使用之前被转换为普通整型，这种转换称为整型提升。比如：即使两个char类型（1个字节==8个比特字节）的相加，在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度.\n3.如何进行整型提升 有符号整数提升是按照变量的数据类型的符号位来提升的\n无符号整数提升，高位补0\n例子如下： int main() { char - signed char unsigned char char a = 5;//char只有一个字节，那么的话char有8个比特位。 //a 的二进制位是：00000101 - a (截断后存储a中) //由于a是无符号类型的，可以整型提升，提升为int类型，以二进制位表示如下：000000000000000000000000 00000101 char b = 127; //类似char a，一些步骤缺省，因此将他转换为int类型以二进制表示如下：00000000000000000000000001111111 01111111 - b (截断后存储到b中) char c = a + b;//发生整型提升 00000000000000000000000000000101 - a 00000000000000000000000001111111 - b 00000000000000000000000010000100 - a+b 10000100 - c//由于char c的最高位是1因此我们用1char c提升为整形，如下： printf(\u0026#34;%d\\n\u0026#34;, c); //这里c还是要整型提升 11111111111111111111111110000100//c的整型提升的原码 10000000000000000000000001111011//c的反码 10000000000000000000000001111100//c的补码 -124 %d 是按照10进制的形式打印有符号的整数 return 0; } 四.算术转换 如果某个操作符的各个操作数属于不同的类型，那么除非其中一个操作数的转换为另一个操作数的类型，否则操作就无法进行。下面的层次体系称为寻常算术转换：\nlong double double float unsigned long int long int unsigned int int 如果某个操作数的类型在上面这个列表中排名靠后，那么首先要转换为另外一个操作数的类型后执行运算。\n五.表达式求值 下面是一些，对操作符运用不太正确的例子：\nint main() { int c = 3 + 4 * 5; //这里由于乘法的优先级是比较高的，因此只有一种计算方式，先算乘法，再算加法，也就是说答案是23. return 0; } #include \u0026lt;stdio.h\u0026gt; int fun() { static int count = 1; return ++count; } int main() { int answer; answer = fun() - fun() * fun();//在该函数之中，虽然乘法是优先级是较高的，但是我们却不知道这个表达式先调用的是哪一个，因此可能用不止一个答案。 //如果是1.fun()-2.fun()*3.fun(),那么的话答案就是-10 //要是3.fun()-1,fun()*2.fun()，那么的话答案就是-2，这些表达式，是不正确的表达式，下面我就为大家继续讲解更多的这种不正确的表达式。 printf(\u0026#34;%d\\n\u0026#34;, answer); //输出多少？ return 0; } #include \u0026lt;stdio.h\u0026gt; int main() { int i = 1; int ret = (++i) + (++i) + (++i); //这种表达式，要是我在vs上运行的话那么结果是12，要是在gcc上运行的话，结果就是10 //首先说一下vs的情况吧，在vs上面是先算++i，最后在算加法，因此结果就是4+4+4=12. //然后我们再说一下，在gcc上运行的结果，在gcc上是开始先算++i，再开始算++i,然后算加法，然后++i，然后算加法，答案也就是：3+3+4=10 printf(\u0026#34;%d\\n\u0026#34;, ret); printf(\u0026#34;%d\\n\u0026#34;, i); return 0; } 写到最后，因此我们平常在写C语言的时候，应当极其谨慎并且小心，尽量不使用，长语句表达式，我们可以将长语句表达式分为多次短语句表达式，虽然这样会比较的麻烦但是，这样做在我们以后工作的时候，起到极其重要的作用，对团队协作有较大的帮助。 结束（完）。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%93%8D%E4%BD%9C%E7%AC%A6%E4%B8%8B/","summary":"\u003ch4 id=\"c语言之操作符下\"\u003eC语言之操作符【下】\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_2\"\u003e一.下标访问[],函数调用()\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_3\"\u003e1.下标访问[]\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_4\"\u003e1.什么是下标访问[]\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_7\"\u003e2.下标访问操作符[]的应用与举例\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_22\"\u003e2.函数调用()\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_29\"\u003e二.操作符的属性：优先级、结合性\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_30\"\u003e1.优先级\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_37\"\u003e2.结合性\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_43\"\u003e3.俩者之间的应用\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_45\"\u003e三.整型提升\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_46\"\u003e1.整型提升\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_48\"\u003e2.整形提升的意义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_50\"\u003e3.如何进行整型提升\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_53\"\u003e例子如下：\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_82\"\u003e四.算术转换\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_94\"\u003e五.表达式求值\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003e在上一节中，我们已经学习了操作符的一些相关知识，我们对C语言的操作符有了新的认识与理解，那么在本节C语言中，我们将继续上一次的讲解，为大家呈现更加有趣并且深刻的讲解，来开启我们的操作符之旅吧！\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"一下标访问函数调用\"\u003e一.下标访问[],函数调用()\u003c/h2\u003e\n\u003ch3 id=\"1下标访问\"\u003e1.下标访问[]\u003c/h3\u003e\n\u003ch4 id=\"1什么是下标访问\"\u003e1.什么是下标访问[]\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1.在C语言学习中，我们会经常用到下标访问操作符，那么相信在屏幕前的你们一定很想知道什么是下标访问操作符吧？下面我就为大家详细的道来：其实下标访问操作符，是数组里面的一个知识点，下标访问操作符是数组遍历当中经常使用的，数组在遍历[]里面会有一些数字，而那些数字其实就可以表示数组的内容，因此就可以对数组进行遍历了。\n\u003ccode\u003e2.形式是[]里面的数字从数字0开始，到最后的数字结束。\u003c/code\u003e\u003c/p\u003e\n\u003ch4 id=\"2下标访问操作符的应用与举例\"\u003e2.下标访问操作符[]的应用与举例\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1.下标访问操作符的应用：下标访问操作符，应用到数组当中，对于数组内容的访问有着极其重要的意义。\n2.下标访问操作符的举例：如在下面的函数当中，我们就用到了下标访问操作符。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include\u0026lt;stdio.h\u0026gt;\nint main()\n{\nint a[5]={1,2,3,4,5};\nfor(int i=0; i\u0026lt;5;i++)\nprintf(\u0026#34;%d\u0026#34;,a[i]);\nreturn 0;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e3.在上例当中，a[i]里面的i其实就是数组里面的内容，它的意思是说数组里面的的i-1个元素，因此我们通过这个方法对其进行遍历。\u003c/p\u003e\n\u003ch3 id=\"2函数调用\"\u003e2.函数调用()\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e1.函数调用操作符是小括号。\u003c/code\u003e\n2.形式如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eint add(int x,int y);\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e在上例之中，小括号之前的是，函数名；在小括号之后的是形参，可以看出该函数有两个形参：一个是int x，另一个是int y，该函数的返回类型是int形。\u003c/p\u003e\n\u003ch2 id=\"二操作符的属性优先级结合性\"\u003e二.操作符的属性：优先级、结合性\u003c/h2\u003e\n\u003ch3 id=\"1优先级\"\u003e1.优先级\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1.每种同类型的运算符都有内部的运算符优先级，不同类型的运算符之间也有相应的优先级顺序。一个表达式中既可以包括相同类型的运算符，也可以包括不同类型的运算符或者函数。当多种运算符出现在同一个表达式中时，应该先按照不同类型运算符间的优先级进行运算。各种运算符间的优先级如下：数值运算符、字符串运算符、关系运算符、逻辑运算符。可以用括号改变优先级顺序，使得括号内的运算优先于括号外的运算。对于多重括号，总是由内到外强制表达式的某些部分优先运行。括号内的运算总是最优先计算。\n2.操作符的优先级如下：\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%93%8D%E4%BD%9C%E7%AC%A6%E4%B8%8B/images/45b5283c219699081fe2ad0d959574f3.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%93%8D%E4%BD%9C%E7%AC%A6%E4%B8%8B/images/a1778a520d624963904ef18d82039467.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%93%8D%E4%BD%9C%E7%AC%A6%E4%B8%8B/images/8826b16df4a51812dbdd2c290e8259df.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"2结合性\"\u003e2.结合性\u003c/h3\u003e\n\u003cp\u003e结合性是，对于多个优先级是一样的操作符的一个表达式，此时他们的就需要用到，结合性，如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e2*3/5\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e此时他们的优先级是一样的，因此我们选择使用结合性，因为，乘法和除法的结合性是做结合的因此该表达式是从左到右计算的。\u003c/p\u003e\n\u003ch3 id=\"3俩者之间的应用\"\u003e3.俩者之间的应用\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e因为在上文的优先级表格中，小括号的优先级是最高的，使用小括号可以提高算术的优先级。因此呢，当有一些表达式的优先级是一样的时候，我们就可以使用小括号，这样小括号括起来的地方，优先级是比较高的，可以达到我们想要的结果\u003c/p\u003e\n\u003ch2 id=\"三整型提升\"\u003e三.整型提升\u003c/h2\u003e\n\u003ch3 id=\"1整型提升\"\u003e1.整型提升\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eC语言中整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度，表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。\u003c/p\u003e\n\u003ch3 id=\"2整形提升的意义\"\u003e2.整形提升的意义\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e整型提升的意义是在表达式中，各种长度可能小于int长度的整型值，都必须先转换为int或unsigned int，然后才能送入CPU去执行运算12。C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度，表达式中的字符和短整型操作数在使用之前被转换为普通整型，这种转换称为整型提升。比如：即使两个char类型（1个字节==8个比特字节）的相加，在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度.\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"3如何进行整型提升\"\u003e3.如何进行整型提升\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e有符号整数提升是按照变量的数据类型的符号位来提升的\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e无符号整数提升，高位补0\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch4 id=\"例子如下\"\u003e例子如下：\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eint main()\n{\n\nchar - signed char\nunsigned char\nchar a = 5;//char只有一个字节，那么的话char有8个比特位。\n//a 的二进制位是：00000101 - a (截断后存储a中)\n//由于a是无符号类型的，可以整型提升，提升为int类型，以二进制位表示如下：000000000000000000000000 00000101\n\tchar b = 127;\n//类似char a，一些步骤缺省，因此将他转换为int类型以二进制表示如下：00000000000000000000000001111111\n01111111 - b (截断后存储到b中)\n\n\tchar c = a + b;//发生整型提升\n00000000000000000000000000000101 - a\n00000000000000000000000001111111 - b\n00000000000000000000000010000100 - a+b\n10000100 - c//由于char c的最高位是1因此我们用1char c提升为整形，如下：\nprintf(\u0026#34;%d\\n\u0026#34;, c);\n//这里c还是要整型提升\n11111111111111111111111110000100//c的整型提升的原码\n10000000000000000000000001111011//c的反码\n10000000000000000000000001111100//c的补码\n-124\n%d 是按照10进制的形式打印有符号的整数\n\treturn 0;\n\t}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"四算术转换\"\u003e四.算术转换\u003c/h2\u003e\n\u003cp\u003e如果某个操作符的各个操作数属于不同的类型，那么除非其中一个操作数的转换为另一个操作数的类型，否则操作就无法进行。下面的层次体系称为寻常算术转换：\u003c/p\u003e","title":"C语言之操作符【下】"},{"content":"例题：\n下面代码的结果是： #include \u0026lt;stdio.h\u0026gt; int i; int main() { i--; if (i \u0026gt; sizeof(i)) { printf(\u0026#34;\u0026gt;\\n\u0026#34;); } else { printf(\u0026#34;\u0026lt;\\n\u0026#34;); } return 0; } A.\u0026gt;\nB.\u0026lt;\nC.不输出\nD.程序有问题\n首先，我可以明确一点的是，这道题不选择D和C，在本函数中，由于int i 是一个全局变量，它存放的是静态区，\nC标准是要求全局变量要被初始化为零值，因此即使你没有初始化该值那么依然会被系统初始化为0，然而，sizeof类型是属于unsigned int 类型，也就是说，-1需要进行一拨转换，然而-1转换为无符号整形数字是比较大的，那么的话，明显是\u0026gt;,那么就选则的是A\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%87/","summary":"\u003cp\u003e例题：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e下面代码的结果是：\n\n#include \u0026lt;stdio.h\u0026gt;\nint i;\nint main()\n{\n    i--;\n    if (i \u0026gt; sizeof(i))\n    {\n        printf(\u0026#34;\u0026gt;\\n\u0026#34;);\n    }\n    else\n    {\n        printf(\u0026#34;\u0026lt;\\n\u0026#34;);\n    }\n    return 0;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eA.\u0026gt;\u003c/p\u003e\n\u003cp\u003eB.\u0026lt;\u003c/p\u003e\n\u003cp\u003eC.不输出\u003c/p\u003e\n\u003cp\u003eD.程序有问题\u003c/p\u003e\n\u003cp\u003e首先，我可以明确一点的是，这道题不选择D和C，在本函数中，由于int i 是一个全局变量，它存放的是静态区，\u003c/p\u003e\n\u003cp\u003eC标准是要求全局变量要被初始化为零值，因此即使你没有初始化该值那么依然会被系统初始化为0，然而，sizeof类型是属于unsigned int 类型，也就是说，-1需要进行一拨转换，然而-1转换为无符号整形数字是比较大的，那么的话，明显是\u0026gt;,那么就选则的是A\u003c/p\u003e","title":"C语言之讲解篇"},{"content":"例题：获取一个整数二进制序列中所有的偶数位和奇数位，分别打印出二进制序列。\n对于这个题我想的是，由于1\u0026amp;二进制的数字，他的意思是判断最低位是否为1，是的话那么就是1，否则就不是1而是0，这样如果我在进行一番右移操做，那么的话，就可以把这个二进制打印出来。\n若是需要奇偶位打印的话，那么可能是在每次右移奇数个位或者是偶数个位，就可以求出二进制的奇数位，以及偶数位，下面我就为大家展示一番。\n#include\u0026lt;stdio.h\u0026gt; int main() { int num = 0; printf(\u0026#34;请输入一个数:\u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;num); printf(\u0026#34;奇数位：\u0026#34;); for (int i = 31; i \u0026gt;= 1; i -= 2) { printf(\u0026#34;%d \u0026#34;, (num \u0026gt;\u0026gt; i) \u0026amp; 1); } printf(\u0026#34;\\n\u0026#34;); printf(\u0026#34;偶数位：\u0026#34;); for (int i = 30; i \u0026gt;= 0; i -= 2) { printf(\u0026#34;%d \u0026#34;, (num \u0026gt;\u0026gt; i) \u0026amp; 1); } printf(\u0026#34;\\n\u0026#34;); return 0; } 这个是运行结果：\n因此这就是本题的代码。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%872/","summary":"\u003cp\u003e例题：获取一个整数二进制序列中所有的偶数位和奇数位，分别打印出二进制序列。\u003c/p\u003e\n\u003cp\u003e对于这个题我想的是，由于1\u0026amp;二进制的数字，他的意思是判断最低位是否为1，是的话那么就是1，否则就不是1而是0，这样如果我在进行一番右移操做，那么的话，就可以把这个二进制打印出来。\u003c/p\u003e\n\u003cp\u003e若是需要奇偶位打印的话，那么可能是在每次右移奇数个位或者是偶数个位，就可以求出二进制的奇数位，以及偶数位，下面我就为大家展示一番。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include\u0026lt;stdio.h\u0026gt;\n\nint main() {\n\n    int num = 0;\n\n    printf(\u0026#34;请输入一个数:\u0026#34;);\n\n    scanf(\u0026#34;%d\u0026#34;, \u0026amp;num);\n\n    printf(\u0026#34;奇数位：\u0026#34;);\n\n    for (int i = 31; i \u0026gt;= 1; i -= 2) {\n\n        printf(\u0026#34;%d \u0026#34;, (num \u0026gt;\u0026gt; i) \u0026amp; 1);\n\n    }\n\n    printf(\u0026#34;\\n\u0026#34;);\n\n    printf(\u0026#34;偶数位：\u0026#34;);\n\n    for (int i = 30; i \u0026gt;= 0; i -= 2) {\n\n        printf(\u0026#34;%d \u0026#34;, (num \u0026gt;\u0026gt; i) \u0026amp; 1);\n\n    }\n\n    printf(\u0026#34;\\n\u0026#34;);\n\n    return 0;\n\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e这个是运行结果：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%872/images/2a48580f851b401c7918f0011416a13e.png\"\u003e\u003c/p\u003e\n\u003cp\u003e因此这就是本题的代码。\u003c/p\u003e","title":"C语言之讲解篇2"},{"content":"例题：求两个数二进制中不同位的个数，写出C代码。\n在我的讲解篇2中可以看到：由于每次右移1位并对1按位与那么就可以将二进制位分离出来，得出与原来一样二进制位。\n思路一样，那么我们可以用上一篇中提到的方法，那么接下来我们就需要对每个二进制位进行比较就可以了，以此类推，若是不同的话统计其个数并将其统计的个数，返回，所求的便是其值，我所写的代码，如下：\n#include\u0026lt;stdio.h\u0026gt; int main() { int x = 0; int y = 0; int count = 0; printf(\u0026#34;输入两个整数:\u0026#34;); scanf(\u0026#34;%d %d\u0026#34;, \u0026amp;x, \u0026amp;y); for (int i = 0; i \u0026lt; 32; i++) { if (((x \u0026gt;\u0026gt; i) \u0026amp; 1) != ((y \u0026gt;\u0026gt; i) \u0026amp; 1)) { count++; } } printf(\u0026#34;有:%d位数不同\u0026#34;, count); return 0; } 下面是我的代码的运行结果：\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%873/","summary":"\u003cp\u003e例题：求两个数二进制中不同位的个数，写出C代码。\u003c/p\u003e\n\u003cp\u003e在我的讲解篇2中可以看到：由于每次右移1位并对1按位与那么就可以将二进制位分离出来，得出与原来一样二进制位。\u003c/p\u003e\n\u003cp\u003e思路一样，那么我们可以用上一篇中提到的方法，那么接下来我们就需要对每个二进制位进行比较就可以了，以此类推，若是不同的话统计其个数并将其统计的个数，返回，所求的便是其值，我所写的代码，如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include\u0026lt;stdio.h\u0026gt;\n\nint main()\n\n{\n\n    int x = 0;\n\n    int y = 0;\n\n    int count = 0;\n\n    printf(\u0026#34;输入两个整数:\u0026#34;);\n\n    scanf(\u0026#34;%d %d\u0026#34;, \u0026amp;x, \u0026amp;y);\n\n\n    for (int i = 0; i \u0026lt; 32; i++)\n\n    {\n\n        if (((x \u0026gt;\u0026gt; i) \u0026amp; 1) != ((y \u0026gt;\u0026gt; i) \u0026amp; 1))\n\n        {\n\n            count++;\n\n        }\n\n    }\n\n    printf(\u0026#34;有:%d位数不同\u0026#34;, count);\n\n\n    return 0;\n\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e下面是我的代码的运行结果：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%873/images/eed7ecde0d51f68f49631ef528cd1cbf.png\"\u003e\u003c/p\u003e","title":"C语言之讲解篇3"},{"content":"例题：\n下面代码的结果是：（ ） #include \u0026lt;stdio.h\u0026gt; int main() { int arr[] = {1,2,3,4,5}; short *p = (short*)arr; int i = 0; for(i=0; i\u0026lt;4; i++) { *(p+i) = 0; } for(i=0; i\u0026lt;5; i++) { printf(\u0026#34;%d \u0026#34;, arr[i]); } return 0; } 由于short是短整型的，那么其，因此p每次只能得到两个字节，而由于原来是4次循环那么其实是相当于4/2=2个数，也就是说对原来的前两个数赋值为0，那么打印出来得话，自然是：0,0，3,4，5\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%874/","summary":"\u003cp\u003e例题：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e下面代码的结果是：（          ）\n\n#include \u0026lt;stdio.h\u0026gt;\nint main()\n{\n  int arr[] = {1,2,3,4,5};\n  short *p = (short*)arr;\n  int i = 0;\n  for(i=0; i\u0026lt;4; i++)\n  {\n    *(p+i) = 0;\n  }\n\n  for(i=0; i\u0026lt;5; i++)\n  {\n    printf(\u0026#34;%d \u0026#34;, arr[i]);\n  }\n  return 0;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e由于short是短整型的，那么其，因此p每次只能得到两个字节，而由于原来是4次循环那么其实是相当于4/2=2个数，也就是说对原来的前两个数赋值为0，那么打印出来得话，自然是：0,0，3,4，5\u003c/p\u003e","title":"C语言之讲解篇4"},{"content":"例题：\n下列程序段的输出结果为（ ） unsigned long pulArray[] = {6,7,8,9,10}; unsigned long *pulPtr; pulPtr = pulArray; *(pulPtr + 3) += 3; printf(\u0026#34;%d,%d\\n\u0026#34;,*pulPtr, *(pulPtr + 3)); 解释：pulPtr已经是数组的首元素6，而*(pulPtr + 3)是指针向后移动3位所得到的结果，同时把该结果再加上3所得的值是12，接下来再把12赋值给*(pulPtr + 3)，然后由于指针是数组的首地址那么的话，*pulPtr就是6，而(pulPtr + 3)自然就是12，接下来我们就可以选择6 12了，本题11就这样结束了。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%875/","summary":"\u003cp\u003e例题：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e下列程序段的输出结果为（ ）\n\nunsigned long pulArray[] = {6,7,8,9,10};\nunsigned long *pulPtr;\npulPtr = pulArray;\n*(pulPtr + 3) += 3;\nprintf(\u0026#34;%d,%d\\n\u0026#34;,*pulPtr, *(pulPtr + 3));\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e解释：pulPtr已经是数组的首元素6，而*(pulPtr + 3)是指针向后移动3位所得到的结果，同时把该结果再加上3所得的值是12，接下来再把12赋值给*(pulPtr + 3)，然后由于指针是数组的首地址那么的话，*pulPtr就是6，而(pulPtr + 3)自然就是12，接下来我们就可以选择6 12了，本题11就这样结束了。\u003c/p\u003e","title":"C语言之讲解篇5"},{"content":"例题：实现一个函数，可以左旋字符串中的k个字符，用C语言写出。\n每次可以把第一个字符存下来，接下来可以以此把每个字符向前移动一个位置，最后把最后一个字符赋值为刚开始存下的值，这样每次做到的就是翻转就是一个字符，那么将总共的大循环，设定为翻转得个数，那么就可以轻轻松松解决问题了，下面是我的代码示例：\n#include\u0026lt;stdio.h\u0026gt; #include\u0026lt;string.h\u0026gt; void programming(char * a,int n,int num); int main() { char num[50]; int n,a; printf(\u0026#34;请输入一些内容：\\n\u0026#34;); gets(num); a=strlen(num); printf(\u0026#34;请输入你n的数值\\n\u0026#34;); scanf(\u0026#34;%d\u0026#34;,\u0026amp;n); printf(\u0026#34;左旋字符串中的n个字符\\n\u0026#34;); programming(num,n,a); printf(\u0026#34;%s\\n\u0026#34;,num); return 0; } void programming(char * a,int n,int num) { int i=0; char temp; for(i=0;i\u0026lt;n;i++) { temp=*a; for(int j=0;j\u0026lt;num-1;j++) *(a+j)=*(a+1+j); *(a+num-1)=temp; } } 该代码的运行结果，是如下：\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%876/","summary":"\u003cp\u003e例题：实现一个函数，可以左旋字符串中的k个字符，用C语言写出。\u003c/p\u003e\n\u003cp\u003e每次可以把第一个字符存下来，接下来可以以此把每个字符向前移动一个位置，最后把最后一个字符赋值为刚开始存下的值，这样每次做到的就是翻转就是一个字符，那么将总共的大循环，设定为翻转得个数，那么就可以轻轻松松解决问题了，下面是我的代码示例：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include\u0026lt;stdio.h\u0026gt;\n\n#include\u0026lt;string.h\u0026gt;\n\nvoid programming(char * a,int n,int num);\n\nint main()\n\n{\n\n    char num[50];\n\n    int n,a;\n\n    printf(\u0026#34;请输入一些内容：\\n\u0026#34;);\n\n    gets(num);\n\n  a=strlen(num);\n\n    printf(\u0026#34;请输入你n的数值\\n\u0026#34;);\n\n    scanf(\u0026#34;%d\u0026#34;,\u0026amp;n);\n\n    printf(\u0026#34;左旋字符串中的n个字符\\n\u0026#34;);\n\n    programming(num,n,a);\n\n    printf(\u0026#34;%s\\n\u0026#34;,num);\n\n    return 0;\n\n}\n\nvoid programming(char * a,int n,int num)\n\n{\n\n    int i=0;\n\n    char temp;\n\n  for(i=0;i\u0026lt;n;i++)\n\n  {\n\n      temp=*a;\n\n      for(int j=0;j\u0026lt;num-1;j++)\n\n      *(a+j)=*(a+1+j);\n\n      *(a+num-1)=temp;\n\n  }\n\n\n  }\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e该代码的运行结果，是如下：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%876/images/d435635e1a3789bb4f9cbaef253b8d82.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%876/images/da4d462074a6440025d3452d7ca7a044.png\"\u003e\u003c/p\u003e","title":"C语言之讲解篇6"},{"content":"例题：写一个函数，可以逆序一个字符串的内容。\n#include \u0026lt;stdio.h\u0026gt; void reverse(char * letter); int main() { char num[50]; int i; while(num[i]!=\u0026#39;\\0\u0026#39;) { scanf(\u0026#34;%c\u0026#34;,\u0026amp;num[i]); i++; } reverse(num); return 0; } void reverse(char * letter) { char * start=letter; while(*letter!=\u0026#39;\\0\u0026#39;) { letter++; } while(letter\u0026gt;=start) { printf(\u0026#34;%c\u0026#34;,*letter); letter--; } } 在这里我们看到用循环设定指针的最大者似乎效率不是很高，那么我们可以使用这个语句：strlen()用他求出字符串的最大值，最后再赋值给指针似乎就完美了：\n/* 思路：该题比较简单，请参考代码 */ #include\u0026lt;stdio.h\u0026gt; void Reverse(char* str) { char* left = str; char* right = str + strlen(str)-1; while(left \u0026lt; right) { char temp = *left; *left = *right; *right = temp; ++left; --right; } } int main() { char str[] = \u0026#34;hello bit\u0026#34;; //在这里完成下面函数，参数自己设计，要求：使用指针 Reverse(str); return 0; } // 注意：如果是在线OJ时，必须要考虑循环输入，因为每个算法可能有多组测试用例进行验证，参考以下main函数写法， int main() { char str[101] = {0}; while(gets(str)) { Reverse(str); printf(\u0026#34;%s\\n\u0026#34;, str); memset(str, 0, sizeof(str)/sizeof(str[0])); } return 0; } 接下来本题就宣告结束了。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%877/","summary":"\u003cp\u003e例题：写一个函数，可以逆序一个字符串的内容。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include \u0026lt;stdio.h\u0026gt;\nvoid reverse(char * letter);\nint main()\n {\n    char num[50];\n    int i;\n    while(num[i]!=\u0026#39;\\0\u0026#39;)\n    {\n        scanf(\u0026#34;%c\u0026#34;,\u0026amp;num[i]);\n        i++;\n    }\n  reverse(num);\n    return 0;\n}\nvoid reverse(char * letter)\n{\n    char * start=letter;\n    while(*letter!=\u0026#39;\\0\u0026#39;)\n    {\n        letter++;\n    }\n    while(letter\u0026gt;=start)\n    {\n        printf(\u0026#34;%c\u0026#34;,*letter);\n        letter--;\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%877/images/9b1592125a58301e5b1633e39c3c0ae7.png\"\u003e\u003c/p\u003e\n\u003cp\u003e在这里我们看到用循环设定指针的最大者似乎效率不是很高，那么我们可以使用这个语句：strlen()用他求出字符串的最大值，最后再赋值给指针似乎就完美了：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%877/images/4aa164b2ff9d1b2072697e4c0a063a3d.png\"\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/*\n思路：该题比较简单，请参考代码\n*/\n#include\u0026lt;stdio.h\u0026gt;\nvoid Reverse(char* str)\n{\n    char* left = str;\n    char* right = str + strlen(str)-1;\n    while(left \u0026lt; right)\n    {\n        char temp = *left;\n        *left = *right;\n        *right = temp;\n        ++left;\n        --right;\n    }\n}\n\n\nint main()\n{\n    char str[] = \u0026#34;hello bit\u0026#34;;\n    //在这里完成下面函数，参数自己设计，要求：使用指针\n    Reverse(str);\n    return 0;\n}\n\n\n// 注意：如果是在线OJ时，必须要考虑循环输入，因为每个算法可能有多组测试用例进行验证，参考以下main函数写法，\nint main()\n{\n    char str[101] = {0};\n    while(gets(str))\n    {\n        Reverse(str);\n        printf(\u0026#34;%s\\n\u0026#34;, str);\n        memset(str, 0, sizeof(str)/sizeof(str[0]));\n    }\n    return 0;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e接下来本题就宣告结束了。\u003c/p\u003e","title":"C语言之讲解篇7"},{"content":"例题：\n输入一个整数数组，实现一个函数，来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分,所有偶数位于数组的后半部分。\n1.首先设置一个逆序输出的函数。 由于他们是没有任何返回值的因此呢，我们可以设定一个没有一个返回值的函数，同时还需要做到，对原来的函数进行修改，因此我们考虑使用数组或者指针。所以结合以上，所设置的函数可以是：\nvoid swap_arr(int arr[], int sz); 2.其次呢再设定两个变量，left,right. 1.left,right的初始化 在这里，我们首先可以假定left是数组的最小者，而right是数组的最大者，由于数组的小标是从0开始的那么最大者就是数组的长度减去1，而求数组的长度就可以使用sz，那么的话，最大者就是：sz-1\nleft 0; right sz-1; 2.left与right的具体实现 1.left的具体实现 是通过遇到偶数时停止循环造成的。\n1.left的的执行功能 left首先是定义为寻找奇数，而遇到偶数时就停止循环，并将那个偶数带回到left身上。\n2.实现操作与基本逻辑 我们可以通过循环来实现，在众多的循环当中我们可以选择的是，while循环而循环条件就是小标为left的数组，是奇数并且保证left\u0026lt;=right，当其为偶数时就跳出循环。\n基本语句如下：\nwhile(left\u0026lt;right) { // 从前往后，找到一个偶数，找到后停止 while((left\u0026lt;right)\u0026amp;\u0026amp;(arr[left]%2==1)) { left++; } } 2.right的具体实现 是通过遇到奇数时停止循环造成的。\n1.right的的执行功能 right首先是定义为寻找偶数数，而遇到奇数时就停止循环，并将那个偶数带回到right身上。\n2.实现操作与基本逻辑 我们可以通过循环来实现，在众多的循环当中我们可以选择的是，while循环而循环条件就是下标为right的数组，是偶数并且保证left\u0026lt;=right，当其为奇数时就跳出循环。\n基本语句如下：\nwhile((left\u0026lt;right)\u0026amp;\u0026amp; (arr[right]%2==0)) { right--; } 3.最后是left,ritht的交换与现实 最后我们将这些整合在一起，得到了函数并将left,right进行交换，由于left是偶数，right是奇数俩者交换，求出则为奇数在前偶数在后，函数完整的代码如下：\nvoid swap_arr(int arr[], int sz) { int left = 0; int right = sz-1; int tmp = 0; while(left\u0026lt;right) { // 从前往后，找到一个偶数，找到后停止 while((left\u0026lt;right)\u0026amp;\u0026amp;(arr[left]%2==1)) { left++; } // 从后往前找，找一个奇数，找到后停止 while((left\u0026lt;right)\u0026amp;\u0026amp; (arr[right]%2==0)) { right--; } // 如果偶数和奇数都找到，交换这两个数据的位置 // 然后继续找，直到两个指针相遇 if(left\u0026lt;right) { tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; } } } 3.最后就可完成函数了。 将函数部分搞定之后我们就可以，把整个代码搞定了，如下：\n#include\u0026lt;stdio.h\u0026gt; void swap_arr(int arr[], int sz) { int left = 0; int right = sz-1; int tmp = 0; while(left\u0026lt;right) { // 从前往后，找到一个偶数，找到后停止 while((left\u0026lt;right)\u0026amp;\u0026amp;(arr[left]%2==1)) { left++; } // 从后往前找，找一个奇数，找到后停止 while((left\u0026lt;right)\u0026amp;\u0026amp; (arr[right]%2==0)) { right--; } // 如果偶数和奇数都找到，交换这两个数据的位置 // 然后继续找，直到两个指针相遇 if(left\u0026lt;right) { tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; } } } int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int len = sizeof(arr) / sizeof(arr[0]); swap_arr(arr, len); for (int i = 0; i \u0026lt; len; i++) { printf(\u0026#34;%d \u0026#34;, arr[i]); } return 0; } 最后我们写这个代码，他可以实现对于无符号整形数组实现奇数在前，偶数在后，我们搞定了本函数。 ","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%AE%B2%E8%A7%A3%E7%AF%878/","summary":"\u003cp\u003e例题：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e输入一个整数数组，实现一个函数，来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分,所有偶数位于数组的后半部分。\u003c/p\u003e\n\u003ch2 id=\"1首先设置一个逆序输出的函数\"\u003e1.首先设置一个逆序输出的函数。\u003c/h2\u003e\n\u003cp\u003e由于他们是没有任何返回值的因此呢，我们可以设定一个没有一个返回值的函数，同时还需要做到，对原来的函数进行修改，因此我们考虑使用数组或者指针。所以结合以上，所设置的函数可以是：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evoid swap_arr(int arr[], int sz);\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"2其次呢再设定两个变量leftright\"\u003e2.其次呢再设定两个变量，left,right.\u003c/h2\u003e\n\u003ch3 id=\"1leftright的初始化\"\u003e1.left,right的初始化\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e在这里，我们首先可以假定left是数组的最小者，而right是数组的最大者，由于数组的小标是从0开始的那么最大者就是数组的长度减去1，而求数组的长度就可以使用sz，那么的话，最大者就是：sz-1\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eleft 0;\nright sz-1;\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2left与right的具体实现\"\u003e2.left与right的具体实现\u003c/h3\u003e\n\u003ch4 id=\"1left的具体实现\"\u003e1.left的具体实现\u003c/h4\u003e\n\u003cp\u003e\u003ccode\u003e是通过遇到偶数时停止循环造成的。\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"1left的的执行功能\"\u003e1.left的的执行功能\u003c/h5\u003e\n\u003cp\u003eleft首先是定义为寻找奇数，而遇到偶数时就停止循环，并将那个偶数带回到left身上。\u003c/p\u003e\n\u003ch4 id=\"2实现操作与基本逻辑\"\u003e2.实现操作与基本逻辑\u003c/h4\u003e\n\u003cp\u003e我们可以通过循环来实现，在众多的循环当中我们可以选择的是，while循环而循环条件就是小标为left的数组，是奇数并且保证left\u0026lt;=right，当其为偶数时就跳出循环。\u003c/p\u003e\n\u003cp\u003e基本语句如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewhile(left\u0026lt;right)\n\t{\n     // 从前往后，找到一个偶数，找到后停止\n\t\twhile((left\u0026lt;right)\u0026amp;\u0026amp;(arr[left]%2==1))\n\t\t{\n\t\t\tleft++;\n\t\t}\n\t}\n\u003c/code\u003e\u003c/pre\u003e\u003ch4 id=\"2right的具体实现\"\u003e2.right的具体实现\u003c/h4\u003e\n\u003cp\u003e\u003ccode\u003e是通过遇到奇数时停止循环造成的。\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"1right的的执行功能\"\u003e1.right的的执行功能\u003c/h5\u003e\n\u003cp\u003eright首先是定义为寻找偶数数，而遇到奇数时就停止循环，并将那个偶数带回到right身上。\u003c/p\u003e\n\u003ch4 id=\"2实现操作与基本逻辑-1\"\u003e2.实现操作与基本逻辑\u003c/h4\u003e\n\u003cp\u003e我们可以通过循环来实现，在众多的循环当中我们可以选择的是，while循环而循环条件就是下标为right的数组，是偶数并且保证left\u0026lt;=right，当其为奇数时就跳出循环。\u003c/p\u003e\n\u003cp\u003e基本语句如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewhile((left\u0026lt;right)\u0026amp;\u0026amp; (arr[right]%2==0))\n\t\t{\n\t\t\tright--;\n\t\t}\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"3最后是leftritht的交换与现实\"\u003e3.最后是left,ritht的交换与现实\u003c/h3\u003e\n\u003cp\u003e最后我们将这些整合在一起，得到了函数并将left,right进行交换，由于left是偶数，right是奇数俩者交换，求出则为奇数在前偶数在后，函数完整的代码如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evoid swap_arr(int arr[], int sz)\n{\n\tint left = 0;\n\tint right = sz-1;\n\tint tmp = 0;\n\n\n\twhile(left\u0026lt;right)\n\t{\n     // 从前往后，找到一个偶数，找到后停止\n\t\twhile((left\u0026lt;right)\u0026amp;\u0026amp;(arr[left]%2==1))\n\t\t{\n\t\t\tleft++;\n\t\t}\n\n\t\t// 从后往前找，找一个奇数，找到后停止\n\t\twhile((left\u0026lt;right)\u0026amp;\u0026amp; (arr[right]%2==0))\n\t\t{\n\t\t\tright--;\n\t\t}\n\n     // 如果偶数和奇数都找到，交换这两个数据的位置\n     // 然后继续找，直到两个指针相遇\n\t\tif(left\u0026lt;right)\n\t\t{\n\t\t\ttmp = arr[left];\n\t\t\tarr[left] = arr[right];\n\t\t\tarr[right] = tmp;\n\t\t}\n\t}\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"3最后就可完成函数了\"\u003e3.最后就可完成函数了。\u003c/h2\u003e\n\u003cp\u003e将函数部分搞定之后我们就可以，把整个代码搞定了，如下：\u003c/p\u003e","title":"C语言之讲解篇8"},{"content":"C语言之扫雷游戏 1.扫雷游戏的准备工作\n2.扫雷游戏的基本逻辑\n一.扫雷的基本框架\n二. 扫雷游戏的基本逻辑\n三.扫雷需要的所有函数\n1.定义初始化函数\n2.定义打印棋盘函数\n3.定义布置雷的函数\n4.定义寻找雷的函数\n三.头文件得声明\n3.扫雷游戏的具体实现\n写到最后\n1.扫雷游戏的准备工作 在进行扫雷工作时，我们需要做的是需要一个编译环境，在市面上有众多的编译器，在这里作为使用过VS的我来说，它简直太适合我们这样的初学者了，里面支持自动换行对于初学者非常友好，同时，最为关键的是，在这款编译器当中，我们使用调试功能，是极其的容易，并且它的调试功能也是极其强大的。 写到最后，我推荐大家观看B站鹏哥的视频，在那里鹏哥会给我们最为直观的讲解，会非常详细的指导大家的。\n2.扫雷游戏的基本逻辑 一.扫雷的基本框架 在扫雷游戏中时，首先需要我们在VS编译器当中准备三个文件，二个C文件一个头文件： 在这里大家可以参考一下我准备的文件 二. 扫雷游戏的基本逻辑 1.在C语言扫雷游戏之中，需要注意的是扫雷游戏时在选定的区域的附近八个点位统计雷的个数并输出在选定的区域之上，但是若果选定的区域是边上亦或是角上，那么就会导致无法遍历，超出应有的范围，这个问题我们可以假设棋盘的大小是9x9然而总共数组的大小可以设定11x11那么，就不是解决了问题吗？ 2.扫雷的基本思路是首先需要准备2个字符数组，一个数组存放排查出的雷的数量信息记录存储，并打印出来，而另一个数组则存放布置好雷的信息，这样就互不干扰了 3.同时为了保持神秘，show数组开始时初始化为字符 ‘*’，为了保持两个数组的类型一致，可以使用同一 套函数处理，mine数组最开始也初始化为字符’0’，布置雷改成’1’。如下如： 三.扫雷需要的所有函数 对于扫雷的实现需要用到，大量的函数，在这里我将为大家呈现出应当使用的具体函数：\n1.定义初始化函数 首先我们需要一个初始化棋盘的函数，而对于二个棋盘,一个接受‘0’的初始化，而另一个则接受‘*’的初始化，像这样的函数：\nvoid InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (i = 0; i \u0026lt; rows; i++) { int j = 0; for (j = 0; j \u0026lt; cols; j++) { board[i][j] = set; } } } 2.定义打印棋盘函数 1.在使用中，我们需要看到棋盘显示在电脑上，那么显而易见的是我们可以使用打印棋盘这个函数。\n2.在这个函数之中我就不为大家介绍了，因为这个函数比较的容易，需要3个循环结束，如下是我对于其的定义：\nvoid DisplayBoard(char board[ROWS][COLS], int row, int col) { int i = 0; printf(\u0026#34;--------扫雷游戏-------\\n\u0026#34;); for (i = 0; i \u0026lt;= col; i++) { printf(\u0026#34;%d \u0026#34;, i); } printf(\u0026#34;\\n\u0026#34;); for (i = 1; i \u0026lt;= row; i++) { printf(\u0026#34;%d \u0026#34;, i); int j = 0; for (j = 1; j \u0026lt;= col; j++) { printf(\u0026#34;%c \u0026#34;, board[i][j]); } printf(\u0026#34;\\n\u0026#34;); } } 3.定义布置雷的函数 1.``当然最为重要的是，布置地雷，那么对于这个函数我们使用到了随机数函数，这个函数在我的上一讲中都有提到，我们需要用随机函数在棋盘上生成雷。```\n2.但是需这注意的是，我们不可以在同一个地方重复生成。因此我们可以对于生成的雷的地方，我们可以使它的值都为1，那么对于后面的来说，只要我们检查是否是1，若果是0，那么说明是这个地方没有放置地雷，而是若果，这个地方是1的话，则说明这个地方已经放置了地雷，自然不再放雷，如此来说我们定义布置雷的函数就可以说结束了。我们定义的雷的个数\n3.下面就位大家介绍一番：\nvoid SetMine(char board[ROWS][COLS], int row, int col) { //布置10个雷 //生成随机的坐标，布置雷 int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] == \u0026#39;0\u0026#39;) { board[x][y] = \u0026#39;1\u0026#39;; count--; } } } 4.定义寻找雷的函数 1.我们当然需要一个寻找地雷的函数，那么在这个函数当中首先需要一个统计地雷个数的函数，定义如下：\nint GetMineCount(char mine[ROWS][COLS], int x, int y) { return (mine[x-1][y]+mine[x-1][y-1]+mine[x][y - 1]+mine[x+1][y-1]+mine[x+1][y]+mine[x+1][y+1]+mine[x][y+1]+mine[x-1][y+1] - 8 * \u0026#39;0\u0026#39;); } 2.最后呢我们需要定义寻找雷的函数。在这里面我们首先需要一个循环，条件是只要输入的点位与棋盘边数的平方-雷的个数相等。 3.每次输入的坐标判断是否是合法的坐标，若果合法就判断它的位置是1说明是雷，则退出循环，游戏结束； 若果不合法就业退出循环 4.若果满足条件则执行，最后判断是否输入的次数是边数的平方-雷的个数是则游戏成功，否则游戏失败。并打印在棋盘上。 5.如下是我的代码实现:\nvoid FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win \u0026lt; row * col - EASY_COUNT) { printf(\u0026#34;请输入要排查的坐标:\u0026gt;\u0026#34;); scanf(\u0026#34;%d %d\u0026#34;, \u0026amp;x, \u0026amp;y); if (x \u0026gt;= 1 \u0026amp;\u0026amp; x \u0026lt;= row \u0026amp;\u0026amp; y \u0026gt;= 1 \u0026amp;\u0026amp; y \u0026lt;= col) { if (mine[x][y] == \u0026#39;1\u0026#39;) { printf(\u0026#34;很遗憾，你被炸死了\\n\u0026#34;); DisplayBoard(mine, ROW, COL); break; } else { //该位置不是雷，就统计这个坐标周围有几个雷 int count = GetMineCount(mine, x, y); show[x][y] = count + \u0026#39;0\u0026#39;; DisplayBoard(show, ROW, COL); win++; } } else { printf(\u0026#34;坐标非法，重新输入\\n\u0026#34;); } } if (win == row * col - EASY_COUNT) { printf(\u0026#34;恭喜你，排雷成功\\n\u0026#34;); DisplayBoard(mine, ROW, COL); } 三.头文件得声明 1.在扫雷游戏中如下是我对于头文件的声明：\n#pragma once #pragma once #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; #define EASY_COUNT 10 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 //初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); //打印棋盘 void DisplayBoard(char board[ROWS][COLS], int row, int col); //布置雷 void SetMine(char board[ROWS][COLS], int row, int col); //排查雷 void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); 2.用到了初始化棋盘，打印棋盘，布置雷，排查雷，以及对于随机函数的头文件得声明。\n3.扫雷游戏的具体实现 一下是对于扫雷游戏用C代码的具体实现： game.c\n#define _CRT_SECURE_NO_WARNINGS 1 #include \u0026#34;game.h\u0026#34; void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (i = 0; i \u0026lt; rows; i++) { int j = 0; for (j = 0; j \u0026lt; cols; j++) { board[i][j] = set; } } } void DisplayBoard(char board[ROWS][COLS], int row, int col) { int i = 0; printf(\u0026#34;--------扫雷游戏-------\\n\u0026#34;); for (i = 0; i \u0026lt;= col; i++) { printf(\u0026#34;%d \u0026#34;, i); } printf(\u0026#34;\\n\u0026#34;); for (i = 1; i \u0026lt;= row; i++) { printf(\u0026#34;%d \u0026#34;, i); int j = 0; for (j = 1; j \u0026lt;= col; j++) { printf(\u0026#34;%c \u0026#34;, board[i][j]); } printf(\u0026#34;\\n\u0026#34;); } } void SetMine(char board[ROWS][COLS], int row, int col) { //布置10个雷 //生成随机的坐标，布置雷 int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] == \u0026#39;0\u0026#39;) { board[x][y] = \u0026#39;1\u0026#39;; count--; } } } int GetMineCount(char mine[ROWS][COLS], int x, int y) { return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x+1][y]+mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * \u0026#39;0\u0026#39;); } void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win \u0026lt; row * col - EASY_COUNT) { printf(\u0026#34;请输入要排查的坐标:\u0026gt;\u0026#34;); scanf(\u0026#34;%d %d\u0026#34;, \u0026amp;x, \u0026amp;y); if (x \u0026gt;= 1 \u0026amp;\u0026amp; x \u0026lt;= row \u0026amp;\u0026amp; y \u0026gt;= 1 \u0026amp;\u0026amp; y \u0026lt;= col) { if (mine[x][y] == \u0026#39;1\u0026#39;) { printf(\u0026#34;很遗憾，你被炸死了\\n\u0026#34;); DisplayBoard(mine, ROW, COL); break; } else { //该位置不是雷，就统计这个坐标周围有几个雷 int count = GetMineCount(mine, x, y); show[x][y] = count + \u0026#39;0\u0026#39;; DisplayBoard(show, ROW, COL); win++; } } else { printf(\u0026#34;坐标非法，重新输入\\n\u0026#34;); } } if (win == row * col - EASY_COUNT) { printf(\u0026#34;恭喜你，排雷成功\\n\u0026#34;); DisplayBoard(mine, ROW, COL); } } game.h\n#pragma once #pragma once #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; #define EASY_COUNT 10 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 //初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); //打印棋盘 void DisplayBoard(char board[ROWS][COLS], int row, int col); //布置雷 void SetMine(char board[ROWS][COLS], int row, int col); //排查雷 void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); test.c\n#define _CRT_SECURE_NO_WARNINGS 1 #include \u0026#34;game.h\u0026#34; void menu() { printf(\u0026#34;***********************\\n\u0026#34;); printf(\u0026#34;***** 1. play *****\\n\u0026#34;); printf(\u0026#34;***** 0. exit *****\\n\u0026#34;); printf(\u0026#34;***********************\\n\u0026#34;); } void game() { char mine[ROWS][COLS];//存放布置好的雷 char show[ROWS][COLS];//存放排查出的雷的信息 //初始化棋盘 //1. mine数组最开始是全\u0026#39;0\u0026#39; //2. show数组最开始是全\u0026#39;*\u0026#39; InitBoard(mine, ROWS, COLS, \u0026#39;0\u0026#39;); InitBoard(show, ROWS, COLS, \u0026#39;*\u0026#39;); //打印棋盘 //DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL); //1. 布置雷 SetMine(mine, ROW, COL); //DisplayBoard(mine, ROW, COL); //2. 排查雷 FindMine(mine, show, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf(\u0026#34;请选择:\u0026gt;\u0026#34;); scanf(\u0026#34;%d\u0026#34;, \u0026amp;input); switch (input) { case 1: game(); break; case 0: printf(\u0026#34;退出游戏\\n\u0026#34;); break; default: printf(\u0026#34;选择错误，重新选择\\n\u0026#34;); break; } } while (input); return 0; } 写到最后 大家可以对这个游戏功能进行丰富，如可以成片的显示，积分，难易程度等等。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%89%AB%E9%9B%B7%E6%B8%B8%E6%88%8F/","summary":"\u003ch4 id=\"c语言之扫雷游戏\"\u003eC语言之扫雷游戏\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_3\"\u003e1.扫雷游戏的准备工作\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_6\"\u003e2.扫雷游戏的基本逻辑\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_7\"\u003e一.扫雷的基本框架\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#__11\"\u003e二. 扫雷游戏的基本逻辑\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_18\"\u003e三.扫雷需要的所有函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_20\"\u003e1.定义初始化函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_37\"\u003e2.定义打印棋盘函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_63\"\u003e3.定义布置雷的函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_87\"\u003e4.定义寻找雷的函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_138\"\u003e三.头文件得声明\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_162\"\u003e3.扫雷游戏的具体实现\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_333\"\u003e写到最后\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1扫雷游戏的准备工作\"\u003e1.扫雷游戏的准备工作\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e在进行扫雷工作时，我们需要做的是需要一个编译环境，在市面上有众多的编译器，在这里作为使用过VS的我来说，它简直太适合我们这样的初学者了，里面支持自动换行对于初学者非常友好，同时，最为关键的是，在这款编译器当中，我们使用调试功能，是极其的容易，并且它的调试功能也是极其强大的。\n写到最后，我推荐大家观看B站鹏哥的视频，在那里鹏哥会给我们最为直观的讲解，会非常详细的指导大家的。\u003c/p\u003e\n\u003ch2 id=\"2扫雷游戏的基本逻辑\"\u003e2.扫雷游戏的基本逻辑\u003c/h2\u003e\n\u003ch3 id=\"一扫雷的基本框架\"\u003e一.扫雷的基本框架\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e在扫雷游戏中时，首先需要我们在VS编译器当中准备三个文件，二个C文件一个头文件：\u003c/strong\u003e\n在这里大家可以参考一下我准备的文件\n\u003cimg alt=\"C语言扫雷游戏的准备工作\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%89%AB%E9%9B%B7%E6%B8%B8%E6%88%8F/images/7f7f24f22c1d4aa16a9abae396f7d3d3.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"二-扫雷游戏的基本逻辑\"\u003e二. 扫雷游戏的基本逻辑\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1.在C语言扫雷游戏之中，需要注意的是扫雷游戏时在选定的区域的附近八个点位统计雷的个数并输出在选定的区域之上，但是若果选定的区域是边上亦或是角上，那么就会导致无法遍历，超出应有的范围，这个问题我们可以假设棋盘的大小是9x9然而总共数组的大小可以设定11x11那么，就不是解决了问题吗？\n\u003ccode\u003e2.扫雷的基本思路是首先需要准备2个字符数组，一个数组存放排查出的雷的数量信息记录存储，并打印出来，而另一个数组则存放布置好雷的信息，这样就互不干扰了\u003c/code\u003e\n3.同时为了保持神秘，show数组开始时初始化为字符 ‘*’，为了保持两个数组的类型一致，可以使用同一\n套函数处理，mine数组最开始也初始化为字符’0’，布置雷改成’1’。如下如：\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%89%AB%E9%9B%B7%E6%B8%B8%E6%88%8F/images/e246c041422930b6e18256de91167949.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"三扫雷需要的所有函数\"\u003e三.扫雷需要的所有函数\u003c/h3\u003e\n\u003cp\u003e对于扫雷的实现需要用到，大量的函数，在这里我将为大家呈现出应当使用的具体函数：\u003c/p\u003e\n\u003ch4 id=\"1定义初始化函数\"\u003e1.定义初始化函数\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e首先我们需要一个初始化棋盘的函数，而对于二个棋盘,一个接受‘0’的初始化，而另一个则接受‘*’的初始化，像这样的函数：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evoid InitBoard(char board[ROWS][COLS], int rows, int cols, char set)\n{\n\tint i = 0;\n\tfor (i = 0; i \u0026lt; rows; i++)\n\t{\n\t\tint j = 0;\n\t\tfor (j = 0; j \u0026lt; cols; j++)\n\t\t{\n\t\t\tboard[i][j] = set;\n\t\t}\n\t}\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch4 id=\"2定义打印棋盘函数\"\u003e2.定义打印棋盘函数\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1.在使用中，我们需要看到棋盘显示在电脑上，那么显而易见的是我们可以使用打印棋盘这个函数。\u003c/p\u003e","title":"C语言之扫雷游戏"},{"content":"在本讲中，我们将会详细的学习sizeof以及strlen的区别，同时呢我也会为大家讲一些经典面试题，相信大家在看过我的本讲之后，一定会对指针有更加深刻的认识与了解，下面就开启我们的指针之旅吧。\nC语言之指针篇第五 一.sizeof和strlen的对⽐\n1.sizeof\n1.sizeof含义\n2.例子：\n2.strlen\n1.strlen的含义\n2.例子\n二.数组指针笔试题解析\n一.sizeof和strlen的对⽐ 首先在学习中时，我们通常会经常遇到sizeof，以及strlen函数，我们在使用时经常会分不清sizeof以及strlen，我们经常会用着用着，将两个函数用颠倒，大家对此简直是苦不堪言，因此呢，我将用这节为大家详细介绍两者区别，相信大家在听过我的讲解之后，一定会在今后的学习中，对两者运用的更加之熟练。\n1.sizeof 1.sizeof含义 sizeof 主要计算的是变量所占内存内存空间⼤⼩的，单位是字节，如果操作数是类型的话，计算的是使⽤类型创建的变量所占内存空间的⼤小，sizeof 只关注占⽤内存空间的⼤⼩，不在乎内存中存放什么数据。\n2.例子： int a=1; sizeof(a);//因此呢，算出的数值是4. 又有例子：\nint a=4; int * s=\u0026amp;a; sizeof(s);//可以看出由于地址的存储在内存中是64根地址线或者是32根地址线因此地址无论是整形或者是字符型亦或者是数组型 //它们其实都是4或者8 2.strlen 1.strlen的含义 它的功能是，求字符的长度，遇到‘\\0’则停止，否则会在内存中找到‘\\0’,这样才会停下来，如此一番，求出的则是随机值。\n2.例子 char a=\u0026#34;hello bit\u0026#34;; int a=strlen(a);//由于“”省略了\\0，因此求出的是8 又有例子： char a[]={\u0026#39;hello\u0026#39;,\u0026#39;bit\u0026#39;}; int a=strlen(a);//在这里，由于没有设置‘\\0’,那么的话，结果就有可能是随机值。 二.数组指针笔试题解析 例题1：\nint a[] = {1,2,3,4}; printf(\u0026#34;%d\\n\u0026#34;,sizeof(a));//sizeof加数组名指的是整个数组的大小，也就是说打印出来的是16. printf(\u0026#34;%d\\n\u0026#34;,sizeof(a+0));//在这里的意思是，数组的首元素的地址而地址的大小则是4或者8，因此打印出来的是4或者是8 printf(\u0026#34;%d\\n\u0026#34;,sizeof(*a));//在这里指的是数组的首元素，因此答案也就是4 printf(\u0026#34;%d\\n\u0026#34;,sizeof(a+1));//在这里的意思是数组的第二个元素的地址也就是说答案是4或者是8. printf(\u0026#34;%d\\n\u0026#34;,sizeof(a[1]));//这里是数组的第二个元素，也就是说答案是4 printf(\u0026#34;%d\\n\u0026#34;,sizeof(\u0026amp;a));//这里是数组的地址，也就是说是4或者是8. printf(\u0026#34;%d\\n\u0026#34;,sizeof(*\u0026amp;a));//注意*\u0026amp;相当于没有因此该式子也就是sizeof(a)，注意是16 12345678 printf(\u0026#34;%d\\n\u0026#34;,sizeof(\u0026amp;a+1));//在这里的意思是指向数组的后面的地址，也就是4或者是8 printf(\u0026#34;%d\\n\u0026#34;,sizeof(\u0026amp;a[0]));//这里是首元素的地址，是4或者是8 printf(\u0026#34;%d\\n\u0026#34;,sizeof(\u0026amp;a[0]+1));//是第二个元素的地址是4或者是8. 例题2：\nchar arr[] = {\u0026#39;a\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;d\u0026#39;,\u0026#39;e\u0026#39;}; printf(\u0026#34;%d\\n\u0026#34;, sizeof(arr));//由于没有‘\\0’那么的话，就是5 printf(\u0026#34;%d\\n\u0026#34;, sizeof(arr+0));//这里是首元素的地址，那么是4或者是8 printf(\u0026#34;%d\\n\u0026#34;, sizeof(*arr));//是首元素那么就是1 printf(\u0026#34;%d\\n\u0026#34;, sizeof(arr[1]));//在这里也是首元素，那么是1 printf(\u0026#34;%d\\n\u0026#34;, sizeof(\u0026amp;arr));//这里是数组的地址是4或者是8 printf(\u0026#34;%d\\n\u0026#34;, sizeof(\u0026amp;arr+1));//这里是在数组之后的地址，也就是4或者是8 printf(\u0026#34;%d\\n\u0026#34;, sizeof(\u0026amp;arr[0]+1));//是第二个元素地址是4或者是8 char arr[]=\u0026#34;abcdef”； printf(\u0026#34;%d\\n\u0026#34;,strlen(arr));//这里是说字符串的长度是6 printf(\u0026#34;%d\\n\u0026#34;,strlen(*arr);//在这里会出错。 printf(\u0026#34;%d\\n\u0026#34;,strlen(arr[1]));//在这里也会出错 printf(\u0026#34;%d\\n\u0026#34;,strlen(\u0026amp;arr));//是6 printf(\u0026#34;%d\\n\u0026#34;,strlen(\u0026amp;arr+1));//这会跳出这个数组，应该是0 printf(\u0026#34;%d\\n\u0026#34;,strlen(\u0026amp;arr[0]+1));//这里第二个元素的地址是5 完（。）\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E4%B9%8B%E6%8C%87%E9%92%88%E7%AF%87%E7%AC%AC%E4%BA%94/","summary":"\u003cp\u003e在本讲中，我们将会详细的学习sizeof以及strlen的区别，同时呢我也会为大家讲一些经典面试题，相信大家在看过我的本讲之后，一定会对指针有更加深刻的认识与了解，下面就开启我们的指针之旅吧。\u003c/p\u003e\n\u003ch4 id=\"c语言之指针篇第五\"\u003eC语言之指针篇第五\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#sizeofstrlen_3\"\u003e一.sizeof和strlen的对⽐\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1sizeof_5\"\u003e1.sizeof\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1sizeof_6\"\u003e1.sizeof含义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_8\"\u003e2.例子：\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2strlen_20\"\u003e2.strlen\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1strlen_21\"\u003e1.strlen的含义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_23\"\u003e2.例子\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_31\"\u003e二.数组指针笔试题解析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一sizeof和strlen的对\"\u003e一.sizeof和strlen的对⽐\u003c/h2\u003e\n\u003cp\u003e首先在学习中时，我们通常会经常遇到sizeof，以及strlen函数，我们在使用时经常会分不清sizeof以及strlen，我们经常会用着用着，将两个函数用颠倒，大家对此简直是苦不堪言，因此呢，我将用这节为大家详细介绍两者区别，相信大家在听过我的讲解之后，一定会在今后的学习中，对两者运用的更加之熟练。\u003c/p\u003e\n\u003ch3 id=\"1sizeof\"\u003e1.sizeof\u003c/h3\u003e\n\u003ch4 id=\"1sizeof含义\"\u003e1.sizeof含义\u003c/h4\u003e\n\u003cp\u003esizeof 主要计算的是变量所占内存内存空间⼤⼩的，单位是字节，如果操作数是类型的话，计算的是使⽤类型创建的变量所占内存空间的⼤小，sizeof 只关注占⽤内存空间的⼤⼩，不在乎内存中存放什么数据。\u003c/p\u003e\n\u003ch4 id=\"2例子\"\u003e2.例子：\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eint a=1;\nsizeof(a);//因此呢，算出的数值是4.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e又有例子：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eint a=4;\nint * s=\u0026amp;a;\nsizeof(s);//可以看出由于地址的存储在内存中是64根地址线或者是32根地址线因此地址无论是整形或者是字符型亦或者是数组型\n//它们其实都是4或者8\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2strlen\"\u003e2.strlen\u003c/h3\u003e\n\u003ch4 id=\"1strlen的含义\"\u003e1.strlen的含义\u003c/h4\u003e\n\u003cp\u003e它的功能是，求字符的长度，遇到‘\\0’则停止，否则会在内存中找到‘\\0’,这样才会停下来，如此一番，求出的则是随机值。\u003c/p\u003e\n\u003ch4 id=\"2例子-1\"\u003e2.例子\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003echar a=\u0026#34;hello bit\u0026#34;;\nint a=strlen(a);//由于“”省略了\\0，因此求出的是8\n又有例子：\nchar a[]={\u0026#39;hello\u0026#39;,\u0026#39;bit\u0026#39;};\nint a=strlen(a);//在这里，由于没有设置‘\\0’,那么的话，结果就有可能是随机值。\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"二数组指针笔试题解析\"\u003e二.数组指针笔试题解析\u003c/h2\u003e\n\u003cp\u003e例题1：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eint a[] = {1,2,3,4};\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(a));//sizeof加数组名指的是整个数组的大小，也就是说打印出来的是16.\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(a+0));//在这里的意思是，数组的首元素的地址而地址的大小则是4或者8，因此打印出来的是4或者是8\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(*a));//在这里指的是数组的首元素，因此答案也就是4\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(a+1));//在这里的意思是数组的第二个元素的地址也就是说答案是4或者是8.\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(a[1]));//这里是数组的第二个元素，也就是说答案是4\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(\u0026amp;a));//这里是数组的地址，也就是说是4或者是8.\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(*\u0026amp;a));//注意*\u0026amp;相当于没有因此该式子也就是sizeof(a)，注意是16\n12345678\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(\u0026amp;a+1));//在这里的意思是指向数组的后面的地址，也就是4或者是8\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(\u0026amp;a[0]));//这里是首元素的地址，是4或者是8\nprintf(\u0026#34;%d\\n\u0026#34;,sizeof(\u0026amp;a[0]+1));//是第二个元素的地址是4或者是8.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e例题2：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003echar arr[] = {\u0026#39;a\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;,\u0026#39;d\u0026#39;,\u0026#39;e\u0026#39;};\nprintf(\u0026#34;%d\\n\u0026#34;, sizeof(arr));//由于没有‘\\0’那么的话，就是5\nprintf(\u0026#34;%d\\n\u0026#34;, sizeof(arr+0));//这里是首元素的地址，那么是4或者是8\nprintf(\u0026#34;%d\\n\u0026#34;, sizeof(*arr));//是首元素那么就是1\nprintf(\u0026#34;%d\\n\u0026#34;, sizeof(arr[1]));//在这里也是首元素，那么是1\nprintf(\u0026#34;%d\\n\u0026#34;, sizeof(\u0026amp;arr));//这里是数组的地址是4或者是8\nprintf(\u0026#34;%d\\n\u0026#34;, sizeof(\u0026amp;arr+1));//这里是在数组之后的地址，也就是4或者是8\nprintf(\u0026#34;%d\\n\u0026#34;, sizeof(\u0026amp;arr[0]+1));//是第二个元素地址是4或者是8\n\u003c/code\u003e\u003c/pre\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003echar arr[]=\u0026#34;abcdef”；\nprintf(\u0026#34;%d\\n\u0026#34;,strlen(arr));//这里是说字符串的长度是6\nprintf(\u0026#34;%d\\n\u0026#34;,strlen(*arr);//在这里会出错。\nprintf(\u0026#34;%d\\n\u0026#34;,strlen(arr[1]));//在这里也会出错\nprintf(\u0026#34;%d\\n\u0026#34;,strlen(\u0026amp;arr));//是6\nprintf(\u0026#34;%d\\n\u0026#34;,strlen(\u0026amp;arr+1));//这会跳出这个数组，应该是0\nprintf(\u0026#34;%d\\n\u0026#34;,strlen(\u0026amp;arr[0]+1));//这里第二个元素的地址是5\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e完（。）\u003c/p\u003e","title":"C语言之指针篇第五"},{"content":"从这一节开始，我们就要开启学习指针之旅，我将此次指针的学习分为5次，今天是第一次学习，在学习指针的时候我们必定会遇到许多困难与挑战，我们会遇到一些比较抽象的概念，也会遇到一些复杂的代码，不管如何，我们能做的只有坚持，坚持只有这样，我们才可以真正搞懂所谓的指针，明白指针的确实含义，来开启我们今天的指针学习吧，亲爱的同学们！\nC语言指针篇第一 1. 内存和地址\n1.内存与地址的介绍\n2.地址的一些性质\n2. 指针变量和地址\n1.取地址操作符（\u0026amp;）\n2.指针变量和解引⽤操作符（*）\n1.指针变量\n1.指针变量的含义\n2.指针变量的意义\n2.解引⽤操作符（*）\n1.解引用操作符的含义\n2.理解指针变量\n3.解引用操作符的意义\n3.指针变量的⼤⼩\n3. 指针变量类型的意义\n1.指针变量类型的含义\n2.指针变量类型的举例\n3. 指针变量类型的意义\n4. const修饰指针\n1.const修饰变量（1）\n2.const修饰变量（2）\n3.const修饰指针\n1.const加在*的前面\n2.const加在*的后面\n5. 指针运算\n1.指针-指针\n2.指针+（-）整数\n6. 野指针\n1.野指针是如何造成的\n1. 指针未初始化\n2. 指针越界访问\n3. 指针指向的空间释放\n2.如何规避野指针\n首先呢，我将为大家介绍有关地址的知识点，接下来我们会学习到指针的运算，以及使用。\n1. 内存和地址 1.内存与地址的介绍 1.在宾馆当中，我们若果需要寻找需要的人时，在不知道对方的房间号时，我们能够做的是，一个房间一个房间挨着寻找，虽然这样可以找到我们需要的人，但是需要注意的是。这样寻找，注定了它的效率是不高的，是低效率的，因此呢，若果我们知道对方的房间号时，那么找到对方是一瞬间的事情。 2.在计算机中，信息也是按照宾馆，按照宿舍存储的，在计算机中信息是以字节为单位，这个字节就有点像宿舍，一个宿舍存储8个人，而一个字节有八个比特位，这样只要我们知道信息的房间号，在计算机中我们常说的是地址\u0026mdash;-指针—内存单元的编号，我们就可以迅速的找到这个信息。 3.若果在32为环境下，地址是按照32位地址线发出电信号，进行编码的，此时地址拥有32个比特位，也就是说地址有4个字节 4.但是，若果在64位的环境下，那么就是地址按照64个地址线编码的，此时地址拥有64个比特位，也就是说地址现在拥有，8个字节。\n2.地址的一些性质 1.首先，在相同环境之下，地址的大小都是一样，要么是4个字节，要么是8个字节，这个同地址（指针）的类型是无关的。 2. 指针是有类型的，有字符型，整型，数组类型。 3. 在计算机存储当中时，是按照字节进行存储的，整型的话是4个字节，字符型是一个字节。\n2. 指针变量和地址 以下我们即将学到，操作符\u0026amp;，以及解引用操作符*，以及我们会学习到，指针的大小如何比较，下面就开启我们的学习之旅吧！\n1.取地址操作符（\u0026amp;） 首先呢，我们举一个例子：\nint a=12; int * pr=\u0026amp;a; 在上例中，我们可以看到，按位与操作符加在变量之前就变成了，取地址操作符，通过以上代码，我们可以发现，我们使用了指针变量加以接收，a的地址，现在把a的地址就存储到pr当中了，通过上例的举出，大家对于取地址操作符，有新的了解与认识吗？似乎，没有非常的困难的样子，下面我就为大家开启以下的重头戏，上面只是本个知识点的开胃菜，下面才是真正的硬菜。\n2.指针变量和解引⽤操作符（*） 在下面我即将会学习到，解引用操作符以及指针变量，这个我希望大家可以认真，听我的讲解，来开启这个知识点的学习吧！\n1.指针变量 1.指针变量的含义 1.当大家首次听到这个名字的时候，会发生疑惑，指针变量不是接收地址，而地址在内存当中不是一样的吗？其实对于这样的想法，我想说，你说的并没有问题，只是由于，不同类型的地址，他们的指针宽度是不同的，因此不能胡子眉毛一把抓，我们在此处定义了指针变量有字符型，整型，数组类型，函数类型，等等。\n2.指针变量的意义 1.由于我们拥有了指针变量，那么的话，它就可以存储我们的地址，这样地址就存储在指针变量当中，这大概就是指针变量的含义。\n2.解引⽤操作符（*） 1.解引用操作符的含义 解引用操作符是当地址存储在指针变量当中之时，我们需要操纵这个地址上面的东西，这个时候我们就用到了解引用操作符，给大家举例子如下：\nint a=12; int * pr=\u0026amp;a; *pr=3; 2.理解指针变量 给大家举出例子如下：\nint w=18; int * a=\u0026amp;w; 在这里呢，*a代表的是，该变量是指针变量而其前面的int 则表示的是，指针的类型是整型，如此我相信大家，可以对指针变量有着深厚的理解。\n3.解引用操作符的意义 通过以上例子，我们可以发现通过解引用，我们就非常的容易对a的地址进行修改，这样解引用就不仅可以得到与原来的变脸一样的变量，同时由于其是对地址的解引用那么的话，同时可以对原变量进行远程修改，这一点是极其重要的。\n3.指针变量的⼤⼩ 指针变量的大小，在我的上面陈述当中时，我们发现在相同环境下，那么指针变量的大小就是一样的，假设在32位的环境之下，那么指针变量的大小都是4个字节；若是在64位的环境之下的话，那么的话，指针变量的大小就是8个字节，给大家举出例子如下：\n#include \u0026lt;stdio.h\u0026gt; //指针变量的⼤⼩取决于地址的⼤⼩ //32位平台下地址是32个bit位（即4个字节） //64位平台下地址是64个bit位（即8个字节） int main() { printf(\u0026#34;%zd\\n\u0026#34;, sizeof(char *)); printf(\u0026#34;%zd\\n\u0026#34;, sizeof(short *)); printf(\u0026#34;%zd\\n\u0026#34;, sizeof(int *)); printf(\u0026#34;%zd\\n\u0026#34;, sizeof(double *)); return 0; } 如下是在32位环境下运行的结果： 由此呢，我相信大家一定会明白指针的大小是相同的，只要是在相同的环境之下，下面我们就开启关于变量的类型的意义的学习。\n3. 指针变量类型的意义 1.指针变量类型的含义 我们需要知道一个东西，是指针的步长，也就是指针的宽度，是指针向后移动一个则实际在内存上移动多少个字节。\n2.指针变量类型的举例 指针变量的类型有字符型，整型，他们的步长分别是1,4。\n3. 指针变量类型的意义 首先我们举出例子如下：\n#include \u0026lt;stdio.h\u0026gt; int main() { int n = 0x11223344; int *pi = \u0026amp;n; *pi = 0; return 0; } 1.在这里，由于指针变量的类型与变量的类型都是整型那么的话，所得出的答案，就是整型a的4个字节全部都是0.\n#include \u0026lt;stdio.h\u0026gt; int main() { int n = 0x11223344; char *pc = (char *)\u0026amp;n; *pc = 0; return 0; } 2.在这里，由于指针变量的类型是字符型与变量的类型是整型那么的话，所得出的答案，就是整型a的4个字节中的一个字节是0. 3.通过上述例子我们看出，指针的类型对于解引用的权限是相关的，需要注意的是void * 可以接收任何的地址类型，但是不能对void 类型的指针进行解引用，因为它的步长我们无法得知。\n4. const修饰指针 1.const修饰变量（1） 若果我们不想要变量被修改的话，那么我们需要在变量的前面加上const，这样变量就不能通过直接的方式修改了，我给大家举出的例子如下：\nconst int a=90; a=98; 那么由于我们在变量的前面加上了const那么变量是无法修改的，在程序运行的时候必然会报错。\n2.const修饰变量（2） 1.我们在上述的例子中发现加上一句const这句话，这样便无法被修改了，但是我们可以采用间接的方法进行修改，举出的例子如下：\nconst int a=90; int * b=\u0026amp;a; *b=98; 2.由于const修饰的是，变量a那么它的地址就没有被修饰，如此我们就可以采用地址的方式修改原来的地址，我们加上const这句话的目的是不要修改原来的变量，但是它被修改了，这不是我们需要的代码，是不正确的，因此我们通过以下的方式，对地址加上const那么就可以，规避这些问题了。\n3.const修饰指针 const修饰指针，可以通过加在指针的前面或者后面加以取得不同的结果，取得不同的修饰的后果。\n1.const加在*的前面 1.这样修饰的是指针指向的内容，保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。 给大家举出的例子如下：\nvoid test2() { //代码2 int n = 10; int m = 20; const int* p = \u0026amp;n; *p = 20;//ok? p = \u0026amp;m; //ok? } 2.在这里由于指针指向的内容不能通过指针来改变，那么*p=20是错误的，在系统运行的时候会报错，但是由于指针变量本⾝的内容可变，那么的话，p = \u0026amp;m是没有问题的。\n2.const加在*的后面 1.这样修饰的是是指针变量本⾝，保证了指针变量的内容不能修改，但是指针指 向的内容，可以通过指针改变。 给大家举出的例子如下：\nvoid test3() { int n = 10; int m = 20; int *const p = \u0026amp;n; *p = 20; //ok? p = \u0026amp;m; //ok? } 2.在这里由于指针变量的内容不能修改，那么 p = \u0026amp;m; 是错误的，在系统运行的时候会报错，但是指针指 向的内容，可以通过指针改变，那么的话，*p = 20是没有问题的。\n5. 指针运算 1.指针-指针 指针-指针是指针之间的元素的个数，给大家举出例子如下：\n#include \u0026lt;stdio.h\u0026gt; int my_strlen(char *s) { char *p = s; while(*p != \u0026#39;\\0\u0026#39; ) p++; return p-s; } int main() { printf(\u0026#34;%d\\n\u0026#34;, my_strlen(\u0026#34;abc\u0026#34;)); return 0; } 上述例子当中，由于把首地址存到另一个指针当中，那么两者相减那么就是，字符串的个数\n2.指针+（-）整数 是指针移动多少个步长，举出一些例子如下：\n#include \u0026lt;stdio.h\u0026gt; //指针+- 整数 int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int *p = \u0026amp;arr[0]; int i = 0; int sz = sizeof(arr)/sizeof(arr[0]); for(i=0; i\u0026lt;sz; i++) { printf(\u0026#34;%d \u0026#34;, *(p+i));//p+i 这⾥就是指针+整数 } return 0; } 在上面的例子当中，*(p+i)就是每次移动i * 步长个单位，而int类型的指针的步长则是4，那么每次的移动次数就是步长乘以i，以此类推。\n6. 野指针 1.野指针是如何造成的 以下是野指针形成的三个成因。\n1. 指针未初始化 例子如下：\n#include \u0026lt;stdio.h\u0026gt; int main() { int *p;//局部变量指针未初始化，默认为随机值 *p = 20; return 0; } 2. 指针越界访问 例子如下：\n#include \u0026lt;stdio.h\u0026gt; int main() { int arr[10] = {0}; int *p = \u0026amp;arr[0]; int i = 0; for(i=0; i\u0026lt;=11; i++) { //当指针指向的范围超出数组arr的范围时，p就是野指针 *(p++) = i; } return 0; } 3. 指针指向的空间释放 例子如下：\n#include \u0026lt;stdio.h\u0026gt; int* test() { int n = 100; return \u0026amp;n; } int main() { int*p = test(); printf(\u0026#34;%d\\n\u0026#34;, *p); return 0; } 2.如何规避野指针 1.指针初始化 2.⼩⼼指针越界 3.指针变量不再使⽤时，及时置NULL，指针使⽤之前检查有效性 完（结束）\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/c%E8%AF%AD%E8%A8%80%E6%8C%87%E9%92%88%E7%AF%87%E7%AC%AC%E4%B8%80/","summary":"\u003cp\u003e从这一节开始，我们就要开启学习指针之旅，我将此次指针的学习分为5次，今天是第一次学习，在学习指针的时候我们必定会遇到许多困难与挑战，我们会遇到一些比较抽象的概念，也会遇到一些复杂的代码，不管如何，我们能做的只有坚持，坚持只有这样，我们才可以真正搞懂所谓的指针，明白指针的确实含义，来开启我们今天的指针学习吧，亲爱的同学们！\u003c/p\u003e\n\u003ch4 id=\"c语言指针篇第一\"\u003eC语言指针篇第一\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1__3\"\u003e1. 内存和地址\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_4\"\u003e1.内存与地址的介绍\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_9\"\u003e2.地址的一些性质\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2__15\"\u003e2. 指针变量和地址\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_17\"\u003e1.取地址操作符（\u0026amp;）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_24\"\u003e2.指针变量和解引⽤操作符（*）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_26\"\u003e1.指针变量\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_27\"\u003e1.指针变量的含义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_29\"\u003e2.指针变量的意义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_31\"\u003e2.解引⽤操作符（*）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_32\"\u003e1.解引用操作符的含义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_39\"\u003e2.理解指针变量\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_46\"\u003e3.解引用操作符的意义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_50\"\u003e3.指针变量的⼤⼩\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__68\"\u003e3. 指针变量类型的意义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_69\"\u003e1.指针变量类型的含义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_71\"\u003e2.指针变量类型的举例\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__73\"\u003e3. 指针变量类型的意义\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_const_99\"\u003e4. const修饰指针\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1const1_100\"\u003e1.const修饰变量（1）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2const2_107\"\u003e2.const修饰变量（2）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3const_115\"\u003e3.const修饰指针\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1const_117\"\u003e1.const加在*的前面\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2const_132\"\u003e2.const加在*的后面\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5__147\"\u003e5. 指针运算\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_148\"\u003e1.指针-指针\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_166\"\u003e2.指针+（-）整数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#6__186\"\u003e6. 野指针\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_187\"\u003e1.野指针是如何造成的\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1__189\"\u003e1. 指针未初始化\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2__200\"\u003e2. 指针越界访问\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__217\"\u003e3. 指针指向的空间释放\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_233\"\u003e2.如何规避野指针\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e首先呢，我将为大家介绍有关地址的知识点，接下来我们会学习到指针的运算，以及使用。\u003c/p\u003e\n\u003ch2 id=\"1-内存和地址\"\u003e1. 内存和地址\u003c/h2\u003e\n\u003ch3 id=\"1内存与地址的介绍\"\u003e1.内存与地址的介绍\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e1.在宾馆当中，我们若果需要寻找需要的人时，在不知道对方的房间号时，我们能够做的是，一个房间一个房间挨着寻找，虽然这样可以找到我们需要的人，但是需要注意的是。这样寻找，注定了它的效率是不高的，是低效率的，因此呢，若果我们知道对方的房间号时，那么找到对方是一瞬间的事情。\n2.在计算机中，信息也是按照宾馆，按照宿舍存储的，在计算机中信息是以字节为单位，这个字节就有点像宿舍，一个宿舍存储8个人，而一个字节有八个比特位，这样只要我们知道信息的房间号，在计算机中我们常说的是地址\u0026mdash;-指针—内存单元的编号，我们就可以迅速的找到这个信息。\n3.若果在32为环境下，地址是按照32位地址线发出电信号，进行编码的，此时地址拥有32个比特位，也就是说地址有4个字节\n4.但是，若果在64位的环境下，那么就是地址按照64个地址线编码的，此时地址拥有64个比特位，也就是说地址现在拥有，8个字节。\u003c/p\u003e\n\u003ch3 id=\"2地址的一些性质\"\u003e2.地址的一些性质\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e1.首先，在相同环境之下，地址的大小都是一样，要么是4个字节，要么是8个字节，这个同地址（指针）的类型是无关的。\u003c/code\u003e\n2. 指针是有类型的，有字符型，整型，数组类型。\n\u003cstrong\u003e3. 在计算机存储当中时，是按照字节进行存储的，整型的话是4个字节，字符型是一个字节。\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"2-指针变量和地址\"\u003e2. 指针变量和地址\u003c/h2\u003e\n\u003cp\u003e以下我们即将学到，操作符\u0026amp;，以及解引用操作符*，以及我们会学习到，指针的大小如何比较，下面就开启我们的学习之旅吧！\u003c/p\u003e\n\u003ch3 id=\"1取地址操作符\"\u003e1.取地址操作符（\u0026amp;）\u003c/h3\u003e\n\u003cp\u003e首先呢，我们举一个例子：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eint a=12;\nint * pr=\u0026amp;a;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e在上例中，我们可以看到，按位与操作符加在变量之前就变成了，取地址操作符，通过以上代码，我们可以发现，我们使用了指针变量加以接收，a的地址，现在把a的地址就存储到pr当中了，通过上例的举出，大家对于取地址操作符，有新的了解与认识吗？似乎，没有非常的困难的样子，下面我就为大家开启以下的重头戏，上面只是本个知识点的开胃菜，下面才是真正的硬菜。\u003c/p\u003e\n\u003ch3 id=\"2指针变量和解引操作符\"\u003e2.指针变量和解引⽤操作符（*）\u003c/h3\u003e\n\u003cp\u003e在下面我即将会学习到，解引用操作符以及指针变量，这个我希望大家可以认真，听我的讲解，来开启这个知识点的学习吧！\u003c/p\u003e","title":"C语言指针篇第一"},{"content":"文章目录 一.DNS服务器软件介绍\n1.. DNS的基本概念\n2.DNS结构\n1.根域 .（root）\n2.顶级域DNS服务器(TLD DNS Servers)\n3.权威DNS服务器（Authoritative DNS Servers）\n3.DNS服务器软件介绍\n二.在DNS服务器上安装BIND\n三.配置BIND主配置⽂件\n四.配置内部区域注册⽂件\n1.正向解析\n1.添加正向解析声明\n2.添加正向解析内容\n2.反向解析\n1.添加反向解析的声明\n2.添加反向解析的内容\n3.语法检测\n五.启动BIND服务\n六.配置防⽕墙\n七.客⼾端配置操作【node1】\n八.测试服务器是否正常\n九.如何清空\n十.总结\n一.DNS服务器软件介绍 1… DNS的基本概念 DNS（Domain Name System，域名系统）是互联⽹的“电话簿”，它⽤于将⼈类易于记忆的域 名（如 www.baidu.com）转换为计算机能够理解的 IP 地址（如 192.0.2.1）。由于直接记住数字地址 不如记住域名⽅便，DNS 使得⼈们能够通过域名访问⽹站，⽽不必关⼼背后的数字地址。 作⽤： • 域名映射到IP地址：DNS使得⽤⼾在浏览器中输⼊域名时，能够查询到相应的IP地址，从⽽连接到 ⽬标服务器。 • 反向解析：除了域名到IP的正向解析，DNS 还⽀持 IP 地址到域名的反向解析，这对⽹络安全和故 障排查等⾮常重要。 • 提供负载均衡：通过 DNS，可以实现不同 IP 地址的轮换，帮助分散流量，提升访问速度和可靠 性。 • 简化⽹络配置：通过 DNS，管理员只需使⽤域名管理服务，简化了⽹络配置和维护。 2.DNS结构 1.根域 .（root） 在整个 DNS 系统的最上⽅⼀定是 . (⼩数点) 这个 DNS 服务器 (称为 root)，也叫”根域“。它们不 直接存储域名和IP地址的映射，⽽是存储指向顶级域（TLD）服务器的信息。 根域 （13台 全世界只有13台。1台为主根服务器，放置在美国。其余12台均为辅根服务器，其中9 台放置在美国，欧洲2台，位于英国和瑞典，亚洲1台，位于⽇本。） 2.顶级域DNS服务器(TLD DNS Servers) 顶级域（如 .com , .org , .net , .cn 等）的DNS服务器存储了关于某个域名下的权威DNS服务 器的信息。 例如， .com 域的TLD服务器会告诉你要去查找与某个 .com 域名相关的权威DNS服务器。``` • 常⻅的顶级域及国家 .com 商业机构 .net ⽹络 .org ⾮商业机构 www.centos.org www.kernel.org .edu 教育机构 .gov 政府机关 .cn 中国域名 .us 美国域名 .ai ⼈⼯智能 .io 云计算 .mil 军事机构 除了顶级域名外， 还有⼆级域名（baidu.com）、三级域名（smartgo.net.cn）、四级域名 （it.smartgo.net.cn）等\n3.权威DNS服务器（Authoritative DNS Servers） 权威DNS服务器存储了某个域名的确切信息（如 IP 地址）。当DNS查询请求到达这些服务器时，服务器会直接返回查询结果。它们对⾃⼰所管理的域名负责。除了这些类型的DNS服务器外，其实还有递归DNS服务器、缓存DNS服务器、前向DNS服务器等\n3.DNS服务器软件介绍 ⽬前市场上⽀持搭建DNS服务器的软件有很多，以下是⼏个常⽤的DNS服务器介绍： • 1- BIND（Berkeley Internet Name Domain） BIND 是最常⻅的 DNS 服务器软件之⼀，⼴泛应⽤于 Linux 和 Unix 系统。它功能强⼤，⽀ 持正向和反向解析，并且具有灵活的配置选项。 优点： 功能强⼤，⽀持主从 DNS 配置。 配置灵活，⽀持多种⾼级功能，如 DNSSEC、访问控制等。 社区活跃，⽀持⼴泛。 缺点： 配置较为复杂，初学者需要时间学习。 • 2- Unbound Unbound 是⼀个轻量级的、开源的 DNS 解析器，主要⽤于递归查询，它的设计⽬标是提供快 速、安全、灵活的 DNS 服务。相⽐ BIND，Unbound 更加简洁易⽤，适合⽤于简单的 DNS 解析服 务。 优点： 配置简便，容易上⼿。 安全性⾼，⽀持 DNSSEC 和 DoH（DNS over HTTPS）。 性能优越，适合⾼负载环境。 缺点： 功能不如 BIND 强⼤，主要⽤于递归解析，⽽⾮权威 DNS 服务。 3- dnsmasq dnsmasq 是⼀个轻量级的 DNS 和 DHCP 服务器，适⽤于⼩型⽹络或家庭环境。它可以提供 DNS 缓存功能，并且⽀持 DHCP 服务。 优点： 配置简单，适合⼩型⽹络和家庭⽹络。 ⽀持 DHCP 服务，可以同时作为 DNS 和 DHCP 服务器。 ⽀持 DNS 缓存，加速访问速度。 缺点： 功能相对有限，适合⼩规模⽹络，不能处理⾼负载。 4- PowerDNS PowerDNS 是⼀个⾼性能的 DNS 服务器，⽀持多种后端数据库，如 MySQL 和 PostgreSQL。 它适⽤于需要⾼性能、⾼可⽤性的企业级应⽤。 优点： ⽀持多种后端数据库，便于管理⼤量 DNS 记录。 ⾼性能，适合⼤规模部署。 缺点： 配置较为复杂，适合有经验的⽤⼾。 5- CoreDNS CoreDNS 是⼀个灵活、可扩展的 DNS 服务器，⼴泛⽤于 Kubernetes 环境中，也可以作为传 统 DNS 服务器使⽤。它由 Go 语⾔编写，⽀持插件扩展。 优点： 可扩展，⽀持插件机制。 轻量级，易于配置。 ⽀持现代云环境，特别适⽤于 Kubernetes 集群。 缺点： 主要⽤于云环境，配置⽅式与传统 DNS 服务有所不同。 本次我们主要采⽤BIND来完成建设DNS服务器的任务 服务器规划： • node1为访问的客⼾端 • node2为DNS服务器\n二.在DNS服务器上安装BIND node1和node2上面是均可以安装这个软件bind的说明： bind：提供 DNS 服务器功能，⽤于 解析域名（包括内部和外部域名），管理 DNS 区域和记录。 bind-utils：提供⼀些 DNS 查询⼯具，如 dig、nslookup、host，⽤于测试和调试 DNS 配置\n三.配置BIND主配置⽂件 我们使用vi /etc/named.conf这个命令来配置文件\n四.配置内部区域注册⽂件 假设⽬前有以下内部⽹站需要配置内部转发： 1 internal.local 192.168.88.101 1.正向解析 1.添加正向解析声明 .我们修改配置文件，vi /etc/named.rfc1912.zones,在文件里面添加正向解析的声明 vi /etc/named.rfc1912.zones 添加以下内容： zone \u0026#34;internal.local\u0026#34; IN { type master; file \u0026#34;/var/named/internal.local.db\u0026#34;; allow-update { none; }; }; 说明: zone \u0026#34;internal.local\u0026#34; IN { 表示一个 DNS 区域的声明 internal.local 是此 DNS 区域的名称 IN 指 Internet 类别，常用默认值 type master; 指定此区域的类型为 主（master）区域 主区域是该域名的权威数据源，DNS 数据直接从此服务器的配置文件加载 file \u0026#34;/var/named/internal.local.db\u0026#34;; 定义该区域的区域数据文件位置 文件 internal.local.db 包含该区域的记录（如 A、MX、NS 等） allow-update { none; }; 指定此区域是否允许动态更新 none 意味着不允许任何动态更新，域名记录只能通过手动修改文件更新 2.添加正向解析内容 .我们需要打开之前在rfc1912.zones这里面声明的文件，然后在这个文件里面进行添加内容 vi /var/named/internal.local.db # 添加以下内容： $TTL 86400 @ IN SOA ns1.internal.local. admin.internal.local. ( 2024011701 ; Serial 3600 ; Refresh 1800 ; Retry 1209600 ; Expire 86400 ) ; Minimum TTL IN NS ns1.internal.local. ns1 IN A 192.168.149.130 web1 IN A 192.168.149.128 web2 IN A 192.168.149.128 @ IN A 192.168.149.128 说明: $TTL 默认 TTL (Time To Live)。 所有资源记录的默认缓存时间，单位为秒。 这里设置为 86400（一天）。客户端在缓存数据时会遵循此值。 SOA 记录（Start of Authority） @: 当前区域的根域（即 internal.local）。 IN: 表示 Internet 类别。 SOA: 开始授权记录，定义该区域的关键元信息。 ns1.internal.local. 主域名服务器的 FQDN（全限定域名）。 admin.internal.local. 管理员的邮箱地址，@ 替换为 .（即 admin@internal.local）。 2024011701 序列号，每次修改区域文件时需递增，用于从服务器检测更新。 3600 刷新时间，从服务器多久检查主服务器是否有更新（秒）。 1800 重试时间，从服务器在刷新失败后再次尝试的等待时间（秒）。 1209600 过期时间，从服务器在无法联系主服务器时数据失效的时间（秒）。 86400 最小 TTL，未覆盖的记录的默认缓存时间（秒）。 NS 记录（Name Server） 指定该区域的域名服务器地址。 ns1.internal.local.: 表示 internal.local 的主域名服务器。 A 记录（Address）: 定义域名到 IPv4 地址的映射。 ns1.internal.local. 解析为 192.168.88.102 web1.internal.local. 和 web2.internal.local. 都解析为 192.168.88.101 扩展常见的DNS记录类型: A 记录（Address Record）: 将域名映射到 IPv4 地址。 AAAA 记录: 将域名映射到 IPv6 地址。 CNAME 记录（Canonical Name Record）: 为域名提供别名。 MX 记录（Mail Exchange Record）: 指定邮件服务器的地址。 NS 记录（Name Server Record）: 指定域名的权威 DNS 服务器。 SOA 记录（Start of Authority Record）: 定义 DNS 区域的起始权威信息。 PTR 记录（Pointer Record）: 用于反向 DNS 查找，将 IP 地址映射回域名。 TXT 记录（Text Record）: 存储任意文本数据，常用于 SPF、DKIM 等验证机制。 要是我们需要其他域名比如说是it_test.cn，那么的话，我们需要先把internal.local都改成it_test.cn，其次我们这个时候需要的子域名是heima以及boxuegu，那么我们需要将web1修改为heima，web2修改为boxuegu，其实就可以了，这个域名其实就添加成功了。\n2.反向解析 1.添加反向解析的声明 1.我们需要打开rfc1912.zones这个文件，并且在文件添加反向解析的声明 vi /etc/named.rfc1912.zones # 添加以下配置 zone \u0026#34;149.168.192.in-addr.arpa\u0026#34; IN { type master; file \u0026#34;/var/named/192.168.149.rev\u0026#34;; allow-update { none; }; }; 说明: zone \u0026#34;149.168.192.in-addr.arpa\u0026#34; IN { 这是一个 反向区域（reverse zone）的声明。 反向区域用于将 IP 地址（IPv4）转换为域名，这和正向 DNS 查询（将域名转换为 IP 地址）是相反的。 149.168.192.in-addr.arpa：这是 192.168.149.x IP 地址段的反向区域名称。反向查找区域的命名规则是：将 IP 地址的每个八位字节倒序并加上 .in-addr.arpa 后缀。例如，192.168.149.x 的反向区域名称就是 149.168.192.in-addr.arpa。 type master; 指定该区域是 主（master）区域，也就是说，这是该区域的权威 DNS 服务器，并且数据会从本地文件加载 file \u0026#34;192.168.149.rev\u0026#34;; 这是该区域的区域数据文件路径。 192.168.149.rev 文件包含了反向解析记录，用于将 IP 地址（如 192.168.149.101）映射到对应的域名 allow-update { none; }; allow-update 指定是否允许动态更新。在这里设置为 none，意味着不允许任何动态更新 这是一种安全配置，防止未经授权的客户端修改 DNS 记录 2.添加反向解析的内容 vi /var/named/192.168.149.rev # 添加以下配置 $TTL 86400 @ IN SOA ns1.internal.local. admin.internal.local. ( 2024011701 ; Serial 3600 ; Refresh 1800 ; Retry 1209600 ; Expire 86400 ) ; Minimum TTL IN NS ns1.internal.local. 130 IN PTR ns1 128 IN PTR web1 128 IN PTR web2 128 IN PTR @ 这个文件其实与正向的文件很相似，只不过的是这个文件刚好在后几行反过来了,it_test.cn，其实添加逻辑也是一样的,和正向文件差不多，把Internal.local修改,以及把web1,web2修改一下就可以了\n然后我们保存文件就可以了\n3.语法检测 我们需要采用named-checkconf /etc/named.conf这个命令来进行检测文件是否有错误\n# 配置文件语法检查 named-checkconf /etc/named.conf 如果报了错误， 一般都是由于配置文件丢失内容导致语法结构不对， 请检查配置文件 /etc/named.conf 【大概率是该文件的问题】 /etc/named.rfc1912.zones 我们再次进行区域文件的检查,查看正向与反向文件哪一个出错了呢\n# 区域文件语法检查 named-checkzone internal.local /var/named/internal.local.db named-checkzone 149.168.192.in-addr.arpa /var/named/192.168.149.rev 五.启动BIND服务 systemctl start named -- 立即启动 systemctl enable named -- 开启自动启动 systemctl status named -- 查看状态 接下来，我们需要把这个dns的bind服务打开就可以了\n可以看到，这个服务我是成功的启动了的，没有报任何的错误，接下来，我们需要在服务器上面把这个服务的端口放行，那么其实node2上面的配置就解决了\n六.配置防⽕墙 firewall-cmd --zone=public --add-port=53/udp --permanent firewall-cmd --zone=public --add-port=53/tcp --permanent firewall-cmd --reload 或者： firewall-cmd --zone=public --add-service=dns --permanent firewall-cmd --reload # 查看规则信息： firewall-cmd --list-all 这上面的是配置防火墙的命令，我们通过截图查看一下是否成功了的 我们把防火墙放行，可以看到其实也是完美的运行，没有报出错误，接下来我们需要对node1进行配置，把node1的dns修改为node2的IP地址\n然后我们，再次查看已经放行的服务列表\n七.客⼾端配置操作【node1】 我们需要把node1的dns追加一条，内容是node2的IP地址\nvim /etc/NetworkManager/system-connections/ens33.nmconnection #打开配置文件，来进行相应的网络配置操作 [connection] id=ens33 uuid=f63e95b4-2d94-316d-9f6e-bddab78c0f43 type=ethernet autoconnect-priority=-999 interface-name=ens33 timestamp=1785329942 [ethernet] [ipv4] address1=192.168.149.128/24 dns=192.168.149.130;1.1.1.1;8.8.8.8; gateway=192.168.149.2 method=manual [ipv6] addr-gen-mode=eui64 method=auto [proxy] 我们显示一下网卡的信息，查看是否dns配置成功了\nnmcli device show ens160 八.测试服务器是否正常 使用 dig或nslookup 命令测试 DNS 查询，确保内部域名解析正常工作。例如： dig web1.internal.local 我们再使用命令ping一下看一下是否与设定的域名可以连通 **使用 dig 或 nslookup 命令测试 DNS 查询，确保外部域名解析正常工作。 ** 可以看到，域名解析服务是可以正常解析外部链接的\n九.如何清空 node2: dnf -y remove bind bind-utils dnf clean all rm -rf /etc/named* node1: 修改网卡的DNS服务器， 将其调整为 8.8.8.8;114.114.114; 我们通过这个命令把今天做的再删掉，多做几遍，同时删掉的话，可以更好的完成dns的作业\n十.总结 **本次学习任务，佳乐老师讲解了dns服务器的相关设定，从软件包的安装，文件的修改，端口放行。以及node1的相关设定。修改的文件本次在node2里面主要是有1个主配置文件，1个区域文件，区域文件要追加正向解析和反向解析，正向解析和反向解析都还要在你声明的时候指定的文件，来进行修改就可以了，我们在检测一下刚刚的配置文件是否出错，最后进行服务的启动以及端口的放行.\n然后我们在node1中我们需要修改他的网络，将他的dns新增一条记录，内容是node2的IP地址，我们再用dig检测是否node2的dns服务器可以正常运行,这个其实也是可以的，windows也是同理将dns修改为node2的IP地址。需要注意我这里修改的是vmnet1网卡，最后Windows也是成功ping通了。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/dns%E6%9C%8D%E5%8A%A1%E5%99%A8/","summary":"\u003ch4 id=\"文章目录\"\u003e文章目录\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#DNS_3\"\u003e一.DNS服务器软件介绍\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_DNS_7\"\u003e1.. DNS的基本概念\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2DNS_23\"\u003e2.DNS结构\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_root_28\"\u003e1.根域 .（root）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2DNSTLD_DNS_Servers_37\"\u003e2.顶级域DNS服务器(TLD DNS Servers)\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3DNSAuthoritative_DNS_Servers_59\"\u003e3.权威DNS服务器（Authoritative DNS Servers）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3DNS_63\"\u003e3.DNS服务器软件介绍\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#DNSBIND_121\"\u003e二.在DNS服务器上安装BIND\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#BIND_137\"\u003e三.配置BIND主配置⽂件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_147\"\u003e四.配置内部区域注册⽂件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_154\"\u003e1.正向解析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_156\"\u003e1.添加正向解析声明\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_194\"\u003e2.添加正向解析内容\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_267\"\u003e2.反向解析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_269\"\u003e1.添加反向解析的声明\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_304\"\u003e2.添加反向解析的内容\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_334\"\u003e3.语法检测\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#BIND_362\"\u003e五.启动BIND服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_377\"\u003e六.配置防⽕墙\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#node1_404\"\u003e七.客⼾端配置操作【node1】\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_452\"\u003e八.测试服务器是否正常\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_481\"\u003e九.如何清空\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_500\"\u003e十.总结\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一dns服务器软件介绍\"\u003e一.DNS服务器软件介绍\u003c/h2\u003e\n\u003ch3 id=\"1-dns的基本概念\"\u003e1… DNS的基本概念\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eDNS（Domain Name System，域名系统）是互联⽹的“电话簿”，它⽤于将⼈类易于记忆的域\n名（如 www.baidu.com）转换为计算机能够理解的 IP 地址（如 192.0.2.1）。由于直接记住数字地址\n不如记住域名⽅便，DNS 使得⼈们能够通过域名访问⽹站，⽽不必关⼼背后的数字地址。\n作⽤：\n• 域名映射到IP地址：DNS使得⽤⼾在浏览器中输⼊域名时，能够查询到相应的IP地址，从⽽连接到\n⽬标服务器。\n• 反向解析：除了域名到IP的正向解析，DNS 还⽀持 IP 地址到域名的反向解析，这对⽹络安全和故\n障排查等⾮常重要。\n• 提供负载均衡：通过 DNS，可以实现不同 IP 地址的轮换，帮助分散流量，提升访问速度和可靠\n性。\n• 简化⽹络配置：通过 DNS，管理员只需使⽤域名管理服务，简化了⽹络配置和维护。\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2dns结构\"\u003e2.DNS结构\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/dns%E6%9C%8D%E5%8A%A1%E5%99%A8/images/172e794e0a404cd78fab9914d1449c84.png\"\u003e\u003c/p\u003e\n\u003ch4 id=\"1根域-root\"\u003e1.根域 .（root）\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e在整个 DNS 系统的最上⽅⼀定是 . (⼩数点) 这个 DNS 服务器 (称为 root)，也叫”根域“。它们不\n直接存储域名和IP地址的映射，⽽是存储指向顶级域（TLD）服务器的信息。\n根域 （13台 全世界只有13台。1台为主根服务器，放置在美国。其余12台均为辅根服务器，其中9\n台放置在美国，欧洲2台，位于英国和瑞典，亚洲1台，位于⽇本。）\n\u003c/code\u003e\u003c/pre\u003e\u003ch4 id=\"2顶级域dns服务器tld-dns-servers\"\u003e2.顶级域DNS服务器(TLD DNS Servers)\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e顶级域（如 .com , .org , .net , .cn 等）的DNS服务器存储了关于某个域名下的权威DNS服务\n器的信息。\n例如， .com 域的TLD服务器会告诉你要去查找与某个 .com 域名相关的权威DNS服务器。```\n• 常⻅的顶级域及国家\n.com 商业机构\n.net ⽹络\n.org ⾮商业机构 www.centos.org www.kernel.org\n.edu 教育机构\n.gov 政府机关\n.cn 中国域名\n.us 美国域名\n.ai ⼈⼯智能\n.io 云计算\n.mil 军事机构\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003e除了顶级域名外， 还有⼆级域名（baidu.com）、三级域名（smartgo.net.cn）、四级域名 （it.smartgo.net.cn）等\u003c/code\u003e\u003c/p\u003e","title":"DNS服务器"},{"content":"文章目录 一.⽂件传输协议FTP\n二.FTP的两种⼯作模式\n主动模式\n被动模式\n三.FTP服务器搭建\n1.准备\n2.服务端配置\n1.安装软件vsftp\n2.启动并配置 vsftpd 服务\n3. 配置防⽕墙\n4.基本访问配置修改配置文件\n准备工作\n修改配置文件\n3.客⼾端配置\n1.安装软件\n2.连接ftp服务器\n4. windows浏览器访问\n1根目录是不能放权的\n四.禁⽌匿名⽤⼾访问\n1.修改配置文件\n2.创建⼀个普通⽤⼾⽤于后续访问FTP\n3.node1 客⼾端访问查看\n五.禁锢在指定的数据⽬录中\n1.创建⼀个本地⽤⼾的数据⽬录\n2.修改配置⽂件\n3.创建⽤⼾， 指定⽤⼾的家⽬录为禁锢的数据⽬录下\n4.客⼾端测试访问\n七.⽤⼾名单列表使⽤\n一.⽂件传输协议FTP FTP（File Transfer Protocol） 是⼀种⽤于在⽹络上进⾏⽂件传输的协议，允许⽤⼾通过客⼾端和 服务器之间上传、下载⽂件.\n二.FTP的两种⼯作模式 主动模式 主动模式，其实就是请求是由客户端发起的，但是数据传输过程则是由服务器发过来的\n1- 客⼾端与服务器之间建⽴ 控制连接，通常是通过端⼝ 21（即 FTP 的默认控制端⼝） 2- 当客⼾端准备接收数据时，它会通过 随机端⼝（通常是 20 以外的端⼝）向服务器请求建⽴ 数据连接 3- 服务器通过控制连接得知客⼾端的端⼝号后，从端⼝ 20（服务器的 FTP 数据端⼝） 发起⼀ 个连接到客⼾端指定的端⼝，⽤于数据传输 优势 1 服务器主动向客⼾端发起数据连接，通常对⼤多数服务器来说，这种⽅式较容易实现。 缺点 因为客⼾端通常位于防⽕墙或 NAT 后⾯，防⽕墙可能阻⽌来⾃服务器的外部连接，所以这种模式 可能会受到⽹络环境的限制，尤其在客⼾端位于 NAT 后时，可能⽆法建⽴连接。 被动模式 被动模式，其实就是请求是由客户端发起的，但是数据传输过程则是由客户端发过来的\n客⼾端与服务器之间建⽴ 控制连接（通常是端⼝ 21）。 客⼾端请求服务器在某个端⼝（⼀个随机端⼝）上等待数据连接。 服务器响应并告知客⼾端该数据端⼝号。 客⼾端随后向该端⼝发起连接进⾏数据传输。 优势 被动模式能更好地穿越防⽕墙和 NAT，因为所有连接都是由客⼾端发起的，服务器只是监听连 接，不会主动连接客⼾端。 缺点 需要服务器配置和开放更多的端⼝（通常是端⼝范围），可能会导致⼀些安全隐患 总结： • 主动模式：服务器发起数据连接，适合客⼾端有公⽹ IP 的情况，但可能受到客⼾端防⽕墙或 NAT 的限制。 • 被动模式：客⼾端发起数据连接，适⽤于客⼾端位于防⽕墙或 NAT 后时，能够绕过这些⽹络限制， 安全性和兼容性更⾼。 三.FTP服务器搭建 1.准备 在这里，我们需要准备两台主机，一台充当服务器node2，另一台充当客户端node1\n2.服务端配置 1.安装软件vsftp 首先我们需要使用命令dnf install -y vsftpd\ndnf install vsftpd 2.启动并配置 vsftpd 服务 这里面我们需要使用系统服务，将他在本次活动中启动，以及在下一次开机的时候启动\nsystemctl start vsftpd systemctl status vsftpd systemctl enable vsftpd 3. 配置防⽕墙 我们在使用ftp服务的时候，我们必须要做到，开放端口，但是呢，由于服务器的端口的开放是随机的，因此我们无法在防火墙里面采用端口的方式添加，只能采用依靠服务的方式添加了\nss -tunlp #这里呢我们是查看服务的端口是21 #然后我们知道的是，21端口显然不是这个服务用到的所有端口，21端口只是客户端的端口 firewall-cmd --permanent --add-service=ftp firewall-cmd --reload 然后呢，我这一步就相当于解决了，接下来呢，我们就要进行下一步了\n4.基本访问配置修改配置文件 这里呢，我们需要修改服务器的配置文件，这样才可以进行访问的\n注意，我们是在/etc/vsftp/vsftp.conf里面修改配置文件的\n准备工作 #前置：创建⼀个⽤于共享的⽬录,已经在⽬录下创建⼏个⽂件 mkdir -p /anon echo \u0026#39;hello\u0026#39; \u0026gt;\u0026gt; /anon/a.txt echo \u0026#39;hi\u0026#39; \u0026gt;\u0026gt; /anon/b.txt 修改配置文件 以下是需要修改的地方的\n#修改配置： vi /etc/vsftpd/vsftpd.conf #设置以下⼏个配置项： anonymous_enable=YES # 允许匿名⽤⼾访问 anon_root=/anon # 设置匿名⽤⼾默认的根⽬录 anon_upload_enable=YES # 允许匿名⽤⼾上传⽂件 anon_mkdir_write_enable=YES # 运⾏匿名⽤⼾创建⽂件夹 anon_other_write_enable=YES # 允许匿名⽤⼾删除和重命名⽂件 #注意：修改配置⽂件， 记得重启下FTP服务 systemctl restart vsftpd 3.客⼾端配置 这个其实与服务器是很类似的，只不过呢，这个不需要配置防火墙，不需要修改配置文件\n1.安装软件 我们需要下载的软件其实就是lftp\ndnf install lftp -y 2.连接ftp服务器 #格式1：lftp ftp://用户名:密码@服务器ip地址 #格式2：lftp ftp://服务器ip地址(匿名) 4. windows浏览器访问 打开windows⽂件资源管理 地址栏中输⼊：ftp://192.168.149.128 我们发现，在客户端登陆的时候是无法修改名字的\n我把windows里的图片复制到服务器里面也是不行的 我通过赋予111文件夹777权限那么的话，是有权力对他进行修改的\n发现在客户端里面是可以创建的\n我再次把一张图片移动到这个文件夹里面发现是可以的\n1根目录是不能放权的 我们把根目录也放权为777，我们来看一下结果\n然后，当我们在客户端登陆的时候，会发现用ls /会出现500错误\n四.禁⽌匿名⽤⼾访问 1.修改配置文件 这个是在/etc/vsftpd/vsftpd.conf 的文件进行修改，\nanonymous_enable=NO # 禁⽤匿名⽤⼾访问 local_enable=YES # 允许本地⽤⼾登录 write_enable=YES # 允许写⼊操作（上传⽂件） #保存⽂件并退出，重启服务 systemctl restart vsftpd 2.创建⼀个普通⽤⼾⽤于后续访问FTP 这一步如果电脑有其他用户的话，就不需要创建了，我就用itheima\n3.node1 客⼾端访问查看 lftp ftp://⽤⼾名[:密码]@ftp服务器主机地址 windows访问\n五.禁锢在指定的数据⽬录中 1.创建⼀个本地⽤⼾的数据⽬录 这里我们需要在data里面创建要给kefu文件夹\nmkdir -p /data/kefu echo \u0026#39;hello\u0026#39; \u0026gt;\u0026gt; /data/kefu/a.txt 2.修改配置⽂件 vi /etc/vsftpd/vsftpd.conf # 添加以下内容 local_root=/data/kefu # 设置默认访问的路径地址 ,如果不指定， 默认访问的是该⽤⼾的家 # 修改以下内容： 前⾯的#去除即可 chroot_local_user=YES # 限制所有本地⽤⼾（即服务器上的普通⽤⼾）只能访问他们的 home #保持退出后，重启vsftpd服务 systemctl restart vsftpd 3.创建⽤⼾， 指定⽤⼾的家⽬录为禁锢的数据⽬录下 这个我们还用itheima\n4.客⼾端测试访问 lftp ftp://ftpuser:123@192.168.88.102六.⽤⼾名单列表使⽤\n七.⽤⼾名单列表使⽤ 1.给配置文件/etc/vsftpd/vsftpd.conf里面涉及到的禁锢命令给禁用掉\n2.把/etc/vsftpd这个黑名单文件的root给注释掉\n3.在配置文件/etc/vsftpd/vsftpd.conf里面把userlist_enable=YES启用用户名单 4.我们重启服务 systemctl restart vsftpd systemctl status vsftpd 5.测试连通性 注意：开了黑白名单，要是不在白名单里面那么就在黑名单里面把user_list里面的root给注释掉，那么我再次用ftp服务访问128主机的话，是不能访问的，原因就是这个，因为此时他在黑名单里面的如果在配置文件里面把userlist_enable设置为NO就相当于不使用黑白名单了，此时如果删掉白名单里面的root是不影响用ftp访问128主机的。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/ftp%E6%9C%8D%E5%8A%A1/","summary":"\u003ch4 id=\"文章目录\"\u003e文章目录\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#FTP_3\"\u003e一.⽂件传输协议FTP\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#FTP_11\"\u003e二.FTP的两种⼯作模式\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_13\"\u003e主动模式\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_43\"\u003e被动模式\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#FTP_87\"\u003e三.FTP服务器搭建\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_89\"\u003e1.准备\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_93\"\u003e2.服务端配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1vsftp_95\"\u003e1.安装软件vsftp\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_vsftpd__103\"\u003e2.启动并配置 vsftpd 服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__113\"\u003e3. 配置防⽕墙\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_132\"\u003e4.基本访问配置修改配置文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_138\"\u003e准备工作\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_147\"\u003e修改配置文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_164\"\u003e3.客⼾端配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_168\"\u003e1.安装软件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2ftp_176\"\u003e2.连接ftp服务器\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_windows_188\"\u003e4. windows浏览器访问\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_232\"\u003e1根目录是不能放权的\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_244\"\u003e四.禁⽌匿名⽤⼾访问\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_246\"\u003e1.修改配置文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2FTP_258\"\u003e2.创建⼀个普通⽤⼾⽤于后续访问FTP\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3node1__262\"\u003e3.node1 客⼾端访问查看\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_279\"\u003e五.禁锢在指定的数据⽬录中\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_281\"\u003e1.创建⼀个本地⽤⼾的数据⽬录\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_290\"\u003e2.修改配置⽂件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__303\"\u003e3.创建⽤⼾， 指定⽤⼾的家⽬录为禁锢的数据⽬录下\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_307\"\u003e4.客⼾端测试访问\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_311\"\u003e七.⽤⼾名单列表使⽤\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一件传输协议ftp\"\u003e一.⽂件传输协议FTP\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eFTP（File Transfer Protocol） 是⼀种⽤于在⽹络上进⾏⽂件传输的协议，允许⽤⼾通过客⼾端和\n服务器之间上传、下载⽂件.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/ftp%E6%9C%8D%E5%8A%A1/images/83e9af719357492fa331d2d8183b5234.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"二ftp的两种作模式\"\u003e二.FTP的两种⼯作模式\u003c/h2\u003e\n\u003ch3 id=\"主动模式\"\u003e主动模式\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e主动模式，其实就是请求是由客户端发起的，但是数据传输过程则是由服务器发过来的\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e1- 客⼾端与服务器之间建⽴ 控制连接，通常是通过端⼝ 21（即 FTP 的默认控制端⼝）\n2- 当客⼾端准备接收数据时，它会通过 随机端⼝（通常是 20 以外的端⼝）向服务器请求建⽴\n数据连接\n3- 服务器通过控制连接得知客⼾端的端⼝号后，从端⼝ 20（服务器的 FTP 数据端⼝） 发起⼀\n个连接到客⼾端指定的端⼝，⽤于数据传输\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e优势\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e1 服务器主动向客⼾端发起数据连接，通常对⼤多数服务器来说，这种⽅式较容易实现。\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e缺点\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e因为客⼾端通常位于防⽕墙或 NAT 后⾯，防⽕墙可能阻⽌来⾃服务器的外部连接，所以这种模式\n可能会受到⽹络环境的限制，尤其在客⼾端位于 NAT 后时，可能⽆法建⽴连接。\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/ftp%E6%9C%8D%E5%8A%A1/images/9f4b1f259e854013a1bbd34c0f159f19.png\"\u003e\u003c/p\u003e","title":"FTP服务"},{"content":"nfs服务器的相关知识 一.NFS基本介绍\n二.安装软件包\n三.在node2创建共享⽬录\n四.修改配置文件\n六.验证是否成功共享\n七.挂载使⽤\n1.客户端下载软件包\n2.建立挂载点\n3.进行挂载\n4.查看挂载是否成功\n5.检查是否可以共享文件\n一.NFS基本介绍 CentOS Stream 9中的NFS（⽹络⽂件系统）是⼀种允许不同计算机通过⽹络共享⽂件和⽬录的协 议，使得远程主机能够像本地磁盘⼀样访问和操作⽂件。 它基于客⼾端-服务器架构，NFS服务器将本地⽂件系统共享给客⼾端，客⼾端可以通过⽹络挂载 这些共享⽬录，像访问本地⽂件⼀样进⾏操作。 NFS主要应⽤于Linux/Unix系统之间的⽂件共享，虽然也⽀持跨操作系统，但其兼容性⼀般较差 二.安装软件包 node2 作为 NFS服务端 node1 作为客⼾端，获取共享⽬录信息\n客⼾端安装： dnf install nfs-utils 服务端安装： dnf install nfs-utils 在node2放行端口 执⾏开放端⼝: firewall-cmd --add-service=nfs --permanent # 核⼼, 有了这个, NFS即可使⽤共享操作 firewall-cmd --reload 三.在node2创建共享⽬录 mkdir -p /export/software 四.修改配置文件 vi /etc/exports 然后保存文件，并重启服务就可以了\nsystemctl restart nfs-server systemctl status systemctl enable 六.验证是否成功共享 服务端执⾏: 查看服务端NFS共享信息 showmount -e （早期版本兼容） exportfs 七.挂载使⽤ 1.客户端下载软件包 dnf install -y nfs-utils 2.建立挂载点 mkdir /export/nfs_disk 3.进行挂载 -我们可以使用命令 mount -t nfs 192.168.149.131:/export/software /export/nfs_disk/ 4.查看挂载是否成功 df -h 5.检查是否可以共享文件 我们在node1中上传一个文件，在node2查看一下，是否ok node2中的结果 发现在客户端上传文件在服务器也是可以看到的，在服务器传一个jdk包客户端也是可以的\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/nfs%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/","summary":"\u003ch4 id=\"nfs服务器的相关知识\"\u003enfs服务器的相关知识\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#NFS_2\"\u003e一.NFS基本介绍\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_10\"\u003e二.安装软件包\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#node2_24\"\u003e三.在node2创建共享⽬录\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_28\"\u003e四.修改配置文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_41\"\u003e六.验证是否成功共享\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_49\"\u003e七.挂载使⽤\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_51\"\u003e1.客户端下载软件包\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_57\"\u003e2.建立挂载点\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_62\"\u003e3.进行挂载\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_70\"\u003e4.查看挂载是否成功\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_75\"\u003e5.检查是否可以共享文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一nfs基本介绍\"\u003e一.NFS基本介绍\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eCentOS Stream 9中的NFS（⽹络⽂件系统）是⼀种允许不同计算机通过⽹络共享⽂件和⽬录的协 议，使得远程主机能够像本地磁盘⼀样访问和操作⽂件。 它基于客⼾端-服务器架构，NFS服务器将本地⽂件系统共享给客⼾端，客⼾端可以通过⽹络挂载 这些共享⽬录，像访问本地⽂件⼀样进⾏操作。 NFS主要应⽤于Linux/Unix系统之间的⽂件共享，虽然也⽀持跨操作系统，但其兼容性⼀般较差\u003c/code\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/nfs%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/images/0b621a9d80f145c1beb3a56cd2072de8.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"二安装软件包\"\u003e二.安装软件包\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003enode2 作为 NFS服务端 node1 作为客⼾端，获取共享⽬录信息\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e 客⼾端安装：\ndnf install nfs-utils\n服务端安装：\ndnf install nfs-utils\n\n在node2放行端口\n执⾏开放端⼝:\nfirewall-cmd --add-service=nfs --permanent # 核⼼, 有了这个, NFS即可使⽤共享操作\nfirewall-cmd --reload\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"三在node2创建共享录\"\u003e三.在node2创建共享⽬录\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emkdir -p /export/software\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"四修改配置文件\"\u003e四.修改配置文件\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evi /etc/exports\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/nfs%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/images/5b6e57b0368a4dc6bb1e6c431f571d1f.png\"\u003e\n\u003ccode\u003e然后保存文件，并重启服务就可以了\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esystemctl restart nfs-server\nsystemctl status\nsystemctl enable\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/nfs%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/images/da2bd4cb8a90441b9c975405064020b6.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"六验证是否成功共享\"\u003e六.验证是否成功共享\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e服务端执⾏: 查看服务端NFS共享信息\nshowmount -e （早期版本兼容）\nexportfs\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/nfs%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/images/698860533bf1417aaf5675d902e6e0a9.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"七挂载使\"\u003e七.挂载使⽤\u003c/h2\u003e\n\u003ch3 id=\"1客户端下载软件包\"\u003e1.客户端下载软件包\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ednf install -y nfs-utils\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/nfs%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/images/8c4b2b359a404bb69fa80010c7daf70b.png\"\u003e\u003c/p\u003e","title":"nfs服务器的相关知识"},{"content":"rm -rf 删掉日志文件后，为什么重启服务能恢复？ 1.问题背景\n1.打开系统⽇志的配置⽂件\n2.在规则模块中， 配置需求要求的内容\n3.进行测试\n1.测试优先级大于这个的\n2.测试优先级小于这个的\n2.出现问题\n1.删除日志文件\n2.出现问题\n3.问题原因\n4.解决问题\n1.重启服务\n2.测试\n5.总结\n我自己总结\n我查阅资料总结\n1.问题背景 把所有服务的“临界点”以上的错误都保存在/var/log/alert.log ⽇志中\n1.打开系统⽇志的配置⽂件 vi /etc/rsyslog.conf 2.在规则模块中， 配置需求要求的内容 # ALL SERVICE CRIT TO /var/log/alert.log *.crit action(type=\u0026#34;omfile\u0026#34; file=\u0026#34;/var/log/alert.log\u0026#34;) # 这个需要在本某块上面，规则模块下面 顺便给服务打开 3.进行测试 1.测试优先级大于这个的 logger -p alert \u0026#34;12324 249nc90ju90nj-0 看测试结果 2.测试优先级小于这个的 logger -p info \u0026#34;12324 249nc90ju90nj-0 2.出现问题 当时我把日志文件alert.log给删掉了，想要从空日志从优先级小的开始测试，再测试优先级高的，结果就出现问题了，怎么也无法找到这个日志文件，即使后面手动创建日志文件也是不可以的，因此呢，我当时就比较着急，因为佳乐老师并没有出现这种问题，我却出现了，不知道是我的配置文件出错了，还是怎么回事，我看了一番发现其实我写的 并没有任何问题，只不过后面我问ai，知道了原因\n1.删除日志文件 rm -rf /var/log/alert.log #我用来清空日志文件，以便再次测试 2.出现问题 问题1：是我无法找到这个日志文件，我听佳乐老师说使用logger这个命令是可以自动创建文件以及文件夹的,但是我的这个所展示的情况与佳乐老师所描述的有些不同 问题2：是我手动创建了这个日志文件，但是启动这个logger服务是依然无法写入日志内容的 理论上说，优先级大于或者等于crit的才会在alarm.log日志里面写入日志的，但是目前所展示的情况好像和我想得不一样，即使与它的优先级一样，但是依然是无法写入文件的，这又是一点 3.问题原因 1.后来，我检查了我的配置文件，以及输入的命令，是没有任何错误的，然后我问了一下元宝，他给了我解决的方法，我开始问他为何无法自动创建日志文件呢，他说\n1.配置规则的触发机制：在 Linux 的 rsyslog 配置中，文件输出通常是“按需创建”的。这意味着，只有当符合条件的日志真正产生并被写入时，对应的文件才会被创建。 2.日志级别不匹配导致“静默丢弃”：在配置文件中，有一条规则是 *.info;mail.none;authpriv.none;cron.none action(...)。这条规则的意思是：将所有设施的 info 级别及以上的日志写入某个文件（被截断了，通常是 /var/log/messages），但同时显式排除了 authpriv（认证授权）和 cron（定时任务）的日志。 3.命令的级别不够高：执行的命令是 logger -p crit \u0026#34;...\u0026#34;。虽然这里指定了级别为 crit（严重），但由于它默认使用的是 user 设施（即 user.crit），它没有被上述那条规则排除，因此被正确地路由到了 info 以上的通用日志规则中。 4.结果：日志被写入了那个通用的大文件里，根本没有走到下面 *.crit action(type=\u0026#34;omfile\u0026#34; file=\u0026#34;/var/log/alert.log\u0026#34;) 这条规则。因此，/var/log/alert.log 这个文件从未被触发创建，自然就找不到了。 2.我看了一眼他说的，我认为是无法正常创建文件导致的，我手动创建了一个，结果依然是不行的，后面我又问了一下他为何我创建的文件还是没有内容呢，他说是 SELinux 可能开启，让我检查一番，我看了，我的这个并没有开放 3.我问了一下，他，我的这个selinux并没有开放,他说可能是因为配置文件有语法冲突，rsyslog 内部报错，/var/log 目录的权限问题，服务没有重启，查看 rsyslog 丢弃日志的统计 这几点中我可以看明白的就只有前4个，但是前3个我之前都检查过了，没有任何问题，我明白可能我的服务没有重启，我就重启了这个服务，然后直接和预料的结果一样，显然成功了\n4.解决问题 1.重启服务 systemctl restart rsyslog 2.测试 我们先在日志文件里面输入一些内容 优先级高的 优先级小的 发现和预想的结果一样，证明这个是可以的，我们的问题解决了，这个小作业也完成了\n5.总结 我自己总结 之所以是删除了日志文件然后再用logger输入相关指令不能写入文件其实是由于，当 rsyslog 服务启动时，它会读取配置文件，发现需要往 /var/log/alert.log 写日志。于是它向内核申请一个文件描述符（FD），就像拿到了一个直通文件的“管道”，然后日志文件删掉之后，FD还存在，但是之前的日志文件没有了，写入数据顺着之前日志文件的FD写入磁盘，只不过因为删了日志文件，无法成功查找到他，但是如果重启这个服务，会释放掉旧的FD，然后会申请一个新的FD，这样写入数据是根据这个新的FD来的，不是按照删除的FD来的，所以这个时候是可以写入的 照我看来，其实这个就有些像指针与地址的关系，当我们写入数据要解引用这个指针，方可写入，但是如果我们把这个变量给赋值给其他变量，还是对这个新变量赋值，但是由于指针指向的地址其实就是之前的变量地址，赋值也是把值送给之气的地址，因此新的变量还是之前的旧的数据。C语言里的全局变量的作用范围其实是全局，但是局部变量因为地址就在本地生效，即使在其他地方定义了一个一样的名字，也很难生效，无法赋值.\n我查阅资料总结 为什么删除了 /var/log/alert.log 后，logger 命令看似“写不进去”？其实数据通常仍然在被写入，只是我们看不到了。\n严格来说，logger 本身一般不是直接写 /var/log/alert.log，而是把日志消息发送给 rsyslog 服务，真正负责写入文件的是 rsyslog。\n当 rsyslog 启动时，它会读取配置文件，发现需要向 /var/log/alert.log 输出日志。于是它调用 open() 打开这个文件，并从内核获得一个文件描述符，也就是 FD。这个 FD 本质上是一个整数句柄，比如 3。可以把它理解成进程文件描述符表中的一个下标：内核根据这个下标找到对应的 struct file 对象，而 struct file 又通过 dentry 关联到 inode 和磁盘数据块。\n当我们执行：\nrm /var/log/alert.log 删除的只是目录项，也就是文件名到 inode 的映射关系。好比把门牌号摘掉了，但房子本身还在。更准确地说，删除的是一个路径入口，而不是立刻销毁文件对象。因为 rsyslog 仍然持有旧的 FD，所以内核中对应的 struct file 仍然有效，它指向的 inode 也仍然有效。\n因此，之后 rsyslog 调用 write() 时，数据仍然会顺着旧的 FD 写入原来的 inode 和数据块。写入本身通常并没有失败，只是 /var/log/alert.log 这个路径已经不存在了，所以你无法再通过原路径 cat 到内容。这就是为什么用 lsof 能看到该文件处于 (deleted) 状态：文件对象还活着，只是没有名字了。\n重启服务后为什么会恢复？\n因为重启会让旧的 rsyslog 进程退出，旧的 FD 会被关闭，对应的 struct file 引用减少。当这个旧 inode 不再被任何打开句柄或硬链接引用时，磁盘空间才会真正释放。随后新的 rsyslog 进程启动，重新调用 open() 打开或创建 /var/log/alert.log，获得新的 FD，指向新的文件对象。从此以后，日志走的是新 FD，写的是新文件，所以就能看见了。\n如果用 C 语言的指针来类比，可以这样理解：\n路径名 /var/log/alert.log 类似于变量名 inode 类似于真正的对象 FD 类似于进程持有的指针/引用 rm 删除文件更像删除变量名、删除别名，或者解除一个目录项引用，而不是立刻释放底层对象。只要 rsyslog 还持有 FD 这个“指针”，它就可以继续通过这个指针写入原来的对象。重启服务则相当于让进程放弃旧指针，重新 open() 一个新对象，拿到新的指针。\n不过，这里不能完全类比成 free(p) 后继续使用 p。在 C 语言里，free(p) 后再解引用 p 是悬垂指针，属于未定义行为；而 Linux 文件这里是引用计数机制：删除目录项只是减少 inode 的一个引用，只要还有进程持有打开的文件描述符，文件对象仍然合法存在。\n同样，在 Shell 里重新创建一个同名的 /var/log/alert.log，也无法把旧的日志流“接回来”。因为新的文件名指向新的 inode，而 rsyslog 手里的旧 FD 仍然指向旧 inode。除非让 rsyslog 重新打开日志文件，例如重启服务，或者在支持的情况下 reload、发送 HUP 信号，它才会关闭旧 FD，并根据新的路径名打开新的文件对象。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/","summary":"\u003ch4 id=\"rm--rf-删掉日志文件后为什么重启服务能恢复\"\u003erm -rf 删掉日志文件后，为什么重启服务能恢复？\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_1\"\u003e1.问题背景\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_5\"\u003e1.打开系统⽇志的配置⽂件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2__9\"\u003e2.在规则模块中， 配置需求要求的内容\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_22\"\u003e3.进行测试\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_23\"\u003e1.测试优先级大于这个的\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_32\"\u003e2.测试优先级小于这个的\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_43\"\u003e2.出现问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_46\"\u003e1.删除日志文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_50\"\u003e2.出现问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_63\"\u003e3.问题原因\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_80\"\u003e4.解决问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_82\"\u003e1.重启服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_88\"\u003e2.测试\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_102\"\u003e5.总结\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_104\"\u003e我自己总结\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_109\"\u003e我查阅资料总结\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1问题背景\"\u003e1.问题背景\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e把所有服务的“临界点”以上的错误都保存在/var/log/alert.log ⽇志中\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"1打开系统志的配置件\"\u003e1.打开系统⽇志的配置⽂件\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evi /etc/rsyslog.conf\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2在规则模块中-配置需求要求的内容\"\u003e2.在规则模块中， 配置需求要求的内容\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# ALL SERVICE CRIT TO /var/log/alert.log\n*.crit   action(type=\u0026#34;omfile\u0026#34; file=\u0026#34;/var/log/alert.log\u0026#34;)\n# 这个需要在本某块上面，规则模块下面\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/8124e4a0880a4a1eb2b8e77088e04fc0.png\"\u003e\n\u003ccode\u003e顺便给服务打开\u003c/code\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/931446c550144ec692235d5db9a7145a.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"3进行测试\"\u003e3.进行测试\u003c/h3\u003e\n\u003ch4 id=\"1测试优先级大于这个的\"\u003e1.测试优先级大于这个的\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elogger -p alert \u0026#34;12324 249nc90ju90nj-0\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e看测试结果\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/ad4b49e551c343d7828f2a69afb8ffa5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/a4e5830517654c02bc964f5ebaa704a8.png\"\u003e\u003c/p\u003e\n\u003ch4 id=\"2测试优先级小于这个的\"\u003e2.测试优先级小于这个的\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elogger -p info \u0026#34;12324 249nc90ju90nj-0\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/e629038b05424584aa457685404be575.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/268522bf9fa34ad485cd24ac8c1a94aa.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"2出现问题\"\u003e2.出现问题\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e当时我把日志文件alert.log给删掉了，想要从空日志从优先级小的开始测试，再测试优先级高的，结果就出现问题了，怎么也无法找到这个日志文件，即使后面手动创建日志文件也是不可以的，因此呢，我当时就比较着急，因为佳乐老师并没有出现这种问题，我却出现了，不知道是我的配置文件出错了，还是怎么回事，我看了一番发现其实我写的 并没有任何问题，只不过后面我问ai，知道了原因\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"1删除日志文件\"\u003e1.删除日志文件\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erm -rf /var/log/alert.log #我用来清空日志文件，以便再次测试\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2出现问题-1\"\u003e2.出现问题\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e问题1：\u003cstrong\u003e是我无法找到这个日志文件，我听佳乐老师说使用logger这个命令是可以自动创建文件以及文件夹的,但是我的这个所展示的情况与佳乐老师所描述的有些不同\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/c62a8936613647339a8bd3d6e93b7120.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/6777fb84f29d4548a854d0a07e7d8d50.png\"\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e问题2：\u003cstrong\u003e是我手动创建了这个日志文件，但是启动这个logger服务是依然无法写入日志内容的\u003c/strong\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/c4236b3b35724e69b5135775f047e203.png\"\u003e\n\u003cstrong\u003e理论上说，优先级大于或者等于crit的才会在alarm.log日志里面写入日志的，但是目前所展示的情况好像和我想得不一样，即使与它的优先级一样，但是依然是无法写入文件的，这又是一点\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"3问题原因\"\u003e3.问题原因\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e1.后来，我检查了我的配置文件，以及输入的命令，是没有任何错误的，然后我问了一下元宝，他给了我解决的方法，我开始问他为何无法自动创建日志文件呢，他说\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e1.配置规则的触发机制：在 Linux 的 rsyslog 配置中，文件输出通常是“按需创建”的。这意味着，只有当符合条件的日志真正产生并被写入时，对应的文件才会被创建。\n\n2.日志级别不匹配导致“静默丢弃”：在配置文件中，有一条规则是 *.info;mail.none;authpriv.none;cron.none action(...)。这条规则的意思是：将所有设施的 info 级别及以上的日志写入某个文件（被截断了，通常是 /var/log/messages），但同时显式排除了 authpriv（认证授权）和 cron（定时任务）的日志。\n\n3.命令的级别不够高：执行的命令是 logger -p crit \u0026#34;...\u0026#34;。虽然这里指定了级别为 crit（严重），但由于它默认使用的是 user 设施（即 user.crit），它没有被上述那条规则排除，因此被正确地路由到了 info 以上的通用日志规则中。\n\n4.结果：日志被写入了那个通用的大文件里，根本没有走到下面 *.crit action(type=\u0026#34;omfile\u0026#34; file=\u0026#34;/var/log/alert.log\u0026#34;) 这条规则。因此，/var/log/alert.log 这个文件从未被触发创建，自然就找不到了。\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003e2.我看了一眼他说的，我认为是无法正常创建文件导致的，我手动创建了一个，结果依然是不行的，后面我又问了一下他为何我创建的文件还是没有内容呢，他说是 SELinux 可能开启，让我检查一番，我看了，我的这个并没有开放\u003c/code\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/rm--rf-%E5%88%A0%E6%8E%89%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%90%8E%E4%B8%BA%E4%BB%80%E4%B9%88-service-restart-%E6%89%8D%E8%83%BD%E6%81%A2%E5%A4%8D/images/6583a39f858f4117b744cf100241b68c.jpeg\"\u003e\n\u003ccode\u003e3.我问了一下，他，我的这个selinux并没有开放,他说可能是因为配置文件有语法冲突，rsyslog 内部报错，/var/log 目录的权限问题，服务没有重启，查看 rsyslog 丢弃日志的统计\u003c/code\u003e\n\u003cstrong\u003e这几点中我可以看明白的就只有前4个，但是前3个我之前都检查过了，没有任何问题，我明白可能我的服务没有重启，我就重启了这个服务，然后直接和预料的结果一样，显然成功了\u003c/strong\u003e\u003c/p\u003e","title":"rm -rf 删掉日志文件后，为什么 service restart 才能恢复？"},{"content":"samba服务器 一.SAMBA基本介绍\n二.安装SAMBA服务\n一.服务端\n二.客户端\n三.创建共享⽬录\n四.修改配置⽂件\n五.配置⽤⼾信息\n1.添加用户user01\n2.把用户追加到samba\n六.启动samba服务\n1.启动samba服务\n2.开放端口\n七.在Linux主机访问\n八. 在windows主机访问\n一.SAMBA基本介绍 需求:构建⼀个SAMBA服务，共享node2服务器中的 /sambda/share ⽬录，分别在Linux和Windows访 问该共享⽬录 介绍：Samba 是⼀个开源的软件套件，允许 Linux 或 Unix 系统与 Windows 系统进⾏⽂件和打印共享。 它实现了 SMB（Server Message Block）协议和 CIFS（Common Internet File System），这两者是 Windows 操作系统⽤于⽂件共享和打印服务的主要协议。 Samba 使 Linux/Unix 系统和 Windows 系统之间的⽂件共享和打印服务变得简单，⼴泛应⽤于家 庭、企业和跨平台环境中。 二.安装SAMBA服务 一.服务端 node2执⾏: dnf -y install samba 二.客户端 dnf install -y samba-client 说明： SAMBA通常会有⼆个主要的服务，分别⽤于不同的⽬的 Samba SMB/CIFS 服务 (smb) : 是Samba提供的主要服务，⽤于实现 SMB (Server Message Block) 和 CIFS (Common Internet File System) 协议的⽂件共享和打印服务 Samba NetBIOS 名称解析服务 (nmb) : 是Samba中另⼀个重要的服务，⽤于提供NetBIOS名称解析 服务，它使得Samba服务器可以通过NetBIOS名称（⽽⾮IP地址）来识别和访问，即将计算机名（例 如 server ）转换为IP地址，便于其他计算机通过名称访问，同时提供Samba服务器在局域⽹中显 ⽰的能⼒，使其在⽹络浏览器中可⻅。\n三.创建共享⽬录 mkdir -p /sambda/share 四.修改配置⽂件 配置⽂件所在位置：/etc/samba/smb.conf 修改配置文件 /etc/samba/smb.conf，在文件末尾追加共享配置。这里我们添加一个用户 user01，并将其加入到 Samba 服务器中。最后重启服务并检查状态，确认配置生效。\n五.配置⽤⼾信息 1.添加用户user01 useradd user01 passwd user01 123456 2.把用户追加到samba smbpasswd -a user01 pdbedit -L 六.启动samba服务 1.启动samba服务 systemctl enable nmb systemctl enable smb systemctl start nmb systemctl start smb 验证是否启动： systemctl status nmb systemctl status smb 2.开放端口 firewall-cmd --permanent --add-service=smb firewall-cmd --reload 七.在Linux主机访问 smbclient //192.168.149.131 -U user01 smbclient //192.168.149.131/smbshare -U user01 在客户端使用 Samba 时，需要先安装 Samba 客户端软件，并在服务器防火墙中开放 Samba 相关端口。接着，从客户端访问 Samba 服务器。此时需要确保共享目录的使用者和所属组均为 user01。在客户端创建文件夹后，回到服务器端查看，确认该文件夹是由 user01 创建的。尝试上传文件时，如果失败，可能是因为当前登录的目录权限不足。例如，在 /home/admin 目录下登录 Samba 服务器时，无法将 /root 目录下的文件传输过去；若切换到 /root 目录下登录，即可成功上传。\n八. 在windows主机访问 ","permalink":"https://dsduyopg-github-io.pages.dev/posts/samba%E6%9C%8D%E5%8A%A1%E5%99%A8/","summary":"\u003ch4 id=\"samba服务器\"\u003esamba服务器\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#SAMBA_1\"\u003e一.SAMBA基本介绍\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#SAMBA_13\"\u003e二.安装SAMBA服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_16\"\u003e一.服务端\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_21\"\u003e二.客户端\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_33\"\u003e三.创建共享⽬录\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_38\"\u003e四.修改配置⽂件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_44\"\u003e五.配置⽤⼾信息\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1user01_46\"\u003e1.添加用户user01\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2samba_54\"\u003e2.把用户追加到samba\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#samba_62\"\u003e六.启动samba服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1samba_63\"\u003e1.启动samba服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_75\"\u003e2.开放端口\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#Linux_82\"\u003e七.在Linux主机访问\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_windows_97\"\u003e八. 在windows主机访问\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一samba基本介绍\"\u003e一.SAMBA基本介绍\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e需求:构建⼀个SAMBA服务，共享node2服务器中的 /sambda/share ⽬录，分别在Linux和Windows访\n问该共享⽬录\n\u003c/code\u003e\u003c/pre\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e介绍：Samba 是⼀个开源的软件套件，允许 Linux 或 Unix 系统与 Windows 系统进⾏⽂件和打印共享。\n它实现了 SMB（Server Message Block）协议和 CIFS（Common Internet File System），这两者是\nWindows 操作系统⽤于⽂件共享和打印服务的主要协议。\nSamba 使 Linux/Unix 系统和 Windows 系统之间的⽂件共享和打印服务变得简单，⼴泛应⽤于家\n庭、企业和跨平台环境中。\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"二安装samba服务\"\u003e二.安装SAMBA服务\u003c/h2\u003e\n\u003ch3 id=\"一服务端\"\u003e一.服务端\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003enode2执⾏: dnf -y install samba\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/samba%E6%9C%8D%E5%8A%A1%E5%99%A8/images/4e9cbe84fda6474ea45a60cfe7998596.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"二客户端\"\u003e二.客户端\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ednf install -y samba-client\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/samba%E6%9C%8D%E5%8A%A1%E5%99%A8/images/c31cf89e084446b8a7b2703cdb5265b3.png\"\u003e\n说明： SAMBA通常会有⼆个主要的服务，分别⽤于不同的⽬的\nSamba SMB/CIFS 服务 (smb) : 是Samba提供的主要服务，⽤于实现 SMB (Server Message Block)\n和 CIFS (Common Internet File System) 协议的⽂件共享和打印服务\nSamba NetBIOS 名称解析服务 (nmb) : 是Samba中另⼀个重要的服务，⽤于提供NetBIOS名称解析\n服务，它使得Samba服务器可以通过NetBIOS名称（⽽⾮IP地址）来识别和访问，即将计算机名（例\n如 server ）转换为IP地址，便于其他计算机通过名称访问，同时提供Samba服务器在局域⽹中显\n⽰的能⼒，使其在⽹络浏览器中可⻅。\u003c/p\u003e","title":"samba服务器"},{"content":"SSh的相关操作\n文章目录 一.基于SSH实现发送命令到远程服务器\n二.基于SSH进⾏免密数据传输\n三.综合案例基于JDK的安装和操作\n一.台机器的jdk的安装和配置\n二.把这个软件和相应的配置文件通过scp发送出去\n四.基于Ubuntu完成SSH服务配置\n一.第⼀步： 需要让三个服务器全部都使⽤itheima⽤⼾登录服务器\n二.第二步: 需要让三台服务器都要配置免密操作\n三.第三步：依次通过： ssh 主机地址 连接各个服务器， 观察是否需要输⼊密码\n综述\n一.基于SSH实现发送命令到远程服务器 SSH远程执⾏命令是通过SSH协议连接到远程服务器，并在远程服务器上执⾏指定的命令或脚本， ⽽⽆需直接登录到服务器的终端\n相关的命令是\nssh [选项] username@remote_host \u0026#34;command [\u0026amp;\u0026amp;] conmmad\u0026#34; 说明： username：远程服务器的⽤⼾名。 remote_host：远程服务器的IP地址或域名。 command：要在远程服务器上执⾏的命令 \u0026amp; ： 异步执⾏， 在远程服务器后台运⾏， SSH不会等待命令完成，会⽴即返回并关闭连接 选项： -t： 如果远程命令需要交互式终端（例如 sudo 命令），可以使⽤ -t 选项 -p： 如果远程服务器的SSH服务运⾏在⾮默认端⼝（默认是22），可以使⽤ -p 选项指定端⼝ -o： 使⽤ -o 选项来指定SSH配置选项，例如禁⽤主机密钥检查（StrictHostKeyChecking=no） -A： 需要转发认证代理连接（例如使⽤SSH密钥进⾏跳板机连接），可以使⽤ -A 选项 需求⼀： 在node1中远程查看node2的主机名 ssh root@192.168.88.102 \u0026#34;hostname\u0026#34; 需求⼆： 在node1中，远程执⾏进⼊root⽤⼾家⽬录，并在此⽬录下创建 a.txt⽂件 说明：如果是执⾏多个命令，可以使⽤分号 或 \u0026amp;\u0026amp; 来分隔命令 分号： 仅分隔命令， 不管前⾯得命令是否执⾏成功， 后⾯的命令依然会执⾏ \u0026amp;\u0026amp; ： 将多个命令合并为⼀个命令， 前⾯得命令如果执⾏失败， 后⾯的命令就⽆法执⾏ ssh root@192.168.88.102 \u0026#34;cd ~ \u0026amp;\u0026amp; touch a.txt\u0026#34; ``这里面我为大家看一下我所在操作过程之中遇到的问题`\n首先呢，就是要注意的是目标主机的IP地址之后是务必要要加上引号的，我当时就没有加上的，然后命令没有跑通，我查了一些资料，之后明白了，其实就是没有加上引号，意思就是连接到目标主机之后，然后执行\u0026amp;\u0026amp;前面的命令然后断开了，在本地的主机执行\u0026amp;\u0026amp;之后的指令 在这里其实，目标主机并没有执行 这个所带来的效果其实是，连接远程主机然后在远程主机里面执行cd /home 命令，之后就断开了，在本地主机执行touch test.txt这个是第一句的指令，然后呢，我在本地主机执行了第二句指令，其实是前面的意思与第一句带来的效果一样，但是呢后面的是在本地主机创建了一个test1.txt，然后呢，我在ubuntu查看是俩个都没有的，其实就是因为我没有加上引号导致的，之后\n之后，我加上了引号，就发现了是可以进行操作的。 之后呢，我就没有遇到了什么问题了，这个方面就解决了，然后，就开始了下一个进展了。\n二.基于SSH进⾏免密数据传输 SCP（Secure Copy Protocol）是基于 SSH 协议的安全⽂件传输⼯具，可⽤于在本地和远程服务 器之间传输⽂件或⽬录。通过 SCP，数据在传输过程中会被加密，确保安全性\n其基本的语法结构是:\n源⽂件路径：本地⽂件或远程服务器上的⽂件。 ⽬标路径：本地路径或远程服务器上的路径。 常⽤选项： -r 递归复制整个⽬录（传输⽬录时必⽤） -P 指定 SSH 连接的端⼝号（默认是 22） -i 指定 SSH 使⽤的私钥⽂件（⽤于免密登录） -q 静默模式，不显⽰进度条和调试信息 -C 开启压缩传输，提⾼传输速度（适合⼤⽂件或低速⽹络） -l 限制传输速度（单位为 Kbit/s，例如 -l 1000 表⽰限制为 1Mbps） SCP的基本语法是\n1. `从本地传输到远程` `scp /local/path/file.txt user@remote:/remote/path/` 2. `从远程传输到本地` `scp user@remote:/remote/path/file.txt /local/path/` 3. `递归传输整个⽬录` scp -r /local/path/dir user@remote:/remote/path/ 下面是我操作出来的，大家可以看一下 在这里面呢，其实还有一个问题:这个是远程给ubuntu发送消息的时候是不能发送到根目录下的，由于ubuntu的权限就是远程登录只可以由普通用户的，也就是说普通用户只可以访问~，对于根目录是没有访问权限的，所以我只能用scp信息给家目录传递\n三.综合案例基于JDK的安装和操作 一.台机器的jdk的安装和配置 在这里面呢，首先呢，我们应当先从网上下载jdk的rpm包，然后再把其传输到本主机就可以了，当然了，在这里我遇到一些问题，我其实在把这个rpm上传到linux的时候发现了一个问题，就是无论如何上传其实都是会报错的。 然后呢，我研究了一下这个问题，发现了其实就是由于，我当时以为我当时Linux所在的系统权限是root所以在上传文件时也是root这个权限可以对于文件夹拥有读写权限，但是呢，其实就是在上传的时候，是默认了普通用户的权限，所以我们要做的其实就是赋权,用命令chmod 777 software就可以了 真正原因：MobaXterm 不是用 root 身份传文件的 你 SSH 终端里确实是 root（# 提示符），但 MobaXterm 左侧那个 SFTP 文件浏览器，走的是另一个会话，它不一定继承你终端的 root 身份： MobaXterm 建立 SFTP 连接时，可能用的是你最开始填的那个普通用户（比如 admins），而不是 root。 哪怕你终端 su - 切到 root 了，SFTP 面板那边还是原来那个连接，不会自动升级。 所以 SFTP 侧对 software 目录的身份是 “other”（其他人），而 other 只有 r-x，没写权限 → 报 Permission denied。 你 chmod 777 后，other 也拿到了 rwx，所以\u0026quot;其他人\u0026quot;（也就是 SFTP 那个连接）就能写了——问题消失\n然后我们用rpm -ivh对于rpm进行安装操作就可以了\n然后还有一个注意的是我们需要修改配置文件vim /etc/profile\n这个是要在文末加上的。\n最后呢，我们重载一下服务：source /etc/profile\n然后，我们在屏幕上输入java javac测试一下就可以了： 二.把这个软件和相应的配置文件通过scp发送出去 1.我们先发送软件包，cd /opt \u0026amp;\u0026amp; scp -r jdk1.8.0_241/ root@192.168.88.102:$PWD 说明： $PWD 常量值 表⽰当前所在的路径\n2.SCP /etc/profile root@192.168.88.102:/etc/ 这里面其实就是opt路径下的，不可以使用$PWD了，\n3.最后我们重启一下服务就可以：source /etc/profile 在这里，我们测试一下： 发现成功了。就可以下一步了\n四.基于Ubuntu完成SSH服务配置 一.第⼀步： 需要让三个服务器全部都使⽤itheima⽤⼾登录服务器 首先呢我们需要都同时打开三台服务器，务必要要保证要小于我们电脑的内存，否则会打不开的\n接着我们打开了之后，使用以下命令增加账户itheima\nuseradd itheima passwd itheima 123456 然后呢，三台主机都同时输入这几行命令就可以了\n最后呢，我们使用三台主机所对应的IP地址在mx上面只要把主机名修改为itheima，登录就可以了 就像如此的\n二.第二步: 需要让三台服务器都要配置免密操作 这个和我的上一个博客是一样的，内容也一样的，只不过操作起来比较麻烦的，\n我们首先是在每台主机里面下载对应的公钥和私钥，然后呢，再以此把对应的公钥发送三分，操作三次就可以了\n下载对应的公钥和私钥 ssh-keygen -f ~/.ssh/id_rsa -P \u0026#39;\u0026#39; -q \u0026amp;\u0026amp; ll ~/.ssh 二.我们开始把对应的公钥发送三分 ssh 192.168.149.128 ssh 192.168.149.129 ssh 192.168.149.131 三台主机其实都是如此写的，只不过我们要注意的是需要一行一行操作的\n三.第三步：依次通过： ssh 主机地址 连接各个服务器， 观察是否需要输⼊密码 依次通过： ssh 主机地址 连接各个服务器， 观察是否需要输⼊密码 建议⽅案， ⼀个节点 ⼀个节点依次测试， 确保不漏测 以node1为例： ssh 192.168.88.101 通过exit退回 --\u0026gt; node1 ssh 192.168.88.102 通过exit退回 --\u0026gt; node1 ssh 192.168.88.130 通过exit退回 --\u0026gt; node1 这个地方是没有上面难度的，和之前的博客一样的，我把我其中一台主机样图给展示出来\n综述 今天在操作的时候，其实是和之前的ssh命令一样的，只不过扩充了一些其他的内容，比如scp，ssh远程传输命令等等,最后我们在综合的案例当中发现了，这个其实我在最后进行网络测试的时候是瞎弄的，就比如是我们不是在测完一个ip的时候不是应该回退测试下一个ip对吧，但是呢，我当时没有注意，就想当然的操作了，这是一点,其实也挺好的，这个命令这些天学习了，博客其实也写了，公钥，私钥其实也明白了，关键是这个命令对于以后的脚本操作有积极意义的。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/ssh%E7%9A%84%E7%9B%B8%E5%85%B3%E6%93%8D%E4%BD%9C/","summary":"\u003cp\u003eSSh的相关操作\u003c/p\u003e\n\u003ch4 id=\"文章目录\"\u003e文章目录\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#SSH_3\"\u003e一.基于SSH实现发送命令到远程服务器\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#SSH_54\"\u003e二.基于SSH进⾏免密数据传输\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#JDK_94\"\u003e三.综合案例基于JDK的安装和操作\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#jdk_98\"\u003e一.台机器的jdk的安装和配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#scp_130\"\u003e二.把这个软件和相应的配置文件通过scp发送出去\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#UbuntuSSH_153\"\u003e四.基于Ubuntu完成SSH服务配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_itheima_157\"\u003e一.第⼀步： 需要让三个服务器全部都使⽤itheima⽤⼾登录服务器\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#___177\"\u003e二.第二步: 需要让三台服务器都要配置免密操作\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_ssh____217\"\u003e三.第三步：依次通过： ssh 主机地址 连接各个服务器， 观察是否需要输⼊密码\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_236\"\u003e综述\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一基于ssh实现发送命令到远程服务器\"\u003e一.基于SSH实现发送命令到远程服务器\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eSSH远程执⾏命令是通过SSH协议连接到远程服务器，并在远程服务器上执⾏指定的命令或脚本， ⽽⽆需直接登录到服务器的终端\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e相关的命令是\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003essh [选项] username@remote_host \u0026#34;command [\u0026amp;\u0026amp;] conmmad\u0026#34;\n说明：\nusername：远程服务器的⽤⼾名。\nremote_host：远程服务器的IP地址或域名。\ncommand：要在远程服务器上执⾏的命令\n\u0026amp; ： 异步执⾏， 在远程服务器后台运⾏， SSH不会等待命令完成，会⽴即返回并关闭连接\n选项：\n-t： 如果远程命令需要交互式终端（例如 sudo 命令），可以使⽤ -t 选项\n-p： 如果远程服务器的SSH服务运⾏在⾮默认端⼝（默认是22），可以使⽤ -p 选项指定端⼝\n-o： 使⽤ -o 选项来指定SSH配置选项，例如禁⽤主机密钥检查（StrictHostKeyChecking=no）\n-A： 需要转发认证代理连接（例如使⽤SSH密钥进⾏跳板机连接），可以使⽤ -A 选项\n需求⼀： 在node1中远程查看node2的主机名\nssh root@192.168.88.102 \u0026#34;hostname\u0026#34;\n需求⼆： 在node1中，远程执⾏进⼊root⽤⼾家⽬录，并在此⽬录下创建 a.txt⽂件\n说明：如果是执⾏多个命令，可以使⽤分号 或 \u0026amp;\u0026amp; 来分隔命令\n分号： 仅分隔命令， 不管前⾯得命令是否执⾏成功， 后⾯的命令依然会执⾏\n\u0026amp;\u0026amp; ： 将多个命令合并为⼀个命令， 前⾯得命令如果执⾏失败， 后⾯的命令就⽆法执⾏\nssh root@192.168.88.102 \u0026#34;cd ~ \u0026amp;\u0026amp; touch a.txt\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e``这里面我为大家看一下我所在操作过程之中遇到的问题`\u003c/p\u003e","title":"SSh的相关操作"},{"content":"warp导致的日志发送node2失败 1.问题背景\n1.案例内容\n2.案例思路\n2.出现的问题\n1.node2配置文件\n2.重启日志文件\n3.node3文件\n4.node3端口和服务放行\n5.出现问题\n3.问题原因\n核心原因剖析：用了 TCP，日志为什么还会\u0026quot;凭空消失\u0026quot;？\n1. 环境层：Cloudflare 客户端重置宿主机网络（根本诱因）\n2. 链路层：虚拟机网卡被连带闪断（触发条件）\n3. 传输/应用层：TCP 握手失败 + rsyslog 默认不缓存（直接原因）\n💡 一句话总结根因\n4.解决问题\n解决对策\n5.总结\n写在最后\n1.问题背景 这个是一个操作，以下是案例\n1.案例内容 需求：基于node1中的/var/log/下的messages和secure日志， 完成将日志发送到日志服务器（node3）， 进行统一保存，要求保存到node3服务器的/var/log/messages和/var/log/secure 思路： 2.案例思路 第一步： 修改 node1的日志配置文件， 将对应写入到messages和secure日志对应规则配置， 将其目的地变更为发送到远端服务器方案 【建议选择：TCP方案】 第二步： 重启node1的系统日志服务 第三步： 修改 node3的日志配置文件， 将用于接收远程日志的模块打开【tcp模块】 第四步： 重启node3的系统日志服务 第五步： 在node3中开放端口号 第六步： 测试message日志： 在node1中通过logger生成日志数据， 分别观察node1的系统日志和node3的系统日志， 哪个日志文件中会产生新的日志【正常:node3打印】 测试安全日志： 通过MX连接下node1，然后在关闭， 观察在这个过程中， node1和node3的安全日志谁会打印新的日志【正常:node3打印】 2.出现的问题 我当时用的是node2和node3，把node1替换成node2了\n1.node2配置文件 打开vi /etc/rsyslog.conf这个文件，然后修改一些地方，图中红圈有一个地方圈错了，应该圈的是有@@的那几行，也是要追加的 2.重启日志文件 systemctl restart rsyslog ststemctl status rsyslog 3.node3文件 和node1的文件是同一个，在模块中把tcp协议放行 4.node3端口和服务放行 firewall-cmd --add-port 514/tcp --permanent firewall-cmd --reload firewall-cmd --list-all systemctl restart rsyslog ststemctl status rsyslog 5.出现问题 我在测试的时候，发现一个问题，没有通过 他报的这个错误我当时很迷，ai让我把setforce设置0，但是已让没有ok，就比较麻烦，当时折腾了许久，就是搞不定，当时也比较晚了，我就直接睡了\n3.问题原因 第二天早上，我仔细有复现了一边，就很离谱，这一次，竟然成功了，配置和昨天一样的，只不过，报了这行文字，我当时想我的虚拟机并没有打开warp这个客户端，为何虚拟机会出现这行文字呢 我问了一下，ai她给我说是这样的，因为，我在打开warp客户端的时候，这个软件会修改计算机的路由表，DNS把DNS设置为它自己的：127.0.2.2 和 127.0.2.3（这是 Cloudflare 在本地建的 DNS 代理）,因为昨天我可能设置完setfoce=0之后呢，没有立即生效，需要重启才会，当时我可能来回开关这个软件，导致由表处于\u0026quot;脏状态\u0026quot;，今天冷启动后，WARP 的虚拟接口和路由规则重新初始化，恰好没有干扰到虚拟机的桥接网络。 核心原因剖析：用了 TCP，日志为什么还会\u0026quot;凭空消失\u0026quot;？ 很多人（包括当时的我）都有一个误区：“我用了 TCP 转发，TCP 是可靠传输，日志不可能丢啊？” 但这次事故告诉我：TCP 的可靠性，只在\u0026quot;连接已建立\u0026quot;的前提下才生效。 这次故障是三层问题叠加的结果：\n1. 环境层：Cloudflare 客户端重置宿主机网络（根本诱因） 宿主机上的 Cloudflare One Client（WARP）是全局代理/隧道工具。当它处于不稳定状态（客户端自己都在提示\u0026quot;Internet 连接不稳定\u0026quot;）或后台重连时，会重置宿主机的路由表和网络栈。这是整起事故的\u0026quot;第一张多米诺骨牌\u0026quot;。\n2. 链路层：虚拟机网卡被连带闪断（触发条件） VMware 虚拟机的虚拟网卡依托宿主机网络存在。宿主机网络一重置，虚拟机网卡跟着断。日志里的铁证——node2 和 node3 同一秒同时出现：\nAug 5 07:14:45 node2 kernel: e1000: ens33 NIC Link is Down Aug 5 07:14:47 node3 kernel: ens33 NIC Link is Up 1000 Mbps Full Duplex 两台机器不可能约好一起坏，问题只能出在宿主机层面。\n3. 传输/应用层：TCP 握手失败 + rsyslog 默认不缓存（直接原因） TCP 要握手，链路断了就是断了。 闪断期间，node2 与 node3:514 的三次握手无法完成，连接建立不起来，数据一个字节都过不去。（这点比 UDP 更\u0026quot;诚实\u0026quot;：UDP 还会盲目往外发包，TCP 直接告诉你连接失败。）\nrsyslog 默认不缓存断连期间的消息。 rsyslog 转发动作默认使用 Direct 队列，没有配置内存/磁盘缓存队列。连接不可用时，这个窗口期内产生的日志默认直接丢弃，网络恢复后也不会补发。\n所以\u0026quot;TCP 可靠\u0026quot;救不了你——它保证的是连接建立后不丢不乱，救不了\u0026quot;连接根本不存在\u0026quot;期间产生的消息。\n💡 一句话总结根因 WARP 不稳定 → 宿主机网络重置 → 虚拟机网卡闪断 → TCP 连接无法建立/维持 → rsyslog 默认无队列，断连窗口期的日志被丢弃 → 接收端什么都看不到。 早上冷启动后链路全程稳定，TCP 握手一次成功，可靠性机制正常上岗，于是\u0026quot;开着软件也一切正常\u0026quot;。\n4.解决问题 其实第二天我一起来，又做了一边实验就顺利了，问题自己解决了 和图中的一样，只不过我当时调整的是本地发一份，远端发一份\n解决对策 做实验前退出所有代理 / VPN / WARP，控制变量再排障；\n转发地址写 IP 不写主机名，避免 DNS 故障牵连日志发送；\n防火墙、SELinux 常规三件套别忘：\nfirewall-cmd --permanent --add-port=514/tcp firewall-cmd --reload setenforce 0 # 实验环境临时关，生产请配策略 systemctl restart rsyslog 排障工具包，一步区分\u0026quot;配置问题\u0026quot;还是\u0026quot;环境问题\u0026quot;： tail -F /var/log/messages # 接收端看日志 tcpdump -i ens33 port 514 # 接收端看包到没到 ss -ulnp | grep 514 # 看监听起没起 tcpdump 有包没日志 → 接收端服务/配置问题；tcpdump 没包 → 网络/链路层问题。 解决对策： 核心是三层入手。环境层：实验前退出宿主机上的 Cloudflare WARP 等代理软件，消除网络重置、虚拟网卡闪断的根因。应用层：保持 TCP 转发，并在发送端启用缓存队列（queue.type=\u0026quot;linkedList\u0026quot;、action.resumeRetryCount=\u0026quot;-1\u0026quot;），使断连期间的日志先入队、网络恢复后自动补发，不再丢弃；转发地址写 IP 不写主机名，防火墙放行 514/tcp，临时关闭 SELinux，改完用 rsyslogd -N1 校验语法并重启服务。验证层：用 tcpdump -i ens33 tcp port 514、ss -tlnp | grep 514、tail -F /var/log/messages 逐层确认\u0026quot;包到了、端口在听、日志显示\u0026quot;，形成闭环。\n5.总结 ``当时我的warp连接是不稳定的，我没有在意，就比较麻烦，做这个实验一直搞不定，很捉急，但是第二天好了，其实应该就是与我的这个软件有关.\n写在最后 很多初学者（包括曾经的我）在遇到服务不通时，总是习惯性地怀疑自己：“是不是我 rsyslog 配置写错了？”“是不是 SELinux 没关？”“是不是防火墙没放行？” 但这次事故告诉我：在真实的运维世界里，80% 的“玄学”故障，根源都在你看不见的基础网络环境里。 一个宿主机代理软件的抽风，足以让你的虚拟机网络经历“闪断-重连”，从而在应用层制造出“配置全对但就是不工作”的假象。能写出正确的配置文件只是基本功，能敏锐地察觉环境变化、熟练运用 tcpdump 抓包定界、懂得在复杂干扰中控制变量，才是一个成熟运维工程师的真正壁垒。 踩坑不可怕，把坑填平并总结出方法论，就是最大的成长。希望这篇复盘，能帮你在未来的 Linux 学习之路上少走弯路。如果觉得有用，欢迎点赞收藏，我们评论区交流！💪``\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/warp%E5%AF%BC%E8%87%B4%E7%9A%84%E6%97%A5%E5%BF%97%E5%8F%91%E9%80%81node2%E5%A4%B1%E8%B4%A5/","summary":"\u003ch4 id=\"warp导致的日志发送node2失败\"\u003ewarp导致的日志发送node2失败\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_1\"\u003e1.问题背景\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_3\"\u003e1.案例内容\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_9\"\u003e2.案例思路\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_28\"\u003e2.出现的问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1node2_30\"\u003e1.node2配置文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_34\"\u003e2.重启日志文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3node3_41\"\u003e3.node3文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4node3_45\"\u003e4.node3端口和服务放行\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_58\"\u003e5.出现问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_64\"\u003e3.问题原因\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_TCP_69\"\u003e核心原因剖析：用了 TCP，日志为什么还会\u0026quot;凭空消失\u0026quot;？\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_Cloudflare__73\"\u003e1. 环境层：Cloudflare 客户端重置宿主机网络（根本诱因）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2__77\"\u003e2. 链路层：虚拟机网卡被连带闪断（触发条件）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_TCP___rsyslog__88\"\u003e3. 传输/应用层：TCP 握手失败 + rsyslog 默认不缓存（直接原因）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#__95\"\u003e💡 一句话总结根因\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_100\"\u003e4.解决问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_107\"\u003e解决对策\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_133\"\u003e5.总结\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_136\"\u003e写在最后\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1问题背景\"\u003e1.问题背景\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e这个是一个操作，以下是案例\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"1案例内容\"\u003e1.案例内容\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e需求：基于node1中的/var/log/下的messages和secure日志， 完成将日志发送到日志服务器（node3）， 进行统一保存，要求保存到node3服务器的/var/log/messages和/var/log/secure\n思路：\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2案例思路\"\u003e2.案例思路\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e第一步： 修改 node1的日志配置文件， 将对应写入到messages和secure日志对应规则配置， 将其目的地变更为发送到远端服务器方案 【建议选择：TCP方案】\n\n第二步： 重启node1的系统日志服务\n\n\n第三步： 修改 node3的日志配置文件， 将用于接收远程日志的模块打开【tcp模块】\n\n第四步： 重启node3的系统日志服务\n\n第五步： 在node3中开放端口号\n\n\n第六步：\n    测试message日志： 在node1中通过logger生成日志数据， 分别观察node1的系统日志和node3的系统日志， 哪个日志文件中会产生新的日志【正常:node3打印】\n\n    测试安全日志： 通过MX连接下node1，然后在关闭， 观察在这个过程中， node1和node3的安全日志谁会打印新的日志【正常:node3打印】\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"2出现的问题\"\u003e2.出现的问题\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e我当时用的是node2和node3，把node1替换成node2了\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"1node2配置文件\"\u003e1.node2配置文件\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e打开vi /etc/rsyslog.conf这个文件，然后修改一些地方，图中红圈有一个地方圈错了，应该圈的是有@@的那几行，也是要追加的\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/warp%E5%AF%BC%E8%87%B4%E7%9A%84%E6%97%A5%E5%BF%97%E5%8F%91%E9%80%81node2%E5%A4%B1%E8%B4%A5/images/f2c4680116e04c009aceb90192dcf5d1.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"2重启日志文件\"\u003e2.重启日志文件\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esystemctl restart rsyslog\n\nststemctl status  rsyslog\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/warp%E5%AF%BC%E8%87%B4%E7%9A%84%E6%97%A5%E5%BF%97%E5%8F%91%E9%80%81node2%E5%A4%B1%E8%B4%A5/images/cd2a18ecf0fa4b0fb43fdd7ca4db01a2.png\"\u003e\u003c/p\u003e","title":"warp导致的日志发送node2失败"},{"content":" 1. 引言\n1.1 自动化 Git 的介绍\n1.2 传统手动推送痛点\n1.3 我为何需要自动同步\n2. 如何想到这个方案\n2.1 背景：Linux 学习带来的启发\n2.2 一段经历：用 OpenClaw 部署 NSSM 服务\n2.2.1 原因\n2.2.2 解决问题\n2.2.3 开始执行步骤\n步骤 1：实施步骤流程\n步骤 2：注册服务\n步骤 3：配置服务账户（第一次踩坑）\n步骤 4：关键一步：给 LocalSystem“指路”\n步骤 5：启动与验证\n步骤 6：总结\n3. 思路与方案定型\n4. 需求分析\n5. 验证方案可行性\n6. 列出可行的方案\n7. 项目实施的大纲\n8. 详细步骤\n8.1 一次性准备\n8.1.1 在本地建一个仓库\n8.1.2 安装 Git\n8.1.3 SSH 准备\n生成无口令密钥\n公钥添加到平台\nGitHub 走 443 端口\n8.2 底层一步一步是怎么实现的\n8.2.1 监听文件变化\n8.2.2 事件泵 + 防抖\n8.2.3 自动提交推送\n8.2.4 服务化（NSSM）\n8.2.5 定时拉取\n9. 核心脚本设计（PowerShell）\n9.1 脚本文件构成\n9.2 推送守护脚本设计（git_sync_realtime_xxx.ps1）\n9.3 定时拉取脚本设计（change_fetcher_generic.ps1）\n9.4 设计上的几个关键决定\n10. 遇到的问题与解决方法（最重要的关键部分）\n10.1 bat 中文乱码\n10.2 SSH 私钥带口令，自动化服务无法使用\n10.3 git 的 \u0026ldquo;dubious ownership\u0026rdquo; 安全保护\n10.4 GitHub 端口 22 被拦截\n10.5 服务“已标记为删除”（错误 1072）\n10.6 定时拉取间隔写死\n10.7 FileSystemWatcher 实时监听不触发\n10.8 服务 PAUSED（启动失败循环）\n11. 安全设计：主仓库只推送 + 镜像区\n11.1 为什么这样设计\n11.2 两条线分别怎么做\n11.3 安全边界（一定要分清）\n11.4 配套安全措施\n11.5 结论\n12. 验证结果\n13. 下载与体验\n14. 总结与展望\n摘要：本文记录我从“AI 机器人老挂机”的痛点出发，到用 NSSM 把 OpenClaw 网关变成 Windows 服务，再到把“Git 自动同步”做成一个完整工具包并打包发布的完整过程。全文包含：方案对比、详细步骤、核心脚本设计、8 个真实踩坑记录、安全设计、验证结果。全程由我提出需求、检查验证、确定改进方向，代码与排错由 AI 辅助完成。\n核心结论（TL;DR）：用 NSSM 把“监听推送 + 定时拉取”脚本注册成 Windows 服务，实现本地改动自动推 Gitee/GitHub、云端变化定时拉回本地、开机自启、崩溃自愈。全程零代码基础：作者提需求、AI 辅助实现、作者逐项验证。全文较长，建议先看第 10 章问题速查和第 12 章验证清单。\n1. 引言 1.1 自动化 Git 的介绍 自动化 Git，简单说就是：让 Git 的“提交（commit）+ 推送（push）”不再需要人手动输入命令，而是由程序自动完成。\n传统 Git 工作流是人驱动的：\n改文件 → 手动 git add → git commit → git push → 完成 自动化 Git 是事件/定时驱动的：\n改文件 → 程序监听到变化 → 自动 add → 自动 commit → 自动 push → 完成 自动化 Git 的核心组件一般包括：\n组件 作用 监听器 实时检测目录里文件的增、删、改 执行器 自动执行 add/commit/push，省去手动输入 调度/服务 让整个过程开机自启、崩溃自愈（如 NSSM 服务） 认证 免密 SSH 密钥，让自动化可以无人值守 它特别适合：笔记仓库、配置文件、文档、个人项目，以及任何“改完就该同步”的场景。配合 Gitee/GitHub，等于给文件加了一层“自动上传备份”。\n1.2 传统手动推送痛点 在动手之前，我已经被手动推送折磨了很久，痛点非常具体：\n总是忘记提交：改完文件经常忘了 git add \u0026amp;\u0026amp; git commit \u0026amp;\u0026amp; git push，等想起来已经是几天后，甚至直接没保存到云端。\n两个平台重复劳动：Gitee 和 GitHub 都要维护，每次手动推两遍，容易漏一个。\n云端和本地经常分叉：电脑关了、忙忘了，云端和本地就对不上，下次同步还要面对一堆冲突。\n没有历史安全感：本地文件一旦误删、磁盘坏了，云端根本不会有最新版本。\n这些痛点反复出现，我逐渐意识到：手动推送这件事，本质上是“可以被程序替代的重复劳动”。既然能交给程序，为什么还要自己记着做？\n1.3 我为何需要自动同步 每天就修改或添加几个文件，却要单独用小乌龟（TortoiseGit）推到远端，麻烦又臃肿。而且文件写入本地仓库后，经常因为各种事情忘记推送。\n其实大学的时候我就在想：如果这些东西都能自己搞定就好了——自动提交、自动把远端变化拉取到本地指定目录，多好呀。这在当时是一个遥远的梦想。但大学这几年，老师不教 Git 知识，也不讲 Windows 脚本、NSSM 系统级服务……很多东西都没讲。大学四年，我甚至听都没听说过这些技术名词，这个想法只能一直藏在心里。\n而且，我们大学有些同学是不会用 Git 这个工具的，对他们来说使用难度很高，这也是一个痛点。要是本地仓库能自动同步，就不需要他们自己折腾了。\n但更关键的是，大学时我用小乌龟手动推送，经常报错：\n一会说“不能在本地仓库里添加目录，只能加入文件”这类莫名其妙的提示。\n有时候小乌龟里设置的用户名和邮箱与 Gitee 不匹配，怎么都推送不上去。\n好几个大文件不能一次推送，需要单独推送——都是痛点与麻烦的地方。\n我大学时的几个同学使用 GitHub 客户端推送，这本身没有问题，但我无法忍受每次要推送本地仓库时，都强制打开这个客户端——多加一步操作流程。我本身就极其不愿意专门腾出时间做这种操作：一来麻烦不说，忙起来还容易忘记；二来我本身就是个极其懒的人，每天做重复的动作心情极差，很不舒服，影响一天的状态——这也是此次项目实施的背景之一。\n当时自己没有手段可以简化这些流程，让它标准化、自动化、自发进行，把流程固定下来，抽象成一套具有普适性的自动化经验。所以我只能每天定一个时间，在时间范围内推送——这样的操作一直持续到大学毕业。\n转机就出现在大学毕业这段时间：我终于有能力也有手段（包括 AI 辅助、NSSM 实践经验的积累）把这些曾经的“梦想”变成现实。于是，这套“Git 自动同步系统”正式立项。最终效果：\n本地文件夹一有改动 → 自动提交并推送到云端。\n云端变化 → 自动拉取到本地镜像目录（不碰主仓库，安全）。\n开机自启、崩溃自动重启，全程无需干预。\n2. 如何想到这个方案 2.1 背景：Linux 学习带来的启发 当时我在学习 Linux，先学到了 SSH 免密登录：生成私钥和公钥，把公钥发给 node2，这样两台主机可以互相免密登录。后面学到 rsync 的时候，通过写 rsync 服务的配置文件、安装监控用的安装包、写一个脚本自动同步，最后把脚本加入服务项并设置开机自启。我看到了：一旦宿主机的指定目录有变化，就会自动把相应的变化同步到服务器 node2。\n学完之后我就在想：Linux 都可以自动监控目录的变化，这不就是我梦寐以求的 Git 流程化思考路径吗？但当时我也有顾虑：老师是在 Linux 机器上演示的，而且用的不是 Gitee 或 GitHub，只是单纯的同步。我在想，如果把这个思路用在 Git 流程化上，而且用的是 Windows 机器，不知道是否可行。当时也不知道我的想法是否天真、是否正确，我就去咨询 AI，问它这个问题。最后 AI 说：这个方案对我来说是可以实施的，具有成功可能，但过程可能不顺利。\n当时我就把这个想法种下了。这是如今我能做出这套系统的启发点。直到后来我把 OpenClaw 加入 NSSM 服务项，才把这个想法具体化，知道了具体该如何实施、该如何操作，大致框架也有了。\n2.2 一段经历：用 OpenClaw 部署 NSSM 服务 2.2.1 原因 我之前在电脑上安装了 OpenClaw，这本身没有什么问题。后来我给 OpenClaw 配置好了 QQ、飞书这些国内机器人，远程、异地访问都能做到。但问题就出在这里：\n有时候我让机器人自发执行任务，它会重启网关，然后再也启动不成了，需要我手动启动网关。\n有时候执行某个任务，OpenClaw 突然任务失败，然后就没有反应，像是突然死掉了一样，这时候也需要我手动重启网关，机器人才会有反应。\n这些问题放在本地其实不算什么，甚至连写博客都没有必要。但如果我在异地、在远处、在其它省份呢？机器人突然任务失败，难道我要从很远的地方跑过来，专门给它重启一下网关吗？显然不能，这也不现实。\n其实这就是我在使用 OpenClaw 时的一个痛点，一个非常致命的地方：一旦它挂机，就会耽误很多事情。所以每次在异地遇到挂机，我都只能忍着。\n后来我咨询 OpenClaw，问它为什么老是挂机、有没有解决方式。它告诉我一个新东西——NSSM。这就是本次 Git 自动化以及 OpenClaw 用到的核心技术：它解决了 OpenClaw 挂机的痛点，也帮助我把想法一步步具体化、落地。\n2.2.2 解决问题 当时 OpenClaw 告诉我的方案有好几种，下面我为大家介绍一下：\n方式 开机自启 崩溃自动重启 需要登录 稳定性 备注 启动文件夹 .bat ✅（登录后） ❌ ✅ 一般 简单粗暴，适合临时脚本 注册表 Run 键 ✅（登录后） ❌ ✅ 一般 常用但易被安全软件拦截 任务计划程序 ✅ 可配置 可配置 好 灵活，支持触发器、条件等 NSSM 服务 ✅（登录前） ✅ 默认 ❌ 最好 Non-Sucking Service Manager，将任意程序包装为 Windows 服务 NSSM（Non-Sucking Service Manager）是一个将普通应用程序包装为 Windows 服务的工具，它具备：\n开机自启：系统启动后即运行，无需用户登录。\n崩溃自愈：服务异常退出后自动重启。\n后台静默：无窗口、无界面，适合长期运行的守护进程。\n通过观察这个表格，我知道，在 OpenClaw 给定的方案当中，最出色的就是 NSSM 服务：服务崩溃会自动重启，稳定性也是最好的，和我的需求贴合得最为紧密。它完全满足了我对 OpenClaw“服务崩溃、重启之后启动不成、自发启动、自我修复进程”的全部要求。\n这里也想说明一下：今天这篇以 Git 自动化 为主，对 OpenClaw 的介绍是为了诠释 Git 自动化，让整个部署过程更加自然，读起来不那么唐突，有一个思考过程。什么东西都是一点点来的，不是一蹴而就的。这个过程其实要比项目本身重要很多，因为这是一个思考路径，一种递进。以后我有其他项目，也是一步一个脚印地走，因为如此，才会逐渐把想法落地。\n2.2.3 开始执行步骤 步骤 1：实施步骤流程 整体的思路只有一条：让 OpenClaw 网关以 Windows 服务的方式常驻运行，由 NSSM 负责开机拉起、崩溃重启。 至于 Git 自动化，是另一条独立的线：由另一个 agent 完成的工具包负责，同样由 NSSM 托管（后面会详细讲）。\n开机 → Windows 启动 OpenClawService → NSSM 拉起 openclaw gateway → 网关连上 QQ/飞书/Telegram → 机器人常驻、异地可用 具体流程如下：\n确认 OpenClaw 网关可以手动运行：openclaw gateway run。\n用 NSSM 将网关注册为 Windows 服务。\n配置服务：工作目录、服务账户、开机自启、崩溃自动重启。\n设置关键环境变量，让服务能找到用户配置。\n启动服务并验证。\n重启电脑，做最终验收。\n步骤 2：注册服务 以下命令需要以管理员身份打开 cmd 或 PowerShell 执行。\n我的 NSSM 程序位于 C:\\Users\\62901\\.openclaw\\nssm.exe，网关入口是 C:\\Users\\62901\\AppData\\Roaming\\npm\\openclaw.cmd：\nC:\\Users\\62901\\.openclaw\\nssm.exe install OpenClawService \u0026#34;C:\\Users\\62901\\AppData\\Roaming\\npm\\openclaw.cmd\u0026#34; gateway run C:\\Users\\62901\\.openclaw\\nssm.exe set OpenClawService AppDirectory C:\\Users\\62901\\.openclaw 图：NSSM 安装与 AppDirectory 配置成功\n步骤 3：配置服务账户（第一次踩坑） 一开始我试图用当前用户账户运行服务：\nC:\\Users\\62901\\.openclaw\\nssm.exe set OpenClawService ObjectName WIN-C0SV93I7DP8\\62901 \u0026lt;真实Windows登录密码\u0026gt; 结果一直报：\nOpenClawService: START: 由于登录失败而无法启动服务。 原因很简单：NSSM 要求的“密码”是真实的 Windows 登录密码（开机密码），不是用户名，也不是随便猜的数字。如果电脑是用 Microsoft 账户登录的，这条路基本走不通。\n图：使用用户账户运行时提示“由于登录失败而无法启动服务”\n当时 OpenClaw 有点“恼火”了，建议我用启动文件夹方案，把脚本放到启动目录里。但我没有按它说的来，因为我觉得这个操作手段不够稳定：服务一崩溃，就再也起不来了，想要自发重启根本不可能。我当时对 OpenClaw 说：不是有一个 LocalSystem 吗，为什么不能用呢？我也问它：开机自启动到底有哪些方式？下面就是它告诉我的。\n当时我又问它：启动文件夹的方案，崩溃后会自发启动吗？它说没有办法。\n因此，我当时就决定还是使用 NSSM 服务项，而且直接使用 LocalSystem。虽然它提醒我说 LocalSystem 权限极高、需要在系统账号下运行，但我还是如此决定。\n于是我换成了 LocalSystem（Windows 内置账户，不需要密码，权限极高）：\nC:\\Users\\62901\\.openclaw\\nssm.exe set OpenClawService ObjectName LocalSystem C:\\Users\\62901\\.openclaw\\nssm.exe set OpenClawService Start SERVICE_AUTO_START C:\\Users\\62901\\.openclaw\\nssm.exe set OpenClawService AppExit Default Restart 图：切换到 LocalSystem 并配置开机自启与崩溃重启\n步骤 4：关键一步：给 LocalSystem“指路” 服务确实 RUNNING 了，但重启电脑后还是没反应。排查后发现，这不是权限问题，而是路径问题：\nOpenClaw 启动时会去 %USERPROFILE%\\.openclaw\\ 找配置和 API 密钥，而 LocalSystem 的 %USERPROFILE% 指向的是 C:\\Windows\\System32\\config\\systemprofile，不是我的用户目录 C:\\Users\\62901。于是网关去系统目录找配置 → 找不到 → 进程在跑，却是个“空壳”，连不上任何机器人。\n解决办法是用 NSSM 的 AppEnvironmentExtra 强制注入环境变量：\nC:\\Users\\62901\\.openclaw\\nssm.exe set OpenClawService AppEnvironmentExtra USERPROFILE=C:\\Users\\62901 C:\\Users\\62901\\.openclaw\\nssm.exe restart OpenClawService 步骤 5：启动与验证 C:\\Users\\62901\\.openclaw\\nssm.exe start OpenClawService C:\\Users\\62901\\.openclaw\\nssm.exe status OpenClawService sc query OpenClawService 图：服务启动成功，状态 RUNNING\n写到这里我发现，OpenClaw 可以正常连接 NSSM 服务，可以完美执行。\n图：OpenClaw 网关正常连接，机器人可正常回复\n这个 OpenClaw 配置的实验是完美收官：服务运行状态相当之好，稳定性异常优秀，崩溃后可以自发启动。对于异地使用 OpenClaw 来说也很完美——运行程序崩掉，不需要提心吊胆害怕了。以后可以稳定地使用这个 OpenClaw 机器人，在飞书、QQ 上完美运行。\n步骤 6：总结 最终配置一览：\n配置项 值 作用 服务名 OpenClawService 服务标识 ObjectName LocalSystem 免密码、权限高 Start SERVICE_AUTO_START 开机自启 AppExit Default Restart 崩溃自动重启 AppEnvironmentExtra USERPROFILE=C:\\Users\\62901 让服务找到用户配置（关键） 最后再回到思考路径：之所以先写 OpenClaw 与 NSSM，是为了解释 Git 自动化为什么能成立。先让执行者稳定活着（NSSM 服务），再把流程固定给自动化工具（另一个 agent 完成的工具包）。一个想法接一个想法，一个脚印接一个脚印，这就是我理解的技术落地过程。这个项目的实施，促进了我对 Git 自动化配置的想法构思：既然 OpenClaw 能这样稳定运行，我完全也可以把 Git 同步脚本注册成 NSSM 服务，让它同样健壮、同样优秀。下面就开始此次的正题：Git + NSSM 落地的自动化。\n3. 思路与方案定型 通过以上的分析，我对 Git 自动化的理解进一步深入。最初的构思是采用类似 Linux 的技术：监听 + 脚本控制 + 免密登录 + 注册服务项。但需要注意，当时的构思非常笼统、不具体，只给了一个“像 Linux 那样的技术方向”，是不可直接实施的。\n经过把 OpenClaw 网关注册到 NSSM 服务项，我进一步具体化了构思：为什么不采用类似 OpenClaw 的 NSSM 系统级服务控制呢？把监听、推送、拉取脚本都注册到 NSSM 里面，那么就是可以实施、可以尝试的。\n因此我的最终方案就是：用脚本化控制手段监听本地仓库动态，把变化推送到远端，或者把远端的变化拉取到本地，再把脚本注册为服务项。这样就可以实现实时监听，加上动态自我修复。\n4. 需求分析 动手前，先把需求列清楚：\n需求 说明 实时监听 目录里增、删、改都要能检测到 自动提交推送 检测到变化后自动 commit + push 到 Gitee/GitHub 开机自启 服务随系统启动，登录前后都能跑 多平台 同一套方案支持 Gitee 和 GitHub 数据安全 云端变化不应直接覆盖本地主仓库 5. 验证方案可行性 为了验证我的想法是否可行，我当时直接问的是腾讯元宝：Git 自动化是否可以实施？它给出了肯定的答复，同时提醒我过程中可能会遇到权限、编码、服务配置等问题。我当时就知道，这个想法不是空中楼阁，是可以试一下的。\n（这一部分的对话截图当时没有保存，这里用文字记录结论。）\n6. 列出可行的方案 Windows 上实现“开机自启后台任务”有两种主流方式：\n对比项 计划任务 (schtasks) NSSM 服务 触发时机 登录之后才启动 开机即启动（登录前） 崩溃恢复 不自动重启 自动重启 后台稳定性 一般 更稳（系统服务管理） 配置自由度 一般 高（日志轮转、环境变量等） 这几个自启动方式在第 2 节其实已经分析过，这里再列一次对比，是为了让读者在进入实现之前更清楚为什么选它。\n最终选择 NSSM（The Non-Sucking Service Manager）——把 PowerShell 脚本包装成 Windows 服务，稳、能自愈、能开机自启。\n7. 项目实施的大纲 整套系统有两条线：\n【自动推送线】本地 → 云端 本地文件夹 → FileSystemWatcher 监听变化 → 自动 git add → commit → push → 推送到 Gitee / GitHub 【定时拉取线】云端 → 本地 Gitee / GitHub 云端仓库 → GitMirrorFetcher 每隔 N 分钟 fetch → reset --hard 同步到本地镜像目录 两条线都由 NSSM 服务托管：开机自启、崩溃自动重启、后台静默运行。\n推送服务名：GitAutoSync_xxx（每个仓库一个）。\n拉取服务名：GitMirrorFetcher（全局一个，管理所有镜像）。\n8. 详细步骤 8.1 一次性准备 8.1.1 在本地建一个仓库 这一步就是在本地建一个目录，目录的名字可以任意取，这一步是为了后面与远端仓库关联起来。\n8.1.2 安装 Git 因为我之前在大学的时候已经下载过 Git，这里就略过了。给大家一个下载链接：\nGit 下载地址\n8.1.3 SSH 准备 生成无口令密钥 ssh-keygen -t ed25519 -f %USERPROFILE%\\.ssh\\gitee_sync -N \u0026#34;\u0026#34; 注意两点：-N \u0026quot;\u0026quot; 表示无口令，自动化服务是非交互环境，带口令的密钥会无法使用（这是我遇到的问题）；另外 Windows 的 cmd 不识别 ~，要用 %USERPROFILE% 或完整路径。\n公钥添加到平台 把 .pub 文件内容分别添加到：\nGitee：设置 → SSH公钥。\nGitHub：Settings → SSH and GPG keys。\n图：Gitee 添加 SSH 公钥\n图：GitHub 添加 SSH 公钥\nGitHub 走 443 端口 git remote set-url origin ssh://git@ssh.github.com:443/用户名/仓库.git 端口 22 被本机网络环境拦截（详见问题 4），GitHub 官方支持 SSH over 443。\n这条命令就是把本地仓库和远端仓库关联起来，指定“以后推送到哪、从哪里拉取”。执行后可以用 git remote -v 确认。\n8.2 底层一步一步是怎么实现的 8.2.1 监听文件变化 用 .NET 的 FileSystemWatcher 监听仓库目录（含子目录），注册 新建/修改/重命名/删除 四类事件：\n$watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = $RepoPath $watcher.IncludeSubdirectories = $true $watcher.EnableRaisingEvents = $true 8.2.2 事件泵 + 防抖 事件动作不能靠 Register-ObjectEvent -Action 自动触发（主线程阻塞时不会执行），所以主循环用 Wait-Event 泵取事件：\nwhile ($true) { $e = Wait-Event -Timeout 1 if ($null -eq $e) { continue } Remove-Event -EventIdentifier $e.EventIdentifier Start-Sleep -Milliseconds 3000 # 防抖：合并连续写入 Sync-Now } 8.2.3 自动提交推送 git add -A git status --porcelain # 没变化就跳过，避免空提交 git commit -m \u0026#34;auto-sync: 时间戳\u0026#34; git push origin \u0026lt;分支\u0026gt; # 失败重试 3 次，间隔 5 秒 关键细节：必须检查 commit/push 的退出码，否则失败会被静默吞掉。\n8.2.4 服务化（NSSM） 核心配置：\nObjectName = LocalSystem # 免密码、权限高 Start = SERVICE_AUTO_START # 开机自启 AppExit = Default Restart # 崩溃自动重启 AppRestartDelay = 5000 # 5 秒后重试 AppEnvironmentExtra = GIT_CONFIG_VALUE_0=* # 允许 SYSTEM 访问任意仓库（safe.directory） 8.2.5 定时拉取 git fetch origin \u0026lt;分支\u0026gt; git reset --hard origin/\u0026lt;分支\u0026gt; # 强制同步到云端最新 git clean -fd # 清理未跟踪文件 启动时立即执行一轮，之后按 git_mirror_settings.txt 里的 interval=N 循环，默认 60 分钟。\n9. 核心脚本设计（PowerShell） 这套工具的核心逻辑全部用 PowerShell 写成，是作者用电脑上的 Codex 运行出来的，这里把关键部分写清楚：\n一次性生成脚本：新建仓库时运行，负责初始化、配置、生成同步脚本。\n常驻守护脚本：被 NSSM 包装成服务，负责监听、推送、定时拉取。\n9.1 脚本文件构成 文件 角色 new_sync_repo_generic.ps1 新建自动推送仓库：初始化 + 生成同步脚本和安装 bat git_sync_realtime_xxx.ps1 自动生成的推送守护脚本，被 NSSM 托管 new_pull_mirror_generic.ps1 登记云端镜像：选择密钥 + 立即克隆 + 写入配置 change_fetcher_generic.ps1 定时拉取守护脚本，被 NSSM 托管 service_manager.ps1 服务管理菜单（中文界面主体） ssh_key_generator.ps1 一键生成无口令 SSH 密钥 9.2 推送守护脚本设计（git_sync_realtime_xxx.ps1） 1）配置区\n$RepoPath = \u0026#39;D:\\我的项目\u0026#39; # 被监听仓库 $Branch = \u0026#39;master\u0026#39; # 推送分支 $Remote = \u0026#39;origin\u0026#39; $LogFile = Join-Path $RepoPath \u0026#39;git_sync.log\u0026#39; $DebounceMs = 3000 # 防抖时间 $RetryTimes = 3 # 推送重试次数 $RetryWaitSec = 5 2）日志函数\nfunction Write-Log { param([string]$Msg) $line = \u0026#34;[{0}] {1}\u0026#34; -f (Get-Date -Format \u0026#39;yyyy-MM-dd HH:mm:ss\u0026#39;), $Msg $line | Out-File -FilePath $LogFile -Append -Encoding utf8 Write-Host $line } 日志文件放在仓库内，同时被 .gitignore 忽略，避免日志污染提交。\n3）启动自检\n脚本启动先做四件事，缺一直接退出，避免“空转”：\nif (-not (Test-Path $RepoPath)) { Write-Log \u0026#39;目录不存在\u0026#39;; exit 1 } if (-not (Get-Command git -ErrorAction SilentlyContinue)) { Write-Log \u0026#39;未找到 git\u0026#39;; exit 1 } if (-not (Test-Path (Join-Path $RepoPath \u0026#39;.git\u0026#39;))) { Write-Log \u0026#39;不是 git 仓库\u0026#39;; exit 1 } $hasRemote = (git -C $RepoPath remote) -match \u0026#39;^origin$\u0026#39; if (-not $hasRemote) { Write-Log \u0026#39;未配置远端\u0026#39;; exit 1 } 4）文件过滤\n.git 目录和同步脚本自身的日志必须过滤，否则会自己触发自己：\nfunction Should-Ignore { param([string]$FullPath) if ($FullPath -like \u0026#34;$RepoPath\\.git*\u0026#34;) { return $true } if ($FullPath -like \u0026#34;$RepoPath\\git_sync*\u0026#34;) { return $true } return $false } 5）同步函数（核心）\nfunction Sync-Now { git -C $RepoPath add -A if ($LASTEXITCODE -ne 0) { Write-Log \u0026#39;git add 失败\u0026#39;; return } $status = git -C $RepoPath status --porcelain if ([string]::IsNullOrWhiteSpace($status)) { Write-Log \u0026#39;无实际变化，跳过\u0026#39;; return } $msg = \u0026#34;auto-sync: {0}\u0026#34; -f (Get-Date -Format \u0026#39;yyyy-MM-dd HH:mm:ss\u0026#39;) git -C $RepoPath commit -m $msg if ($LASTEXITCODE -ne 0) { Write-Log \u0026#39;commit 失败\u0026#39;; return } for ($i = 1; $i -le $RetryTimes; $i++) { git -C $RepoPath push $Remote $Branch if ($LASTEXITCODE -eq 0) { Write-Log \u0026#39;推送成功\u0026#39;; return } Start-Sleep -Seconds $RetryWaitSec } Write-Log \u0026#39;推送失败，将在下次文件变化时重试\u0026#39; } 设计要点：先 add，再用 status --porcelain 判断有没有真实变化，避免产生空提交；commit/push 都检查退出码，失败不静默。\n6）监听器 + 事件泵\n$watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = $RepoPath $watcher.IncludeSubdirectories = $true $watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite -bor ` [System.IO.NotifyFilters]::FileName -bor ` [System.IO.NotifyFilters]::DirectoryName -bor ` [System.IO.NotifyFilters]::Size $watcher.EnableRaisingEvents = $true $null = Register-ObjectEvent $watcher Created -SourceIdentifier Sync.Created $null = Register-ObjectEvent $watcher Changed -SourceIdentifier Sync.Changed $null = Register-ObjectEvent $watcher Renamed -SourceIdentifier Sync.Renamed $null = Register-ObjectEvent $watcher Deleted -SourceIdentifier Sync.Deleted 主循环用 Wait-Event 泵取事件，而不是靠 -Action 自动执行（主线程阻塞时事件动作不会触发）：\nwhile ($true) { $e = Wait-Event -Timeout 1 if ($null -eq $e) { continue } if ($null -ne $e.SourceEventArgs -and (Should-Ignore $e.SourceEventArgs.FullPath)) { Remove-Event -EventIdentifier $e.EventIdentifier continue } Remove-Event -EventIdentifier $e.EventIdentifier Start-Sleep -Milliseconds $DebounceMs # 防抖，合并连续写入 while ($null -ne (Wait-Event -Timeout 0)) { Remove-Event -EventIdentifier (Get-Event).EventIdentifier } Sync-Now } 9.3 定时拉取脚本设计（change_fetcher_generic.ps1） 1）间隔读取\n$SettingsFile = Join-Path $env:USERPROFILE \u0026#39;git_mirror_settings.txt\u0026#39; $IntervalMinutes = 60 if (Test-Path $SettingsFile) { Get-Content $SettingsFile | ForEach-Object { if ($_ -match \u0026#39;^interval\\s*=\\s*(\\d+)$\u0026#39;) { $IntervalMinutes = [int]$Matches[1] } } } 2）配置解析\n每一行是一条镜像记录，格式：目标目录|远端地址|分支|密钥路径：\nfunction Get-Mirrors { Get-Content $ConfigFile -Encoding UTF8 | ForEach-Object { $line = $_.Trim() if ($line -eq \u0026#39;\u0026#39; -or $line.StartsWith(\u0026#39;#\u0026#39;)) { return } $parts = $line -split \u0026#39;\\|\u0026#39; if ($parts.Count -ge 3) { $item = @{ Dir = $parts[0]; Url = $parts[1]; Branch = $parts[2]; Key = $null } if ($parts.Count -ge 4 -and $parts[3]) { $item.Key = $parts[3] } $item } } } 3）单条更新\n每条记录先选密钥（记录里指定，没有就用默认），再克隆或更新：\nfunction Update-Mirror { param($m) $key = if ($m.Key) { $m.Key } else { $DefaultKey } $env:GIT_SSH_COMMAND = \u0026#34;ssh -i `\u0026#34;$($key -replace \u0026#39;\\\\\u0026#39;,\u0026#39;/\u0026#39;)`\u0026#34; -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new\u0026#34; if (-not (Test-Path (Join-Path $m.Dir \u0026#39;.git\u0026#39;))) { git clone $m.Url $m.Dir } else { git -C $m.Dir fetch origin $m.Branch git -C $m.Dir reset --hard \u0026#34;origin/$($m.Branch)\u0026#34; git -C $m.Dir clean -fd } } 4）主循环\nwhile ($true) { Start-Sleep -Seconds ($IntervalMinutes * 60) foreach ($m in Get-Mirrors) { Update-Mirror $m } } 9.4 设计上的几个关键决定 退出码检查优先：所有 git 操作都检查 $LASTEXITCODE，失败必须写日志，绝不静默吞掉。\n防抖合并：一次保存可能触发十几个事件，等 3 秒合并成一次同步，避免重复提交。\n密钥可配置：多账号、多平台时，每条镜像记录可以指定自己的密钥。\n无口令密钥：服务在后台无人值守运行，带口令的密钥无法自动使用。\nsafe.directory：LocalSystem 身份访问用户仓库必须配置，否则 git 直接拒绝。\n日志轮转：NSSM 配置 AppRotateBytes，日志超过 1MB 自动轮转，不会无限膨胀。\n10. 遇到的问题与解决方法（最重要的关键部分） 先给一张速查表，后面每一节再展开详细过程：\n问题 一句话解决 bat 中文乱码 bat 用 ASCII + CRLF + chcp 65001，中文交给带 BOM 的 PowerShell SSH 私钥带口令 自动化必须使用无口令专用密钥 dubious ownership 服务环境变量加 safe.directory GitHub 端口 22 被拦 改用 SSH over 443 服务“已标记删除”（1072） 先停服务，再重装或彻底重建 定时拉取间隔写死 菜单 [F] 设置间隔，保存后自动重启服务 FileSystemWatcher 不触发 用 Wait-Event 循环泵取事件 服务 PAUSED 用正确配置重装服务 10.1 bat 中文乱码 现象：双击 bat 报 '姟' 不是内部或外部命令。\n原因：bat 是 UTF-8，cmd 默认按 GBK 解析；且换行不是 CRLF 会解析错位。\n解决：bat 内容保持纯 ASCII + CRLF 换行 + 顶部 chcp 65001；中文界面放进带 UTF-8 BOM 的 PowerShell 脚本。\n图：bat 中文乱码的实际报错\n10.2 SSH 私钥带口令，自动化服务无法使用 现象：手动 SSH 能连（交互输入口令），但服务里一直 Permission denied (publickey)，ssh -v 显示 Server accepts key 却仍失败。\n原因：私钥加了 passphrase，服务在非交互环境无法输入口令。\n解决：生成无口令的专用密钥给服务用。\n10.3 git 的 “dubious ownership” 安全保护 现象：服务以 LocalSystem 运行，git 报 fatal: detected dubious ownership in repository。\n原因：git 的 CVE-2022-24765 缓解机制——不同用户身份操作他人拥有的仓库时拒绝执行。\n解决：把仓库加入 safe.directory 白名单（本地配置 + 服务环境变量）。最终统一用 safe.directory=*（单用户本机可接受）。\n10.4 GitHub 端口 22 被拦截 现象：ssh -T git@github.com 报 Connection closed by 198.18.0.15。\n原因：代理/过滤工具拦截了 GitHub 的 22 端口。\n解决：改用 SSH over 443（ssh.github.com:443）。\n10.5 服务“已标记为删除”（错误 1072） 现象：服务能运行但改不了配置。\n原因：对运行中的服务执行卸载，Windows 标记为删除待定。\n解决：先停服务完成删除，再重新安装；工具包里的“彻底重建”就是干这个的。\n10.6 定时拉取间隔写死 现象：想改拉取频率要改脚本。\n解决：菜单加 [F] 设置定时拉取间隔，保存到设置文件，自动重启服务生效。\n10.7 FileSystemWatcher 实时监听不触发 现象：Register-ObjectEvent -Action 的事件动作不执行。\n原因：PowerShell 事件动作需要事件引擎泵处理，主线程阻塞（Sleep/WaitOne）时不触发。\n解决：改用 Wait-Event 在循环中泵取事件。\n10.8 服务 PAUSED（启动失败循环） 现象：服务状态 PAUSED，脚本一启动就报“未配置远程仓库”。\n原因：服务配置错误（AppDirectory 错、缺 safe.directory）→ 脚本以 SYSTEM 运行读不到仓库 → 启动即退出 → NSSM 反复重启。\n解决：用正确配置重装。\n11. 安全设计：主仓库只推送 + 镜像区 11.1 为什么这样设计 自动同步最容易出的事故是：云端被改坏，反过来把本地也覆盖了。比如云端仓库被误删、误提交、被恶意改动，如果本地一直跟着 pull，坏内容会直接冲进你的工作目录。\n这套系统用“双向分离”来解决：\n主仓库（本地工作区） → 只推送（push） → 云端永远不覆盖本地 镜像区（独立目录） → 只拉取（pull） → 云端当前状态的可读副本 一句话：主仓库是“源头”，镜像区是“影子”。\n11.2 两条线分别怎么做 1）主仓库：只推送，不做 pull\n自动推送脚本里只有：\ngit add -A git commit -m \u0026#34;auto-sync: 时间戳\u0026#34; git push origin \u0026lt;分支\u0026gt; 脚本中刻意不放 pull / fetch / merge，从代码上保证云端永远进不了主仓库。云端被改坏、误删、甚至整个仓库被清空，本地主仓库都安然无恙。\n2）镜像区：只拉取，云端是唯一权威\n镜像区用独立目录承载，不在主仓库目录内部：\n主仓库 D:\\我的项目 镜像区 D:\\云端镜像\\我的项目（或 change / change_github 等独立目录） 定时拉取时执行：\ngit fetch origin \u0026lt;分支\u0026gt; git reset --hard origin/\u0026lt;分支\u0026gt; # 强制等于云端 git clean -fd # 清理未跟踪文件 镜像区可以随时删除重建，它只反映云端当前状态，本身不承担“备份”职责。\n11.3 安全边界（一定要分清） 区域 行为 谁覆盖谁 丢了怎么办 主仓库 只 push 本地 → 云端 云端有历史可找回 镜像区 只 pull 云端 → 本地 删除后下次自动重建 云端仓库 双向收 两端 依靠 Git 历史恢复 三个容易误会的点：\n镜像区不是备份：它会被 reset --hard 覆盖，里面的本地改动会被清掉，不能当保险箱用。\n主仓库的“保险”在云端：只要推送成功过，云端 Git 历史就保留了每一版提交，本地误删也能从云端找回。\n真备份建议：重要项目额外定期做一次完整克隆，或启用平台的仓库备份功能。\n11.4 配套安全措施 密钥隔离：自动同步使用专用无口令密钥，与手动登录钥匙分开；镜像记录可逐条指定密钥。\nsafe.directory：服务以 LocalSystem 运行时，用环境变量放行仓库目录，避免 git 安全保护误拦。\n日志不污染仓库：同步日志写入仓库但被 .gitignore 忽略；NSSM 服务日志开启轮转。\n可关闭清理：GIT_MIRROR_CLEAN=0 可关闭镜像区的 git clean -fd，防止误删你放在镜像里的文件。\n11.5 结论 “主仓库只推送 + 镜像区只拉取”这个设计，用很小的代价换来了最大的安全感：本地不会被云端带坏，云端坏了对本地也无从下手。 所有“看云端最新”的需求都走镜像区，所有“保存本地成果”的需求都走主仓库推送，职责清清楚楚。\n12. 验证结果 下面我把测试结果发给大家看一下：\n图：自动推送成功，云端出现 auto-sync 提交\n实测清单：\n自动推送 ✅：本地测试目录写入文件 → 云端仓库自动出现 auto-sync 提交。\n定时拉取 ✅：云端改动 → 本地镜像目录按设置间隔同步。\n开机自启 ✅：重启电脑后服务自动运行，无需手动启动。\n崩溃自动重启 ✅：进程异常退出后由 NSSM 自动拉起。\n图：定时拉取验证成功\n我发现测试目录自动上传成功。现在这个本地仓库正式具有了：自动同步 + 自我修复 + 开机自启 + 远端定时把变化拉取到本地。它对任务的执行效率和处理稳定性都达到了预期。我梦寐以求的工具，在 agent 的辅助之下直接搞定了。我当时看到这个结果还是异常兴奋的，因为它意味着我的想法从空想变为实际。中间虽然有些曲折，虽然经过了 OpenClaw 的 NSSM 服务部署才把想法彻底定性，但结果是一步一个脚印走出来的，是好的。\n13. 下载与体验 这套工具已经打包成 zip 版 和 安装版 exe，源码与发布文件在：\nGitHub Releases（最新版下载）：Git 自动同步工具包 · Releases\nGitHub 源码目录：linux_heima/my_data/发布\nGitee（可选）：Gitee 仓库\n工具包目录结构：\nGit自动同步工具包/ ├── 环境自检.bat 检查 Git / SSH / nssm ├── 生成SSH密钥.bat 一键生成密钥并复制公钥 ├── 新建自动推送仓库.bat 本地 → Gitee/GitHub 自动推送 ├── 新建云端镜像拉取.bat 云端 → 本地立即克隆/更新 ├── 安装定时拉取服务.bat 定时拉取服务一键安装 ├── 服务管理.bat 日常管理菜单 ├── 修复服务.bat / 彻底重建服务.bat ├── README.md / 使用说明.txt / docs 教程 └── LICENSE 环境要求：Windows 10 / 11，安装 Git，SSH 密钥已添加到 Gitee/GitHub。\n使用顺序：\n双击 环境自检.bat 检查 Git / SSH / nssm。\n双击 生成SSH密钥.bat 生成密钥并复制公钥。\n双击 新建自动推送仓库.bat 或 新建云端镜像拉取.bat 建立同步任务。\n双击 安装定时拉取服务.bat 开启定时拉取。\n之后所有日常操作都通过 服务管理.bat 完成。\n提示：安装版 exe 未做商业签名，Windows 智能应用控制可能拦截，优先使用 zip 版（解压即用）。\n14. 总结与展望 写到最后，我想先说一句真实感受：这个工具包从最初“一堆脚本手动控制”，到如今“一个安装包 + 一个管理菜单”就能完成所有操作，整个过程确实给了我很大的成就感。\n经过我在自己电脑上的完整测试，自动推送、定时拉取、崩溃自动重启、开机自启这些核心功能都验证通过，功能也算比较完善了。虽然整个过程中我没有写过核心代码，主要是由 agent（AI）辅助实现，但需求是我提的、方向是我定的、每一步结果都是我亲自验证的。所以当它真正自己跑起来的那一刻，那种“原来我也可以做出来”的兴奋感，是真实存在的。\n这段经历让我最大的体会是：技术不是目的，把想法变成能用的东西才是。\n如果你心里也有什么想法，哪怕只是“文件不想手动推送”“机器人挂了要自动重启”这样的小事，都可以借助 agent 把它变成一个落地的项目。一次完整的“想法 → 实现 → 验证 → 发布”，对未来的自己是一种实打实的积累，也会慢慢建立起“我能把事情做成”的信心。\n也想对还不了解 agent 的朋友说一句：不用等“学会编程”再开始。CSDN 的 Inscode、Codex 这类 AI 工具都可以尝试；有些工具可以通过配置模型供应商（例如搭配模型切换工具接入 DeepSeek 等 API）来使用，具体按工具说明操作即可。重要的是先迈出第一步，让 AI 帮你把想法跑起来，再一步步验证和打磨。\n如果越来越多的人愿意把想法变成实际的东西，整个社会的创新氛围会越来越好，国家也会因此更加强大。哪怕只是一个个人小工具，也值得认真做、认真写下来、认真分享出去。\n最后，把这几天的收获浓缩成一句话与大家共勉：\n想法是起点，行动是过程，坚持验证才是真正的落地。\n项目仓库：源码与发布文件见 Git 自动同步工具包（GitHub）\n说明：本文由作者提出需求、检查验证并确定改进方向，文字由 AI 辅助整理完成。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/ai-%E8%BE%85%E5%8A%A9%E4%B8%8Bwindows-%E4%B8%8A%E8%AE%A9-git-%E8%87%AA%E5%8A%A8%E5%90%8C%E6%AD%A5nssm-%E6%9C%8D%E5%8A%A1-+-%E5%AE%9E%E6%97%B6%E7%9B%91%E5%90%AC%E5%AE%9E%E6%88%98/","summary":"\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1__8\"\u003e1. 引言\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#11__Git__10\"\u003e1.1 自动化 Git 的介绍\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#12__33\"\u003e1.2 传统手动推送痛点\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#13__44\"\u003e1.3 我为何需要自动同步\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2__70\"\u003e2. 如何想到这个方案\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#21_Linux__72\"\u003e2.1 背景：Linux 学习带来的启发\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#22__OpenClaw__NSSM__80\"\u003e2.2 一段经历：用 OpenClaw 部署 NSSM 服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#221__82\"\u003e2.2.1 原因\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#222__95\"\u003e2.2.2 解决问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#223__116\"\u003e2.2.3 开始执行步骤\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_1_118\"\u003e步骤 1：实施步骤流程\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_2_134\"\u003e步骤 2：注册服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_3_145\"\u003e步骤 3：配置服务账户（第一次踩坑）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_4_LocalSystem_177\"\u003e步骤 4：关键一步：给 LocalSystem“指路”\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_5_190\"\u003e步骤 5：启动与验证\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_6_204\"\u003e步骤 6：总结\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__220\"\u003e3. 思路与方案定型\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4__230\"\u003e4. 需求分析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5__244\"\u003e5. 验证方案可行性\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#6__252\"\u003e6. 列出可行的方案\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#7__269\"\u003e7. 项目实施的大纲\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#8__290\"\u003e8. 详细步骤\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#81__292\"\u003e8.1 一次性准备\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#811__294\"\u003e8.1.1 在本地建一个仓库\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#812__Git_298\"\u003e8.1.2 安装 Git\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#813_SSH__304\"\u003e8.1.3 SSH 准备\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_306\"\u003e生成无口令密钥\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_312\"\u003e公钥添加到平台\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#GitHub__443__323\"\u003eGitHub 走 443 端口\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#82__331\"\u003e8.2 底层一步一步是怎么实现的\u003c/a\u003e\u003c/p\u003e","title":"Windows 上让 Git 自动同步：NSSM 服务 + 实时监听实战"},{"content":"大家好，我是一个对于技术绝对热爱的人，其实呢我我对于技术学习有着强烈而浓厚的热爱，尤其是对于C语言的学习，在学习C当中，我决定必须用每日的坚持来践实我对于他的热爱，具体是每天抽出大约3个小时左右时间来阅读C方面的书籍，以及呢我同时注意维护我的gitee，使其成为我简历上亮眼的一部分，说到最后了我决定必须为进入像百度这样的大公司而奋发努力\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E5%8D%9A%E5%AE%A2%E6%97%A5%E8%AE%B0%E4%B8%8E%E6%88%91%E7%9A%84%E7%9C%8B%E6%B3%95/","summary":"\u003cp\u003e大家好，我是一个对于技术绝对热爱的人，其实呢我我对于技术学习有着强烈而浓厚的热爱，尤其是对于C语言的学习，在学习C当中，我决定必须用每日的坚持来践实我对于他的热爱，具体是每天抽出大约3个小时左右时间来阅读C方面的书籍，以及呢我同时注意维护我的gitee，使其成为我简历上亮眼的一部分，说到最后了我决定必须为进入像百度这样的大公司而奋发努力\u003c/p\u003e","title":"博客日记与我的看法"},{"content":"磁盘的使用以及挂载\n文章目录 一.准备工作\n二.查看新添加磁盘\n三.分区\n四.格式化\n五.挂载\n1.临时挂载\n2.长期挂载\n六.重启并查看\n一.准备工作 在这一步当中，我们应当先关闭虚拟机，从设置里面添加一块磁盘，并设置合理的磁盘大小,随后呢，我们重新开启虚拟机就可以了，并登录mx,进行下一步操作\n二.查看新添加磁盘 我们在这一步当中使用lsblk 查看新添加的磁盘\nlsblk #查看新添加的磁盘 三.分区 fdisk /dev/sdb 然后，我们使用n来追加磁盘\n注意，再最后一个扇区的时候我们也可以指定其特定大小+20G也是可以的\n我们要使用w保存信息\n我们再次使用命令df -h 查看分区\n四.格式化 格式化，我们使用的是mkfs.格式\n#格式化所支持的格式有mkfs.ext4 是⼀个⽤来格式化分区为 EXT4 ⽂件系统 的命令。EXT4（第四代扩展⽂件系统）是 #Linux 系统中使⽤最⼴泛的⽂件系统类型，兼具性能、可靠性和兼容性，适⽤于⼤多数场景。 #XFS 是⼀种⾼性能的⽇志⽂件系统，⼴泛⽤于企业级场景，特别是对⼤⽂件和⾼并发 I/O 的处理有 #明显优势。它是 CentOS Stream 9 等现代 Linux 发⾏版的默认⽂件系统类型 mkfs.xfs /dev/sdb 五.挂载 这一步，比较麻烦了，需要修改配置文件,修改的方式有两种\n1.临时挂载 我们使用mount进行临时挂载\nmount /dev/sdb1 /mnt/sdb1 mount /dev/sdb2 /mnt/sdb2 2.长期挂载 uuid修改 如果我们需要一直挂载，就需要修改配置文件，/etc/fstab\nblkid /dev/sdb1 blkid /dev/sdb2 先在，我们得到了他们的uuid，然后打开配置文件，进行修改就可以了\n磁盘路径修改 除了，这种方式还有利用路径挂载的\n在磁盘挂载里面使用mount的话进行挂载是临时的，下一次开机挂载点是会消失的，因此我们需要使用命令blkid /dev/sdb1以及blkid /dev/sdb2的，然后我们把得到的uuid写入到/etc/fstab里面就可以了，就像上图一样的，然后我们保存文件，需要注意的是，这个配置是在下一次会生效的，本次是无法生效的，因此我们需要重启服务器，此时我们再次查看挂载点，发现是可以的，变成永久了,上图一样的。\n六.重启并查看 我们通过重启服务器，查看，发现已经成功挂载了\n还有一点就是如果想要取消这种永久的的挂载点的话，我们是可以把配置文件/etc/fstab里面之前添加的两行给删掉就可以的，然后我们重启服务器，发现此时开机是不显示sdb1 和sdb2这两个挂载点的\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8A%E6%8C%82%E8%BD%BD/","summary":"\u003cp\u003e磁盘的使用以及挂载\u003c/p\u003e\n\u003ch4 id=\"文章目录\"\u003e文章目录\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_3\"\u003e一.准备工作\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_9\"\u003e二.查看新添加磁盘\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_21\"\u003e三.分区\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_51\"\u003e四.格式化\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_63\"\u003e五.挂载\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_67\"\u003e1.临时挂载\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_76\"\u003e2.长期挂载\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_106\"\u003e六.重启并查看\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一准备工作\"\u003e一.准备工作\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e在这一步当中，我们应当先关闭虚拟机，从设置里面添加一块磁盘，并设置合理的磁盘大小,随后呢，我们重新开启虚拟机就可以了，并登录mx,进行下一步操作\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"二查看新添加磁盘\"\u003e二.查看新添加磁盘\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e我们在这一步当中使用lsblk 查看新添加的磁盘\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elsblk  #查看新添加的磁盘\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8A%E6%8C%82%E8%BD%BD/images/0a99a6fc298341e9a3b5cc8b878f6d21.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"三分区\"\u003e三.分区\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efdisk /dev/sdb\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8A%E6%8C%82%E8%BD%BD/images/4f9b3037f8e041279e65509f0090bd81.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e然后，我们使用n来追加磁盘\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8A%E6%8C%82%E8%BD%BD/images/69de7f653f474ada8fbe882760c9884e.png\"\u003e\u003c/p\u003e\n\u003cp\u003e注意，再最后一个扇区的时候我们也可以指定其特定大小+20G也是可以的\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8A%E6%8C%82%E8%BD%BD/images/ed070ba2ef934f5a80de0302d402764c.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e我们要使用w保存信息\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8A%E6%8C%82%E8%BD%BD/images/6a2aa4daa1ab49708199f3963dfae6f9.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e我们再次使用命令df -h 查看分区\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"四格式化\"\u003e四.格式化\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e格式化，我们使用的是mkfs.格式\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#格式化所支持的格式有mkfs.ext4 是⼀个⽤来格式化分区为 EXT4 ⽂件系统 的命令。EXT4（第四代扩展⽂件系统）是\n#Linux 系统中使⽤最⼴泛的⽂件系统类型，兼具性能、可靠性和兼容性，适⽤于⼤多数场景。\n#XFS 是⼀种⾼性能的⽇志⽂件系统，⼴泛⽤于企业级场景，特别是对⼤⽂件和⾼并发 I/O 的处理有\n#明显优势。它是 CentOS Stream 9 等现代 Linux 发⾏版的默认⽂件系统类型\nmkfs.xfs /dev/sdb\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"五挂载\"\u003e五.挂载\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e这一步，比较麻烦了，需要修改配置文件,修改的方式有两种\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"1临时挂载\"\u003e1.临时挂载\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e我们使用mount进行临时挂载\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003emount /dev/sdb1 /mnt/sdb1\nmount /dev/sdb2 /mnt/sdb2\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2长期挂载\"\u003e2.长期挂载\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003euuid修改\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ccode\u003e如果我们需要一直挂载，就需要修改配置文件，/etc/fstab\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eblkid /dev/sdb1\nblkid /dev/sdb2\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8A%E6%8C%82%E8%BD%BD/images/f18c10da20b64c668081bb8480ce2afa.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e先在，我们得到了他们的uuid，然后打开配置文件，进行修改就可以了\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E4%BD%BF%E7%94%A8%E4%BB%A5%E5%8F%8A%E6%8C%82%E8%BD%BD/images/4805e4fe84664eb48b248f5b152242c5.png\"\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e磁盘路径修改\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ccode\u003e除了，这种方式还有利用路径挂载的\u003c/code\u003e\u003c/p\u003e","title":"磁盘的使用以及挂载"},{"content":"磁盘的综合案例 1.修改网络配置\n1.改成88网段\n情况一：全局迁移至 88 网段（推荐彻底解决）\n第一步：修改 VMware 底层配置\n第二步：修改虚拟机内部配置\n第三步：验证连通性\n情况二：临时恢复连接（最省事）\n2.ssh免密登录\n0.对于hosts文件进行修改\n1.生成私钥和公钥\n2.发送公钥\n3.测试一下\n2.DNS配置\n1.要求\n2.配置\n1.正解析文件的配置\n3.反解析文件配置\n3.测试\n4.开放端口\n5.开启服务\n6.修改客户端dns\n7.测试\n3. 日志的配置\n1. 要求\n2. 修改rsyslog.conf\n3. 重启服务与放行端口\n4.测试\n4.磁盘配置\n0.需求\n1.添加一个100G大容量磁盘\n2.添加到物理卷\n2.添加到卷组\n3.添加到逻辑卷\n4.格式化分区的格式\n5.挂载到指定路径\n6.添加剩下的15G\n1.设置让扩容生效\n2.查看是否配置生效\n1.修改网络配置 我们这里修改网络配置其实需要，进行对于ip的更改以及对于ssh的相关配置，ip要以此修改为192.168.88.170 171 172\n1.改成88网段 注意。这里可能我使用88网段的话，还需要对于网卡进行修改，因为我虚拟机的之前的网段其实是149网段，如果不修改的话，会导致ping的时候没有反应，这是我第一次操作时遇到的一个问题，这里写出来给大家说明一下 这个问题是因为 VMware 的虚拟交换机（VMnet8）底层网段 与 虚拟机操作系统内部配置的 IP 网段 不一致导致的。只改虚拟机内部 IP 而不改 VMware 配置，数据包是发不出去的。\n情况一：全局迁移至 88 网段（推荐彻底解决） 如果你想把所有机器都规范到 88 网段，请按以下步骤操作：\n第一步：修改 VMware 底层配置 在 Windows 宿主机上打开 VMware，点击顶部菜单栏的 编辑 → 虚拟网络编辑器。\n选中左侧的 VMnet8 (NAT 模式)。\n点击右下角的 更改设置（需管理员权限）。\n将 子网 IP 从 192.168.149.0 修改为 192.168.88.0，子网掩码保持 255.255.255.0。\n点击旁边的 NAT 设置，确认网关 IP 已自动变为 192.168.88.2（如果不是请手动修正）。\n点击 DHCP 设置，将地址池起始范围调整到 88 网段（例如 192.168.88.128 ~ 192.168.88.254）。\n点击 应用，等待服务重启。\n第二步：修改虚拟机内部配置 启动虚拟机，进入系统。\n编辑网卡配置文件（以 CentOS/RedHat 为例）：\nvi /etc/sysconfig/network-scripts/ifcfg-ens33 修改以下关键字段： BOOTPROTO=static IPADDR=192.168.88.170 NETMASK=255.255.255.0 GATEWAY=192.168.88.2 DNS1=8.8.8.8 ONBOOT=yes 将上述 IPADDR 替换为每台机器对应的规划 IP（170 / 171 / 172）。\n重启网络服务： systemctl restart network 或重启虚拟机：\nreboot 第三步：验证连通性 在 Windows 宿主机上执行：\nping 192.168.88.170 ping 192.168.88.171 ping 192.168.88.172 全部能通即说明配置成功。\n情况二：临时恢复连接（最省事） 如果你只是想快速让环境通起来，不想折腾 VMware 的设置，最简单的方法是**“妥协”**：\n直接将那台连不上的虚拟机 IP 改回 149 网段（例如 192.168.149.170），只要它回到 VMware 当前设定的网段，宿主机立刻就能连通。\nip addr add 192.168.149.170/24 dev ens33 因此，我采用的也是最省事的方案，修改主机号就可以了\n2.ssh免密登录 0.对于hosts文件进行修改 我们在这一步操作的时候，需要注意的是，应当对于/etc/hosts文件进行修改，把三个主机的ip记录添加进去，用映射关系表述 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.149.170 node1 node1.itcast.cn 192.168.149.171 node2 node2.itcast.cn 192.168.149.172 node3 node3.itcast.cn 随后我们测试一下连通性如何 这里我们发现是可以的，配制太大的问题,但是需要注意的是我们需要服务和网卡都重启一下，这样配置才会生效\n1.生成私钥和公钥 我们先再宿主机上面生成对应的公钥与私钥，然后将公钥发送给远端\nssh-keygen -f ~/.ssh/id_rsa -P \u0026#39;\u0026#39; -q 其他两台主机同理，也是如此操作的\n2.发送公钥 ssh-copy-id node1 #发送公钥给node1 ssh-copy-id node2 #发送公钥给node2 ssh-copy-id node3 #发送公钥给node3 3.测试一下 我发现ssh免密登录也是配置好了，这台虚拟机网络部分解决了\n2.DNS配置 1.要求 是用node2作为dns服务器，node1和node3作为客户端，域名要求是web.internal.local 是node1的ip，而log.internal.local则是node3的ip ns1.internal.local和inter.local则是node2的\n2.配置 注意，之前我们安装过这个dns软件包bind-utils,下面我就把需要注意的部分写出来，其他或是和之前配置的重复或是没有写的必要,主配置文件和区域声明文件因为之前用的就是这个网段,所以我就不需要重新配置这几个文件了\n1.正解析文件的配置 $TTL 86400 @ IN SOA ns1.internal.local. admin.internal.local. ( 2024011701 ; Serial 3600 ; Refresh 1800 ; Retry 1209600 ; Expire 86400 ) ; Minimum TTL IN NS ns1.internal.local. ns1 IN A 192.168.149.171 web IN A 192.168.149.170 log IN A 192.168.149.172 @ IN A 192.168.149.171 3.反解析文件配置 $TTL 86400 @ IN SOA ns1.internal.local. admin.internal.local. ( 2024011701 ; Serial 3600 ; Refresh 1800 ; Retry 1209600 ; Expire 86400 ) ; Minimum TTL IN NS ns1.internal.local. 171 IN PTR ns1 170 IN PTR web 172 IN PTR log 171 IN PTR @ 3.测试 4.开放端口 firewall-cmd --permanent --add-service=dns firewall-cmd --reload 5.开启服务 systemctl start named systemctl status named 6.修改客户端dns vim /etc/NetworkManager/system-connections/ens33.nmconnection #打开配置文件 [ipv4] address1=192.168.149.171/24 dns=192.168.149.171; gateway=192.168.149.2 method=manual #这里只需要修改这个部分的dns最好将dns只保留一个，否则容易解析失败， #我可能dns后面呢跟的太多了，之前解析失败了 其余两台也需要这样改，注意，node2也需要进行修改自己指向自己 修改完之后，将服务和网卡都重新启动一下，然后随后进行测试\n7.测试 #在三台主机分别输入 ping web.internal.local; ping log.internal.local;i ping nternal.local. 3. 日志的配置 1. 要求 要求：node1和node2作为客户端，将info级别的日志发送到node3\n2. 修改rsyslog.conf #注意这里是单个转发不是所有日志转发不用转发模块 vim /etc/rsyslog.conf *.info;mail.none;authpriv.none;cron.none action(type=\u0026#34;omfwd\u0026#34; protocol=\u0026#34;tcp\u0026#34; target=\u0026#34;log.internal.local\u0026#34; port=\u0026#34;514\u0026#34;) #注意，node2也是如此修改的 #node3也是修改这个文件不过需要将tcp放开 module(load=\u0026#34;imtcp\u0026#34;) # needs to be done just once input(type=\u0026#34;imtcp\u0026#34; port=\u0026#34;514\u0026#34;) 这个是node3的图片 3. 重启服务与放行端口 systemctl restart rsyslog systemctl status rsyslog #重启服务 firewall-cmd --add-port 514/tcp --permanent firewall-cmd --reload firewall-cmd --list-all #放行端口 4.测试 #在node1和node2分别输入logger -p info \u0026#39;111\u0026#39; 4.磁盘配置 0.需求 我们需要搞一个lvm，大小为30G\n1.添加一个100G大容量磁盘 我需要在虚拟机针对node1新增一块磁盘，大小就为100G\n2.添加到物理卷 我们使用lsblk查看新增的磁盘，然后使用pvcreate进而增加到物理卷\npvcreate /dev/sdb 2.添加到卷组 之后我们把这个物理卷添加到卷组，就可以了\nvgcreate vg_logs /dev/sdb # 可选 -s vgdisplay #查看卷组信息 3.添加到逻辑卷 #我们先加15G的逻辑卷之后再扩容15G lvcreate -L +15GB -n lv_logdata vg_logs lvdisplay 4.格式化分区的格式 mkfs.xfs /dev/vg_logs/lv_logdata 5.挂载到指定路径 1.创建挂载路径 mkdir -p /var/log 2.开始挂载 mount /dev/vg_logs/lv_logdata /var/log/ 3设置永久挂载 先打开fstab文件把挂载信息添加到文件\nvim /etc/fstab /dev/mapper/cs_192-root / xfs defaults 0 0 UUID=2fac3fef-f7ab-4148-ab3a-56b65bd8dc8c /boot xfs defaults 0 0/dev/mapper/cs_192-swap none swap defaults 0 0/dev/mapper/vg_logs-lv_logdata /var/log xfs defaults 0 0 3.开机测试一下是否文件设置成功 df -h 6.添加剩下的15G lvextend -L +15GB /dev/vg_logs/lv_logdata 1.设置让扩容生效 xfs_growfs /dev/vg_logs/lv_logdata 2.查看是否配置生效 df -h ","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E7%A3%81%E7%9B%98%E7%9A%84%E7%BB%BC%E5%90%88%E6%A1%88%E4%BE%8B/","summary":"\u003ch4 id=\"磁盘的综合案例\"\u003e磁盘的综合案例\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_1\"\u003e1.修改网络配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#188_3\"\u003e1.改成88网段\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_88__7\"\u003e情况一：全局迁移至 88 网段（推荐彻底解决）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_VMware__11\"\u003e第一步：修改 VMware 底层配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_21\"\u003e第二步：修改虚拟机内部配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_55\"\u003e第三步：验证连通性\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_69\"\u003e情况二：临时恢复连接（最省事）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2ssh_80\"\u003e2.ssh免密登录\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#0hosts_81\"\u003e0.对于hosts文件进行修改\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_97\"\u003e1.生成私钥和公钥\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_105\"\u003e2.发送公钥\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_111\"\u003e3.测试一下\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2DNS_116\"\u003e2.DNS配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_117\"\u003e1.要求\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_119\"\u003e2.配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_121\"\u003e1.正解析文件的配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_138\"\u003e3.反解析文件配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_157\"\u003e3.测试\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_159\"\u003e4.开放端口\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_165\"\u003e5.开启服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#6dns_171\"\u003e6.修改客户端dns\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#7_187\"\u003e7.测试\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__197\"\u003e3. 日志的配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1__198\"\u003e1. 要求\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_rsyslogconf_200\"\u003e2. 修改rsyslog.conf\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__215\"\u003e3. 重启服务与放行端口\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_226\"\u003e4.测试\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_233\"\u003e4.磁盘配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#0_234\"\u003e0.需求\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1100G_237\"\u003e1.添加一个100G大容量磁盘\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_239\"\u003e2.添加到物理卷\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_245\"\u003e2.添加到卷组\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_253\"\u003e3.添加到逻辑卷\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_261\"\u003e4.格式化分区的格式\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_266\"\u003e5.挂载到指定路径\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#615G_293\"\u003e6.添加剩下的15G\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_299\"\u003e1.设置让扩容生效\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_306\"\u003e2.查看是否配置生效\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1修改网络配置\"\u003e1.修改网络配置\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e我们这里修改网络配置其实需要，进行对于ip的更改以及对于ssh的相关配置，ip要以此修改为192.168.88.170 171 172\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"1改成88网段\"\u003e1.改成88网段\u003c/h3\u003e\n\u003cp\u003e注意。这里可能我使用88网段的话，还需要对于网卡进行修改，因为我虚拟机的之前的网段其实是149网段，如果不修改的话，会导致ping的时候没有反应，这是我第一次操作时遇到的一个问题，这里写出来给大家说明一下\n这个问题是因为 \u003cstrong\u003eVMware 的虚拟交换机（VMnet8）底层网段\u003c/strong\u003e 与 \u003cstrong\u003e虚拟机操作系统内部配置的 IP 网段\u003c/strong\u003e 不一致导致的。只改虚拟机内部 IP 而不改 VMware 配置，数据包是发不出去的。\u003c/p\u003e\n\u003ch4 id=\"情况一全局迁移至-88-网段推荐彻底解决\"\u003e情况一：全局迁移至 88 网段（推荐彻底解决）\u003c/h4\u003e\n\u003cp\u003e如果你想把所有机器都规范到 88 网段，请按以下步骤操作：\u003c/p\u003e","title":"磁盘的综合案例"},{"content":"磁盘管理 1.文件系统\n常见文件系统对比\n一、主流文件系统横向对比\n2.常用的磁盘管理命令\n2.1 查看磁盘与分区信息\n1. lsblk —— 查看块设备（推荐首选）\n2. fdisk —— 查看分区表\n3. parted —— 高级分区查看\n4. blkid —— 查看 UUID 和文件系统类型\n5. 其他硬件信息\n2.2 分区管理命令\n1. fdisk —— MBR 分区（交互式）\n2. gdisk —— GPT 分区（交互式）\n3. parted —— 高级分区工具\n4. 刷新内核分区表\n2.3 文件系统格式化\n1. 创建文件系统\n2. 格式化时指定参数\n3. 查看文件系统信息\n2.4 挂载与卸载\n1. 临时挂载\n2. 卸载\n3. 查看挂载信息\n2.5 磁盘空间监控\n1. 查看磁盘使用情况\n2. 查看目录大小\n3. 找出占用空间最大的目录（实战排障）\n2.6 LVM 逻辑卷管理\n1. 物理卷（PV）\n2. 卷组（VG）\n3. 逻辑卷（LV）\n4. 扩容后刷新文件系统\n2.7 磁盘 I/O 性能排查\n1. iostat —— 磁盘 I/O 统计\n2. iotop —— 按进程查看 I/O\n3. 其他 I/O 排查工具\n2.8 磁盘故障排查\n1. SMART 健康检测\n2. 文件系统修复\n3. 坏道检测\n2.9 SWAP 交换分区管理\n1. 查看 SWAP\n2. 创建 SWAP 分区\n3. 永久生效（写入 fstab）\n4. 调整 SWAP 使用策略\n2.10 fstab 配置文件详解\n1. 文件格式\n2. 示例\n3. 常用挂载选项\n4. 安全验证 fstab\n📌 速查口诀\n3.磁盘分区操作\n1.准备工作\n2.GPT分区\n1.查看磁盘情况\n2.使用parted命令开始分区操作\n1.创建GPT分区表\n2.创建一个新分区\n3.查看分区情况\n3.设置交换内存\n1.分配4G的分区大小\n2.使用mkswap格式化分区\n3.设置swapon启动交换分区\n4.查看交换分区大小\n1.文件系统 文件系统的主要作用是将存储设备（如硬盘、SSD）上的物理空间组织为逻辑单元，提供数据存储、检索和管理的标准方法。一般文件系统主要由超级块（Superblock）、inode、数据块、目录结构构成的\n常见文件系统对比 一、主流文件系统横向对比 文件系统 所属系统 最大单文件 最大分区 核心特点 优点 缺点 典型场景 ext4 Linux 16TB 1EB Linux 默认文件系统 稳定、成熟、兼容性好、支持日志 不支持快照、COW、校验 大多数 Linux 发行版默认 xfs Linux 8EB 8EB SGI 开发的高性能文件系统 并行 I/O 强、大文件读写快、在线扩容 不能缩容、修复工具较弱 服务器、大数据（RHEL/CentOS 默认） btrfs Linux 16EB 16EB 写时复制（COW）现代化文件系统 支持快照、子卷、压缩、校验、RAID 性能略低、成熟度不如 ext4 需要快照/高级特性的场景 ext3 Linux 2TB 16TB ext4 的前身 稳定 性能低、功能少 老系统兼容 FAT32 跨平台 4GB 2TB 最广泛兼容的格式 几乎所有设备都能读 单文件 ≤4GB、无权限、无日志 U 盘、移动设备 exFAT 跨平台 16EB 128PB FAT32 的现代继任者 单文件超大、跨平台 无日志、无权限 大容量 U 盘、SD 卡 NTFS Windows 16EB 256TB Windows 现代文件系统 支持权限、加密、压缩、日志 Linux 下写操作稳定性略差 Windows 系统盘、与 Win 共享数据 APFS macOS — — Apple 的现代文件系统 快照、加密、空间共享 仅 macOS Mac 电脑 ZFS 跨平台 16EB 256ZB 企业级文件系统（COW+卷管理） 数据完整性校验、快照、RAID-Z、压缩 内存占用高、Linux 下许可协议有争议 企业存储、NAS（TrueNAS） HFS+ macOS — — APFS 前身 — 老旧 老 Mac 单位换算：EB = Exabyte (10¹⁸)，PB = Petabyte (10¹⁵)，TB = Terabyte (10¹²)\n2.常用的磁盘管理命令 2.1 查看磁盘与分区信息 1. lsblk —— 查看块设备（推荐首选） lsblk # 树状显示所有磁盘和分区 lsblk -f # 显示文件系统类型和 UUID lsblk -d -o NAME,SIZE,ROTA # 查看磁盘是否为 SSD（ROTA=0 为 SSD） lsblk -o NAME,SIZE,TYPE,MOUNTPOINT # 自定义输出列 2. fdisk —— 查看分区表 sudo fdisk -l # 查看所有磁盘分区表 sudo fdisk -l /dev/sda # 查看指定磁盘分区表 3. parted —— 高级分区查看 sudo parted -l # 查看所有磁盘分区（同时支持 MBR/GPT） sudo parted /dev/sda print # 查看指定磁盘详细信息 4. blkid —— 查看 UUID 和文件系统类型 sudo blkid # 查看所有分区 UUID 和文件系统类型 sudo blkid /dev/sda1 # 查看指定分区 5. 其他硬件信息 sudo hdparm -I /dev/sda # 查看硬盘硬件信息 sudo dmidecode -t disk # 查看磁盘 DMI 信息 ls -la /dev/disk/by-uuid/ # 查看 UUID 软链接 2.2 分区管理命令 1. fdisk —— MBR 分区（交互式） sudo fdisk /dev/sdb # 进入 fdisk 交互界面 # 常用交互命令： # p → 打印分区表 # n → 新建分区 # d → 删除分区 # t → 修改分区类型（82=swap, 83=linux, 8e=lvm, ef=EFI） # w → 保存并退出 # q → 不保存退出 # g → 创建 GPT 分区表 # o → 创建 MBR 分区表 2. gdisk —— GPT 分区（交互式） sudo gdisk /dev/sdb # 进入 gdisk 交互界面 # 常用交互命令与 fdisk 类似 # 新建分区后需刷新内核分区表： sudo partprobe /dev/sdb 3. parted —— 高级分区工具 sudo parted /dev/sdb (parted) mklabel gpt # 创建 GPT 分区表 (parted) mklabel msdos # 创建 MBR 分区表 (parted) mkpart primary xfs 0% 50% # 创建占前半磁盘的主分区 (parted) mkpart primary xfs 50% 100% # 创建占后半磁盘的主分区 (parted) print # 打印分区表 (parted) rm 2 # 删除第 2 个分区 (parted) quit 4. 刷新内核分区表 sudo partprobe /dev/sdb # 通知内核重新读取分区表 sudo partx -a /dev/sdb # 添加分区到内核 sudo partx -d /dev/sdb # 从内核删除分区信息 2.3 文件系统格式化 1. 创建文件系统 sudo mkfs.ext4 /dev/sdb1 # 格式化为 ext4 sudo mkfs.xfs /dev/sdb1 # 格式化为 xfs sudo mkfs.btrfs /dev/sdb1 # 格式化为 btrfs sudo mkfs.vfat -F 32 /dev/sdb1 # 格式化为 FAT32 sudo mkfs.exfat /dev/sdb1 # 格式化为 exFAT sudo mkfs.ntfs /dev/sdb1 # 格式化为 NTFS 2. 格式化时指定参数 # ext4：指定卷标和 inode 大小 sudo mkfs.ext4 -L data_disk -I 256 /dev/sdb1 # xfs：指定块大小和标签 sudo mkfs.xfs -L data_disk -b size=4096 /dev/sdb1 # 强制格式化（无需确认） sudo mkfs.ext4 -F /dev/sdb1 3. 查看文件系统信息 sudo dumpe2fs /dev/sdb1 | head -30 # 查看 ext 系列超级块信息 sudo xfs_info /dev/sdb1 # 查看 xfs 文件系统信息 sudo btrfs filesystem show /dev/sdb1 # 查看 btrfs 信息 2.4 挂载与卸载 1. 临时挂载 sudo mount /dev/sdb1 /mnt/data # 挂载到目录 sudo mount -t xfs /dev/sdb1 /mnt/data # 指定文件系统类型 sudo mount -o ro /dev/sdb1 /mnt/data # 只读挂载 sudo mount -o rw,noexec,nosuid /dev/sdb1 /mnt/data # 指定安全选项 sudo mount -a # 挂载 fstab 中所有未挂载的设备 2. 卸载 sudo umount /mnt/data # 按挂载点卸载 sudo umount /dev/sdb1 # 按设备卸载 sudo umount -l /mnt/data # 懒卸载（lazy，等设备空闲后卸载） sudo umount -f /mnt/data # 强制卸载 3. 查看挂载信息 mount # 查看所有挂载 findmnt # 树状显示挂载信息 findmnt /mnt/data # 查看指定挂载点 cat /proc/mounts # 查看内核挂载表 2.5 磁盘空间监控 1. 查看磁盘使用情况 df -h # 人类可读格式显示磁盘使用 df -hT # 显示文件系统类型 df -i # 查看 inode 使用情况 df -h --total # 显示总计行 2. 查看目录大小 du -sh /path/to/dir # 查看目录总大小 du -sh /* # 查看根目录下各目录大小 du -h --max-depth=1 /var # 查看 /var 下一级目录大小 du -ah /path | sort -rh | head -20 # 找出最大的 20 个文件/目录 3. 找出占用空间最大的目录（实战排障） # 找出根分区下占用最高的目录 du -sh /* 2\u0026gt;/dev/null | sort -rh | head -10 # 逐层深入定位\u0026#34;磁盘满\u0026#34;的元凶 du -sh /var/* 2\u0026gt;/dev/null | sort -rh | head -10 du -sh /var/log/* 2\u0026gt;/dev/null | sort -rh | head -10 2.6 LVM 逻辑卷管理 1. 物理卷（PV） sudo pvcreate /dev/sdb1 # 创建物理卷 sudo pvcreate /dev/sdb1 /dev/sdc1 # 同时创建多个 PV pvs # 查看物理卷（简洁） pvdisplay # 查看物理卷（详细） pvremove /dev/sdb1 # 删除物理卷 2. 卷组（VG） sudo vgcreate vg_data /dev/sdb1 # 创建卷组 sudo vgcreate vg_data /dev/sdb1 /dev/sdc1 # 多个 PV 加入卷组 vgs # 查看卷组（简洁） vgdisplay # 查看卷组（详细） sudo vgextend vg_data /dev/sdc1 # 向卷组添加新 PV sudo vgreduce vg_data /dev/sdb1 # 从卷组移除 PV 3. 逻辑卷（LV） sudo lvcreate -L 10G -n lv_data vg_data # 从卷组创建 10G 逻辑卷 sudo lvcreate -l 100%FREE -n lv_data vg_data # 使用卷组全部剩余空间 lvs # 查看逻辑卷（简洁） lvdisplay # 查看逻辑卷（详细） sudo lvextend -L +5G /dev/vg_data/lv_data # 扩容 5G sudo lvextend -l +100%FREE /dev/vg_data/lv_data # 扩容到最大 sudo lvremove /dev/vg_data/lv_data # 删除逻辑卷 4. 扩容后刷新文件系统 # ext4 扩容后刷新 sudo resize2fs /dev/vg_data/lv_data # xfs 扩容后刷新 sudo xfs_growfs /mnt/data 2.7 磁盘 I/O 性能排查 1. iostat —— 磁盘 I/O 统计 iostat -x 1 # 每 1 秒刷新，显示扩展统计 iostat -x 1 5 # 每 1 秒刷新，共 5 次 iostat -d -k 1 # 以 KB 为单位显示磁盘吞吐 关键指标解读：\n指标 含义 告警阈值 %util 磁盘利用率 持续 \u0026gt;80% 说明瓶颈 await I/O 平均等待时间(ms) \u0026gt;20ms 说明慢 r/s, w/s 每秒读/写请求数 视磁盘类型而定 rkB/s, wkB/s 每秒读/写吞吐量 接近磁盘极限则瓶颈 2. iotop —— 按进程查看 I/O sudo iotop # 实时显示每个进程的 I/O 占用 sudo iotop -o # 只显示有 I/O 活动的进程 sudo iotop -a # 累积模式 3. 其他 I/O 排查工具 sudo pidstat -d 1 # 每 1 秒显示进程 I/O 统计 cat /proc/diskstats # 内核磁盘统计原始数据 2.8 磁盘故障排查 1. SMART 健康检测 sudo smartctl -a /dev/sda # 查看完整 SMART 信息 sudo smartctl -H /dev/sda # 快速查看健康状态（PASS/FAIL） sudo smartctl -t short /dev/sda # 执行短时自检 sudo smartctl -t long /dev/sda # 执行长时自检 sudo smartctl -l selftest /dev/sda # 查看自检结果 2. 文件系统修复 # ⚠️ 修复前必须先卸载！ sudo umount /dev/sdb1 # ext 系列修复 sudo fsck.ext4 -y /dev/sdb1 sudo e2fsck -f -y /dev/sdb1 # 强制检查并自动修复 # xfs 修复 sudo xfs_repair /dev/sdb1 sudo xfs_repair -L /dev/sdb1 # 强制清零日志后修复（危险） # 查看文件系统错误计数 sudo dumpe2fs -h /dev/sdb1 | grep \u0026#34;FS Error\u0026#34; 3. 坏道检测 sudo badblocks -v /dev/sdb # 扫描坏道（只读模式） sudo badblocks -wsv /dev/sdb # 读写模式扫描（⚠️ 会破坏数据！） 2.9 SWAP 交换分区管理 1. 查看 SWAP swapon -s # 查看当前 SWAP 设备 free -h # 查看内存和 SWAP 使用情况 2. 创建 SWAP 分区 # 方法一：使用独立分区 sudo mkswap /dev/sdb2 # 格式化为 SWAP sudo swapon /dev/sdb2 # 启用 SWAP # 方法二：使用文件（更灵活） sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 # 创建 2G 文件 sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile 3. 永久生效（写入 fstab） # /etc/fstab 添加： UUID=xxxx swap swap defaults 0 0 # 或文件方式： /swapfile swap swap defaults 0 0 4. 调整 SWAP 使用策略 cat /proc/sys/vm/swappiness # 查看当前值（默认 60） sudo sysctl vm.swappiness=10 # 临时降低（值越小越倾向于用物理内存） # 永久生效：写入 /etc/sysctl.conf echo \u0026#34;vm.swappiness=10\u0026#34; | sudo tee -a /etc/sysctl.conf 2.10 fstab 配置文件详解 1. 文件格式 # /etc/fstab 每行 6 个字段： # \u0026lt;设备\u0026gt; \u0026lt;挂载点\u0026gt; \u0026lt;文件系统\u0026gt; \u0026lt;挂载选项\u0026gt; \u0026lt;dump\u0026gt; \u0026lt;fsck顺序\u0026gt; 2. 示例 # 用 UUID 挂载（推荐） UUID=1234-5678-90ab /mnt/data xfs defaults 0 2 # 用设备名挂载（不推荐，设备名可能变化） /dev/sdb1 /mnt/backup ext4 defaults 0 2 # 网络文件系统 server:/share /mnt/nfs nfs defaults 0 0 # 绑定挂载（将同一目录挂到两个位置） /mnt/data /srv/data none bind 0 0 3. 常用挂载选项 选项 含义 defaults rw,suid,dev,exec,auto,nouser,async 的组合 noatime 不更新访问时间，提升性能 nodiratime 不更新目录访问时间 noexec 不允许执行二进制文件（安全加固） nosuid 忽略 setuid/setgid 位（安全加固） nodev 不解释字符/块设备（安全加固） ro 只读挂载 usrquota,grpquota 启用用户/组磁盘配额 4. 安全验证 fstab sudo mount -a # 挂载 fstab 中所有未挂载的设备（不重启） sudo mount -fav # 模拟执行（dry-run），不真正挂载 📌 速查口诀 查磁盘用 lsblk，看分区用 fdisk -l 分完区要 partprobe，格式化用 mkfs.xfs 临时挂用 mount，永久写在 fstab 看空间用 df -h，找大文件用 du -sh LVM 三步曲：pvcreate → vgcreate → lvcreate 扩容完别忘 resize2fs 或 xfs_growfs 磁盘慢用 iostat -x 1，找进程用 iotop\n3.磁盘分区操作 1.准备工作 首先，我应该先关闭虚拟机，在设置里面追加20GB的磁盘空间，之后打开虚拟机\n2.GPT分区 我打开虚拟机之后，使用lsblk命令查看磁盘的情况\nlsblk 1.查看磁盘情况 ``通过观察，我知道，刚刚新增的磁盘应该就是sdc了\n2.使用parted命令开始分区操作 parted /dev/sdc 1.创建GPT分区表 使用mklabel创建分区表\nmklabel gpt mktable gpt 2.创建一个新分区 mkpart primary xfs 1MiB 100% #注意这个开头至少填的是1MB，中间我们可以随便写的，只要保证 最后是100%即可 3.查看分区情况 print lsblk 最后我们使用rm 1删除磁盘，接下来呢，我们直接分区5个看看\nmkpart primary xfs 1MiB 20% mkpart primary xfs 20% 40% mkpart primary xfs 40% 60% mkpart primary xfs 60% 80% mkpart primary xfs 80% 100% 注意，就是不能使用rm 1 2 3 4 5 直接删掉，这样做，只会把1号盘干掉 3.设置交换内存 1.分配4G的分区大小 2.使用mkswap格式化分区 mkswap 3.设置swapon启动交换分区 swapon /dev/sdc1 4.查看交换分区大小 这个是之前的没有增加交换分区的时候\n这个是增加交换分区的情况，发现增加了4G，也就是说这个操作成功了.\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E7%A3%81%E7%9B%98%E7%AE%A1%E7%90%86%E7%9A%84%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/","summary":"\u003ch4 id=\"磁盘管理\"\u003e磁盘管理\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_1\"\u003e1.文件系统\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_3\"\u003e常见文件系统对比\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_5\"\u003e一、主流文件系统横向对比\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_22\"\u003e2.常用的磁盘管理命令\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#21__25\"\u003e2.1 查看磁盘与分区信息\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_lsblk___27\"\u003e1. lsblk —— 查看块设备（推荐首选）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_fdisk___36\"\u003e2. fdisk —— 查看分区表\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_parted___43\"\u003e3. parted —— 高级分区查看\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_blkid___UUID__50\"\u003e4. blkid —— 查看 UUID 和文件系统类型\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5__57\"\u003e5. 其他硬件信息\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#22__67\"\u003e2.2 分区管理命令\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_fdisk__MBR__69\"\u003e1. fdisk —— MBR 分区（交互式）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_gdisk__GPT__85\"\u003e2. gdisk —— GPT 分区（交互式）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_parted___95\"\u003e3. parted —— 高级分区工具\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4__109\"\u003e4. 刷新内核分区表\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#23__119\"\u003e2.3 文件系统格式化\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1__121\"\u003e1. 创建文件系统\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2__132\"\u003e2. 格式化时指定参数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__145\"\u003e3. 查看文件系统信息\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#24__155\"\u003e2.4 挂载与卸载\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1__157\"\u003e1. 临时挂载\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2__167\"\u003e2. 卸载\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__176\"\u003e3. 查看挂载信息\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#25__187\"\u003e2.5 磁盘空间监控\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1__189\"\u003e1. 查看磁盘使用情况\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2__198\"\u003e2. 查看目录大小\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3__207\"\u003e3. 找出占用空间最大的目录（实战排障）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#26_LVM__220\"\u003e2.6 LVM 逻辑卷管理\u003c/a\u003e\u003c/p\u003e","title":"磁盘管理的相关知识"},{"content":"非齐次方程当中的代入数值问题 一.遇到的问题\n0.0那个数学公式在梨米特讲义的位置\n0.1那个公式在梨米特讲义上的例题\n0.2那道数学公式在同济课本的地方\n1.那道题目\n2.我算的结果和答案给的区别\n1.我算的结果\n2.答案给的是这样子的\n二.解决的方案\n1.我的错误分析\n2.测试的结果分析\n1.首先是在网站symbol这个网站测试的结果，如下，大家有兴趣可以试一下。\n2.然后答案给的看一下\n3.分析结果\n三.我的建议\n一.遇到的问题 首先我在写数学题的时候，遇到了一个问题，就是我用非齐次方程的代入数值的公式进行操作的时候，遇到了，一个问题，就是，我用公式算的竟然和答案给的不一样，当时我就有一点困惑，然后，我一点一点研究，探明这个公式的使用范围。\n0.0那个数学公式在梨米特讲义的位置 0.1那个公式在梨米特讲义上的例题 0.2那道数学公式在同济课本的地方 1.那道题目 2.我算的结果和答案给的区别 1.我算的结果 2.答案给的是这样子的 二.解决的方案 当时我在淘宝问了一下，然后，我就知道我的问题在哪里了，\n1.我的错误分析 通过和淘宝上的那位老师探究，我明白了，我的问题正是没有将二阶导的系数化为1，所导致的问题，老师，说，你可以方程俩边都除以2，将2阶导的系数化为1，就可以就解决问题了，我一想这个形式不正是和书上给的形式一样吗？那结果一个是对的，然后我就用symbol这个网站测试了一下，下面是测试结果。\n2.测试的结果分析 1.首先是在网站symbol这个网站测试的结果，如下，大家有兴趣可以试一下。 2.然后答案给的看一下 3.分析结果 通过这些结果我们很清楚的看到，通过把二阶导的系数化为1，算出来是对的\n三.我的建议 通过以上的分析相信大家都认识到了，这个公式的适用范围，那就是二阶导的系数必须为1，其他的倒是没有要求。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/","summary":"\u003ch4 id=\"非齐次方程当中的代入数值问题\"\u003e非齐次方程当中的代入数值问题\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_1\"\u003e一.遇到的问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#00_4\"\u003e0.0那个数学公式在梨米特讲义的位置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#01_8\"\u003e0.1那个公式在梨米特讲义上的例题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#02_13\"\u003e0.2那道数学公式在同济课本的地方\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_18\"\u003e1.那道题目\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_20\"\u003e2.我算的结果和答案给的区别\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_21\"\u003e1.我算的结果\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_23\"\u003e2.答案给的是这样子的\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_26\"\u003e二.解决的方案\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_28\"\u003e1.我的错误分析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_30\"\u003e2.测试的结果分析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1symbol_31\"\u003e1.首先是在网站symbol这个网站测试的结果，如下，大家有兴趣可以试一下。\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_33\"\u003e2.然后答案给的看一下\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_35\"\u003e3.分析结果\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_37\"\u003e三.我的建议\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一遇到的问题\"\u003e一.遇到的问题\u003c/h2\u003e\n\u003cp\u003e首先我在写数学题的时候，遇到了一个问题，就是我用非齐次方程的代入数值的公式进行操作的时候，遇到了，一个问题，就是，我用公式算的竟然和答案给的不一样，当时我就有一点困惑，然后，我一点一点研究，探明这个公式的使用范围。\u003c/p\u003e\n\u003ch3 id=\"00那个数学公式在梨米特讲义的位置\"\u003e0.0那个数学公式在梨米特讲义的位置\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/5d230e8d0e1f4933ae887ea03abd272e.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/100a52cb58ed4ccca1bb533e77dded34.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"01那个公式在梨米特讲义上的例题\"\u003e0.1那个公式在梨米特讲义上的例题\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/27762776699a4a6f8b4b90f0ee344fc9.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/56d470883fea4a55b880a4be5af441fe.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"02那道数学公式在同济课本的地方\"\u003e0.2那道数学公式在同济课本的地方\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/5dd57640c7b24ad28154cb37395b8fa2.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/b0ef15ab9fc1472bab2d77fd7861c1f0.png\"\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/b2b05bc72cde4aa2ac55360e34165245.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"1那道题目\"\u003e1.那道题目\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/3bbd06245b1143ba8e7a22c28ee944e9.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"2我算的结果和答案给的区别\"\u003e2.我算的结果和答案给的区别\u003c/h3\u003e\n\u003ch4 id=\"1我算的结果\"\u003e1.我算的结果\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/d07ee4b0c6eb41cf8a56dad33fa46308.png\"\u003e\u003c/p\u003e\n\u003ch4 id=\"2答案给的是这样子的\"\u003e2.答案给的是这样子的\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/87deddfe4bc8479eb00a02cc80df3e69.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"二解决的方案\"\u003e二.解决的方案\u003c/h2\u003e\n\u003cp\u003e当时我在淘宝问了一下，然后，我就知道我的问题在哪里了，\u003c/p\u003e\n\u003ch3 id=\"1我的错误分析\"\u003e1.我的错误分析\u003c/h3\u003e\n\u003cp\u003e通过和淘宝上的那位老师探究，我明白了，我的问题正是没有将二阶导的系数化为1，所导致的问题，老师，说，你可以方程俩边都除以2，将2阶导的系数化为1，就可以就解决问题了，我一想这个形式不正是和书上给的形式一样吗？那结果一个是对的，然后我就用symbol这个网站测试了一下，下面是测试结果。\u003c/p\u003e\n\u003ch3 id=\"2测试的结果分析\"\u003e2.测试的结果分析\u003c/h3\u003e\n\u003ch4 id=\"1首先是在网站symbol这个网站测试的结果如下大家有兴趣可以试一下\"\u003e1.首先是在网站symbol这个网站测试的结果，如下，大家有兴趣可以试一下。\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/be077d78062647098f5200b9e871c307.png\"\u003e\u003c/p\u003e\n\u003ch4 id=\"2然后答案给的看一下\"\u003e2.然后答案给的看一下\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E9%AB%98%E6%95%B0%E5%AD%A6%E4%B9%A0%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%B3%BB%E6%95%B0%E9%9D%9E%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%E7%9A%84%E9%97%AE%E9%A2%98/images/bee85b53074f45afa8d6cb2fff2c9833.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"3分析结果\"\u003e3.分析结果\u003c/h3\u003e\n\u003cp\u003e通过这些结果我们很清楚的看到，通过把二阶导的系数化为1，算出来是对的\u003c/p\u003e\n\u003ch2 id=\"三我的建议\"\u003e三.我的建议\u003c/h2\u003e\n\u003cp\u003e通过以上的分析相信大家都认识到了，这个公式的适用范围，那就是二阶导的系数必须为1，其他的倒是没有要求。\u003c/p\u003e","title":"高数学习中的常系数非齐次线性微分方程的问题"},{"content":"关于ftp与SELinux的冲突问题 1.问题背景\n2.遇到的问题\n3.问题解决的方法\n1.ai的方法\n为什么本地能写、FTP 不能写？\n嫌疑一：write_enable 没开（最常见！）\n嫌疑二：SELinux 拦住了 ftpd\n兜底：看日志，让服务器自己告诉你原因\n建议的排查顺序\n2.我看出了什么\n第 2 步：临时关掉 SELinux 做隔离测试\n第 3 步：回客户端 lftp 重试\n看结果分支判断\n3.我的解决方案\n4.给我们的警示\n1.问题背景 首先这个问题诞生于如此条件下，我在跟随佳乐老师创建完lisi，zhangsan，wangwu之后呢，我就用lisi作为用户登录服务器node1，这里面设置了禁锢操作，路径是/export/public，而public里面拥有2个文件夹.一个是logs，另一个是src，同时设置了黑白名单,张三设置在黑名单，王五和李四设置在白名单\n2.遇到的问题 1.遇到的问题其实就是，在我用lisi把客户端的根目录下的a.log，发送给服务器的logs目录，无法操作 2.后来，我在logs目录下创建文件夹也是无法操作的，也是显示权限不足 但是呢，我在服务器的这个logs目录下创建文件夹是可以创建的 3.就很奇怪，再去服务器是可以搞定的，是可以创建文件夹的，但是在客户端是没有办法进行创建的，我当时以为是logs的权限设置的不对，用的是root创建，权限大的缘故，但是其实并不是如此的,后面我在服务器用了lisi照样是可以创建的 4.后来，我仔细检查了这些文件夹的权限，并再次赋权,这次显然也是不行的 3.问题解决的方法 解决的办法，后来，我把我的问题，这些比较奇怪的地方，我问了千文，后来，在他的帮助之下我成功了，在客户端我成功的在服务器的logs上面创建了文件夹，这意味着什么呢，显然这意味着，我可以把客户端家目录的a.log上传给服务器的logs里面，结果显然和我想的一样 下面是千文告诉我的解决方案，让我一步一步排查，最后成功找到了问题关键所咋\n1.ai的方法 这个对比信息非常关键！它直接帮我们排除了文件系统权限问题。\n为什么本地能写、FTP 不能写？ 本质区别在于执行写入的\u0026quot;身份\u0026quot;不同：\n场景 执行者 受什么约束 本地 mkdir lisi 自己的 shell 进程 只看文件系统的 rwx 权限 ✅ FTP mkdir vsftpd 守护进程代为执行 受 vsftpd.conf 开关 + SELinux 对 ftpd 的策略双重约束 ❌ 所以本地能写，完全不能证明 FTP 也能写。问题 100% 卡在 FTP 守护进程这一层。下面两个是头号嫌疑。\n嫌疑一：write_enable 没开（最常见！） vsftpd 的 write_enable 默认是 NO。如果配置文件里没显式写 write_enable=YES，那么所有写操作（上传、mkdir、删除、改名）一律拒绝，正好对应你看到的 533 和 550。而它不影响读和 ls，所以你能登录、能列目录。\ngrep -i write_enable /etc/vsftpd/vsftpd.conf 如果没有这一行，或显示 write_enable=NO → 就是它！ 修复：\necho \u0026#34;write_enable=YES\u0026#34; \u0026gt;\u0026gt; /etc/vsftpd/vsftpd.conf systemctl restart vsftpd 嫌疑二：SELinux 拦住了 ftpd SELinux 是针对守护进程上下文（ftpd_t） 做限制的，本地 shell 不受这套规则管，所以同样会出现\u0026quot;本地行、FTP 不行\u0026quot;。\n最快验证法——临时关掉 SELinux 测一次：\ngetenforce # 先看状态，多半是 Enforcing setenforce 0 # 临时切到宽容模式 然后不要重启 vsftpd，直接在客户端 lftp 里再 mkdir 一次：\n如果成功了 → 确认是 SELinux，永久解决： setsebool -P ftpd_full_access on setenforce 1 # 记得开回去 如果还是失败 → 不是 SELinux，回到嫌疑一。 兜底：看日志，让服务器自己告诉你原因 # vsftpd / PAM 的拒绝记录 tail -n 30 /var/log/secure # SELinux 的拒绝记录（最有价值） ausearch -m avc -ts recent | grep ftp # 或 cat /var/log/audit/audit.log | grep -i denied | tail 日志里会写明到底是 write_enable 拒绝，还是 SELinux 的 avc: denied。\n建议的排查顺序 先 grep write_enable /etc/vsftpd/vsftpd.conf，没有就加上并重启 → 重试\n还不行就 setenforce 0 重试 → 判断是否 SELinux\n都不行就看 /var/log/secure 和 ausearch 的报错贴给我\n补充一句：你之前把 chroot 根目录 /export/public 设成 777 的话，记得要么改回 755，要么在配置里加 allow_writeable_chroot=YES，否则某些版本会直接拒绝登录。不过你现在能登录能 cd，说明这一项暂时没卡你，先聚焦上面两点。\n把第 1 步 grep 的结果和第 2 步 setenforce 0 后的现象告诉我，基本就能一锤定音。\n2.我看出了什么 看了千文的描述，我知道了，大概率就是 SELinux 的权限问题，解决方案，千文也告诉我了\n`Enforcing` —— 这就对上了！SELinux 正处于**强制模式**，而它对 `ftpd` 守护进程的写操作是单独设防的，本地 shell 完全不受这套规则管，所以才会出现\u0026#34;服务器本地能 mkdir、客户端 FTP 不能\u0026#34;的典型症状。再加上你的目录在 `/export/public` 这种**自定义路径**下，SELinux 给它的文件类型往往不是 FTP 可写的类型，被拦几乎是必然的。 下面按顺序做，逻辑上能一次性把\u0026#34;开关\u0026#34;和\u0026#34;SELinux\u0026#34;两个变量都隔离清楚。 ## 第 1 步：先确认写开关是开的（避免误判） ```bash grep -i write_enable /etc/vsftpd/vsftpd.conf 如果没有这一行或显示 write_enable=NO → 补上并重启： echo \u0026#34;write_enable=YES\u0026#34; \u0026gt;\u0026gt; /etc/vsftpd/vsftpd.conf systemctl restart vsftpd 如果已经是 write_enable=YES → 不用动，直接下一步。 为什么先查它：写要成功必须 write_enable 和 SELinux 同时放行。若这个开关没开，下一步关 SELinux 也救不活，会让你误以为\u0026quot;不是 SELinux 的锅\u0026quot;。\n第 2 步：临时关掉 SELinux 做隔离测试 setenforce 0 # 切到宽容模式，立即生效，无需重启 vsftpd getenforce # 应显示 Permissive 第 3 步：回客户端 lftp 重试 lftp lisi@192.168.149.128 cd /logs mkdir 222 看结果分支判断 ✅ 如果这次成功了 → 元凶确认含 SELinux。做永久修复（二选一）：\n# 方案A：放开 ftpd 的整体访问（最省事，覆盖大多数场景） setsebool -P ftpd_full_access on # 方案B：只给目标目录打上\u0026#34;FTP可读写\u0026#34;的上下文（更精细、更安全） semanage fcontext -a -t public_content_rw_t \u0026#34;/export/public/logs(/.*)?\u0026#34; semanage fcontext -a -t public_content_rw_t \u0026#34;/export/public/src(/.*)?\u0026#34; restorecon -Rv /export/public # 修完务必把 SELinux 开回去验证 setenforce 1 开回 Enforcing 后再到客户端测一次，能写才算真正解决。\n❌ 如果 setenforce 0 后仍然失败 → 不是 SELinux（或不止它），去看日志，让服务器自报原因：\ntail -n 30 /var/log/secure ausearch -m avc -ts recent 2\u0026gt;/dev/null | grep -i ftp ⚠️ 提醒：setenforce 0 是临时的，机器一重启就恢复 Enforcing，所以验证完一定要执行上面的永久修复，别让它一直裸奔在宽容模式。\n请把 **第 1 步 grep` 的输出** 和 第 3 步关 SELinux 后客户端的现象 发给我，基本就能一锤定音了。\n3.我的解决方案 显然，我是按照setenforce 0 之后，重启服务器，然后就可以了 4.给我们的警示 给我们的启发其实就是，在学习Linux过程之中，人工智能永远是你的最好的老师，只要你将自己的问题发给他，跟着他的提示一步一步进行排查，然后将排查得到的结果发送给ai，那么他将会告诉你一个中肯的建议，一般都是成功的，这个 SELinux 确实挺麻烦的，当时我以为是我的配置文件出错了，后来呢，我不断尝试，修改配置文件，但是依然没有成功，我才在千文的指导之下明白了他的存在。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E5%85%B3%E4%BA%8Eftp%E4%B8%8Eselinux%E7%9A%84%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/","summary":"\u003ch4 id=\"关于ftp与selinux的冲突问题\"\u003e关于ftp与SELinux的冲突问题\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_3\"\u003e1.问题背景\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_5\"\u003e2.遇到的问题\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_18\"\u003e3.问题解决的方法\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1ai_21\"\u003e1.ai的方法\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#FTP__28\"\u003e为什么本地能写、FTP 不能写？\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#write_enable__41\"\u003e嫌疑一：\u003ccode\u003ewrite_enable\u003c/code\u003e 没开（最常见！）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#SELinux__ftpd_59\"\u003e嫌疑二：SELinux 拦住了 ftpd\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_78\"\u003e兜底：看日志，让服务器自己告诉你原因\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_93\"\u003e建议的排查顺序\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_102\"\u003e2.我看出了什么\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_2__SELinux__124\"\u003e第 2 步：临时关掉 SELinux 做隔离测试\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_3__lftp__131\"\u003e第 3 步：回客户端 lftp 重试\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_139\"\u003e看结果分支判断\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_169\"\u003e3.我的解决方案\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_174\"\u003e4.给我们的警示\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1问题背景\"\u003e1.问题背景\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e首先这个问题诞生于如此条件下，我在跟随佳乐老师创建完lisi，zhangsan，wangwu之后呢，我就用lisi作为用户登录服务器node1，这里面设置了禁锢操作，路径是/export/public，而public里面拥有2个文件夹.一个是logs，另一个是src，同时设置了黑白名单,张三设置在黑名单，王五和李四设置在白名单\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"2遇到的问题\"\u003e2.遇到的问题\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e1.遇到的问题其实就是，在我用lisi把客户端的根目录下的a.log，发送给服务器的logs目录，无法操作\u003c/strong\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%85%B3%E4%BA%8Eftp%E4%B8%8Eselinux%E7%9A%84%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/images/31d543a0e0d74a2facd0b57caacd8359.png\"\u003e\n\u003cstrong\u003e2.后来，我在logs目录下创建文件夹也是无法操作的，也是显示权限不足\u003c/strong\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%85%B3%E4%BA%8Eftp%E4%B8%8Eselinux%E7%9A%84%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/images/55c072190ed842009aa2962c3868c3fc.png\"\u003e\n但是呢，我在服务器的这个logs目录下创建文件夹是可以创建的\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%85%B3%E4%BA%8Eftp%E4%B8%8Eselinux%E7%9A%84%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/images/96f60e65512b4a38be2d95aa4d888fd8.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e3.就很奇怪，再去服务器是可以搞定的，是可以创建文件夹的，但是在客户端是没有办法进行创建的，我当时以为是logs的权限设置的不对，用的是root创建，权限大的缘故，但是其实并不是如此的,后面我在服务器用了lisi照样是可以创建的\u003c/code\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%85%B3%E4%BA%8Eftp%E4%B8%8Eselinux%E7%9A%84%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/images/5596133494e8476a96424bbf30353246.png\"\u003e\n4.\u003ccode\u003e后来，我仔细检查了这些文件夹的权限，并再次赋权,这次显然也是不行的\u003c/code\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%85%B3%E4%BA%8Eftp%E4%B8%8Eselinux%E7%9A%84%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/images/ed81cf0aff1743229e25473bf7b232d7.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"3问题解决的方法\"\u003e3.问题解决的方法\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e解决的办法，后来，我把我的问题，这些比较奇怪的地方，我问了千文，后来，在他的帮助之下我成功了，在客户端我成功的在服务器的logs上面创建了文件夹，这意味着什么呢，显然这意味着，我可以把客户端家目录的a.log上传给服务器的logs里面，结果显然和我想的一样\u003c/code\u003e\n\u003cstrong\u003e下面是千文告诉我的解决方案，让我一步一步排查，最后成功找到了问题关键所咋\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"1ai的方法\"\u003e1.ai的方法\u003c/h3\u003e\n\u003cp\u003e这个对比信息非常关键！它直接帮我们\u003cstrong\u003e排除了文件系统权限问题\u003c/strong\u003e。\u003c/p\u003e\n\u003ch3 id=\"为什么本地能写ftp-不能写\"\u003e为什么本地能写、FTP 不能写？\u003c/h3\u003e\n\u003cp\u003e本质区别在于\u003cstrong\u003e执行写入的\u0026quot;身份\u0026quot;不同\u003c/strong\u003e：\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e场景\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e执行者\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e受什么约束\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e本地 mkdir\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003elisi 自己的 shell 进程\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e只看文件系统的 rwx 权限 ✅\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eFTP mkdir\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003evsftpd 守护进程代为执行\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e受 vsftpd.conf 开关 + SELinux 对 ftpd 的策略双重约束 ❌\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e所以本地能写，\u003cstrong\u003e完全不能证明\u003c/strong\u003e FTP 也能写。问题 100% 卡在 FTP 守护进程这一层。下面两个是头号嫌疑。\u003c/p\u003e","title":"关于ftp与SELinux的冲突问题"},{"content":"我是谁 我是 dsduyopg，目前是一名大专在读学生，也在餐厅工作。业余时间我自学 Linux、运维和自动化，喜欢把想法做成真实能用的东西。\n我在做什么 维护这个博客，记录 Linux、运维、自动化和我自己的学习过程 用 AI 辅助开发了一套 Telegram 双备份系统，自动备份博客、代码和课件 做了一个 CSDN 博客本地化工具，把文章和图片一键保存到本地 正在学习 Linux 运维，目标是转行到运维方向 这个博客 本站使用 Hugo + PaperMod 构建，部署在 GitHub Pages 和 Cloudflare Pages。\n部分文章由 AI 辅助创作，但需求设计、测试、排障和验收都由我自己完成。\n联系我 GitHub: https://github.com/dsduyopg CSDN: https://blog.csdn.net/m0_74369759 ","permalink":"https://dsduyopg-github-io.pages.dev/about/","summary":"\u003ch2 id=\"我是谁\"\u003e我是谁\u003c/h2\u003e\n\u003cp\u003e我是 dsduyopg，目前是一名大专在读学生，也在餐厅工作。业余时间我自学 Linux、运维和自动化，喜欢把想法做成真实能用的东西。\u003c/p\u003e\n\u003ch2 id=\"我在做什么\"\u003e我在做什么\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e维护这个博客，记录 Linux、运维、自动化和我自己的学习过程\u003c/li\u003e\n\u003cli\u003e用 AI 辅助开发了一套 Telegram 双备份系统，自动备份博客、代码和课件\u003c/li\u003e\n\u003cli\u003e做了一个 CSDN 博客本地化工具，把文章和图片一键保存到本地\u003c/li\u003e\n\u003cli\u003e正在学习 Linux 运维，目标是转行到运维方向\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"这个博客\"\u003e这个博客\u003c/h2\u003e\n\u003cp\u003e本站使用 Hugo + PaperMod 构建，部署在 GitHub Pages 和 Cloudflare Pages。\u003c/p\u003e\n\u003cp\u003e部分文章由 AI 辅助创作，但需求设计、测试、排障和验收都由我自己完成。\u003c/p\u003e\n\u003ch2 id=\"联系我\"\u003e联系我\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGitHub: \u003ca href=\"https://github.com/dsduyopg\"\u003ehttps://github.com/dsduyopg\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eCSDN: \u003ca href=\"https://blog.csdn.net/m0_74369759\"\u003ehttps://blog.csdn.net/m0_74369759\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","title":"关于我"},{"content":"数据结构链表当中的头结点问题 ==示例举证== 出现的什么例子（单链表的表头节点）\n给出自己原来写的代码及源码\n2.出现的问题\n出现的问题分类，代码里的哪些函数出错了。\n我的解决方案 从网上chatgpt查找得到的解决方案\n自己研究得出的方案\n给出自己修改过的代码\n给予大家的警示 1. 示例举证 出现的什么例子（单链表的表头节点） 首先呢，我是在牛客网上做关于反转链表的操作，然后呢，做完之后，自己又把里面的具体的代码都写了一下，有打印，输入都有，这就是背景摘要。\n给出自己原来写的代码及源码 下面我将给出大家我首先第一次写的代码如下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; // 定义链表节点结构 struct ListNode { int value; struct ListNode* next; }; // 反转链表函数 struct ListNode* reverseList(struct ListNode* head) { struct ListNode* prev = NULL; struct ListNode* current = head; struct ListNode* nextNode = NULL; while (current != NULL) { nextNode = current-\u0026gt;next; // 暂存下一个节点 current-\u0026gt;next = prev; // 反转当前节点的指针 prev = current; // 前移 prev current = nextNode; // 前移 current } return prev; // 返回新的头节点 } // 辅助函数，用于创建新节点 struct ListNode* createNode() { struct ListNode* head; // 初始化头节点为空 head=(struct ListNode*)malloc(sizeof(struct ListNode)); head-\u0026gt;next=NULL; // 用于记录链表的尾节点 int x; // x为链表数据域中的数据 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;x) != EOF) { struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode)); newNode-\u0026gt;value = x; newNode-\u0026gt;next =head-\u0026gt;next; // 将新节点链接到尾节点之后 head-\u0026gt;next=newNode; // 更新尾节点 } return head; } // 辅助函数，用于打印链表 void printList(struct ListNode* head) { struct ListNode* current = head; while (current != NULL) { printf(\u0026#34;%d -\u0026gt; \u0026#34;, current-\u0026gt;value); current = current-\u0026gt;next; } printf(\u0026#34;NULL\\n\u0026#34;); } // 辅助函数，用于释放链表内存 void freeList(struct ListNode* head) { struct ListNode* current = head; while (current != NULL) { struct ListNode* next = current-\u0026gt;next; free(current); current = next; } } // 主函数，用于测试反转链表 int main() { // 创建链表 printf(\u0026#34;请输入链表节点值，以EOF结束：\\n\u0026#34;); struct ListNode* head = createNode(); printf(\u0026#34;原链表: \u0026#34;); printList(head); // 反转链表 struct ListNode* reversedHead = reverseList(head); printf(\u0026#34;反转后的链表: \u0026#34;); printList(reversedHead); // 释放内存 freeList(reversedHead); return 0; } 2.出现的问题 出现的问题呢就是在运行的时候，会在尾结点多出一个异常节点，当时我很懵逼，不知道，究竟错在什么地方了，然后，我以为我自己抄错了代码，然后有检验了一番，发现依旧是如此的，当时我真的很崩溃，不知道如何是好，我想到我是否可以在淘宝上面，问一下上面的大佬，花个10元，但是又觉得不值得，下面就是我的解决的经过之路\n出现的问题分类，代码里的哪些函数出错了。 我在ChatGPT上面经过询问得知，我不应该为头结点开辟一块内存空间即用malloc函数，上面解释说，若果强行要用malloc开辟的话，就会操作起来比较的麻烦，但是我想起来我们用到的头插法的雨具是这样子的,\n假设新插入的空间是space那么的话，语句如下： head-\u0026gt;next=NULL; space-\u0026gt;next=head\u0026gt;next; head-\u0026gt;next=space; 我想既然，教材里面都给出了，head有next指针那么的话，head一定是用malloc开辟的空间，但是呢ChatGPT说不需要为头结点开辟空间，我想着若果头结点不需要开辟空间的话，那么的话，教材里为何给介绍开辟空间的那种头插法，我着教材不可能骗人吧，但是呢，经过测试，发现确实书本上的知识有一点问题，正确的头插法应该是这样子的不需要头结点的插入方法：\n假设新插入的空间是space那么的话，语句如下： head=NULL; space-\u0026gt;next=head; head-=space; 这样子会更加的方便一些，不会有后续的错误，我把这个ChatGPT上给出的头插法带入到我的代码里结果非常显而易见，我成功了，结果也是非常喜人的。\n下面我就为大家带来一下我的错误的运行的截图，如下所示：\n并且呢，我为大家带来ChatGPT上面的截图如下：\n**大家好，这就是ChatGPT给我的建！\n下面我就为大家介绍如何解决该问题。**\n3. 我的解决方案 在前文当中，我已经为大家介绍过了，我如何知道我的错误的问题，就是经过ChatGPT，询问得知，下面我就大模型，以及自己的总结俩方面给出合理的建议。\n从网上chatgpt查找得到的解决方案 很明显他给出的解决方案就是把headbxy建议一个头结点，这样就很容易成功，他给出的源码如下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; // 定义链表节点结构 typedef struct ListNode { int value; struct ListNode* next; } ListNode; // 反转链表函数 ListNode* reverseList(ListNode* head) { ListNode* prev = NULL; ListNode* current = head; ListNode* nextNode = NULL; while (current != NULL) { nextNode = current-\u0026gt;next; // 暂存下一个节点 current-\u0026gt;next = prev; // 反转当前节点的指针 prev = current; // 前移 prev current = nextNode; // 前移 current } return prev; // 返回新的头节点 } // 创建新节点函数 ListNode* createNode(int value) { ListNode* newNode = (ListNode*)malloc(sizeof(ListNode)); newNode-\u0026gt;value = value; newNode-\u0026gt;next = NULL; return newNode; } // 辅助函数，用于创建链表 ListNode* createList() { ListNode* head = NULL; // 初始化头节点为空 int x; // x为链表数据域中的数据 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;x) != EOF) { ListNode* newNode = createNode(x); newNode-\u0026gt;next = head; // 将新节点插入到链表头部 head = newNode; // 更新头节点 } return head; } // 辅助函数，用于打印链表 void printList(ListNode* head) { ListNode* current = head; while (current != NULL) { printf(\u0026#34;%d -\u0026gt; \u0026#34;, current-\u0026gt;value); current = current-\u0026gt;next; } printf(\u0026#34;NULL\\n\u0026#34;); } // 辅助函数，用于释放链表内存 void freeList(ListNode* head) { ListNode* current = head; while (current != NULL) { ListNode* next = current-\u0026gt;next; free(current); current = next; } } // 主函数，用于测试反转链表 int main() { // 创建链表 printf(\u0026#34;请输入链表节点值，以EOF结束：\\n\u0026#34;); ListNode* head = createList(); printf(\u0026#34;原链表: \u0026#34;); printList(head); // 反转链表 ListNode* reversedHead = reverseList(head); printf(\u0026#34;反转后的链表: \u0026#34;); printList(reversedHead); // 释放内存 freeList(reversedHead); return 0; } 但是呢，我并不满足这个解决方案，经过思考我也得出我的解决方案\n自己研究得出的方案 我是把打印的函数的\nstruct ListNode* current = head; 这个语句，修改为下面这个语句：\nstruct ListNode* current = head-\u0026gt;next; 然后再在反转单链表函数的里面修改就可以了，\n如下：需要把这个语句\nstruct ListNode* current = head; 修改为下面这个语句就可以了：\nstruct ListNode* current = head; 然后呢，需要在末尾加上这个语句，并修改返回值为head即可\nhead-\u0026gt;next = prev; return head; 给出自己修改过的代码 下面我就为大家带来我修改完之后的代码语句，如下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; // 定义链表节点结构 typedef struct ListNode { int value; struct ListNode* next; }ListNode; // 反转链表函数 struct ListNode* reverseList(struct ListNode* head) { struct ListNode* prev = NULL; struct ListNode* current = head-\u0026gt;next; struct ListNode* nextNode = NULL; while (current!= NULL) { nextNode = current-\u0026gt;next; // 暂存下一个节点 current-\u0026gt;next = prev; // 反转当前节点的指针 prev = current; // 前移 prev current = nextNode; // 前移 current } head-\u0026gt;next = prev; return head; // 返回新的头节点 } ListNode* create(int x){ ListNode*newnode=(ListNode*)malloc(sizeof(ListNode)); newnode-\u0026gt;value=x; newnode-\u0026gt;next=NULL; return newnode; } // 辅助函数，用于创建新节点 ListNode* headNode() { struct ListNode* head=(ListNode*)malloc(sizeof(ListNode)); head-\u0026gt;next=NULL; int x; // x为链表数据域中的数据 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;x) != EOF) { struct ListNode* newNode =create(x); newNode-\u0026gt;next = head-\u0026gt;next; head-\u0026gt;next= newNode; // 更新尾节点 } return head; } // 辅助函数，用于打印链表 void printList(struct ListNode* head) { struct ListNode* current = head-\u0026gt;next; while (current != NULL) { printf(\u0026#34;%d -\u0026gt; \u0026#34;, current-\u0026gt;value); current = current-\u0026gt;next; } printf(\u0026#34;NULL\\n\u0026#34;); } // 主函数，用于测试反转链表 int main() { // 创建链表 printf(\u0026#34;请输入链表节点值，以EOF结束：\\n\u0026#34;); struct ListNode* head = headNode(); printf(\u0026#34;原链表: \u0026#34;); printList(head); // 反转链表 struct ListNode* reversedHead = reverseList(head); printf(\u0026#34;反转后的链表: \u0026#34;); printList(reversedHead); return 0; } 4. 给予大家的警示 总的来说，比较俩种方法，可能第二种的方案比较麻烦，需要修改二处函数，可以想见，假设在以后工作之中，处理的函数体量庞大，那么的话，修改的函数也会有心无力的，因此呢我觉得，用第一种的不带头结点的方案是很喜人的，具体的话，自己看，但是在外面考试当中还是需要明白第二种的诀窍\n最后写到，谨祝大家在数据解构课里面要善于思考，用于使用ChatGPT，会使用ChatGPT，他是一个很好的工具，同时呢，要是大家学有余力的的话，推荐大家学习咸鱼老师的数据结构，在b战里面大家可以搜索，王道的咸鱼老师讲的非常好。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E9%93%BE%E8%A1%A8%E5%BD%93%E4%B8%AD%E7%9A%84%E5%A4%B4%E7%BB%93%E7%82%B9%E9%97%AE%E9%A2%98/","summary":"\u003ch4 id=\"数据结构链表当中的头结点问题\"\u003e数据结构链表当中的头结点问题\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003col\u003e\n\u003cli\u003e==示例举证==\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e出现的什么例子（单链表的表头节点）\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e给出自己原来写的代码及源码\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e2.出现的问题\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e出现的问题分类，代码里的哪些函数出错了。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003col start=\"3\"\u003e\n\u003cli\u003e我的解决方案\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e从网上chatgpt查找得到的解决方案\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e自己研究得出的方案\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e给出自己修改过的代码\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003col start=\"4\"\u003e\n\u003cli\u003e给予大家的警示\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1-示例举证\"\u003e1. 示例举证\u003c/h2\u003e\n\u003ch3 id=\"出现的什么例子单链表的表头节点\"\u003e出现的什么例子（单链表的表头节点）\u003c/h3\u003e\n\u003cp\u003e首先呢，我是在牛客网上做关于反转链表的操作，然后呢，做完之后，自己又把里面的具体的代码都写了一下，有打印，输入都有，这就是背景摘要。\u003c/p\u003e\n\u003ch3 id=\"给出自己原来写的代码及源码\"\u003e给出自己原来写的代码及源码\u003c/h3\u003e\n\u003cp\u003e下面我将给出大家我首先第一次写的代码如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include \u0026lt;stdio.h\u0026gt;\n#include \u0026lt;stdlib.h\u0026gt;\n\n// 定义链表节点结构\nstruct ListNode {\n    int value;\n    struct ListNode* next;\n};\n\n// 反转链表函数\nstruct ListNode* reverseList(struct ListNode* head) {\n    struct ListNode* prev = NULL;\n    struct ListNode* current = head;\n    struct ListNode* nextNode = NULL;\n\n    while (current != NULL) {\n        nextNode = current-\u0026gt;next;  // 暂存下一个节点\n        current-\u0026gt;next = prev;      // 反转当前节点的指针\n        prev = current;            // 前移 prev\n        current = nextNode;        // 前移 current\n    }\n\n    return prev;  // 返回新的头节点\n}\n\n// 辅助函数，用于创建新节点\nstruct ListNode* createNode() {\n    struct ListNode* head;  // 初始化头节点为空\n    head=(struct ListNode*)malloc(sizeof(struct ListNode));\n  head-\u0026gt;next=NULL; // 用于记录链表的尾节点\n    int x;                         // x为链表数据域中的数据\n    while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;x) != EOF) {\n        struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));\n        newNode-\u0026gt;value = x;\n        newNode-\u0026gt;next =head-\u0026gt;next;  // 将新节点链接到尾节点之后\n        head-\u0026gt;next=newNode;        // 更新尾节点\n    }\n    return head;\n}\n\n// 辅助函数，用于打印链表\nvoid printList(struct ListNode* head) {\n    struct ListNode* current = head;\n    while (current != NULL) {\n        printf(\u0026#34;%d -\u0026gt; \u0026#34;, current-\u0026gt;value);\n        current = current-\u0026gt;next;\n    }\n    printf(\u0026#34;NULL\\n\u0026#34;);\n}\n\n// 辅助函数，用于释放链表内存\nvoid freeList(struct ListNode* head) {\n    struct ListNode* current = head;\n    while (current != NULL) {\n        struct ListNode* next = current-\u0026gt;next;\n        free(current);\n        current = next;\n    }\n}\n\n// 主函数，用于测试反转链表\nint main() {\n    // 创建链表\n    printf(\u0026#34;请输入链表节点值，以EOF结束：\\n\u0026#34;);\n    struct ListNode* head = createNode();\n\n    printf(\u0026#34;原链表: \u0026#34;);\n    printList(head);\n\n    // 反转链表\n    struct ListNode* reversedHead = reverseList(head);\n\n    printf(\u0026#34;反转后的链表: \u0026#34;);\n    printList(reversedHead);\n\n    // 释放内存\n    freeList(reversedHead);\n\n    return 0;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"2出现的问题\"\u003e2.出现的问题\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e出现的问题呢就是在运行的时候，会在尾结点多出一个异常节点，当时我很懵逼，不知道，究竟错在什么地方了，然后，我以为我自己抄错了代码，然后有检验了一番，发现依旧是如此的，当时我真的很崩溃，不知道如何是好，我想到我是否可以在淘宝上面，问一下上面的大佬，花个10元，但是又觉得不值得，下面就是我的解决的经过之路\u003c/strong\u003e\u003c/p\u003e","title":"数据结构链表当中的头结点问题"},{"content":"文章目录 一.数据同步服务的基本介绍\n1.什么是数据同步？\n2.如何进行实时同步\n3.特点\n4.怎么实现仅同步变化的数据呢？\n5.同步方式\n二.如何安装数据同步服务： dnf -y install rsync\n三.综合案例\n1. 本地同步操作:\n2.远程同步操作:\n3.综合案例,实现定时与增量备份\n四.结合INOTIFY实现数据实时同步\n1.对node2进行配置\n1.创建用户\n2.修改配置文件\n3.创建密码⽂件\n4. 启动rsync服务\n五.设置开机自启动\n1.杀死进程\n2.设置开机⾃启动\n六.基于RSYNC服务完成⼿动同步\n1.格式\n2.基于⼿动同步⽅式完成数据同步\n3.创建日志文件\n4.开始传输文件了\n解决方案\n找到根本原因了！\n解决方案\n方案1：临时关闭 SELinux（快速验证）\n方案2：永久关闭 SELinux\n方案3：设置正确的 SELinux 上下文（推荐，更安全）\n建议\n七,使⽤密码⽂件， 实现不需要输⼊密码\n八.使用主机名\n九.使用脚本自动配置\n1.安装使用的软件\n2.编写脚本\n3给文件加权以及启动脚本文件\n4.最后测试一下\n一.数据同步服务的基本介绍 1.什么是数据同步？ 指的将业务中数据，定时 或者实时的将其备份到其他目录或者其他服务器的目录下过程\n2.如何进行实时同步 常⽤选项： • -v ：详细模式输出，显⽰传输过程中的⽂件信息。 • -a ：归档模式，表⽰递归传输⽂件，并保持所有⽂件属性。 • -z ：对备份的⽂件在传输时进⾏压缩处理。 • -P ：显⽰进度条。 • --delete ：删除⽬标⽬录中源⽬录中没有的⽂件（可选，⽤于实现双向同步） • -e ：⽤于指定⽤于传输⽂件的远程 shell 程序 默认为 ssh，也可以选择其他如rsh或者⾃定义的ssh 端⼝ 本地同步： rsync -av /src/directory/ /dest/directory/ 远程同步： rsync -avz /src/directory/ root@192.168.88.102:/dest/directory/ 拉取远程数据到本地： rsync -avz user@192.168.88.102:/src/directory/ /dest/directory/ 3.特点 • ⽀持增量同步：只传输发⽣变化的数据部分。 • ⽀持 SSH 加密传输。 • 可保持⽂件权限、时间戳、连接和等元数据。\n4.怎么实现仅同步变化的数据呢？ 1.会对需要同步的数据文件进行分块(底层) 2.每一个数据块会基于数据生成校验码，只要数据发生变化 这个校验码也会发生改变 3.在同步文件的时候， 只需要监测哪些数据块的校验码发生变化， 就可以指定那个块数据改变了， 此时仅需要同步这些变化数据块即可 5.同步方式 手动同步–\u0026gt; 定时同步\n实时同步\n二.如何安装数据同步服务： dnf -y install rsync dnf install rsync -y 三.综合案例 1. 本地同步操作: 准备⼯作 1- 在node1节点的 /usr/local⽬录下,创建⼀个nginx-1.8.0⽬录,并在此⽬录下构建⼀个 html⽬录 2- 在html⽬录下,创建index.html、favicon.ico、logo.png⽂件，以及assets⽬录 3- 在assets⽬录下，创建index.js、index.css⽂件 4- 给各个⽂件随机写⼊⼀些内容 需求：对html⽬录进⾏同步操作。 将其同步到/usr/local/nginx-1.8.0/backup/下，并且如果 源⽬录下已经删除的，在⽬标⽬录下也应该被删除 我们应当用rsync命令进行相应的操作，下面就是我操作出来的\n2.远程同步操作: 需求：对/usr/local/nginx-1.8.0/html⽬录进⾏操作，将其同步到node2的家⽬录的backup的⽬ 录中 注意： 需要双⽅均安装rsync，同时配置了SSH密钥（否则需要输⼊密码） 这个案例也是没有什么太大的问题的，下面是我在这个案例中做出的\n3.综合案例,实现定时与增量备份 需求说明： 在centos stream 9的 /var/log⽬录下， 存储了⼤量的关于系统的⽇志：如messages⽂件存储⼤量 的系统⽇志信息，secure⽇志⽂件存储了⼤量的与系统安全的⽇志。请对此⽬录中的messages、 secure⽇志⽂件进⾏定时的备份到node2节点的/export/data/node1_backup_system_log⽬录中， 要求每⽇凌晨2点进⾏备份⼀次。 在这案例之中我们可以使用定时任务进行操作的\n四.结合INOTIFY实现数据实时同步 ⾸先将⼆个路径分别创建出来， 便于后续演⽰ node1执⾏： mkdir -p /export/data/logs node2执⾏： mkdir -p /export/data/node1_exe_backup_log 优势： ⾼效性: rsyncd 的守护进程模式可以避免每次连接都启动 SSH 进程，因此在某些情况下可能会⽐ SSH ⽅式略微提⾼性能。 简化配置: ⼀旦配置好 rsyncd，⽆需每次都输⼊ SSH 密码，适合⽤于定时备份等⾃动化任务。 缺点： 安全性问题: rsyncd 是基于开放端⼝的⽅式进⾏⽂件同步，默认情况下并不加密传输的数据。这使 得数据传输过程可能暴露给中间⼈攻击（MITM）。虽然可以配置 rsyncd 使⽤ --password-file 来限制访问，但仍然不如 SSH 安全。 需要开放端⼝: 您需要在防⽕墙上开放 rsync 服务使⽤的端⼝（通常是 873），这可能增加潜在的 安全⻛险。 配置复杂: 需要在服务器上配置 rsyncd.conf，并管理模块、权限、⽤⼾等细节。 1.对node2进行配置 1.创建用户 在这里面呢，我们需要在node2中创建2个用户，一个是backup_user,一个是rsync用户\nuseradd backup_user useradd rsync passwd backup_user passwd rsync 注意密码，都设置为123456\n2.修改配置文件 注意，这个配置文件要是在node2中修改的，以下是修改命令\n# 全局设置 uid = rsync gid = rsync use chroot = no port = 873 max connections = 200 pid file = /var/run/rsyncd.pid log file = /var/log/rsyncd.log transfer logging = yes ignore nonreadable = yes [node1_logs] path = /export/data/node1_exe_backup_log comment = Backup Directory read only = no secrets file = /etc/rsyncd.passwd hosts allow = 192.168.149.0/24 hosts deny = * auth users = backup_user 3.创建密码⽂件 echo \u0026#34;backup_user:123456\u0026#34; \u0026gt; /etc/rsyncd.passwd # 设置密码⽂件的权限为600，确保该⽂件只有 RSYNC 服务可以读取 chmod 600 /etc/rsyncd.passwd 4. 启动rsync服务 1 rsync --daemon 测试是否可以正常访问： 在node1执⾏ rsync rsync://backup_user@192.168.88.102/node1_logs 注意在node2需要开放端⼝： firewall-cmd --permanent --add-port=873/tcp 五.设置开机自启动 1.杀死进程 -- 查看进程 ps -ef | grep rsync -- 查看进程ID -- 杀死进程 kill -9 rsync的进程ID 2.设置开机⾃启动 由于rsync并不是⼀个系统服务（简单判断：通过systemctl启动和关闭的服务），故需要先 设置为系统服务后才可以设置\n修改配置文件 [Unit] Description=Rsync Daemon After=network.target [Service] ExecStart=/usr/bin/rsync --daemon --config=/etc/rsyncd.conf --no-detach ExecReload=/bin/kill -HUP $MAINPID Restart=always [Install] WantedBy=multi-user.target 特殊说明： ExecReload=/bin/kill -HUP $MAINPID：通过发送 SIGHUP 信号，告诉 rsync 守护进程重新 加载其配置⽂件，⽽⽆需停⽌并重新启动进程。 这个时候，我们需要把旧的日志文件给删掉 因为我们可能在操作的时候会产出一些错误的日志，就是由于这些错误的文件会导致，我们后续无法成功启动rsync服务\n就像我这样的错误一样，这个时候，我们就要学会查看日志文件了，因为一些错误会记录在日志里面\n其实，当我把旧的日志文件给删掉之后哦，成功了，应该是之前启动失败过一次，日志文件，没有删除，然后再次启动，通过对比这个日志文件，发现有问题，就无法启动，删除文件意思是把里面的的错误清理掉，之后相当于重新启动，所以可以成功启动后\n总结这次成功的逻辑 这次能成功，其实是两个动作配合的结果： 清理现场：rm -f /var/run/rsyncd.pid 清除了旧的工作证，扫清了路障。 修正配置：你之前（或者在我们的排查中）修正了 /etc/rsyncd.conf 里的错误（比如把密码文件的路径写对了，或者模块路径建好了）。 所以，并不是“这样修改就可以”这么简单，而是“清理了旧障碍 + 配置本身正确”共同作用的结果。 💡 小贴士：以后遇到类似问题怎么办？ 下次如果再遇到服务启动失败，且提示 already running、permission denied 或 status=11，可以尝试这个“万能重启三部曲”： 查日志：journalctl -xeu 服务名 看具体报错。 删 pid 文件：rm -f /var/run/服务名.pid 清理旧状态。 重启服务：systemctl restart 服务名。 记住，日志是你最好的朋友，它能告诉你服务到底是哪里不舒服。现在你的 rsync 服务已经稳稳运行了，可以去客户端测试同步啦！ 六.基于RSYNC服务完成⼿动同步 1.格式 ⽤⼾名@主机地址::模块名称 或 rsync://⽤⼾名@主机地址/模块名称 说明： 模块名： 指的是配置到rsync.conf 中 【】的内容 ⽤⼾名： 指的是模块中指定的⽤⼾名， 如果没有指定， 可以是主机存在的⽤⼾名即可（前提 是该⽤⼾需要有权限） 例如： backup_user@192.168.88.102::node1_logs 2.基于⼿动同步⽅式完成数据同步 rsync -avz /export/data/logs/ backup_user@node2::node1_logs 3.创建日志文件 在创建的日志文件里面录入数据\ncd /export/data/logs touch log.txt \u0026amp;\u0026amp; echo \u0026#34;2491 797 1243124 9127\u0026#34;\u0026gt;\u0026gt;log.txt 4.开始传输文件了 在这一步里面呢，我们需要注意上下文，把这个权限给关掉，否则，后面传输就像我一样不会成功的\n大家可以看到，我通过传送指令，无法发送成功，但是文件的权限都是开的，是最大的权限\n当时，我就比较迷了，不知道该如何办，然后我问了千问，它给予了我很大的帮助了，我明白了其实是我Linux的上下文的权限比较严格，需要将其关闭\n解决方案 以下是千问告诉我的\n找到根本原因了！ SELinux 处于 Enforcing（强制）模式，这就是 rsync 报 Permission denied 的真正原因。**\n即使目录权限是 drwxrwxrwx（777），SELinux 的安全策略仍然会阻止 rsync 进程写入文件。\n解决方案 方案1：临时关闭 SELinux（快速验证） 在远程服务器 192.168.149.131 上执行：\n# 临时设置为 Permissive 模式（不需要重启） sudo setenforce 0 # 验证 getenforce # 应该输出: Permissive 然后重新执行 rsync 命令，应该就能成功了。\n⚠️ 这只是临时生效，重启后会恢复为 Enforcing。\n方案2：永久关闭 SELinux # 编辑 SELinux 配置文件 sudo vi /etc/selinux/config # 将 SELINUX=enforcing 改为： SELINUX=disabled # 重启服务器生效 sudo reboot 方案3：设置正确的 SELinux 上下文（推荐，更安全） 如果不想关闭 SELinux，可以给目录设置正确的标签：\n# 查看当前目录的 SELinux 上下文 ls -Zd /export/data/node1_exe_backup_log/ # 设置 rsync 可以写入的上下文 sudo chcon -Rt public_content_rw_t /export/data/node1_exe_backup_log/ # 如果 rsync 以守护进程方式运行，还需要： sudo setsebool -P rsync_full_access on 建议 先用方案1快速验证，确认是 SELinux 的问题后，再决定用方案2还是方案3长期解决。\n# 在远程服务器上执行 sudo setenforce 0 然后回到本地重新执行 rsync 命令即可。\n这个是我按照，他的提示放权的步骤\n接下来，很明显，我再次发送文件，直接成功了\n注意，ai说的是下一次重启还会权限不足，因此我们需要修改配置文件\n将SELINUX修改为disabled就可以了\n然后，我们重启一起机器，是配置生效，就可以了。\n七,使⽤密码⽂件， 实现不需要输⼊密码 步骤⼀： 配置密码⽂件 echo \u0026#39;123456\u0026#39; \u0026gt; /etc/rsync.password chmod 600 /etc/rsync.password 注意： 权限必须是600，否则会报错 步骤⼆： 使⽤密码⽂件， 实现不需要输⼊密码 rsync -avz --password-file=/etc/rsync.password /export/data/logs/ backup_user@node2::node1_logs 查看node2机器\n八.使用主机名 这个应该修改配置文件hosts\necho \u0026#34;192.168.149.128 node1 node1.itcast.cn\u0026#34;\u0026gt;\u0026gt;/etc/hosts echo \u0026#34;192.168.149.131 node2 node2.itcast.cn\u0026#34;\u0026gt;\u0026gt;/etc/hosts 九.使用脚本自动配置 1.安装使用的软件 安装第三方源 ⾸先启⽤EPEL仓库： dnf install epel-release 安装监视软件 安装inotify-tools dnf install -y inotify-tools 2.编写脚本 1.vi /root/real_time_sync.sh 创建sh脚本文件\n#!/bin/bash # 定义源⽬录和⽬标⽬录（⽬标⽬录是远程服务器的⽬录） SOURCE_DIR=\u0026#34;/export/data/logs\u0026#34; TARGET_DIR=\u0026#34;root@192.168.88.102:/export/data/node1_exe_backup_log\u0026#34; # 替换为⽬ 标服务器的⽤⼾名和⽬录 # ⽇志⽂件 LOG_FILE=\u0026#34;/var/log/rsync_realtime_sync.log\u0026#34; # 使⽤inotifywait监听${WATCH_DIR}⽬录中的⽂件变化 inotifywait -m -r -e modify,create,delete,move \u0026#34;${SOURCE_DIR}\u0026#34; | while read path action file; do echo \u0026#34;Detected ${action} on ${file} in ${path}. Starting rsync...\u0026#34; \u0026gt;\u0026gt; \u0026#34;$LOG_FILE\u0026#34; # 执⾏rsync同步操作 rsync -avz --port=873 --delete -e ssh \u0026#34;${SOURCE_DIR}/\u0026#34; \u0026#34;${TARGET_DIR}\u0026#34; \u0026gt;\u0026gt; \u0026#34;$LOG_FILE\u0026#34; 2\u0026gt;\u0026amp;1 echo \u0026#34;Sync completed for ${file} in ${path}.\u0026#34; \u0026gt;\u0026gt; \u0026#34;$LOG_FILE\u0026#34; done 3给文件加权以及启动脚本文件 4.最后测试一下 在node1的/export/data/logs 里面上传几个文件，然后再打开一个node1服务器开启tail -100f监听文件，查看是否有修改，以及node2是否也跟着变化呢\n最后，发现成功在node2里面显示，这个实验就完成了\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E6%95%B0%E6%8D%AE%E5%90%8C%E6%AD%A5%E6%9C%8D%E5%8A%A1/","summary":"\u003ch4 id=\"文章目录\"\u003e文章目录\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_3\"\u003e一.数据同步服务的基本介绍\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_5\"\u003e1.什么是数据同步？\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_9\"\u003e2.如何进行实时同步\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_28\"\u003e3.特点\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_34\"\u003e4.怎么实现仅同步变化的数据呢？\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#5_42\"\u003e5.同步方式\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_dnf_y_install_rsync_50\"\u003e二.如何安装数据同步服务： dnf -y install rsync\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_60\"\u003e三.综合案例\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1__62\"\u003e1. 本地同步操作:\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_85\"\u003e2.远程同步操作:\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_98\"\u003e3.综合案例,实现定时与增量备份\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#INOTIFY_118\"\u003e四.结合INOTIFY实现数据实时同步\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1node2_153\"\u003e1.对node2进行配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_155\"\u003e1.创建用户\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_168\"\u003e2.修改配置文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_193\"\u003e3.创建密码⽂件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_rsync_201\"\u003e4. 启动rsync服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_220\"\u003e五.设置开机自启动\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_222\"\u003e1.杀死进程\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_234\"\u003e2.设置开机⾃启动\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#RSYNC_300\"\u003e六.基于RSYNC服务完成⼿动同步\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_302\"\u003e1.格式\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_314\"\u003e2.基于⼿动同步⽅式完成数据同步\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_320\"\u003e3.创建日志文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_329\"\u003e4.开始传输文件了\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_346\"\u003e解决方案\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_350\"\u003e找到根本原因了！\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_358\"\u003e解决方案\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_SELinux_360\"\u003e方案1：临时关闭 SELinux（快速验证）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_SELinux_381\"\u003e方案2：永久关闭 SELinux\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_SELinux__398\"\u003e方案3：设置正确的 SELinux 上下文（推荐，更安全）\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_417\"\u003e建议\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#__457\"\u003e七,使⽤密码⽂件， 实现不需要输⼊密码\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_486\"\u003e八.使用主机名\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_508\"\u003e九.使用脚本自动配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_510\"\u003e1.安装使用的软件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_526\"\u003e2.编写脚本\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_552\"\u003e3给文件加权以及启动脚本文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4_557\"\u003e4.最后测试一下\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一数据同步服务的基本介绍\"\u003e一.数据同步服务的基本介绍\u003c/h2\u003e\n\u003ch3 id=\"1什么是数据同步\"\u003e1.什么是数据同步？\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e指的将业务中数据，定时 或者实时的将其备份到其他目录或者其他服务器的目录下过程\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"2如何进行实时同步\"\u003e2.如何进行实时同步\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e常⽤选项：\n• -v ：详细模式输出，显⽰传输过程中的⽂件信息。\n• -a ：归档模式，表⽰递归传输⽂件，并保持所有⽂件属性。\n• -z ：对备份的⽂件在传输时进⾏压缩处理。\n• -P ：显⽰进度条。\n• --delete ：删除⽬标⽬录中源⽬录中没有的⽂件（可选，⽤于实现双向同步）\n• -e ：⽤于指定⽤于传输⽂件的远程 shell 程序 默认为 ssh，也可以选择其他如rsh或者⾃定义的ssh\n端⼝\n本地同步：\nrsync -av /src/directory/ /dest/directory/\n远程同步：\nrsync -avz /src/directory/ root@192.168.88.102:/dest/directory/\n拉取远程数据到本地：\nrsync -avz user@192.168.88.102:/src/directory/ /dest/directory/\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"3特点\"\u003e3.特点\u003c/h3\u003e\n\u003cp\u003e• ⽀持增量同步：只传输发⽣变化的数据部分。\n• ⽀持 SSH 加密传输。\n• 可保持⽂件权限、时间戳、连接和等元数据。\u003c/p\u003e","title":"数据同步服务"},{"content":"题目如下：limit as x approaches 0 of (cosx)/(x^2)-(sinx)/(x ^3) 中，可以用等价无穷小写吗？ 我们考虑 𝑥趋近于 0 的情况，用 x 的泰勒展开式来近似 cos(𝑥)cos(x) 和sin(𝑥)sin(x)：\n在处理极限时，使用等价无穷小是一种常用的方法，但在特定情况下需要特别小心。虽然 (sin(x) 在 (x) 趋近于 0 时的等价无穷小确实是 (x)，但在这个题目中，仅仅使用 (sin(x)作为近似并不足以获得正确的结果。这是因为表达式中的每一项都涉及较高次的项，这些项对最终的极限值有重要影响。具体来说，如果只将 (sin(x)) 近似为 (x)，我们会得到：\n这允许我们保留表达式中所有对极限有显著贡献的项，从而准确地计算出极限值。总结来说，简单地将 ( sin(x) 近似为 ( x ) 忽略了 ( x^3/6) 这类高阶项，而这些项在本题目中对最终结果是至关重要的，因此不能仅用 sin(x) ~x 来处理该问题。\n大家好，资料来源于GPT-4，我整理了GPT的回答，若果有错误，还请各位大佬指正出来，感谢感谢！！！\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E6%95%B0%E5%AD%A6---%E7%AD%89%E4%BB%B7%E6%97%A0%E7%A9%B7%E5%B0%8F%E9%97%AE%E9%A2%98/","summary":"\u003ch2 id=\"题目如下limit-as-x-approaches-0-of-cosxx2-sinxx-3-中可以用等价无穷小写吗\"\u003e题目如下：limit as x approaches 0 of (cosx)/(x^2)-(sinx)/(x ^3) 中，可以用等价无穷小写吗？\u003c/h2\u003e\n\u003cp\u003e我们考虑 𝑥趋近于 0 的情况，用 x 的泰勒展开式来近似 cos(𝑥)cos(x) 和sin(𝑥)sin(x)：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E6%95%B0%E5%AD%A6---%E7%AD%89%E4%BB%B7%E6%97%A0%E7%A9%B7%E5%B0%8F%E9%97%AE%E9%A2%98/images/06a1785fcde34fe998527c8341058ff2.png\"\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E6%95%B0%E5%AD%A6---%E7%AD%89%E4%BB%B7%E6%97%A0%E7%A9%B7%E5%B0%8F%E9%97%AE%E9%A2%98/images/86dc8791739241308a86f5d526f9e900.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e在处理极限时，使用等价无穷小是一种常用的方法，但在特定情况下需要特别小心。虽然 (sin(x) 在 (x) 趋近于 0 时的等价无穷小确实是 (x)，但在这个题目中，仅仅使用 (sin(x)作为近似并不足以获得正确的结果。这是因为表达式中的每一项都涉及较高次的项，这些项对最终的极限值有重要影响。具体来说，如果只将 (sin(x)) 近似为 (x)，我们会得到：\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E6%95%B0%E5%AD%A6---%E7%AD%89%E4%BB%B7%E6%97%A0%E7%A9%B7%E5%B0%8F%E9%97%AE%E9%A2%98/images/56031de23ca742fe976846d27d96ba42.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e这允许我们保留表达式中所有对极限有显著贡献的项，从而准确地计算出极限值。总结来说，简单地将 ( sin(x)  近似为 ( x ) 忽略了 ( x^3/6) 这类高阶项，而这些项在本题目中对最终结果是至关重要的，因此不能仅用 sin(x) ~x 来处理该问题。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e大家好，资料来源于GPT-4，我整理了GPT的回答，若果有错误，还请各位大佬指正出来，感谢感谢！！！\u003c/p\u003e","title":"数学---等价无穷小问题"},{"content":"这个是关于ssh的相关问题这个是关于ssh的相关问题 一.首先问题是：局域网内的两台主机如何可以相互登录\n一.检查网络的连通性\n二. 检查了是否安装了ssh服务\n二.如何配置SSH免密登录\n一.root用户的登录配置\n1.修改配置文件\n2.重启ssh服务\n二.免密钥登录\n1.安装私钥和公钥\n2.发送公钥\n一.首先问题是：局域网内的两台主机如何可以相互登录 在这里面的话，我们其实是分成了2步的步骤流程：\n首先第一步是检查是否我们局域网内的两台主机是否可以ping通\n第二步其实就是检查我们的这两台主机，是否安装了ssh服务\n接下来呢，我们将在这2个方面探究第一个的问题，这里面我会将我遇到的问题都写出来，并给予解决方案\n一.检查网络的连通性 其实，在这里面，大家务必要小心，不要觉得这个简单，就不好好看了，这个也是开始的时候困扰了我很久，后来才知道，了是这个问题没有解决，以下我就为大家详细的说一下吧：\n首先呢，大家务必确定两台Linux是NAT模式，这个模式对于后续操作是有帮助的，否则的话，后面大家就会遇到各种各样的麻烦的，这里我就给大家说一下给如何进行NAT设置吧\n首先呢，大家要点击虚拟机里面的主机当中的设置按钮\n接下来，在里面出现的界面我们选择网络适配器，选择NAT就可以了\n然后，接下来的一台Linux的系统也是同理的，在这里我就不赘述了，操作到这里，其实两台主机就具有连通行了，我们可以ping一下的 这一步，到这里就结束了，然后我们进行下一步操作的的.\n二. 检查了是否安装了ssh服务 在这里呢，我们开始进入Linux里面，开始真正意义上的操作的\n首先就是我们查询是否安装了ssh相关服务，相比大家都知道的是，我们要安装ssh的客户端和服务端两个服务的，以下是查询的命令 ssh -V # 这个是检查SSH客⼾端版本，如果安装会显⽰版本信息 systemctl status sshd # 这个是检查SSH服务状态，如果服务正在运⾏则说明已安装SSH服务器 如果提⽰： command not found或类似的， 说明SSH服务未安装， 需要安装服务\n此时呢，就需要安装这几个服务了\n1.安装客户端 dnf install -y openssh-clients 2.安装服务端 dnf install -y openssh-service 3.启动ssh服务 systemctl start sshd # 启动服务 systemctl enable sshd # 将服务添加到⾃启动项，下次开机就⾃动启动了 这一步，到这里，我们也就结束了\n实是完成的，我们输入ssh 主机名@ip地址，最后输入密码就可以了，但是呢，两台主机都必须要用非root用户登录的，下面我大家探讨不需要输入密码就可以进行登陆的，同时我们也可以进行root用户的登录的。\n二.如何配置SSH免密登录 在这里面呢，我就为大家解决上面留下来的问题，一个是使用root账户也是可以登陆的；第二个是不使用密码也是可以进行登陆的。\n一.root用户的登录配置 在这里，我分了两个的步骤的，首先是修改配置文件，然后就是对所在的服务进行重启的。\n1.修改配置文件 在这里面呢，我们需要把配置文件：/etc/ssh/sshd_config给修改了，将里面的PermitRootLogin prohibit-password改成PermitRootLogin yes，然后保存就可以了。\nvim etc/ssh/sshd_config # 修改配置文件 大家在这里，如果修改完配置文件之后就需要，保存，这一步就搞定了。\n2.重启ssh服务 sudo systemctl restart sshd 接下来，我们就迎来了，最后的一步就可以了\n二.免密钥登录 在这里，我们需要，先在一台Linux机器里面安装私钥和公钥，然后我们就把公钥发送给另一台Linux机器。 1.安装私钥和公钥 ssh-keygen -f ~/.ssh/id_rsa -P \u0026#39;\u0026#39; -q ![[Pasted image 20260712232607.png]] 这样就可以了 另一台机器同理，也是如此的安装方法。\n2.发送公钥 在这里，我们需要先给对方的机器发送公钥，其次再给我们自己的本机发送公钥。\n1.给对方的机器发送公钥。 ssh-copy-id 主机名@目标地址的IP地址 大家做完之后，其实应该就是这个样子的。\n2.然后自己给自己发送公钥 同理，这里我就不叙述了。\n这样我们就把其中的一台主机搞定了，另一台主机的操作流程其实是一样的，做出来的效果，大家可以看一下的，由于，我的另一个是Ubuntu所以呢，只可以使用普通用户登陆的。 写到最后，在这里呢，大家可能觉得搞一个两台主机相互ping通的操作是没有任何意义的，但是呢，我在这里诚挚的告诉大家，没有用处是在这里的，但是呢，黑马的佳乐老师，告诉了我们，在以后的脚本学习当中，脚本的配置当中是需要两台主机可以相互访问的，因为这样可以节省出大量的功夫的，我觉得在学习当中其实是可以研究一下自己有什么不明白的，然后把自己的不懂之处都写出来，虽然不可以做到极致，但是呢，我还是希望帮助到更多的人，最后说一句，希望这篇博客对于大家是有帮助的，大家如果大于计算机学习有兴趣是可以跟随黑马的课程的。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/","summary":"\u003ch4 id=\"这个是关于ssh的相关问题这个是关于ssh的相关问题\"\u003e这个是关于ssh的相关问题这个是关于ssh的相关问题\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_1\"\u003e一.首先问题是：局域网内的两台主机如何可以相互登录\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_9\"\u003e一.检查网络的连通性\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#__ssh_31\"\u003e二. 检查了是否安装了ssh服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#SSH_81\"\u003e二.如何配置SSH免密登录\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#root_85\"\u003e一.root用户的登录配置\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_89\"\u003e1.修改配置文件\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2ssh_103\"\u003e2.重启ssh服务\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_110\"\u003e二.免密钥登录\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_116\"\u003e1.安装私钥和公钥\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_126\"\u003e2.发送公钥\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一首先问题是局域网内的两台主机如何可以相互登录\"\u003e一.首先问题是：局域网内的两台主机如何可以相互登录\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e在这里面的话，我们其实是分成了2步的步骤流程：\u003c/code\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ccode\u003e首先第一步是检查是否我们局域网内的两台主机是否可以ping通\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ccode\u003e第二步其实就是检查我们的这两台主机，是否安装了ssh服务\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ccode\u003e接下来呢，我们将在这2个方面探究第一个的问题，这里面我会将我遇到的问题都写出来，并给予解决方案\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"一检查网络的连通性\"\u003e一.检查网络的连通性\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e其实，在这里面，大家务必要小心，不要觉得这个简单，就不好好看了，这个也是开始的时候困扰了我很久，后来才知道，了是这个问题没有解决，以下我就为大家详细的说一下吧：\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e首先呢，大家务必确定两台Linux是NAT模式，这个模式对于后续操作是有帮助的，否则的话，后面大家就会遇到各种各样的麻烦的，这里我就给大家说一下给如何进行NAT设置吧\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e首先呢，大家要点击虚拟机里面的主机当中的设置按钮\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/images/3d39734c50c845b1a3dca94f9d54d26d.png\"\u003e\u003c/p\u003e\n\u003cp\u003e接下来，在里面出现的界面我们选择网络适配器，选择NAT就可以了\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/images/dc4cf5b90f0745e4adcce11cd8f03886.png\"\u003e\u003c/p\u003e\n\u003cp\u003e然后，接下来的一台Linux的系统也是同理的，在这里我就不赘述了，操作到这里，其实两台主机就具有连通行了，我们可以ping一下的\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/images/3ddf11ca5ee34c9faa74d73d95b0bf6c.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/images/8357eaf939444b7bae739442e507c10a.png\"\u003e\n这一步，到这里就结束了，然后我们进行下一步操作的的.\u003c/p\u003e\n\u003ch3 id=\"二-检查了是否安装了ssh服务\"\u003e二. 检查了是否安装了ssh服务\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003e 在这里呢，我们开始进入Linux里面，开始真正意义上的操作的\u003c/code\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e首先就是我们查询是否安装了ssh相关服务，相比大家都知道的是，我们要安装ssh的客户端和服务端两个服务的，以下是查询的命令\u003c/strong\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003essh -V   # 这个是检查SSH客⼾端版本，如果安装会显⽰版本信息\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/images/ba6760b2476d4378a5896bca27def448.png\"\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esystemctl status sshd # 这个是检查SSH服务状态，如果服务正在运⾏则说明已安装SSH服务器\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98%E8%BF%99%E4%B8%AA%E6%98%AF%E5%85%B3%E4%BA%8Essh%E7%9A%84%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/images/50c753ae2698405ab406a8ae7dd025c4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e如果提⽰： command not found或类似的， 说明SSH服务未安装， 需要安装服务\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"![[Pasted image 20260712225903.png]]\" loading=\"lazy\" src=\"https://i-blog.csdnimg.cn/direct/8df8e7b8cc384d2883e169ae05d52064.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e 此时呢，就需要安装这几个服务了\u003c/code\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e1.安装客户端\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ednf install -y openssh-clients\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e2.安装服务端\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ednf install -y openssh-service\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e3.启动ssh服务\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esystemctl start sshd # 启动服务\nsystemctl enable sshd # 将服务添加到⾃启动项，下次开机就⾃动启动了\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003e这一步，到这里，我们也就结束了\u003c/strong\u003e\u003c/p\u003e","title":"这个是关于ssh的相关问题这个是关于ssh的相关问题"},{"content":"大家好，今天我为大家介绍关于整数与浮点数在内存中的存储，我们通过今天的讲解一定会更加理解浮点数的储存模式，更好修炼大家的内功，来开启我们的学习之旅吧！\n整数和浮点数在内存中存储 1.整数与浮点数在内存中存储是否一样\n整数与浮点数在内存中存储的例子\n2.整数在内存中的存储\n整数在内存中存储的具体例子\n3.浮点数在内存中的存储\n1.规定相关东西\nIEEE754的详细相关规定\n2.浮点数在内存中的具体示例\n3.讲解上例当中的题目\n1.整数与浮点数在内存中存储是否一样 首先我们大家都有一个疑问，在内存中数据的存储不都是以二进制形式存储的吗？还有什么不同呢？其实我在学习本节课之前我和大家的认知是一样的，都是认为他们的存储都是一样的，但是今天我可以告诉给大家，在内存中存储其实并不一样，下面我为大家详细的介绍一下。\n整数与浮点数在内存中存储的例子 下面我为大家举出一个关于他们在内存存储的例子，如下：\nint main() { int n = 9;//整型的存储方式，补码 //00000000000000000000000000001001 9的补码 // float* pFloat = (float*)\u0026amp;n; printf(\u0026#34;n的值为：%d\\n\u0026#34;, n);//9 printf(\u0026#34;*pFloat的值为：%f\\n\u0026#34;, *pFloat); //0 00000000 00000000000000000001001 //E 为全0 //(-1)^0 * 0.00000000000000000001001 * 2^-126 //1* //0.000000 *pFloat = 9.0; //1001.0 //(-1)^0 * 1.001*2^3 //S=0 //M=1.001 //E = 3 //01000001000100000000000000000000 // printf(\u0026#34;num的值为：%d\\n\u0026#34;, n); printf(\u0026#34;*pFloat的值为：%f\\n\u0026#34;, *pFloat);//9.0 return 0; } 大家觉得这个会是多少呢？我想大家所想的可能会和标准运行结果会有差距，那么的话，下面我就为大家介绍一下，这个程序的运行结果：是9,0，1091567616，9.0 因此我们可以知道，浮点数在内存中的存储与整数是不一样的，存储原理是不同的，那么为何会造成这种不同呢？下面我就为大家介绍一下，他们存储的具体实现。\n2.整数在内存中的存储 这个我们都知道，整数在内存中是按照二进制的补码进行存储的，而正数的补码就是正数其本身，但是负数的补码则是其原二进制的反码+1，具体的讲在二进制当中我已经为大家详细介绍过了，下面我们看一下他们的具体的例子就可以了。\n整数在内存中存储的具体例子 下面我为大家介绍关于整数在内存中存储的一些例子，\nint a=9; int w=-9; 我们通过这个例子可以看出在内存中，a的存储是原来的二进制编码，但是呢w则是反码加1,通过以上我对于整数的诠释相信大家一定会明白整数在内存中存储原理，下面我就为大家介绍关于浮点数在内存中的存储原理。\n3.浮点数在内存中的存储 1.规定相关东西 首先呢，\nIEEE754规定了，任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式： V = (−1) S *M * 2E •(-1)S表⽰符号位，当S=0，V为正数；当S=1，V为负数 • M 表⽰有效数字，M是⼤于等于1，⼩于2的 • 2E 表⽰指数位\nIEEE754的详细相关规定 1.这个组织对于浮点数的详细相关规定是一下几个方面：\n1.对于32位的浮点数，最⾼的1位存储符号位S，接着的8位存储指数E，剩下的23位存储有效数字M 2.对于64位的浮点数，最⾼的1位存储符号位S，接着的11位存储指数E，剩下的52位存储有效数字M\n大家看有图所示： 以及这个图案： 2.详细的相关定如下： 1.IEEE754规定M必须是大于等于1但是小于等于2，在书写中系统默认前面有一那么就相当于，在写M中前面的数字1可以不用自己书写只用书写1后面的部分就可以了，我给大家举出例子如下所示： 比如M=1.00001那么在内存中存储M的时候相当于将1后面的部分存储下来，也就是说存储的是00001 2，IEEE754规定对于指数部分就有些麻烦了，对于有8位的E来说，就是相当于给指数加上127，但是对于有11为的E来说，那么相等于是给指数加上1023. 3.我相信大家对于S一定是比较明白的那么的话，我就不为大家详细介绍了。\n2.浮点数在内存中的具体示例 首先在给大家举出例子的时候，希望大家明白一下几点。 1.若果E全为0那么的话就是不用再给M加上1了而是用0.xxxx表示，而指数则是需要用1-127表示，因此这个数字是正负都很接近0的数字 2.若果E全为1的话那么就表示的数字是正负无穷大。\n3.讲解上例当中的题目 大家通过我的相信分析，那么的话相信大家对于浮点数在内存中的存储一定有一番理解了，下面我就为大家介绍刚刚的题目。 1.首先呢题目中说n=9而9又是一个正数，根据前面讲到的，可以知道9的原码，反码，补码都是一样的，9的补码是00000000 00000000 00000000 001001 但是呢由于后面的语句意思是说将9.0的浮点数数地址强制转换为整数的地址，根据上面讲到的IEEE754标准可以知道的是在内存中其9.0的补码存储为浮点数地址，V=（-1）0 *1.001 *23 那么E等于3+127=130；M等于0001，S=0，也就是这样子的：0 100 0001 0001 0000 0000 0000 0000 0000 由于最高位是0那么原反补一样，是01000001000100000000000000000000，转换为十进制整数，就是1,091,567,616 2.由于整数9转化为浮点数，是0 00000000 0000000000000000000001001 由于E全为0那么这个数就非常接近0，也就是0 因此呢，通过以上的分析，大家想必对于上例也有了一番新的认识与理解，非常遗憾，这次的讲解再一次的结束了，我会在下一篇文章中给大家带来详细讲解的 完（结束）.\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E6%95%B4%E6%95%B0%E5%92%8C%E6%B5%AE%E7%82%B9%E6%95%B0%E5%9C%A8%E5%86%85%E5%AD%98%E4%B8%AD%E5%AD%98%E5%82%A8/","summary":"\u003cp\u003e\u003ccode\u003e大家好，今天我为大家介绍关于整数与浮点数在内存中的存储\u003c/code\u003e，我们通过今天的讲解一定会更加理解浮点数的储存模式，更好修炼大家的内功，来开启我们的学习之旅吧！\u003c/p\u003e\n\u003ch4 id=\"整数和浮点数在内存中存储\"\u003e整数和浮点数在内存中存储\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_2\"\u003e1.整数与浮点数在内存中存储是否一样\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_5\"\u003e整数与浮点数在内存中存储的例子\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_40\"\u003e2.整数在内存中的存储\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_42\"\u003e整数在内存中存储的具体例子\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_49\"\u003e3.浮点数在内存中的存储\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_50\"\u003e1.规定相关东西\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#IEEE754_56\"\u003eIEEE754的详细相关规定\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_70\"\u003e2.浮点数在内存中的具体示例\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3_74\"\u003e3.讲解上例当中的题目\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"1整数与浮点数在内存中存储是否一样\"\u003e1.整数与浮点数在内存中存储是否一样\u003c/h2\u003e\n\u003cp\u003e首先我们大家都有一个疑问，在内存中数据的存储不都是以二进制形式存储的吗？还有什么不同呢？其实我在学习本节课之前我和大家的认知是一样的，都是认为他们的存储都是一样的，但是今天我可以告诉给大家，在内存中存储其实并不一样，下面我为大家详细的介绍一下。\u003c/p\u003e\n\u003ch3 id=\"整数与浮点数在内存中存储的例子\"\u003e整数与浮点数在内存中存储的例子\u003c/h3\u003e\n\u003cp\u003e下面我为大家举出一个关于他们在内存存储的例子，如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\nint main()\n{\n    int n = 9;//整型的存储方式，补码\n    //00000000000000000000000000001001   9的补码\n    //\n    float* pFloat = (float*)\u0026amp;n;\n\n    printf(\u0026#34;n的值为：%d\\n\u0026#34;, n);//9\n    printf(\u0026#34;*pFloat的值为：%f\\n\u0026#34;, *pFloat);\n    //0 00000000 00000000000000000001001\n    //E 为全0\n    //(-1)^0 * 0.00000000000000000001001 *  2^-126\n    //1*\n    //0.000000\n\n    *pFloat = 9.0;\n    //1001.0\n    //(-1)^0 * 1.001*2^3\n    //S=0\n    //M=1.001\n    //E = 3\n    //01000001000100000000000000000000\n    //\n    printf(\u0026#34;num的值为：%d\\n\u0026#34;, n);\n    printf(\u0026#34;*pFloat的值为：%f\\n\u0026#34;, *pFloat);//9.0\n    return 0;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e大家觉得这个会是多少呢？我想大家所想的可能会和标准运行结果会有差距，那么的话，下面我就为大家介绍一下，这个程序的运行结果：是9,0，1091567616，9.0\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E6%95%B4%E6%95%B0%E5%92%8C%E6%B5%AE%E7%82%B9%E6%95%B0%E5%9C%A8%E5%86%85%E5%AD%98%E4%B8%AD%E5%AD%98%E5%82%A8/images/4b360db0fd17a82162a9734848396396.png\"\u003e\n因此我们可以知道，浮点数在内存中的存储与整数是不一样的，存储原理是不同的，那么为何会造成这种不同呢？下面我就为大家介绍一下，他们存储的具体实现。\u003c/p\u003e\n\u003ch2 id=\"2整数在内存中的存储\"\u003e2.整数在内存中的存储\u003c/h2\u003e\n\u003cp\u003e这个我们都知道，整数在内存中是按照二进制的补码进行存储的，而正数的补码就是正数其本身，但是负数的补码则是其原二进制的反码+1，具体的讲在二进制当中我已经为大家详细介绍过了，下面我们看一下他们的具体的例子就可以了。\u003c/p\u003e","title":"整数和浮点数在内存中存储"},{"content":"字符串中删除后导星号的问题 一.题目分析\n二.我所犯的错误\n2.1我的代码\n2.1我的错误的代码运行的结果\n2.1.1我的结果分析\n三.错误分析\n3.1 我总结的错误缘由\n主要问题总结：\n3.2 修改的意见\n改进点解释：\n举个例子：\n测试：\n3.3 修正好后的代码运行结果如下：\n3.4 我的修正后的代码如下：\n四.总结\n一.题目分析 二.我所犯的错误 2.1我的代码 #include \u0026lt;stdio.h\u0026gt; void fun( char *a ) { int count=0,count_a=0,i,j=0; while(*a!=\u0026#39;\\0\u0026#39;) { a++; count++; } a=a-1; while(*a==\u0026#39;*\u0026#39;) { a--; count_a++; } for(i=0;i\u0026lt;count-count_a;i++) { a[j++]=a[i]; } a[j]=\u0026#39;\\0\u0026#39;; } main() { char s[81];void NONO (); printf(\u0026#34;Enter a string:\\n\u0026#34;);gets(s); fun( s ); printf(\u0026#34;The string after deleted:\\n\u0026#34;);puts(s); NONO(); getchar(); } void NONO () {/* 本函数用于打开文件，输入数据，调用函数，输出数据，关闭文件。 */ FILE *in, *out ; int i ; char s[81] ; in = fopen(\u0026#34;in.dat\u0026#34;,\u0026#34;r\u0026#34;) ; out = fopen(\u0026#34;out.dat\u0026#34;,\u0026#34;w\u0026#34;) ; for(i = 0 ; i \u0026lt; 10 ; i++) { fscanf(in, \u0026#34;%s\u0026#34;, s) ; fun(s) ; fprintf(out, \u0026#34;%s\\n\u0026#34;, s) ; } fclose(in) ; fclose(out) ; } 2.1我的错误的代码运行的结果 2.1.1我的结果分析 通过我的运行结果可以知道，我并没有完成删除后导星号的任务，这个函数的功能一点都没有执行。但是我觉得的写的代码是正确的，我一遍一遍分析我的代码，发现是我的代码思路可行的，就是不知道为何运行出来的是错误的，我一遍一遍分析，就是不知道，在哪里出错了，这种感觉是异常痛苦的，百思不得其解的痛苦感，我想我就这样搞不定了吗？后来，我我问了一位老师，人家给我讲了一下。\n三.错误分析 3.1 我总结的错误缘由 我的问题出在指针操作和逻辑处理上，主要有以下几点：\n指针的回退与字符覆盖冲突：\n在第一次遍历时，a 被移动到字符串末尾，并且在移除末尾的星号时没有保存起始位置。当我想再次使用 a 去操作字符串时，我已经丢失了它的原始位置，所以会导致逻辑错误。 字符的移动逻辑有问题：\n我试图在移除星号后通过 a[j++]=a[i]; 这种方式将有效字符重新赋值，但 a 指针已经被移动过，并且我没有合适的边界检查，可能会导致覆盖和数组越界。 没有保存指针的初始位置：\n一旦我用 a++ 移动指针，你就失去了对字符串起始位置的引用，所以后续的字符操作都可能出错。 主要问题总结： 指针丢失原始位置：a 指针在遍历时被不断递增，导致我无法恢复到字符串的开始位置。\n字符覆盖错误：在进行字符移位时，a[j++] = a[i]; 中的 a[i] 实际上是错误的，因为 a 已经被修改，无法正确访问原字符。\n边界处理不正确：最后没有处理字符串末尾的 '\\0'，使得最终的字符串可能没有正确终止。\n3.2 修改的意见 改进点解释： start 和 end 指针：\n保留了原字符串的起始位置 start，并使用 end 指向字符串的最后一个字符。这样可以避免对原始字符串指针 a 进行过多修改，从而使代码更清晰。 指针回退和字符移动：\n在计算完字符串长度后，end 指针回退以跳过末尾的星号，同时计算星号的数量。\n使用 for 循环将有效的字符（不包括星号）复制到前面的位置。\n末尾终止符 '\\0'：\n复制完有效字符后，在新的字符串末尾添加终止符 '\\0'，确保字符串的结束标志正确。 举个例子： 假设输入字符串是 \u0026quot;hello***\u0026quot;，程序会做以下事情：\n计算字符串总长度为 8。\n统计末尾的 3 个星号。\n将 hello 的字符移动到前面，并添加结束符 '\\0'，最终结果是 \u0026quot;hello\u0026quot;。\n测试： 我可以通过如下代码来测试：\n#include \u0026lt;stdio.h\u0026gt; void fun(char *a); int main() { char str[] = \u0026#34;hello***\u0026#34;; fun(str); printf(\u0026#34;Modified string: %s\\n\u0026#34;, str); // 输出 \u0026#34;hello\u0026#34; return 0; } 这段代码应该会输出修改后的字符串 hello。\n3.3 修正好后的代码运行结果如下： 3.4 我的修正后的代码如下： #include \u0026lt;stdio.h\u0026gt; void fun( char *a ) { int count=0,count_a=0,i,j=0; char *end,*start; start=a; while(*a!=\u0026#39;\\0\u0026#39;) { a++; count++; } end=a-1; while(*end==\u0026#39;*\u0026#39;) { end--; count_a++; } for(i=0;i\u0026lt;count-count_a;i++) { start[j++]=start[i]; } start[j]=\u0026#39;\\0\u0026#39;; } main() { char s[81];void NONO (); printf(\u0026#34;Enter a string:\\n\u0026#34;);gets(s); fun( s ); printf(\u0026#34;The string after deleted:\\n\u0026#34;);puts(s); NONO(); getchar(); } void NONO () {/* 本函数用于打开文件，输入数据，调用函数，输出数据，关闭文件。 */ FILE *in, *out ; int i ; char s[81] ; in = fopen(\u0026#34;in.dat\u0026#34;,\u0026#34;r\u0026#34;) ; out = fopen(\u0026#34;out.dat\u0026#34;,\u0026#34;w\u0026#34;) ; for(i = 0 ; i \u0026lt; 10 ; i++) { fscanf(in, \u0026#34;%s\u0026#34;, s) ; fun(s) ; fprintf(out, \u0026#34;%s\\n\u0026#34;, s) ; } fclose(in) ; fclose(out) ; } 四.总结 在以上的代码当中我错误的使用一个指针，这样在后续操作的时候，会出现挺 多的异常，这样就运行不出我们想要的结果了，总的来看，我写的代码的大概 框架是正确的，但是呢对于指针的处理不太熟练，以后在操作指针的时候，要 用不同变量进行处理，希望同学们，汲取我的错误的教训，吸收经验，从而更 好的成长， 再见！！！\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E5%88%A0%E9%99%A4%E5%90%8E%E5%AF%BC%E6%98%9F%E5%8F%B7%E7%9A%84%E9%97%AE%E9%A2%98/","summary":"\u003ch4 id=\"字符串中删除后导星号的问题\"\u003e字符串中删除后导星号的问题\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_1\"\u003e一.题目分析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_4\"\u003e二.我所犯的错误\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#21_5\"\u003e2.1我的代码\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#21_54\"\u003e2.1我的错误的代码运行的结果\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#211_56\"\u003e2.1.1我的结果分析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_58\"\u003e三.错误分析\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#31__59\"\u003e3.1 我总结的错误缘由\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_71\"\u003e主要问题总结：\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#32__77\"\u003e3.2 修改的意见\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_79\"\u003e改进点解释：\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_91\"\u003e举个例子：\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_99\"\u003e测试：\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#33__117\"\u003e3.3 修正好后的代码运行结果如下：\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#34__119\"\u003e3.4 我的修正后的代码如下：\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_171\"\u003e四.总结\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一题目分析\"\u003e一.题目分析\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E5%88%A0%E9%99%A4%E5%90%8E%E5%AF%BC%E6%98%9F%E5%8F%B7%E7%9A%84%E9%97%AE%E9%A2%98/images/8774c783cdc14135aa464ad771ff5fc6.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"二我所犯的错误\"\u003e二.我所犯的错误\u003c/h2\u003e\n\u003ch3 id=\"21我的代码\"\u003e2.1我的代码\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include \u0026lt;stdio.h\u0026gt;\nvoid  fun( char *a )\n{\n\tint count=0,count_a=0,i,j=0;\n\twhile(*a!=\u0026#39;\\0\u0026#39;)\n\t{\n\t\ta++;\n\t\tcount++;\n\t}\n\ta=a-1;\n\twhile(*a==\u0026#39;*\u0026#39;)\n\t{\n\t\ta--;\n\t\tcount_a++;\n\t}\n\tfor(i=0;i\u0026lt;count-count_a;i++)\n\t{\n\t\ta[j++]=a[i];\n\t}\n\ta[j]=\u0026#39;\\0\u0026#39;;\n}\nmain()\n{  char  s[81];void NONO ();\n   printf(\u0026#34;Enter a string:\\n\u0026#34;);gets(s);\n   fun( s );\n   printf(\u0026#34;The string after deleted:\\n\u0026#34;);puts(s);\n   NONO();\n  getchar();\n}\nvoid NONO ()\n{/* 本函数用于打开文件，输入数据，调用函数，输出数据，关闭文件。 */\n  FILE *in, *out ;\n  int i ; char s[81] ;\n  in = fopen(\u0026#34;in.dat\u0026#34;,\u0026#34;r\u0026#34;) ;\n  out = fopen(\u0026#34;out.dat\u0026#34;,\u0026#34;w\u0026#34;) ;\n  for(i = 0 ; i \u0026lt; 10 ; i++) {\n    fscanf(in, \u0026#34;%s\u0026#34;, s) ;\n    fun(s) ;\n    fprintf(out, \u0026#34;%s\\n\u0026#34;, s) ;\n  }\n  fclose(in) ;\n  fclose(out) ;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"21我的错误的代码运行的结果\"\u003e2.1我的错误的代码运行的结果\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E5%88%A0%E9%99%A4%E5%90%8E%E5%AF%BC%E6%98%9F%E5%8F%B7%E7%9A%84%E9%97%AE%E9%A2%98/images/b9e2d83c241e4609b894240ba88389f4.png\"\u003e\u003c/p\u003e","title":"字符串中删除后导星号的问题"},{"content":"在本节中我将为大家介绍一些关于字符函数以及字符串函数，在本节中我将为大家详细介绍某些重要函数的模拟实现，使大家更好的了解这些函数的具体逻辑，下面就开启了本次的学习之旅，希望大家都有所收获。\n字符函数和字符串函数 一.字符函数\n1.字符分类函数\n1.字符分类函种类及作用\n2.字符转换函数\n1.字符转换函数包括哪些\n1.大写字符转化为小写字符\n1.大写字符转化为小写字符函数的具体诠释\n2.大写字符转化为小写字符函数的模拟实现\n2.小写字符转化为大写字符的函数\n1.小写字符转化为大写字符函数的具体诠释\n2.小写字符转化为大写字符函数的模拟实现\n二.字符串函数\n1.字符串函数包括哪些\n1.strlen函数\n2.strcpy函数\n3.strcat函数\n4.strcmp函数\n一.字符函数 字符是组成字符串的根本，那么学好字符函数对我们今后的学习起到了重要的作用，而在众多的字符函数当中有两种类型一个是判断的那种函数也就是字符分类函数，另一种则是转化大小写的那种，下面就为大家具体的诠释其中的奥妙。\n1.字符分类函数 1.字符分类函种类及作用 iscntrl 判断是否输入控制字符 isspace 输入空白字符返回真 isdigit 输入数字字符返回真 isxdigit 输入16进制数字字符返回真 islower 输入小写字符返回真 isupper 输入大写字符返回真 isalpha 输入字母字符返回真 isalnum 输入数字或者字母字符返回真 ispunct 输入标点字符返回真 isgraph 输入图形字符返回真 isprint 输入可打印字符返回真 2.字符转换函数 字符转化函数具体有，大写字符转化成小写字符，另有小写字符转化为大写字符，当然这些都是针对的是输入的是字母字符时方可使用的，但是若果使用的是非字母字符那么的话可能会导致错误，从而程序无法运行。\n1.字符转换函数包括哪些 1.大写字符转化为小写字符 1.大写字符转化为小写字符函数的具体诠释 形如以下形式：\nint tolower ( int c ); 给大家举出一下例子，我们一起看一下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;ctype.h\u0026gt; int main () { int i=0; char str[]=\u0026#34;Test String.\\n\u0026#34;; char c; while (str[i]) { c=str[i]; putchar (tolower(c)); i++; } return 0; } 当然这样写没有什么问题，但是我们也可以这样子写如下：\n#define _CRT_SECURE_NO_WARNINGS 1 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;ctype.h\u0026gt; int main() { int i = 0; char str[] = \u0026#34;Test String.\\n\u0026#34;; char c; while (str[i]) { //c = str[i]; putchar(tolower(str[i])); i++; } return 0; } 因为int 与 char 都是整型的因此两种写法是没有什么问题的，但是我们平常在书写的时候要尽量采用第二种写法，因为这样可能更快一些，下面是在VS上运行的结果，如下： 2.大写字符转化为小写字符函数的模拟实现 int my_tolower(int a) { if (a \u0026gt;= \u0026#39;A\u0026#39; \u0026amp;\u0026amp; a \u0026lt;= \u0026#39;Z\u0026#39;) a = a + 32; return a; } 这个模拟实现出来的代码与库里面的代码是一模一样的。\n2.小写字符转化为大写字符的函数 1.小写字符转化为大写字符函数的具体诠释 形如以下形式：\nint toupper ( int c ); 给大家举出一下例子，我们一起看一下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;ctype.h\u0026gt; int main () { int i=0; char str[]=\u0026#34;Test String.\\n\u0026#34;; char c; while (str[i]) { c=str[i]; putchar (toupper(c)); i++; } return 0; } 当然这样写没有什么问题，但是我们也可以这样子写如下：\n#define _CRT_SECURE_NO_WARNINGS 1 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;ctype.h\u0026gt; int main() { int i = 0; char str[] = \u0026#34;Test String.\\n\u0026#34;; char c; while (str[i]) { //c = str[i]; putchar(toupper(str[i])); i++; } return 0; } 因为int 与 char 都是整型的因此两种写法是没有什么问题的，但是我们平常在书写的时候要尽量采用第二种写法，因为这样可能更快一些，下面是在VS上运行的结果，如下： 2.小写字符转化为大写字符函数的模拟实现 int my_upper(int a) { if (a \u0026gt;= \u0026#39;a\u0026#39; \u0026amp;\u0026amp; a \u0026lt;= \u0026#39;z\u0026#39;) a = a - 32; return a; } 这个模拟实现出来的代码与库里面的代码是一模一样的\n二.字符串函数 在这里面我们会遇到比较多的字符串函数，但是你会发现这些函数其实都差不多，所以当我为各位介绍一下函数时，请各位不要过于惊慌，不要太害怕，我们会在今后的学习中会经常遇到这些函数，所以我会用我最大的努力为大家详细的介绍这些有用的函数，望大家喜欢，下面就开始我们的字符串函数之旅。\n1.字符串函数包括哪些 其实字符串函数是比较多的，那么今天我就为大家介绍几种字符串函数，余下的函数，等以后再为大家介绍，我今天为大家介绍的函数是一下几种：strlen函数，strcpy函数，strcat函数，strcmp函数。\n1.strlen函数 这个函数主要的功能是求字符串的长度的。 我给大家举出例子如下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main () { char szInput[256]; printf (\u0026#34;Enter a sentence: \u0026#34;); gets (szInput); printf (\u0026#34;The sentence entered is %u characters long.\\n\u0026#34;,(unsigned)strlen(szInput)); return 0; } 对于这个函数，我模拟实现如下：\nint my_strlen(char* a) { char* b = a; while (*a++); return a - b-1; } 其余地方都是差不多的，我可以相信大家对于这个函数一定有更加深刻的认识与了解。\n2.strcpy函数 这个函数的主要功能是将一个字符串复制到另一个字符串当中，如下我给大家举出下面的例子：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main () { char str1[]=\u0026#34;Sample string\u0026#34;; char str2[40]; char str3[40]; strcpy (str2,str1); strcpy (str3,\u0026#34;copy successful\u0026#34;); printf (\u0026#34;str1: %s\\nstr2: %s\\nstr3: %s\\n\u0026#34;,str1,str2,str3); return 0; } 对于这个函数我模拟实现如下：\nchar* my_strcpy(char* a,const char *b) { char* ret = a; assert(a); assert(a); while (*a++ = *b++); return ret; } 通过我对于这个函数的模拟我有理由相信大家一定会对于这个函数会有新的认识与了解，知道其中实现的具体的逻辑，对这个函数使用会更加的熟练。\n3.strcat函数 这个函数是将一个字符串复制到另一个字符串的末尾，具体的例子如下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main () { char str[80]; strcpy (str,\u0026#34;these \u0026#34;); strcat (str,\u0026#34;strings \u0026#34;); strcat (str,\u0026#34;are \u0026#34;); strcat (str,\u0026#34;concatenated.\u0026#34;); puts (str); return 0; } 大家好这个程序的运行结果如下所示： 那么接下来，大家好我将为大家展示出我对于这个函数的模拟实现，下面就是具体的例子如下：\nchar* my_strcat(char* a, const char* b) { char* ret = a; assert(a); assert(b); while (*a++); while (*a++ = *b++); return ret; } 大家好，我的这个函数实现的基本逻辑与库函数里面的逻辑可以说是一样的，那么大家对于该函数的阅读，一定会在一定程度上增进对于库函数strcat的了解与认识，今后我们会对该函数的使用会愈加的频繁，下面我就为大家介绍今天的最后一个函数。\n4.strcmp函数 这个函数是比较俩个字符串是否都是一样的，下面我就为大家举出具体的例子；\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main () { char key[] = \u0026#34;apple\u0026#34;; char buffer[80]; do { printf (\u0026#34;Guess my favorite fruit? \u0026#34;); fflush (stdout); scanf (\u0026#34;%79s\u0026#34;,buffer); } while (strcmp (key,buffer) != 0); puts (\u0026#34;Correct answer!\u0026#34;); return 0; } 大家好这个函数的具体运行结果如下： 大家只要脊柱这个库函数就可以了，具体的实现我今天就不为大家介绍了，大家若果有兴趣的话可观看一些视频，例如鹏哥的视频，鹏哥会为大家详细的介绍这个函数的具体实现及基本逻辑的，很遗憾今天我为大家讲解的知识点结束了，若果大家感兴趣的话，可以关注的账号，我会定期的为大家讲解的。 完（结束）\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E5%AD%97%E7%AC%A6%E5%87%BD%E6%95%B0%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%87%BD%E6%95%B0/","summary":"\u003cp\u003e在本节中我将为大家介绍一些关于字符函数以及字符串函数，在本节中我将为大家详细介绍某些重要函数的模拟实现，使大家更好的了解这些函数的具体逻辑，下面就开启了本次的学习之旅，希望大家都有所收获。\u003c/p\u003e\n\u003ch4 id=\"字符函数和字符串函数\"\u003e字符函数和字符串函数\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_2\"\u003e一.字符函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_4\"\u003e1.字符分类函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_5\"\u003e1.字符分类函种类及作用\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_19\"\u003e2.字符转换函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_21\"\u003e1.字符转换函数包括哪些\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_22\"\u003e1.大写字符转化为小写字符\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_23\"\u003e1.大写字符转化为小写字符函数的具体诠释\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_67\"\u003e2.大写字符转化为小写字符函数的模拟实现\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_79\"\u003e2.小写字符转化为大写字符的函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_80\"\u003e1.小写字符转化为大写字符函数的具体诠释\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2_124\"\u003e2.小写字符转化为大写字符函数的模拟实现\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#_135\"\u003e二.字符串函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1_137\"\u003e1.字符串函数包括哪些\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#1strlen_139\"\u003e1.strlen函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#2strcpy_165\"\u003e2.strcpy函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#3strcat_194\"\u003e3.strcat函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"#4strcmp_226\"\u003e4.strcmp函数\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"一字符函数\"\u003e一.字符函数\u003c/h2\u003e\n\u003cp\u003e字符是组成字符串的根本，那么学好字符函数对我们今后的学习起到了重要的作用，而在众多的字符函数当中有两种类型一个是判断的那种函数也就是字符分类函数，另一种则是转化大小写的那种，下面就为大家具体的诠释其中的奥妙。\u003c/p\u003e\n\u003ch3 id=\"1字符分类函数\"\u003e1.字符分类函数\u003c/h3\u003e\n\u003ch4 id=\"1字符分类函种类及作用\"\u003e1.字符分类函种类及作用\u003c/h4\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eiscntrl\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e判断是否输入控制字符\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eisspace\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入空白字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eisdigit\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入数字字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eisxdigit\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入16进制数字字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eislower\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入小写字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eisupper\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入大写字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eisalpha\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入字母字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eisalnum\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入数字或者字母字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eispunct\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入标点字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eisgraph\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入图形字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eisprint\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e输入可打印字符返回真\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"2字符转换函数\"\u003e2.字符转换函数\u003c/h3\u003e\n\u003cp\u003e字符转化函数具体有，大写字符转化成小写字符，另有小写字符转化为大写字符，当然这些都是针对的是输入的是字母字符时方可使用的，但是若果使用的是非字母字符那么的话可能会导致错误，从而程序无法运行。\u003c/p\u003e\n\u003ch4 id=\"1字符转换函数包括哪些\"\u003e1.字符转换函数包括哪些\u003c/h4\u003e\n\u003ch5 id=\"1大写字符转化为小写字符\"\u003e1.大写字符转化为小写字符\u003c/h5\u003e\n\u003ch6 id=\"1大写字符转化为小写字符函数的具体诠释\"\u003e1.大写字符转化为小写字符函数的具体诠释\u003c/h6\u003e\n\u003cp\u003e形如以下形式：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eint tolower ( int c );\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e给大家举出一下例子，我们一起看一下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#include \u0026lt;stdio.h\u0026gt;\n#include \u0026lt;ctype.h\u0026gt;\nint main ()\n{\n  int i=0;\n  char str[]=\u0026#34;Test String.\\n\u0026#34;;\n  char c;\n  while (str[i])\n  {\n    c=str[i];\n    putchar (tolower(c));\n    i++;\n  }\n  return 0;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e当然这样写没有什么问题，但是我们也可以这样子写如下：\u003c/p\u003e","title":"字符函数和字符串函数"},{"content":"@TOC\n1.问题背景 首先这个问题诞生于如此条件下，我在跟随佳乐老师创建完lisi，zhangsan，wangwu之后呢，我就用lisi作为用户登录服务器node1，这里面设置了禁锢操作，路径是/export/public，而public里面拥有2个文件夹.一个是logs，另一个是src，同时设置了黑白名单,张三设置在黑名单，王五和李四设置在白名单\n2.遇到的问题 1.遇到的问题其实就是，在我用lisi把客户端的根目录下的a.log，发送给服务器的logs目录，无法操作 2.后来，我在logs目录下创建文件夹也是无法操作的，也是显示权限不足 ==但是呢，我在服务器的这个logs目录下创建文件夹是可以创建的== 3.就很奇怪，再去服务器是可以搞定的，是可以创建文件夹的，但是在客户端是没有办法进行创建的，我当时以为是logs的权限设置的不对，用的是root创建，权限大的缘故，但是其实并不是如此的,后面我在服务器用了lisi照样是可以创建的 4.后来，我仔细检查了这些文件夹的权限，并再次赋权,这次显然也是不行的 3.问题解决的方法 解决的办法，后来，我把我的问题，这些比较奇怪的地方，我问了千文，后来，在他的帮助之下我成功了，在客户端我成功的在服务器的logs上面创建了文件夹，这意味着什么呢，显然这意味着，我可以把客户端家目录的a.log上传给服务器的logs里面，结果显然和我想的一样 下面是千文告诉我的解决方案，让我一步一步排查，最后成功找到了问题关键所咋\n1.ai的方法 这个对比信息非常关键！它直接帮我们排除了文件系统权限问题。\n为什么本地能写、FTP 不能写？ 本质区别在于执行写入的\u0026quot;身份\u0026quot;不同：\n场景 执行者 受什么约束 本地 mkdir lisi 自己的 shell 进程 只看文件系统的 rwx 权限 ✅ FTP mkdir vsftpd 守护进程代为执行 受 vsftpd.conf 开关 + SELinux 对 ftpd 的策略双重约束 ❌ 所以本地能写，完全不能证明 FTP 也能写。问题 100% 卡在 FTP 守护进程这一层。下面两个是头号嫌疑。\n嫌疑一：write_enable 没开（最常见！） vsftpd 的 write_enable 默认是 NO。如果配置文件里没显式写 write_enable=YES，那么所有写操作（上传、mkdir、删除、改名）一律拒绝，正好对应你看到的 533 和 550。而它不影响读和 ls，所以你能登录、能列目录。\ngrep -i write_enable /etc/vsftpd/vsftpd.conf 如果没有这一行，或显示 write_enable=NO → 就是它！ 修复：\necho \u0026#34;write_enable=YES\u0026#34; \u0026gt;\u0026gt; /etc/vsftpd/vsftpd.conf systemctl restart vsftpd 嫌疑二：SELinux 拦住了 ftpd SELinux 是针对守护进程上下文（ftpd_t） 做限制的，本地 shell 不受这套规则管，所以同样会出现\u0026quot;本地行、FTP 不行\u0026quot;。\n最快验证法——临时关掉 SELinux 测一次：\ngetenforce # 先看状态，多半是 Enforcing setenforce 0 # 临时切到宽容模式 然后不要重启 vsftpd，直接在客户端 lftp 里再 mkdir 一次：\n如果成功了 → 确认是 SELinux，永久解决： setsebool -P ftpd_full_access on setenforce 1 # 记得开回去 如果还是失败 → 不是 SELinux，回到嫌疑一。 兜底：看日志，让服务器自己告诉你原因 # vsftpd / PAM 的拒绝记录 tail -n 30 /var/log/secure # SELinux 的拒绝记录（最有价值） ausearch -m avc -ts recent | grep ftp # 或 cat /var/log/audit/audit.log | grep -i denied | tail 日志里会写明到底是 write_enable 拒绝，还是 SELinux 的 avc: denied。\n建议的排查顺序 先 grep write_enable /etc/vsftpd/vsftpd.conf，没有就加上并重启 → 重试 还不行就 setenforce 0 重试 → 判断是否 SELinux 都不行就看 /var/log/secure 和 ausearch 的报错贴给我 补充一句：你之前把 chroot 根目录 /export/public 设成 777 的话，记得要么改回 755，要么在配置里加 allow_writeable_chroot=YES，否则某些版本会直接拒绝登录。不过你现在能登录能 cd，说明这一项暂时没卡你，先聚焦上面两点。\n把第 1 步 grep 的结果和第 2 步 setenforce 0 后的现象告诉我，基本就能一锤定音。\n2.我看出了什么 看了千文的描述，我知道了，大概率就是 SELinux 的权限问题，解决方案，千文也告诉我了\n`Enforcing` —— 这就对上了！SELinux 正处于**强制模式**，而它对 `ftpd` 守护进程的写操作是单独设防的，本地 shell 完全不受这套规则管，所以才会出现\u0026#34;服务器本地能 mkdir、客户端 FTP 不能\u0026#34;的典型症状。再加上你的目录在 `/export/public` 这种**自定义路径**下，SELinux 给它的文件类型往往不是 FTP 可写的类型，被拦几乎是必然的。 下面按顺序做，逻辑上能一次性把\u0026#34;开关\u0026#34;和\u0026#34;SELinux\u0026#34;两个变量都隔离清楚。 ## 第 1 步：先确认写开关是开的（避免误判） ```bash grep -i write_enable /etc/vsftpd/vsftpd.conf 如果没有这一行或显示 write_enable=NO → 补上并重启： echo \u0026#34;write_enable=YES\u0026#34; \u0026gt;\u0026gt; /etc/vsftpd/vsftpd.conf systemctl restart vsftpd 如果已经是 write_enable=YES → 不用动，直接下一步。 为什么先查它：写要成功必须 write_enable 和 SELinux 同时放行。若这个开关没开，下一步关 SELinux 也救不活，会让你误以为\u0026quot;不是 SELinux 的锅\u0026quot;。\n第 2 步：临时关掉 SELinux 做隔离测试 setenforce 0 # 切到宽容模式，立即生效，无需重启 vsftpd getenforce # 应显示 Permissive 第 3 步：回客户端 lftp 重试 lftp lisi@192.168.149.128 cd /logs mkdir 222 看结果分支判断 ✅ 如果这次成功了 → 元凶确认含 SELinux。做永久修复（二选一）：\n# 方案A：放开 ftpd 的整体访问（最省事，覆盖大多数场景） setsebool -P ftpd_full_access on # 方案B：只给目标目录打上\u0026#34;FTP可读写\u0026#34;的上下文（更精细、更安全） semanage fcontext -a -t public_content_rw_t \u0026#34;/export/public/logs(/.*)?\u0026#34; semanage fcontext -a -t public_content_rw_t \u0026#34;/export/public/src(/.*)?\u0026#34; restorecon -Rv /export/public # 修完务必把 SELinux 开回去验证 setenforce 1 开回 Enforcing 后再到客户端测一次，能写才算真正解决。\n❌ 如果 setenforce 0 后仍然失败 → 不是 SELinux（或不止它），去看日志，让服务器自报原因：\ntail -n 30 /var/log/secure ausearch -m avc -ts recent 2\u0026gt;/dev/null | grep -i ftp ⚠️ 提醒：setenforce 0 是临时的，机器一重启就恢复 Enforcing，所以验证完一定要执行上面的永久修复，别让它一直裸奔在宽容模式。\n请把 **第 1 步 grep` 的输出** 和 第 3 步关 SELinux 后客户端的现象 发给我，基本就能一锤定音了。\n3.我的解决方案 显然，我是按照setenforce 0 之后，重启服务器，然后就可以了 4.给我们的警示 ``给我们的启发其实就是，在学习Linux过程之中，人工智能永远是你的最好的老师，只要你将自己的问题发给他，跟着他的提示一步一步进行排查，然后将排查得到的结果发送给ai，那么他将会告诉你一个中肯的建议，一般都是成功的，这个 SELinux 确实挺麻烦的，当时我以为是我的配置文件出错了，后来呢，我不断尝试，修改配置文件，但是依然没有成功，我才在千文的指导之下明白了他的存在。\n","permalink":"https://dsduyopg-github-io.pages.dev/posts/%E5%85%B6%E4%BB%96%E5%8D%9A%E5%AE%A2/%E5%85%B3%E4%BA%8Eftp%E4%B8%8Eselinux%E7%9A%84%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98/","summary":"\u003cp\u003e@\u003ca href=\"%E5%85%B3%E4%BA%8Eftp%E4%B8%8ESELinux%E7%9A%84%E5%86%B2%E7%AA%81%E9%97%AE%E9%A2%98\"\u003eTOC\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"1问题背景\"\u003e1.问题背景\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e首先这个问题诞生于如此条件下，我在跟随佳乐老师创建完lisi，zhangsan，wangwu之后呢，我就用lisi作为用户登录服务器node1，这里面设置了禁锢操作，路径是/export/public，而public里面拥有2个文件夹.一个是logs，另一个是src，同时设置了黑白名单,张三设置在黑名单，王五和李四设置在白名单\u003c/strong\u003e\u003c/p\u003e\n\u003ch1 id=\"2遇到的问题\"\u003e2.遇到的问题\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e1.遇到的问题其实就是，在我用lisi把客户端的根目录下的a.log，发送给服务器的logs目录，无法操作\u003c/strong\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"https://i-blog.csdnimg.cn/direct/31d543a0e0d74a2facd0b57caacd8359.png\"\u003e\n\u003cstrong\u003e2.后来，我在logs目录下创建文件夹也是无法操作的，也是显示权限不足\u003c/strong\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"https://i-blog.csdnimg.cn/direct/55c072190ed842009aa2962c3868c3fc.png\"\u003e\n==但是呢，我在服务器的这个logs目录下创建文件夹是可以创建的==\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"https://i-blog.csdnimg.cn/direct/96f60e65512b4a38be2d95aa4d888fd8.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e3.就很奇怪，再去服务器是可以搞定的，是可以创建文件夹的，但是在客户端是没有办法进行创建的，我当时以为是logs的权限设置的不对，用的是root创建，权限大的缘故，但是其实并不是如此的,后面我在服务器用了lisi照样是可以创建的\u003c/code\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"https://i-blog.csdnimg.cn/direct/5596133494e8476a96424bbf30353246.png\"\u003e\n4.\u003ccode\u003e后来，我仔细检查了这些文件夹的权限，并再次赋权,这次显然也是不行的\u003c/code\u003e\n\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"https://i-blog.csdnimg.cn/direct/ed81cf0aff1743229e25473bf7b232d7.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"3问题解决的方法\"\u003e3.问题解决的方法\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003e解决的办法，后来，我把我的问题，这些比较奇怪的地方，我问了千文，后来，在他的帮助之下我成功了，在客户端我成功的在服务器的logs上面创建了文件夹，这意味着什么呢，显然这意味着，我可以把客户端家目录的a.log上传给服务器的logs里面，结果显然和我想的一样\u003c/code\u003e\n\u003cstrong\u003e下面是千文告诉我的解决方案，让我一步一步排查，最后成功找到了问题关键所咋\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"1ai的方法\"\u003e1.ai的方法\u003c/h2\u003e\n\u003cp\u003e这个对比信息非常关键！它直接帮我们\u003cstrong\u003e排除了文件系统权限问题\u003c/strong\u003e。\u003c/p\u003e\n\u003ch2 id=\"为什么本地能写ftp-不能写\"\u003e为什么本地能写、FTP 不能写？\u003c/h2\u003e\n\u003cp\u003e本质区别在于\u003cstrong\u003e执行写入的\u0026quot;身份\u0026quot;不同\u003c/strong\u003e：\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e场景\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e执行者\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e受什么约束\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e本地 \u003ccode\u003emkdir\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003elisi 自己的 shell 进程\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e只看文件系统的 rwx 权限 ✅\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eFTP \u003ccode\u003emkdir\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003evsftpd 守护进程\u003c/strong\u003e代为执行\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e受 \u003ccode\u003evsftpd.conf\u003c/code\u003e 开关 + \u003cstrong\u003eSELinux 对 ftpd 的策略\u003c/strong\u003e双重约束 ❌\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e所以本地能写，\u003cstrong\u003e完全不能证明\u003c/strong\u003e FTP 也能写。问题 100% 卡在 FTP 守护进程这一层。下面两个是头号嫌疑。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"嫌疑一write_enable-没开最常见\"\u003e嫌疑一：\u003ccode\u003ewrite_enable\u003c/code\u003e 没开（最常见！）\u003c/h2\u003e\n\u003cp\u003evsftpd 的 \u003ccode\u003ewrite_enable\u003c/code\u003e \u003cstrong\u003e默认是 NO\u003c/strong\u003e。如果配置文件里没显式写 \u003ccode\u003ewrite_enable=YES\u003c/code\u003e，那么所有写操作（上传、mkdir、删除、改名）一律拒绝，正好对应你看到的 \u003ccode\u003e533\u003c/code\u003e 和 \u003ccode\u003e550\u003c/code\u003e。而它\u003cstrong\u003e不影响读和 ls\u003c/strong\u003e，所以你能登录、能列目录。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003egrep -i write_enable /etc/vsftpd/vsftpd.conf\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cul\u003e\n\u003cli\u003e如果\u003cstrong\u003e没有这一行\u003c/strong\u003e，或显示 \u003ccode\u003ewrite_enable=NO\u003c/code\u003e → 就是它！\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e修复：\u003c/p\u003e","title":""}]