-
大家好,二月的合集又来了,本次涵盖了java,linux,spirng诸多内容供大家学习。 1.Vue中的$nextTick有什么作用?【转】 https://bbs.huaweicloud.com/forum/thread-02109144317537776031-1-1.html 2.基于Python的地图绘制教程【转】 https://bbs.huaweicloud.com/forum/thread-0240144319944756030-1-1.html 3.hcache 介绍(1)--Ehcache 功能特性 【转】 https://bbs.huaweicloud.com/forum/thread-0268144320319800023-1-1.html 4.Java 操作 XML(1)--DOM 方式处理 XML【转】 https://bbs.huaweicloud.com/forum/thread-02109144320463478034-1-1.html 5.Java操作XML(3)--StAX方式处理XML 【转】 https://bbs.huaweicloud.com/forum/thread-0240144320615655031-1-1.html 6.Java操作XML(4)--使用woodstox处理XML【转】 https://bbs.huaweicloud.com/forum/thread-0270144320690142025-1-1.html 7.Java操作XML(5)--使用JDOM处理XML【转】 https://bbs.huaweicloud.com/forum/thread-0240144320817429032-1-1.html 8.Java操作XML(6)--使用dom4j处理XML【转】 https://bbs.huaweicloud.com/forum/thread-02101144320893290020-1-1.html 9.基于原生Go语言开发一个博客系统【转】 https://bbs.huaweicloud.com/forum/thread-0269144558086016006-1-1.html 10.详解Golang如何使用Debug库优化代码【转】 https://bbs.huaweicloud.com/forum/thread-0225144558556243005-1-1.html 11.Python3中的指针你了解吗【转】 https://bbs.huaweicloud.com/forum/thread-0274144558593208011-1-1.html 12.Python绘图实现坐标轴共享与复用详解【转】 https://bbs.huaweicloud.com/forum/thread-02104144558721772010-1-1.html 13.使用Golang开发一个简易版shell【转】 https://bbs.huaweicloud.com/forum/thread-0226144559187797004-1-1.html 14.python导入其它py文件的实现步骤【转】 https://bbs.huaweicloud.com/forum/thread-0284144559442791006-1-1.html 15.Python property函数的具体使用【转】 https://bbs.huaweicloud.com/forum/thread-0259144559705810008-1-1.html 16.正则表达式的神奇世界之表达、匹配和提取全解析【转】 https://bbs.huaweicloud.com/forum/thread-02104144569438876012-1-1.html 17.正则去除中括号(符号)及里面包含的内容(最新推荐)【转】 https://bbs.huaweicloud.com/forum/thread-0226144569604407008-1-1.html 18.Javaweb项目启动Tomcat常见的报错解决方案【转】 https://bbs.huaweicloud.com/forum/thread-0274144571948983013-1-1.html 19.SpringBoot+Vue前后端分离实现审核功能的示例【转】 https://bbs.huaweicloud.com/forum/thread-0269144572025869007-1-1.html 20.Java中Collections.sort()排序方法举例详解【转】 https://bbs.huaweicloud.com/forum/thread-0269144572170830008-1-1.html 21.Java中回调函数 (callback) 及其实际应用场景【转】 https://bbs.huaweicloud.com/forum/thread-0259144572260172012-1-1.html 22.Redis实现商品秒杀的示例代码【转】 https://bbs.huaweicloud.com/forum/thread-0274144574350854015-1-1.html
-
什么是协议在网络编程中,协议是指计算机系统之间进行通信和交互所遵循的规则和约定。它定义了数据的格式、传输方式、错误处理、认证和授权等方面的规范,以确保不同计算机之间能够正确地交换信息。协议分为多个层次,每个层次负责不同的功能。常见的协议包括物理层协议、数据链路层协议、网络层协议、传输层协议和应用层协议等。这些协议共同构成了网络通信的基础,使得不同类型的计算机和设备能够互相通信。在网络编程中,使用协议可以确保数据的可靠传输、安全性和互操作性。开发者需要根据具体需求选择合适的协议,并按照协议规范进行编码和解码,以实现网络通信功能。常见的网络协议包括TCP/IP协议、HTTP协议、FTP协议、SMTP协议等。总之,协议在网络编程中起着举足轻重的作用,它定义了通信双方的行为规范,确保了网络通信的顺利进行。scoket函数在Linux系统中,使用socket函数进行网络编程时,需要包含 <sys/socket.h> 头文件,并使用以下格式进行函数声明:#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);这里的 socket 函数用于创建一个套接字(socket),返回一个整数值作为套接字的文件描述符。参数含义如下:domain:套接字的地址族,可以是 AF_INET(IPv4),AF_INET6(IPv6)等。type:套接字的类型,可以是 SOCK_STREAM(面向连接的可靠字节流,如TCP),SOCK_DGRAM(无连接的不可靠数据报,如UDP)等。protocol:协议类型,通常为0表示选择默认协议。在IPv4网络协议家族中,数据传输方式为SOCK_STREAM的协议只有IPPROTO_TCP,数据传输方式为SOCK_DGRAM的协议只有IPPROTO_UDP。函数调用成功时,返回套接字的文件描述符,即一个非负整数,表示成功创建了一个套接字。如果出现错误,返回值为-1,并设置相应的错误码,例如通过 errno 变量进行获取。在函数声明之后,可以使用返回的文件描述符进行各种网络通信操作,如连接到远程主机、发送和接收数据等。需要注意的是,socket函数的具体用法和参数可能会因不同的操作系统和编译环境而有所差异。TCP和UDPTCP和UDP是两种常用的网络传输协议,它们在传输数据时有以下区别:1.连接性:TCP是面向连接的协议,而UDP是面向无连接的协议。对于TCP来说,通信双方需要先建立连接,然后才能进行数据传输;而UDP则不需要建立连接,可以直接发送数据。2.可靠性:TCP提供可靠的数据传输,它使用确认机制和重传机制,确保数据的完整性和按序传输。如果数据丢失或损坏,TCP会重新发送丢失的数据;而UDP不提供可靠性保证,它不进行数据确认和重传,可能会导致数据丢失或乱序。3.效率:由于TCP保证数据的可靠性,所以在传输过程中会引入一定的额外开销,例如确认和重传的处理。相比之下,UDP的开销较小,传输效率更高。4.应用场景:由于TCP具有可靠性的特点,适合用于需要保证数据完整性和顺序的应用,如文件传输、网页浏览、电子邮件等。而UDP适用于实时性要求较高的应用,如音视频传输、在线游戏等,它们对数据的实时性要求比数据的完整性更重要。
-
Linux 中的 Bash 脚本语言支持对变量的操作。但是,如果您从事过其他流行的编程语言开发,那么使用 Bash 处理变量会很容易出错。因为它的语法与其他语法不同(甚至对某些开发人员来说有点奇怪)。这篇文章会根据我的经验给你5个友情提示。阅读后,它可以帮助您避免不必要的错误。0. 等号周围没有空格对于许多语言,例如 Python,在等号周围添加空格是一个好习惯,因为它可以提高代码的可读性。但是在 Bash 中,你不应该在定义变量时添加额外的空格。如果我声明一个变量 name,如下:name = "Yang"这里会有一个错误:-bash: name: command not found如上所示,Bash 将 name 视为命令而不是变量,因为我添加了不必要的空格。因此,定义变量的正确方法如下:name="Yang"1.正确地定义数组在 Bash 中定义一个数组并不难。我们只需要使用一对括号来包含所有元素,如下例所示:names=("Yang" "Elon" "Bill")俗话说得好,“细节决定成败"。我之前在编写 Bash 脚本时犯了一个严重的错误。我习惯性地使用逗号(而非空格)来分隔数组中的每个元素。names=("Yang","Elon","Bill")Bash 不会将逗号视为分隔符,并且在 names 数组中只有一个元素 Yang,Elon,Bill 而不是预期的三个。2.正确选择引号当我们在 Bash 中声明一个变量时,关于引号的使用有 3 个可选的方案:没有引号用单引号用双引号默认情况下,Bash 中的每个值都是一个字符串。因此,如果不需要空格,我们就不需要使用任何引号。(同样,它与其他语言有些不同,在其他语言中,您不能在没有引号的情况下定义字符串)当您需要使用引号时,请注意单引号和双引号之间的区别。例如,让我们声明 3 个变量,如下所示。那么,truth1 和 truth2 有什么区别呢?如果你打印它们,你会看到 truth1 是 ${name} is amazing.。而 truth2 是 Yang is amazing.。总而言之,一对单引号内的字符串将始终被解释为它的本身。3. 避免在命令中使用反引号在 Bash 中,我们可以将命令的结果保存到变量中。有两种可能的方法来做到这一点:variable=`command`variable=$(command)例如,要保存 ls 命令的结果,我们可以这样写:file_list=`ls`或者file_list=$(ls)但是,这里的最佳实践始终是使用第二种方法,尤其是在编写较长的脚本时。因为反引号和单引号看起来很相似,有时你可能会混淆它们。4. 避免使用特殊名称一些名称在 Linux 中被预定义为环境变量,如 USER、HOME、PATH 等。我们可以使用 printenv 命令列出它们。显然,我们不应该用与环境变量相同的名称来声明我们的变量。好消息是 Linux 中所有的环境变量都是用大写字母写的,所以我们可以用小写字母来自定义变量,以区别环境变量。这是一个好习惯。
-
监控我们的环境对于服务器运维来说至关重要,尤其是在部署新的应用程序时。如今,公司每天都使用开源解决方案来监控系统资源。但是,当出于测试的目的来监控一定时间时,bash 脚本会派上用场。在本教程中,我们将编写一个 bash shell 脚本,它将输出一个三列表,来显示我们机器上的内存、磁盘和 CPU 的百分比。让我们开始吧!该脚本基本上由三个主要部分组成:监控内存free -m | awk 'NR==2{printf "%.2f%%\t\t", $3*100/$2 }'9.24%free -m是用于显示已使用和空闲内存的命令,输出如下:[root@localhost tmp]# free -m total used free shared buffers cachedMem: 996 92 904 0 11 31-/+ buffers/cache: 49 947Swap: 1583 0 1583但是,我们需要从上面的输出中获取第二行的总内存和已用内存。我们可以使用 AWK 来从给定输出中提取数据。AWK 是一种用于文本处理和数据提取的编程语言。它是大多数 UNIX 系统的标准功能。awk ‘NR==2’从第二行提取数据。$3和$2分别充当已用量和总量。监控磁盘df -h | awk '$NF=="/"{printf "%s\t\t", $5}'7%第二个命令输出使用的磁盘百分比。df -h输出与磁盘使用情况和分区相关的数据。[root@localhost tmp]# df -hFilesystem Size Used Avail Use% Mounted on/dev/mapper/VolGroup-lv_root 14G 814M 12G 7% /tmpfs 499M 0 499M 0% /dev/shm/dev/sda1 485M 32M 428M 7% /bootawk $NF输出字段数。但是,df -h | awk '$NF=="/"将转到包含字符/的那一行。$5将从该行中选择第 5 个字段。这可确保该命令能够提取正确的磁盘使用百分比(在我们的示例中为 %7)。监控 CPUtop -bn1 | grep load | awk '{printf "%.2f%%\t\t\n", $(NF-2)}'top -bn1命令将只执行一次top命令(n1= 一次迭代),当我们想在 bash 脚本中使用top或将其数据输出到文件时使用可以使用-b选项。grep load将输出包含字符串load的行。$(NF-2)将计算该行上的字段数并减 2。[root@localhost tmp]# top -bn1 | grep loadtop - 19:31:25 up 1:47, 1 user, load average: 0.00, 0.00, 0.00在浏览完 bash 脚本的基本部分之后,我们需要将这些命令保存到变量MEMORY、DISK和CPU中:MEMORY=$(free -m | awk 'NR==2{printf "%.2f%%\t\t", $3*100/$2 }')DISK=$(df -h | awk '$NF=="/"{printf "%s\t\t", $5}')CPU=$(top -bn1 | grep load | awk '{printf "%.2f%%\t\t\n", $(NF-2)}')我们需要脚本运行一段时间(比如,一个小时)。为了做到这一点,我们需要使用while do循环,每次循环后延迟 x 秒(取决于您的测试):end=$((SECONDS+3600))while [ $SECONDS -lt $end ]; doecho "$MEMORY$DISK$CPU"sleep 5done为了在一定时间内运行一次循环,我们可以定义一个变量 $end,它从 bash 脚本开始的时间开始计算当前的秒数,因此是SECONDS,并在当前秒数上加上一个数字。所以一个小时就是 3600 秒。上述代码片段的第二行指出,只要$SECONDS(当前秒数)小于$SECONDS+3600,while循环就会继续执行。因此,我们定义了循环的开始时间和结束时间,以及将每个循环暂停 5 秒的睡眠时间。循环内部是每 5 秒将那三个变量分配一次新值,以及将输出三个资源使用情况的echo "$MEMORY$DISK$CPU"。完整代码如下:#! /bin/bashprintf "Memory\t\tDisk\t\tCPU\n"end=$((SECONDS+3600))while [ $SECONDS -lt $end ]; doMEMORY=$(free -m | awk 'NR==2{printf "%.2f%%\t\t", $3*100/$2 }')DISK=$(df -h | awk '$NF=="/"{printf "%s\t\t", $5}')CPU=$(top -bn1 | grep load | awk '{printf "%.2f%%\t\t\n", $(NF-2)}')echo "$MEMORY$DISK$CPU"sleep 5done上面的代码将输出以下内容:[root@localhost tmp]# ./stats.shMemory Disk CPU9.34% 7% 0.00%9.34% 7% 0.00%9.34% 7% 0.00%9.34% 7% 0.00%^C[root@localhost tmp]#您始终可以将数据输出到日志文件:[root@localhost tmp]# ./stats.sh >> log.txtStress 测试由于机器上几乎没有任何负载,我们可以使用stress来使 CPU 和内存负载一段时间。使用以下命令安装(在 CentOS 上):[root@localhost tmp]# yum install stressFailed to set locale, defaulting to CLoaded plugins: fastestmirrorDetermining fastest mirrorsepel/metalink | 4.2 kB 00:00 * base: ba.mirror.garr.it * epel: ftp.riken.jp * extras: centos.fastbull.org * updates: centos.fastbull.orgbase | 3.7 kB 00:00 epel | 4.3 kB 00:00 epel/primary_db | 5.9 MB 00:37 extras | 3.4 kB 00:00 extras/primary_db | 37 kB 00:00 updates | 3.4 kB 00:00 updates/primary_db | 5.2 MB 00:30 Setting up Install ProcessResolving Dependencies--> Running transaction check---> Package stress.x86_64 0:1.0.4-4.el6 will be installed--> Finished Dependency ResolutionDependencies Resolved=================================================================================================== Package Arch Version Repository Size===================================================================================================Installing: stress x86_64 1.0.4-4.el6 epel 36 kTransaction Summary===================================================================================================Install 1 Package(s)Total download size: 36 kInstalled size: 89 kIs this ok [y/N]: yDownloading Packages:stress-1.0.4-4.el6.x86_64.rpm | 36 kB 00:01 Running rpm_check_debugRunning Transaction TestTransaction Test SucceededRunning Transaction Installing : stress-1.0.4-4.el6.x86_64 1/1 Verifying : stress-1.0.4-4.el6.x86_64 1/1 Installed: stress.x86_64 0:1.0.4-4.el6 Complete!现在我们可以使用命令 stress 来负载我们的机器。例如,通过指定两个 CPU 密集型进程、一个 I/O 密集型进程和一个内存分配器进程,对系统施加 4 个平均负载,如下所示。以下压力测试将运行 1 小时。[root@localhost tmp]# stress -c 2 -i 1 -m 1 --vm-bytes 128M -t 3600sstress: info: [1574] dispatching hogs: 2 cpu, 1 io, 1 vm, 0 hddstress: info: [1574] successful run completed in 3600s[root@localhost tmp]# ./stats.sh Memory Disk CPU20.48% 7% 1.21% 20.48% 7% 1.02% 20.48% 7% 0.94% 21.89% 7% 1.18% 20.68% 7% 1.41% 22.09% 7% 1.62% 24.10% 7% 1.81% 24.90% 7% 1.98% 32.93% 7% 2.14% 30.32% 7% 2.29% 20.58% 7% 2.63% 27.91% 7% 2.82% 20.48% 7% 2.59% 20.48% 7% 2.38% 20.48% 7% 2.19% 20.48% 7% 2.02% 20.48% 7% 1.86%关于如何为所有系统管理员创建一个非常容易且有用的 bash 脚本的教程到此结束。
-
1、简介存储、内存和 CPU(中央处理器)等系统资源不足会极大地影响应用程序的性能。因此,监控这些组件至关重要。与磁盘和内存不同,监控 Linux 系统上的 CPU 使用率并不那么简单。在本文中,我们将了解如何解释 CPU 指标并以人类可读的格式显示它们。2. CPU 负载与 CPU 使用率尽管 CPU 负载和 CPU 使用率听起来很相似,但它们是不可互换的。CPU 负载定义为在单个时间点使用或等待使用一个内核的进程数。假设我们有一个单核系统,我们的 CPU 平均负载始终低于 0.6。这表明每个需要使用 CPU 的进程都可以立即使用它,而无需等待。如果 CPU 平均负载大于 1,则表示有进程需要使用 CPU,但由于 CPU 不可用,目前无法使用。但是,多处理器系统中高于 1 的平均负载不会成为问题,因为有更多内核可用。uptime命令为我们提供了以 1、5 和 15 分钟为间隔的平均负载视图:[root@localhost ~]# uptime 12:40:05 up 2:29, 1 user, load average: 0.37, 0.08, 0.03如果不知道系统的核心数,就无法解释平均负载:[root@localhost ~]# cat /proc/cpuinfo |grep corecore id : 0cpu cores : 1另一方面,CPU 使用率是 CPU 处理非空闲任务所花费的时间百分比。CPU 使用率只能在指定的时间间隔内测量。我们可以通过将空闲时间的百分比从 100 中减去来确定 CPU 使用率。3.计算CPU使用率3.1 使用vmstat获取 CPU 使用率vmstat命令近乎实时地显示 CPU 活动:[root@localhost ~]# vmstat 3 4procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 4 0 0 1347080 6120 941464 0 0 68 11 72 137 1 2 97 0 0 1 0 0 1347080 6120 941464 0 0 0 0 84 157 1 2 97 0 0 1 0 0 1347080 6120 941464 0 0 0 0 59 107 1 1 98 0 0 1 0 0 1347080 6120 941464 0 0 0 1 59 104 1 1 98 0 0CPU 下的列提供了处理器时间花费在哪里的概览:us –运行非内核代码所花费的时间sy -运行内核代码所花费的时间id –空闲时间wa –等待 I/O 所花费的时间st -从虚拟机中窃取时间id列是我们感兴趣的。延迟一秒,我们使用 vmstat 计算CPU使用率:[root@localhost ~]# echo "CPU Usage: "$[100-$(vmstat 1 2|tail -1|awk '{print $15}')]"%"CPU Usage: 2%没有提供任何参数的vmstat命令将给出自引导以来的 CPU 时间。这不会提供准确的 CPU 使用百分比。因此,参数只能是 1 和 2,我们采用一秒钟后计算的指标:vmstat 1 23.2. 使用/proc/stat获取 CPU 使用率CPU 活动也可以从/proc/stat文件中提取。该文件包含自启动以来有关系统的各种指标:[root@localhost ~]# cat /proc/stat cpu 3020 28 1863 22404 35 432 47 0 0 0cpu0 3020 28 1863 22404 35 432 47 0 0 0intr 96468 28 100 0 0 0 0 0 0 1 0 0 0 1263 0 0 0 3696 0 153 928 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 207 0 41 14600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 343 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0ctxt 340950btime 1628404433processes 3276procs_running 2procs_blocked 0softirq 112867 1 16857 56 269 510 0 261 0 0 94913第一行, 'cpu' 是系统所有核心指标的聚合。在具有 4 个内核的系统上,将有 4条cpu线——cpu0、cpu1、cpu2和cpu3。' cpu ' 行中的列表示处理不同任务所花费的时间:user – 在用户模式下花费的时间nice – 在用户模式下处理 nice 进程所花费的时间system – 执行内核代码所花费的时间idle - 空闲时间iowait – 等待 I/O 所花费的时间irq - 服务中断所花费的时间softirq – 服务软件中断所花费的时间steal —从虚拟机中窃取的时间guest - 为来宾操作系统运行虚拟 CPU 所花费的时间guest_nice – 为“不错的”客户操作系统运行虚拟 CPU 所花费的时间我们将使用这些指标来计算平均空闲百分比。随后,我们将使用计算值来计算 CPU 使用率。需要注意的是,较旧的 Linux 发行版不计算窃取、来宾或来宾_nice指标。如果我们使用的是旧系统,我们会在计算中忽略这些指标:平均空闲时间 (%) = (idle * 100) / (user + nice + system + idle + iowait + irq + softirq +steal + guest + guest_nice)cat /proc/stat |grep cpu |tail -1|awk '{print ($5*100)/($2+$3+$4+$5+$6+$7+$8+$9+$10)}'|awk '{print "CPU Usage: " 100-$1}'CPU Usage: 2.4219由于我们正在开发单核系统,因此“cpu”行将与“cpu1 ”相同。因此,tail -1 的使用是 只检索其中一行。然而,我们会在多处理器系统上使用“ cpu ”行,因为它是所有内核上的指标的集合。3.3. 使用top获取 CPU 使用率通常,top命令通常用于显示系统上的活动进程以及这些进程消耗了多少资源。不过,我们可以使用这个命令来测量 CPU 的状态:[root@localhost ~]# toptop - 07:08:31 up 2:41, 1 user, load average: 0.00, 0.00, 0.00Tasks: 322 total, 2 running, 320 sleeping, 0 stopped, 0 zombie%Cpu(s): 10.0 us, 15.0 sy, 0.0 ni, 97.8 id, 0.0 wa, 5.0 hi, 0.0 si, 0.0 stMiB Mem : 3709.4 total, 1483.1 free, 1402.0 used, 824.4 buff/cacheMiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 2053.4 avail Mem 此外,需要注意的是,top 命令显示了单个内核的 CPU 百分比。在多处理器系统中,CPU 百分比可能超过 100%。例如,如果 4 个核心为 75%,top命令将显示 CPU 为 300%。我们需要获取空闲时间的值,以便我们可以从 100 中减去它来获得使用情况:[root@localhost ~]# top -bn2 | grep '%Cpu' | tail -1 | grep -P '(....|...) id,'|awk '{print "CPU Usage: " 100-$8 "%"}'CPU Usage: 2.2%-n选项是top命令在结束前应该使用的迭代次数。我们避免使用第一个循环,因为我们检索的指标将是自启动以来的值。因此,我们进行了第二次迭代。或者,在多处理器系统中,我们必须将给定的“ id”值除以内核数,然后从 100 中减去该值。例如,如果我们在四核系统上运行,并且“ id ”值为 304%,我们将 CPU 使用率计算为:CPU 使用率 % = 100 – (304/4)[root@localhost ~]# top -bn2 | grep '%Cpu' | tail -1 | grep -P '(....|...) id,'|awk '{print "CPU Usage: " 100-($8/4) "%"}'4.结论在本文中,我们讨论了 CPU 使用率和 CPU 负载之间的区别。许多人交替使用这两个概念,这是不正确的。之后,我们深入研究了用于检索 CPU 利用率指标的各种方法。
-
可以很肯定地说,find命令是 Linux 后台开发人员必须熟知的操作之一,除非您使用的是 Windows Server。对于技术面试,它也是一个热门话题。让我们看一道真题:如果你的 Linux 服务器上有一个名为logs的目录,如何删除该目录下最后一次访问时间超过一年的日志文件呢?这种情况很常见,但令人惊讶的是,并非每个开发人员都能在面试中清楚地写出这个命令。答案:首先我们需要使用 cd 命令进入对应的目录,然后,命令如下:find . -type f -atime +365 -exec rm -rf {} \; 如果你还没有完全理解上面的命令,完全不用担心。本文将介绍find命令的 7 个实际用途,您最终会掌握它。如果您已经知道了,那么阅读这篇文章对您来说将是一个很好的回顾。按名称或正则表达式查找文件让我们从最简单的用法开始。要按特定名称搜索文件,命令如下:find . -name test.txt如何查找所有格式为 pdf 的书籍?使用正则表达式:find ./yang/books -name "*.pdf"默认情况下,find 命令会搜索常规文件,但最好进行指定(-type f)以使所有内容更清晰:find ./yang/books -type f -name "*.pdf"查找不同类型的文件除了搜索常规文件外,我们还可以通过指定-type选项来搜索其他类型的文件。例如目录:find . -type d -name "yang*"或者符号链接:find . -type l -name "yang*"按指定的时间戳查找文件要按指定的时间戳搜索文件,我们需要知道 Linux 系统中的 3 个不同的时间戳:访问时间戳(atime):最后一次读取文件的时间。修改时间戳 (mtime):文件内容最后一次被修改的时间。更改时间戳 (ctime):上次更改文件元数据的时间(如,所有权、位置、文件类型和权限设置)所以,正如开头提到的面试题,要搜索atime超过一年的文件,我们可以编写如下命令:find . -type f -atime +365如果我们需要查找 mtime 正好是 5 天前的文件,请不要包含 +,因为它的意思是“大于”。find . -type f -mtime 5显然,+ 表示“大于”,- 表示“小于”。所以我们可以搜索 ctime 在 5~10 天前的文件:find . -type f -ctime +5 -ctime -10按大小查找文件-size选项使我们能够按指定大小查找文件。我们可以将其计量单位指定为以下约定:b:512 字节块(默认)c:字节w:双字节字k:KBM:MBG:GB类似于按时间戳查找文件,+表示“大于”,-表示“小于”。例如,要查找大小为 10 MB ~ 1 GB 的文件:find . -type f -size +10M -size -1G按权限查找文件合理控制文件的权限是 Linux 管理员的一项重要任务。find命令的-perm选项可以帮助我们按指定权限查找文件:find . -type f -perm 777例如,上面的命令会搜索所有具有 777 权限的文件,这意味着一个文件对其持有者、组和所有用户具有所有的读、写和可执行权限。按所有权查找文件这个任务很简单。我们可以使用-user选项指定用户名。例如,以下命令将查找所有属于yang的文件:find -type f -user yang在找到文件后执行命令在大多数情况下,我们希望在找到我们需要的文件后进行后续操作。例如将其删除,或检查它们的详细信息等等。-exec命令使这些所有事情变得更加容易。现在,要了解如何使用它,让我们回到之前提到的面试问题:find . -type f -atime +365 -exec rm -rf {} \;上述命令在-exec选项后是rm -rf,其用于删除文件。{}是用于查找结果的占位符。注意:占位符{}非常重要,尤其是在您想删除文件时。因为,如果您不使用它,该命令将对所有文件执行(而不是您刚刚通过 find 命令找到的文件)。做一个尝试,请在终端上执行以下两个命令,并检查它们的结果有何不同:一个使用占位符:find . -type f -atime +5 -exec ls {} \;另一个不使用:find . -type f -atime +5 -exec ls \;-exec 选项后面的命令必须以分号(;)结束。众所周知,转义字符用于去除单个字符的特殊含义。在 Linux 中,反斜杠\用作转义字符。所以我们将它用于分号字符。总结看完 find 命令的 7 种用法后,开始提到的面试题现在好像很容易解决了。你现在可以直接写下它的答案并将其解释清楚吗?find . -type f -atime +365 -exec rm -rf {} \;
-
在我们生产环境中使用到了地图服务,每个月有免费请求次数,近一个月请求次数突然暴涨,导致直接开启付费模式,一个月上百刀的花销着实难扛,根据实际我们的业务使用情况,远达不到付费标准,故考虑做白名单和限流措施,基于以上情况并遇到春节急需快速处理,所以选择了最简单方便的方式,通过nginx做限流 我们都知道nginx里面是可以用lua脚本做一些稍微复杂些的逻辑处理的,要使用lua脚本需要编译lua解释器,时间有限我直接用了openresty,它集成了lua和nginx1、openresty是什么?OpenResty是一个基于Nginx的高性能Web平台,用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。具备下列特点:具备Nginx的完整功能基于Lua语言进行扩展,集成了大量精良的 Lua 库、第三方模块,允许使用Lua自定义业务逻辑、自定义库二、OpenResty的安装1、添加OpenResty仓库# 由于公共库中找不到openresty,所以需要添加openresty的源仓库 yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo # 注意,如果上面命令提示不存在,那就先安装一下 yum install -y yum-utils2. 安装OpenResty# 安装openresty yum install -y openresty # 安装OpenResty管理工具,帮助我们安装第三方的Lua模块 yum install -y openresty-opm3、目录结构 默认安装在/usr/local/openresty看到里面有一个nginx目录,进去可以看到跟我们平常用的nginx是一模一样的,OpenResty就是在Nginx基础上集成了一些Lua模块到这里我们就安装好了7. 启动和运行OpenResty底层是基于Nginx的,查看OpenResty目录的nginx目录,结构与windows中安装的nginx基本一致:所以这个里面的nginx和平常的nginx是一样的1)nginx配置文件worker_processes 1; events { worker_connections 1024; } http { server{ listen 999; server_name localhost; location /mapbox/ { access_by_lua_file "/usr/local/openresty/nginx/lua_script/rule.lua"; proxy_pass https://api.mapbox.com/; proxy_ssl_server_name on; } } } 2)lua脚本文件(白名单加限流)通过两个redis的key,map_request_limitation:存放令牌数量,map_request_white_list:白名单列表;白名单的IP,无需限流,只有白名单之外的才需要限流-- 其实这两个值可以从redis取 甚至可以给每个qrcode设置单独的速率和容积 -- 但如果想监听桶的状态 需要持续的请求, 只有每次请求后才重新计算并更新桶状态 否则桶状态不变 local tokens_per_second = 0.2 -- 生成速率 /s local max_tokens = 10 -- 最大溶剂 local current_time = ngx.now() local path = ngx.var.uri local redis_key = "map_request_limitation" local redis_key_white_list = "map_request_white_list" local client_ip = ngx.var.remote_addr -- local redis_key = "path:" .. path -- 连接Redis local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.log(ngx.ERR, "Redis连接失败: ", err) return ngx.exit(500) end -- 权限校验 local res, err = red:auth("123456") if not res then ngx.say("failed to authenticate: ", err) return end -- 发送 Lua 脚本(保证redis原子性操作) local script = [[ local redis_key = KEYS[1] local redis_white_list_key = KEYS[2] local tokens_per_second = tonumber(ARGV[1]) local max_tokens = tonumber(ARGV[2]) local current_time = tonumber(ARGV[3]) local client_ip = ARGV[4] -- ip是否存在列表中 local is_in_whitelist, err = redis.call('sismember', redis_white_list_key, client_ip) if is_in_whitelist == 1 then return 1 end -- 获取上次访问时间和令牌数量 local res = redis.call('HMGET', redis_key, 'last_access_time', 'tokens') local last_access_time local last_tokens if res[1] and res[2] then last_tokens = res[2] last_access_time = res[1] end -- 计算时间间隔 local time_passed = current_time - (tonumber(last_access_time) or 0) -- 计算新的令牌数量 last_tokens = last_tokens and tonumber(last_tokens) or max_tokens local new_tokens = math.min(max_tokens, last_tokens + time_passed * tokens_per_second) -- 判断令牌数量是否足够 if new_tokens >= 1 then -- 消耗令牌 redis.call('HMSET', redis_key, 'last_access_time', current_time, 'tokens', new_tokens - 1) return 1 else return 0 end ]] -- 执行脚本 local result = red:eval(script, 2, redis_key, redis_key_white_list,tokens_per_second, max_tokens,current_time,client_ip) if result == 1 then -- 成功 ngx.log(ngx.INFO, "成功") else -- 令牌不足 ngx.status = ngx.HTTP_TOO_MANY_REQUESTS ngx.say("OVERLOAD!!!!",result) return ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS) end -- 返还redis连接到连接池 local ok, err = red:set_keepalive(10000, 100) if not ok then ngx.log(ngx.ERR, err) end启动之后当通过这个999端口访问之后,我们在redis里面可以看到以下两个key,白名单可以自行添加,即时生效转载自https://www.cnblogs.com/KingArmy/p/18019489
-
OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。1.更新系统软件包: 运行以下命令来确保您的系统软件包已经是最新的: sudo apt update sudo apt upgrade 2.安装编译OpenResty所需的依赖项: sudo apt install -y build-essential libpcre3 libpcre3-dev zlib1g-dev libssl-dev curl 3.下载OpenResty: 在浏览器中访问OpenResty的官方网站(https://openresty.org/),找到最新版本的下载链接,或者使用以下命令来下载最新版本: curl -OL https://openresty.org/download/openresty-VERSION.tar.gz 请将"VERSION"替换为您想要安装的OpenResty版本号。 4.解压文件并进入目录: tar -xzvf openresty-VERSION.tar.gz cd openresty-VERSION 5.配置和编译: ./configure --prefix=/usr/local/openresty make sudo make install 这将在/usr/local/openresty目录下安装OpenResty。 6.添加OpenResty到环境变量: 编辑~/.bashrc文件,添加以下内容: export PATH=/usr/local/openresty/nginx/sbin:$PATH 7.然后运行以下命令使其生效: source ~/.bashrc 启动OpenResty: 使用以下命令启动OpenResty: 8.sudo /usr/local/openresty/nginx/sbin/nginx 现在,OpenResty已经成功安装并运行。
-
使用LVM方式创建Linux文件系统在Linux系统中,LVM(Logical Volume Manager)是一种灵活的磁盘管理技术,它允许用户更方便地管理磁盘空间。LVM将物理磁盘分区划分为物理卷(PV),然后将一个或多个物理卷组合成卷组(VG),最后在卷组上划分逻辑卷(LV)。通过这种方式,用户可以灵活地调整逻辑卷的大小,而无需关心底层物理磁盘的布局。本文将介绍如何使用LVM在Linux系统上创建文件系统。步骤1:安装LVM工具首先,确保您的Linux系统已经安装了LVM工具。在大多数发行版中,LVM工具默认已经安装。如果没有安装,可以使用包管理器进行安装。例如,在Debian/Ubuntu系统上,可以使用以下命令安装:sudo apt-get update sudo apt-get install lvm2步骤2:创建物理卷接下来,将磁盘分区初始化为物理卷。假设您有一个名为/dev/sdb的磁盘,您想将其整个空间用作物理卷。运行以下命令:sudo pvcreate /dev/sdb步骤3:创建卷组现在,创建一个卷组并将物理卷添加到其中。假设您想将卷组命名为myvg,运行以下命令:sudo vgcreate myvg /dev/sdb步骤4:创建逻辑卷在卷组上创建一个逻辑卷。假设您想在myvg卷组上创建一个名为mylv的逻辑卷,大小为10GB,运行以下命令:sudo lvcreate -L 10G -n mylv myvg步骤5:格式化逻辑卷在逻辑卷上创建一个文件系统。假设您想使用ext4文件系统,运行以下命令:sudo mkfs.ext4 /dev/myvg/mylv步骤6:挂载逻辑卷创建一个目录作为挂载点,然后将逻辑卷挂载到该目录。例如,将逻辑卷挂载到/mnt/mylv目录,运行以下命令:sudo mkdir /mnt/mylv sudo mount /dev/myvg/mylv /mnt/mylv现在,您已经成功使用LVM在Linux系统上创建了一个文件系统,并将其挂载到了/mnt/mylv目录。您可以在该目录中存储和管理文件。总结LVM提供了一种灵活且可扩展的磁盘管理方案,使用户能够更方便地调整和管理磁盘空间。通过创建物理卷、卷组和逻辑卷,用户可以将多个磁盘分区组合成一个逻辑单元,并在逻辑卷上创建文件系统。LVM还支持在线调整逻辑卷的大小,无需卸载文件系统或重新启动系统。这使得LVM成为大规模存储和管理数据的理想选择。
-
一、Inode号 1.inode和block 文件是存储在硬盘上的,硬盘的最小存储单位叫做扇区sector,每个扇区存储512字节。操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个块block。这种由多个扇区组成的块,是文件存取的最小单位。块的大小,最常见的是4KB,即连续八个sector组成一个block。 文件数据存储在块中,那么还必须找到一个地方存储文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种存储文件元信息的区域就叫做inode,中文译名为索引节点,也叫i节点。因此,一个文件必须占用一个inode,但至少占用一个block。 元信息-----inode 数据-------block 2.查看inode信息 stat:查看inode信息 [root@localhost ~]# mkdir test [root@localhost ~]# echo "this is test file" > test.txt 三个主要的时间属性: ctime:change time是最后一次改变文件或目录(属性)的时间,例如执行chmod,chown等命令。 atime:access time是最后一次访问文件或目录的时间。 mtime:modify time是最后一次修改文件或目录(内容)的时间。 ls -i:直接查看文件i节点号 [root@localhost ~]# ls -i 33574991 anaconda-ks.cfg 2086 test 33574994 test.txt df-i: 查看硬盘分区的inode总数和已使用情况 [root@localhost ~]# df -i Filesystem Inodes IUsed IFree IUse% Mounted on /dev/mapper/centos-root 8910848 26029 8884819 1% / devtmpfs 230602 384 230218 1% /dev tmpfs 233378 1 233377 1% /dev/shm tmpfs 233378 487 232891 1% /run tmpfs 233378 16 233362 1% /sys/fs/cgroup /dev/sda1 524288 328 523960 1% /boot tmpfs 233378 1 233377 1% /run/user/0 二、日志服务管理 作用: 将系统和应用发生的事件记录至日志中,以助于排错和分析使用。 记录的内容包括:时间,地点,人物,事件 1.日志的级别 级别 消息 级别 说明 0 EMERG 紧急 会导致主机系统不可用的情况 1 ALERT 警告 必须马上采取措施解决问题 2 CRIT 严重 比较严重的情况 3 ERR 错误 运行出现错误 4 WARNING 提醒 可能会影响系统功能的事件 5 NOTICE 注意 不会影响系统但值得注意 6 INFO 信息 一般信息 7 DEBUG 调试 程序或系统调试信息等 日志级别数字越小越紧急,一般运维过程中出现4级就要进行检查注意了。 2.日志的种类 系统日志: messages:系统大部分信息 secure:和安全相关的信息 用户登录日志: btmp:查看用户登陆失败的信息。(lastb命令可以查看,应为btmp是一个二进制的文件) wtmp:哪些用户正常登陆到系统中。(可以使用last命令查看) 3.日志的功能和日志文件的分类 功能: 用于记录系统、程序运行中发生的各种事件 通过阅读日志,有助于诊断和解决系统故障 分类: 内核及系统日志 由系统服务rsyslog统一进行管理,目志格式基本相似 用户日志 记录系统用户登录及退出系统的相关信息 程序日志 由各种应用程序独立管理的日志文件,记录格式不统一 4.日志的格式和分析工具 分析工具:users、who、w、lastb、last 三、rsyslog日志处理系统 1、使用Rsyslog创建日志优点 在运维过程中某些服务时自带错误日志和运行日志的,但是有一些服务安装完成后是没有日志的。下面我们以sshd服务为例创建一个日志。 例如sshd本身是有日志的,但是与其他安全服务一样都存放在/var/log/secure/messages文件中不方便我们查看sshd的日志,如果我们将sshd服务的日志单独生成一个日志文件便于我们查看,注意使用Rsyslog创建日志的服务必须支持Rsyslog。 2、Rsyslog配置文件解析 配置文件路径:/etc/rsyslog.conf 分为三大块: MODULES:相关模块配置 GLOBAL DIRECTIVES:全局配置 RULES:日志记录相关的规则配置 3.通过rsyslog将ssh服务的日志单独设置 第一步在rsyslog的配置文件(/etc/rsyslog.conf)# RULES #模块中设置ssh服务的local等级为local6并设置日志存路径为/mnt/ssh.logs。其中local6后的.*表示所等级的日志都输出, .表示等于或高于后面等级日志的都会输出,如果是.=则表示只有等于后面的日志的等级才会输出,如果是.!则表示除了后面等级的其他等级日志全部记录 第二步在ssh服务的服务器端配置文件中(/etc/ssh/sshd_conf)修改自定义rsyslog 重启sshd服务和rsyslog服务 本机ssh自己并查看实时日志是否生成记录 将 ssh服务的日志 文件 独立成出来 原来ssh /var/log/secure [root@localhost ~]#tail -f /var/log/secure #查看ssh服务的日志位置 [root@localhost ~]#vim /etc/ssh/sshd_config #修改ssh配置文件,32下一行添加自己的自定义 32 #SyslogFacility AUTHPRIV 33 SyslogFacility LOCAL6 local 0-7 [root@localhost ~]#vim /etc/rsyslog.conf #76 行添加自己的文件位置 local6.* /var/log/ssh.log [root@localhost log]#systemctl restart rsyslog.service sshd #重启服务 4、使用rsyslog搭建日志服务器 第一步将2台服务器的防火墙以及selinux防护关闭 systemctl stop firewalld setenforce 0 第二步在业务服务器A和日志服务器B上配置rsyslog服务开启tcp协议,在业务服务器A上将内核和公共日志实时传输到日志服务器B上 第三步业务服务器A和日志服务器B上重启rsyslog服务并检查514端口是否开启 ———————————————— 版权声明:本文为CSDN博主「Miraitowa_xu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Miraitowa_xu/article/details/135458684
-
一、基本概念 1.1 概念提出 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体(百度百科)。 1.2 特征 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。 并发性:任何进程都可以同其他进程一起并发执行 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位; 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进 结构特征:进程由程序、数据和进程控制块三部分组成。 多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。 二、描述进程-PCB 引入一个新概念: 进程控制块PCB(process control block)。PCB就是进程属性的集合(数据结构),里面存储的是进程信息。 2.1 什么是进程控制块PCB 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合,称之为PCB(process control block),Linux操作系统下的PCB是: task_struct。 即task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含进程的信息。 2.2 task_struct内容分类(成员) 标示符(PID): 描述本进程的唯一标示符,用来区别其他进程(每次启动都会变化)。 状态: 任务状态,退出代码,退出信号等。 优先级: 相对于其他进程的优先级。 程序计数器(pc): 程序中即将被执行的下一条指令的地址。 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。 上下文数据: 进程执行时处理器的寄存器中的数据。 I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。 记账信息: 可能包括处理器时间总和,使用的时钟总和,时间限制,记账号等。 其他信息 2.3 进程控制块如何对进程进行管理 磁盘中的可执行程序在将要运行时,所有的进程中的数据(并不是程序本身)加载到内存中,此时操作系统会建立起一个PCB来保存每一个程序的信息; 这个时候PCB就会对每一个进程都建立起相应的结构体(即进程控制块)将对应的进程的属性、代码等匹配的传到这个结构体中:(这就是先描述) 此时,操作系统就会将每一个进程控制块都连接起来,形成链表结构,并返回头结点。这样便通过数据结构的形式将进程的信息组织起来。 三、查看进程 3.1 通过系统目录查看 根目录下的proc目录,/proc下存储着进程信息。 目录名为数字的即为进程信息的目录,每个目录内存储着他们对应的进程信息。而这些数字对应着该进程的标识符PID。如下查看标识符PID=18964的进程信息: 我们进入进程 18964进程目录 3.2 通过用户级工具ps查看 实例:ps ajx/ps aux 该命令可以查看所有系统进程。如下查看PID=18964的进程信息 创建如下测试脚本并执行 #!/bin/bash proc_test() { while(true) do echo "I am a process!" sleep 1 done } proc_test ## 执行 sh proc_test_18964.sh 另外通过指令对进程进行检测,检测它是否运行: ps ajx | head -1 && ps ajx | grep proc_test | grep -v grep 这里grep实际也是进程,且该进程内包含有proc_test的信息,所以也显示出来了。-v选项是反向搜索的意思,即过滤掉包含有grep内容的信息。 PPID (Parent Process ID):代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号 PID (Process ID): 代表这个进程的代号 PGID(Process Group):PGID就是进程所属的Group的Leader的PID,如果PGID=PID,那么该进程是Group Leader SID(Session ID):和PGID非常相似,SID就是进程所属的Session Leader的PID,如果SID==PID,那么该进程是session leader TPGID:终端进程组ID STAT:进程状态 四、通过系统调用获取进程标识符(PID) 4.1 使用getpid和getppid #include<stdio.h> #include<unistd.h> #include<sys/types.h> int main() { pid_t id = getpid(); while (1) { printf("I am a process!pid:%d\n", id); sleep(1); } return 0; } 执行上面可执行程序并运行如下命令进行监控 while :; do ps ajx | head -1 && ps ajx | grep proc_test | grep -v grep ; sleep 1;done 函数getppid(获取父进程的进程标识符),一般在Linux中普通进程都有他的父进程。 每一个子进程都是由父进程创建出来的。 子进程只能有一个父进程,父进程可以有多个子进程。每次执行可执行程序,进程标识符会改变,因为每次都会创建新的进程。 编写如下测试代码 #include<stdio.h> #include<unistd.h> #include<sys/types.h> int main() { pid_t id = getpid(); pid_t fid = getppid(); while (1) { printf("I am a process!pid:%d ppid:%d\n", id, fid); sleep(1); } return 0; } 执行结果如下图: 多次执行我们发现程序多次执行子进程每次都会创建新的新的进程标识符,父进程标识符没有改变。如下父进程是bash命令行解释器。 五、通过系统调用创建进程-fork初识 5.1 fork函数 pid_t fork(void); 一个进程包括代码、数据和分配给进程的资源。fork 函数会新生成一个进程,调用 fork 函数的进程为父进程,新生成的进程为子进程。在父进程中返回子进程的 pid,在子进程中返回 0,失败返回-1。 5.2 fork函数创建子进程 创建测试代码: #include<stdio.h> #include<sys/types.h> #include<unistd.h> int main() { pid_t pid; printf("before fork : pid is : %d, ppid is : %d\n", getpid(), getppid()); pid=fork(); printf("after fork : pid is : %d, ppid is : %d, fork return %d\n", getpid(), getppid(),pid); sleep(2); return 0; } 编译后执行 fork函数执行后,后面的"after fork"执行了两次,8897作为父进程创建了子进程 8898。也就是说fork之后,代码共享,从一个进程分为两个分支,一为父,一为子。 进程调用fork,当控制转移到内核中的fork代码后,内核会: 分配新的内存块和内核数据结构给子进程; 将父进程部分数据结构内容拷贝至子进程; 添加子进程到系统进程列表当中; fork返回,开始调度器调度; 所以,fork之前父进程独立执行,fork之后父子两个执行流分别执行。注意:fork之后谁先执行完全由调度器决定。 fork它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值: (1) 在父进程中,fork返回新创建子进程的进程ID; (2)在子进程中,fork返回0; (3)如果出现错误,fork返回一个负值; 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。 六、Linux进程状态 进程状态: TASK_RUNNING:就绪/可运行状态 TASK_INTERRUPTABLE:进程被挂起(睡眠),直到等待条件为真被唤醒 TASK_UNINTERRUPTABLE:深度睡眠,睡眠期间不响应信号 TASK_STOPPED:进程的执行被暂停 TASK_TRACED:被其它进程跟踪,常用于调试 EXIT_ZOMBIE:僵死状态,进程的执行被终止 EXIT_DEAD:僵死撤销状态,防止wait类系统调用的竞争状态发送 可以使用man ps 查看进程状态 Ss、Sl、S+、Z、I 释义 具体解释如下: D (TASK_UNINTERRUPTIBLE) 不可中断的睡眠状态 R (TASK_RUNNING) 正在运行,或在队列中的进程 S (TASK_INTERRUPTIBLE) 可中断的睡眠状态 T (TASK_STOPPED) 停止状态 t (TASK_TRACED) 被跟踪状态 Z (TASK_DEAD - EXIT_ZOMBIE) 退出状态,但没被父进程收尸,成为僵尸状态 W 进入内存交换(从内核2.6开始无效) X (TASK_DEAD - EXIT_DEAD) 退出状态,进程即将被销毁 < 高优先级 N 低优先级 L 有些页被锁进内存 s 包含子进程 位于前台的进程组; l 多线程,克隆线程 multi-threaded (using CLONE_THREAD, like NPTL pthreads do) 七、两种特殊进程 7.1 僵尸进程 1.僵死状态(Zombies)是一个比较特殊的状态。当子进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程 2.僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 3.所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态 测试代码 : #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { pid_t id = fork(); if (id < 0) { perror("fork"); return 1; } else if (id > 0) { //parent while(1) { printf("parent[%d] is sleeping...,ppid: %d\n", getpid(),getppid()); sleep(1); } } else { printf("child[%d] is begin Z...,ppid: %d\n", getpid(),getppid()); sleep(5); exit(1); } return 0; } fork函数执行后子进程PID:15156 在5 s后exit(1)异常退出 ,父进程15155没有读取到正常的退出代码导致产生僵尸进程。 进程PID:15156的名称加上了【】且后面跟着< defunct > (失效的) 7.2 孤儿进程 修改代码,让子进程和父进程同时不间断运行 #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { pid_t id = fork(); if (id < 0) { perror("fork"); return 1; } else if (id > 0) { //parent while(1) { printf("parent[%d] is sleeping...,ppid: %d\n", getpid(),getppid()); sleep(1); } } else { while(1) { printf("child[%d] is begin Z...,ppid: %d\n", getpid(),getppid()); sleep(1); } } return 0; } 运行 while :; do ps ajx | head -1 && ps ajx | grep pro_test | grep -v grep ; sleep 1;done 监控进程状态 杀掉父进程(10552),子进程被编号为1的进程接管,该1号进程就是bash,而bash就是父进程的父进程,父进程被杀死后,bash进程就接管了子进程,这种失去“爸爸”后被接管的进程就被称为孤儿进程。且子进程从前台进程变成了后台进程。 ———————————————— 版权声明:本文为CSDN博主「阿龙先生啊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_35995514/article/details/135243157
-
经过了创建进程,终止进程,进程等待和进程程序替换之后, 我们就可以借助这些知识实现一个简单的shell命令行解释器了 温馨提示: 建议大家自己写一遍,这些代码分块之后每一个函数都很简单, 不过实现过程中可能会有各种各样非常细枝末节的地方被我们所忽视 因此可能会发生一看就懂,一写就废的情况… 一.最终版本展示 输入命令行时想要删除字符时不能直接按backspace,而是要按ctrl+backspace才能成功删除 1.动图展示 2.代码展示 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> //#define DEBUG 1 #define SEP " " char cwd[1024]={'\0'}; int lastcode=0;//上一次进程退出时的退出码 char env[1024][1024]={'\0'}; int my_index=0; const char* getUsername() { const char* username=getenv("USER"); if(username==NULL) return "none"; return username; } const char* getHostname() { const char* hostname=getenv("HOSTNAME"); if(hostname==NULL) return "none"; return hostname; } const char* getPwd() { const char* pwd=getenv("PWD"); if(pwd==NULL) return "none"; return pwd; } //分割字符串填入usercommand数组当中 //例如: "ls -a -l" 分割为"ls" "-a" "-l" void CommandSplit(char* usercommand[],char* command) { int i=0; usercommand[i++]=strtok(command,SEP); while(usercommand[i++]=strtok(NULL,SEP)); } //解析命令行 void GetCommand(char* command,char* usercommand[]) { command[strlen(command)-1]='\0';//清理掉最后的'\0' CommandSplit(usercommand,command); #ifdef DEBUG int i=0; while(usercommand[i]!=NULL) { printf("%d : %s\n",i,usercommand[i]); i++; } #endif } //创建子进程,完成任务 void Execute(char* usercommand[]) { pid_t id=fork(); if(id==0) { //子进程执行部分 execvp(usercommand[0],usercommand); //如果子进程程序替换失败,已退出码为1的状态返回 exit(1); } else { //父进程执行部分 int status=0; //阻塞等待 pid_t rid=waitpid(id,&status,0); if(rid>0) { lastcode=WEXITSTATUS(status); } } } void cd(char* usercommand[]) { chdir(usercommand[1]); char tmp[1024]={'\0'}; getcwd(tmp,sizeof(tmp)); sprintf(cwd,"PWD=%s",tmp); putenv(cwd); lastcode=0; } int echo(char* usercommand[]) { //1.echo后面什么都没有,相当于'\n' if(usercommand[1]==NULL) { printf("\n"); lastcode=0; return 1; } //2.echo $? echo $PWD echo $ char* cmd=usercommand[1]; int len=strlen(cmd); if(cmd[0]=='$' && len>1) { //echo $? if(cmd[1]=='?') { printf("%d\n",lastcode); lastcode=0; } //echo $PWD else { char* tmp=cmd+1; const char* env=getenv(tmp); //找不到该环境变量,打印'\n',退出码依旧为0 if(env==NULL) { printf("\n"); } else { printf("%s\n",env); } lastcode=0; } } else { printf("%s\n",cmd); } return 1; } void export(char* usercommand[]) { //export if(usercommand[1]==NULL) { lastcode=0; return; } strcpy(env[my_index],usercommand[1]); putenv(env[my_index]); my_index++; } int doBuildIn(char* usercommand[]) { //cd if(strcmp(usercommand[0],"cd")==0) { if(usercommand[1]==NULL) return -1; cd(usercommand); return 1; } //echo else if(strcmp(usercommand[0],"echo")==0) { return echo(usercommand); } //export else if(strcmp(usercommand[0],"export")==0) { export(usercommand); } return 0; } int main() { while(1) { //1.打印提示符信息并获取用户的指令 printf("[%s@%s %s]$ ",getUsername(),getHostname(),getPwd()); char command[1024]={'\0'}; fgets(command,sizeof(command),stdin); char* usercommand[1024]={NULL}; //2.解析command字符串,放入usercommand指针数组当中 GetCommand(command,usercommand); //3.检测并执行内建命令,如果是内建命令并成功执行,返回1,未成功执行返回-1,不是内建返回0 int flag=doBuildIn(usercommand); //返回值!=0说明是内建命令,无需执行第4步 if(flag!=0) continue; //4.创建子进程,交由子进程完成任务 Execute(usercommand); } return 0; } 二.具体步骤 1.打印提示符 const char* getUsername() { const char* username=getenv("USER"); if(username==NULL) return "none"; return username; } const char* getHostname() { const char* hostname=getenv("HOSTNAME"); if(hostname==NULL) return "none"; return hostname; } const char* getPwd() { const char* pwd=getenv("PWD"); if(pwd==NULL) return "none"; return pwd; } int main() { while(1) { //1.打印提示符信息并获取用户的指令 printf("[%s@%s %s]$ ",getUsername(),getHostname(),getPwd()); char command[1024]={'\0'}; fgets(command,sizeof(command),stdin); } return 0; } 注意: 1.因为scanf默认读取到空格或者’\n’时就会停止继续读取, 可是我们命令行中要求读取到空格也不能停止,(否则我们的指令就无法带选项了,因为选项之间是用空格来分割的) 因此我们需要fgets函数 char command[1024]={'\0'}; fgets(command,sizeof(command),stdin); 1 2 从命令行当中读取一行字符串 2.因为我们用户输入的时候在最后的时候一定会输入一个’\n’,因此我们需要把’\n’置为’\0’ command[strlen(command)-1]='\0';//去除我们最后输入的'\n' 1 把’\n’置为’\0’的操作我们放到了下一个函数当中来完成 2.解析命令行 因为有些命令带有选项:例如 “ls -a -l” 我们在进行程序替换的时候需要分别传入"ls" “-a” "-l"这几个字符串,所以需要把用户输入的字符串分割为若干个字符串存放到一个指针数组当中,可以使用strtok字符串切割函数 #define DEBUG 1 #define SEP " " //分割字符串填入usercommand数组当中 //例如: "ls -a -l" 分割为"ls" "-a" "-l" void CommandSplit(char* usercommand[],char* command) { int i=0; usercommand[i++]=strtok(command,SEP); while(usercommand[i++]=strtok(NULL,SEP)); } //解析命令行 void GetCommand(char* command,char* usercommand[]) { command[strlen(command)-1]='\0';//清理掉最后的'\0' CommandSplit(usercommand,command); #ifdef DEBUG int i=0; while(usercommand[i]!=NULL) { printf("%d : %s\n",i,usercommand[i]); i++; } #endif } 我们可以使用条件编译来方便我们自由选择是否需要打印 解析命令行后的usercommand数组中的内容 3.分析是否是内建命令 1.shell对于内建名令的处理 下面我们就一起来实现一下 cd,echo,export这几个内建命令 2.cd命令 可是如果shell进行进程程序替换了,那么shell执行完之后不就没了吗? 因此shell执行内建命令时直接封装函数调用系统调用接口即可 内建命令的个数是有限的,所以shell是可以对内建命令进行穷举的 因此我们就能更好地理解内建命令了: 内建命令:不需要创建子进程执行,shell自己执行,本质就是调用系统调用接口 3.cd函数的实现 对于cd而言,我们可以调用chdir这个系统调用接口来改变当前进程的工作目录 同时也可以设置一个全局的char类型的数组cwd来保存当前路径 每次cd之后用cwd来记录新路径,然后通过putenv来修改环境变量PWD 让我们第一步打印的提示符中的PWD路径可以动态调整 //cwd:存放当前路径,是一个全局变量 char cwd[1024]={'\0'}; void cd(char* usercommand[]) { //1.chdir改变当前进程的工作目录 chdir(usercommand[1]); //2.获取当前进程所在工作目录到tmp数组当中 char tmp[1024]={'\0'}; getcwd(tmp,sizeof(tmp)); //3.把tmp数组中的内容格式化为"PWD=tmp数组中的内容"放到cwd数组当中 sprintf(cwd,"PWD=%s",tmp); //4.导入环境变量 putenv(cwd); //5.最后一次进程的退出码置为0(是为了echo $?获取最后一次进程的退出码的实现,跟cd无关) lastcode=0; } 注意:cwd数组必须是全局变量,如果cwd是局部变量,那么出了cd这个函数之后cwd数组就会销毁,其中的内容也将会消失 而我们putenv导入环境变量时其实是把cwd的地址放入了环境变量当中, 而不是拷贝一份这个cwd放入环境变量当中,因此cwd数组销毁时,对应导入的环境变量中的内容就不是原来的内容了 4.echo命令的实现 echo命令也是一个内建命令 echo 空串 打印换行 echo $ ? 打印上次进程退出时的退出码 echo $环境变量 打印环境变量 echo 字符串 打印字符串 注意: echo $ 打印$这个字符串 因此即使判断了$,还要继续判断$后面还有没有字符 //全局变量 int lastcode=0;//上一次进程退出时的退出码 int echo(char* usercommand[]) { //1.echo后面什么都没有,相当于'\n' if(usercommand[1]==NULL) { printf("\n"); lastcode=0; return 1; } //2.echo $? echo $PWD echo $ char* cmd=usercommand[1]; int len=strlen(cmd); if(cmd[0]=='$' && len>1) { //echo $? if(cmd[1]=='?') { printf("%d\n",lastcode); lastcode=0; } //echo $PWD else { char* tmp=cmd+1; const char* env=getenv(tmp); //找不到该环境变量,打印'\n',退出码依旧为0 if(env==NULL) { printf("\n"); } else { printf("%s\n",env); } lastcode=0; } } else { printf("%s\n",cmd); } return 1; } 5.export命令的实现 export导入环境变量 注意: 1.刚才介绍cd函数的时候.我们说明了环境变量导入时其实是导入的对应字符串的地址 因此我们环境变量字符串必须要保证全局有效 2.由于我们可以导入很多环境变量,因此env需要是一个二维数组,同时还需要一个index下标来标记该数组当中已经导入过的环境变量 char env[1024][1024]={'\0'}; int my_index=0; void export(char* usercommand[]) { //1.export后面什么都没跟,什么都不执行,直接返回即可 if(usercommand[1]==NULL) { lastcode=0; return; } //2.要导入的环境变量拷贝到env数组当中 strcpy(env[my_index],usercommand[1]); //3.将env数组当中的环境变量导入该进程当中 putenv(env[my_index]); my_index++; } 6.内建命令函数的实现 写好了cd,echo,export这几个函数之后,我们只需要在内建命令函数当中调用这几个函数即可 //返回值=0,说明不是内建命令 //返回值=1,说明是内建命令并且执行成功 int doBuildIn(char* usercommand[]) { //cd if(strcmp(usercommand[0],"cd")==0) { if(usercommand[1]==NULL) return -1; cd(usercommand); return 1; } //echo else if(strcmp(usercommand[0],"echo")==0) { return echo(usercommand); } //export else if(strcmp(usercommand[0],"export")==0) { export(usercommand); } return 0; } int main() { while(1) { //1.打印提示符信息并获取用户的指令 printf("[%s@%s %s]$ ",getUsername(),getHostname(),getPwd()); char command[1024]={'\0'}; fgets(command,sizeof(command),stdin); char* usercommand[1024]={NULL}; //2.解析command字符串,放入usercommand指针数组当中 GetCommand(command,usercommand); //3.检测并执行内建命令,如果是内建命令并成功执行,返回1,未成功执行返回-1,不是内建返回0 int flag=doBuildIn(usercommand); //返回值!=0说明是内建命令,无需执行第4步 if(flag!=0) continue; //4.创建子进程,交由子进程完成任务 Execute(usercommand); } return 0; } 4.创建子进程通过程序替换执行命令 使用execvp这个函数来进行程序替换 带上v:因为我们的命令都是放在数组当中的 带上p:因为我们输入的都是系统指令,带上p才可以自动在环境变量中查找我们的命令 否则就要显式传入路径 注意lastcode的设置 //创建子进程,完成任务 void Execute(char* usercommand[]) { pid_t id=fork(); if(id==0) { //子进程执行部分 execvp(usercommand[0],usercommand); //如果子进程程序替换失败,已退出码为1的状态返回 exit(1); } else { //父进程执行部分 int status=0; //阻塞等待 pid_t rid=waitpid(id,&status,0); if(rid>0) { lastcode=WEXITSTATUS(status); } } } 5.循环往复即可 main函数加上while(1)死循环即可 int main() { while(1) { //1.打印提示符信息并获取用户的指令 printf("[%s@%s %s]$ ",getUsername(),getHostname(),getPwd()); char command[1024]={'\0'}; fgets(command,sizeof(command),stdin); char* usercommand[1024]={NULL}; //2.解析command字符串,放入usercommand指针数组当中 GetCommand(command,usercommand); //3.检测并执行内建命令,如果是内建命令并成功执行,返回1,未成功执行返回-1,不是内建返回0 int flag=doBuildIn(usercommand); //返回值!=0说明是内建命令,无需执行第4步 if(flag!=0) continue; //4.创建子进程,交由子进程完成任务 Execute(usercommand); } return 0; } 三.shell运行原理 shell内部提取用户输入的命令行进行解析 判断是否是内建命令, 1.如果是内建命令的话,shell自己通过调用自己封装的函数来执行该命令 2.如果不是内建命令,shell创建子进程,通过程序替换来然子进程执行该命令 shell进程阻塞等待回收子进程的退出状态 然后循环往复 以上就是Linux自定义shell编写的全部内容,希望能对大家有所帮助! ———————————————— 版权声明:本文为CSDN博主「program-learner」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Wzs040810/article/details/135181981
-
一、原因 原因是Ubuntu22.04使用的gnome启用了Wayland。vnc、teamviewer、向日葵、todesk等均无法使用或者远程黑屏等。 简单的说vnc、teamviewer、向日葵、todesk等均基于xorg实现(xorg太流行),并不兼容Wayland,所以vnc无法正常使用。 realvnc官方也明确说明,目前在Linux上无法支持 Wayland。 GNOME是一套桌面环境,包括一系列应用程序。比如显示管理器,窗口管理器,firefox浏览器等都是XClient。 Xorg:广义的说已经成为一种统称它是Linux上通用的桌面环境(后端)服务器(X11的一种具体开源实现)。 X Window System(又称为X11):以前好像说是一种Linux桌面后端的协议或者规范?应该也提供了一个基本抽象库。如今广泛使用的实现的似乎只有Xorg,所以现在大多时候,X11、Xorg、X Server说的都是一个东西就是Linux桌面的X11后端服务器,也就是Xorg。 Wayland是新一代的Linux桌面后端服务器的协议(或者说规范)而且其也实现了一个此协议的基本库,但是这个不属于具体实现,只是提供一个协议的基础抽象。它有一个参考实现叫Weston。Gnome和KDE(也是一个桌面环境前端)似乎也都有对应的Wayland实现。 因为X11已经很老了,而且有很多问题不好在原有基础上动刀了等等一系列原因,才有的Wayland。 二、现象复现 2.1 按照往常惯例在设置,共享,启用远程桌面,勾选vnc协议,如下图: 2.2 vnc viewer直接输入ip,Ubuntu弹出提示,点允许,连接成功了,但如下图2完全黑屏,仅有鼠标指针,完全无法使用; 三、解决方案 两种思路,一种登录界面切换到Xorg,另一种使用Windows自带的远程桌面mstc 3.1、一般的主流发行版本在登陆时都可以切换到Xorg,此时常用远程软件就可以使用,如下图: 此时还需要在vnc viewer里改一下下图参数,不然还是黑屏‘ 3.2、如下图Wayland居然支持了Windows自带的远程桌面mstc; ———————————————— 版权声明:本文为CSDN博主「闫有尽意无琼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/yanchenyu365/article/details/133038162
-
前言 今天新到了一台服务器,需要安装服务;因为在安装服务的时候,发现/根目录下没有空间了,通过fdisk -l查看了一下磁盘空间,发现还有多余的100G没有使用,所以,这时候就需要去扩容磁盘分区; 当然,在扩容的时候也是有受阻的,比如遇到了磁盘损坏问题“报错fdisk: cannot write disk label: Invalid argument”,这个需要去修复一下:使用parted -l此命令去修复一下,即可重新进行扩容分区;因为在安装的时候也是遇到了一些问题的,因为好几年都没对磁盘空间进行分区了,所以有些忘了,在这里记录一下; 因为服务器已经安装好服务,我们就用虚拟机来进行磁盘分区和挂载并设置永久挂载。 首先,我们需要准备一个虚拟机,并在添加一块硬盘;添加的时候使用推荐的即可,然后创建一个新的虚拟磁盘,这个就不会影响其他的了;磁盘大小我这里就设置成50G即可;过多的细节就不一一说了,一直下一步即可。 设置好之后,启动虚拟机,进行磁盘管理,和磁盘扩容。 一、查看磁盘空间 可以使用fdsik -l查看磁盘空间,这样查看的比较详细; fdisk -l 也可以使用lsblk查看现有的磁盘,看着比较简便明了。 lsblk 使用fdisk -l可以看到/dev/sdb下还有50G空间;目前是空闲的;我们需要将他这50G挂载到/data/上; 二、进行磁盘分区 首先我们先说说磁盘分区的一些命令: 磁盘分区的管理工具 fdisk gdisk lsblk # 查看磁盘分区 fdisk -l # 查看所有磁盘信息 lsblk # 查看所有磁盘信息 # 对磁盘分区进行操作 fdisk -l # 对磁盘分区进行操作 gdisk -l # 对磁盘分区进行操作(用于划分容量超过2T磁盘分区,需要手动安装) 进入磁盘分区的常用的操作命令 命令 解析 m /help 帮助命令,查看其他参数 p 列出分区表 n 创建新的分区 d 删除一个分区 v 查看分区详细信息 e 扩展分区 q 不保存,退出 w 保存,退出 分区讲解 MBR中只可以划分4个主分区,或者时3个主分区+1个扩展分区 扩展分区后,可以划分逻辑分区 进行磁盘分区 # 首先刷新一下磁盘分区表(如果没执行成功也无所谓,这步可有可无) partprobe # 进行磁盘分区 [root@localhost ~]# fdisk /dev/sdb Welcome to fdisk (util-linux 2.32.2). Changes will remain in memory only, until you decide to write them. Be careful before using the write command. Device does not contain a recognized partition table. Created a new DOS disklabel with disk identifier 0x14c524a5. # n 创建磁盘分区 Command (m for help): n Partition type p primary (0 primary, 0 extended, 4 free) e extended (container for logical partitions) Select (default p): # 默认为主分区,直接回车;e为扩展分区。 Using default response p. # 选择1-4分区,默认为1,因为sdb还没有分区,所以直接回车默认就行 Partition number (1-4, default 1): # 选择起始扇区,默认为2048,直接回车就好了 First sector (2048-104857599, default 2048): # 选择要分的磁盘空间大小,默认就是最大,直接回车即可;如果有其他需求,写对应的值即可; Last sector, +sectors or +size{K,M,G,T,P} (2048-104857599, default 104857599): # 提示,成功创建一个Linux类型的磁盘分区,大小为50G。 Created a new partition 1 of type 'Linux' and of size 50 GiB. # p 进行查看,列出分区表;这块是最后我在虚拟机上复制的,虽然是中文,但是都一样。 Command (m for help): p 磁盘 /dev/sdb:53.7 GB, 53687091200 字节,104857600 个扇区 Units = 扇区 of 1 * 512 = 512 bytes 扇区大小(逻辑/物理):512 字节 / 512 字节 I/O 大小(最小/最佳):512 字节 / 512 字节 磁盘标签类型:dos 磁盘标识符:0xe5a06cd9 设备 Boot Start End Blocks Id System /dev/sdb1 2048 104857599 52427776 83 Linux # 可以看到已经列出来了一个50G的sdb1磁盘分区,这时候我们要保存退出; # w 保存退出,如果不想保存直接退出可以使用 q 。 Command (m for help): w The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks. 这样磁盘分区就创建完了; 查看磁盘分区 fdisk -l lsblk 可以看到sdb分出来一个区为50G; 接下来我们就是挂载磁盘分区。 三、挂载/dev/sdb1到/data/目录 首先,需要创建一个/data/空目录 # 记得先查看一下,如果没有就可以创建了,如果有可以重新找一个挂载目录。 然后对磁盘分区进行挂载;直接挂载肯定是不行的,会提示报错的; 所以需要先格式化磁盘分区。 这里针对一下centos格式化磁盘空间的类型; centos7是xfs,centos6是ext4 #Cetnos6格式化磁盘命令为: mkfs.ext4 -f /dev/[sda] #Centos7格式化磁盘命令为: mkfs.xfs -f /dev/[sda] 这里我们是centos7的环境,那么就使用xfs来格式化; # 格式化磁盘分区 [root@192 ~]# mkfs.xfs -f /dev/sdb1 meta-data=/dev/sdb1 isize=512 agcount=4, agsize=3276736 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=0, sparse=0 data = bsize=4096 blocks=13106944, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal log bsize=4096 blocks=6399, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 # 格式化完之后,这时候我们就可以挂载目录了 [root@192 ~]# mount /dev/sdb1 /data/ # 挂载完成之后df -Th查看一下磁盘空间即可; [root@192 ~]# df -Th 文件系统 类型 容量 已用 可用 已用% 挂载点 devtmpfs devtmpfs 898M 0 898M 0% /dev tmpfs tmpfs 910M 0 910M 0% /dev/shm tmpfs tmpfs 910M 9.6M 901M 2% /run tmpfs tmpfs 910M 0 910M 0% /sys/fs/cgroup /dev/mapper/centos-root xfs 17G 1.2G 16G 7% / /dev/sda1 xfs 1014M 150M 865M 15% /boot tmpfs tmpfs 182M 0 182M 0% /run/user/0 /dev/sdb1 xfs 50G 33M 50G 1% /data # 可以看到/dev/sdb1已经挂载上了,类型为 xfs。 扩展: mkfs: 创建文件系统 -f: 强制覆盖 红帽系统8.8格式化命令为: /sbin/mke2fs -j /dev/sdb1 (-j 是ext2,而日志是ext3) 永久挂载为: /dev/sdb1 /data ext3 defaults 1 2 至于为什么是ext3,我也不是很清楚,因为这是商家的一个要求,毕竟永久挂载错了,服务器就起不来了,就需要去虚拟机或者连接服务器去看报错信息;一般配置完这个导致服务器启动不来大部分都是因为类型配置错了。 这个直接mount挂载完只是临时挂载,如果不设置永久挂载,服务器重启之后就会掉,还需要手动去挂载,容易丢失数据,所以我们还需要设置永久挂载。 临时卸载挂载的磁盘为:numount /data/ 四、设置永久挂载 永久挂载我们需要去/etc/fstab 配置文件中来配置; [root@192 ~]# vim /etc/fstab # 在最后一行添加 /dev/sdb1 /data/ xfs defaults 0 0 然后保存退出,重启测试reboot重启,如果配置的挂载有问题会导致服务器启动不了,需要到虚拟机或服务器去排查问题,查看报错信息,一般配置完这个导致服务器启动不了或者是大部分都是因为类型配置错了,所以要谨慎更谨慎; 解析: /dev/sdb1:为磁盘分区的目录,也就是挂载到data的源目录 /data/:这个是目录路径,挂载的目标路径及目录 xfs:centos7是xfs,centos6是ext4,如果不确定可以使用df -Th命令看临时挂载的时候的类型是什么。 defaults:挂载的参数 defaults默认参数 第五段:是否使用dump备份 0不备份 1备份 (0) 第六段:是否使用fsck检测 0不检测 1检测 (0) 重启测试,可以连接上,在使用df -Th看看,没有问题即可;那便是完成永久挂载了。 五、配置完成 ———————————————— 版权声明:本文为CSDN博主「A-刘晨阳」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/liu_chen_yang/article/details/134648396
-
对于服务器运维工作来说,我们少不了SSH远程客户端管理工具。我们在用哪款呢?比如常见用的包括PuTTY、XShell、WindTerm等,有很多的付费或者免费的,既然有这么多免费且好用的为什么我们还会选择付费的呢?在这篇文章中,乐小虎就来盘点市面上6款好用的SSH客户端软件,如果我们有需要的化当然建议到官方下载,确保软件的版本最新 ,以及安全。PS:对于FTP、SSH等服务器运维软件工具,不建议下载第三方的破解版或者汉化版,这样很容易有不安全的后门问题。1、PuTTYPuTTY SSH软件算是相当有年代感的免费SSH工具之一。相信很多老一辈的服务器运维都会用过这款软件,这款软件算是比较早的,当然也是免费的。只不过这么多年来PuTTY软件一直没有进行更新UI界面,所以对于颜值要求较高的用户来说逐渐的选择新的SSH工具。PuTTY SSH软件只支持Windows系统。2、XShellXShell SSH软件近几年比较流行的,基本上很多朋友从PuTTY都会过度到XShell。主要原因是颜值高、中文界面支持,而且还有多标签可以同时管理多态服务器。当然,这款软件的学生和家庭版是免费的,商业用途是需要授权。一般情况下,我们个人都会选择下载用学生和家庭版。XShell SSH软件也是只支持Win客户端的,同时也有配套的XFTP FTP软件。我在实用过程中不清楚是兼容性问题还是版本问题,我在有些电脑中用添加站点的时候会出现偶尔的无法添加的情况,尤其是FTP工具,我是用WinSCP FTP代替的。3、WindTermWindTerm 终端工具的特点支持 Windows、MacOS、Linux 三个系统镜像,最为主要的还是免费开源。我们看到 WindTerm 的界面就像IDE编辑器,体验上也是较好的,而且直接默认是本地语言版本,我们是简体中文版本。对于我们有些用的SSH工具,是不是比较讨厌SFTP和SSH工具是分开的。在WindTerm这里,我们可以在一个界面软件中实现文件上传和下载。上传直接拖动文件拉到文件管理器中就可以。下载直接看到文件右键指定下载到本地。包括文件的移动、复制等都可以可视化操作。WindTerm 支持Win、Mac等多跨平台,当然也是免费的。4、MobaXtermMobaXterm,一款功能齐全的远程工具箱。适用于在Win系统中,给我们提供SSH、FTP,以及RDP远程桌面连接任务。MobaXterm提供了所有重要的远程网络工具,比如支持SSH, X11, RDP, VNC, FTP, MOSH,以及Unix命令(bash, ls, cat, sed, grep, awk, rsync,…)到Windows桌面,有提供便携版和安装版。比如我们在使用SSH连接到远程服务器时,图形SFTP浏览器将自动弹出,以便直接编辑您的远程文件。远程应用程序还将使用嵌入式服务器无缝地显示在Windows桌面上。MobaXterm有提供家庭版免费版本,以及高级付费版本,当然免费版本是有同时打开服务器的数量限制的,以及线程数限制,但是不影响我们的日常使用。5、TermiusTermius,一个全平台的SSH客户端,支持安卓、MacOS、Windows、Mac、Linux,单机版SSH是免费的,如果需要用到云端和同步功能是付费的,需要在软件中自行订阅,我们只是简单的使用它的SSH功能,那么直接下载使用Termius免费版即可。与众不同的是,Termius支持SSH和SFTP,我们只需要一个软件就可以管理服务器。像我们用的XSHELL只有SSH功能。如果我们是安卓或者iOS客户端下载,在软件商店都应该是有的直接下载。如果是Linux、MacOS、Windows客户端,可以官方下载。6、FinalshellFinalshell,一款国产的SSH远程客户端工具。一体化的的服务器,网络管理软件,不仅是SSH客户端,还是功能强大的开发,运维工具,充分满足开发,运维需求。多平台支持,支持Linux、Win和Mac系统。支持云端同步、免费海外服务器远程桌面加速、SSH加速、本地化命令输入框、支持自动补全等。我们在用Finalshell软件的时候可以可视化的看到服务器的性能监控,如果不用云端同步功能无需付费。以上6款SSH软件肯定有一款是可以满足工作业务需要的。我没有准备移动端SSH软件的理由是,我们真运维服务器很少会在手机端操作的,也没有准备十款或者多款,毕竟能让我们选择的在这里几款中肯定有一款适合你。原创文章 乐小虎,如若转载,请注明出处:cid:link_3
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签