简述 :(){ :|: & };: 代码的意义
Posted 白-胖-子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简述 :(){ :|: & };: 代码的意义相关的知识,希望对你有一定的参考价值。
面试题:
- 请简述以下shell 代码的意义,并说明运行此段代码会对服务器产生什么影响?
:(){ :|: & };:
分析
这么看不直观,加个壳看的就清楚了
#!/bin/bash
:(){
:|: &
};
:
还看不明白么?
那么好,我们言归正传!
shell函数声明的方式:
- shell脚本中函数声明的方式通常有以下三种
#语法一:
func_name (){
...函数体...
}
#语法二:
function func_name {
...函数体...
}
#语法三:
function func_name () {
...函数体...
}
- 拆分源代码,其实开始声明了一个函数名为冒号<:>的函数
:(){函数体}
冒号命令 :
- linux bash shell中确实有一个什么也不做的冒号 :命令
#whatis :
: (1) - bash built-in commands, see bash(1)
#type :
: is a shell builtin
BASH BUILTIN COMMANDS
Unless otherwise noted, each builtin command documented in this section as accepting options preceded by - accepts -- to signify the end of the options. The
:, true, false, and test builtins do not accept options and do not treat -- specially. The exit, logout, return, break, continue, let, and shift builtins
accept and process arguments beginning with - without requiring --. Other builtins that accept arguments but are not specified as accepting options interpret
arguments beginning with - as invalid options and require -- to prevent this interpretation.
: [arguments]
No effect; the command does nothing beyond expanding arguments and performing any specified redirections. The return status is zero.
- 说人话,就是冒号:命令和true命令一样,命令返回的结果永远都是0
#:;echo $?;true;echo $?
0
0
其实这个函数与冒号:命令没有关系,使用冒号:命令只不过用来挖坑掩人耳目
管道符 |
- 管道(使用符号“|”表示)用来连接多个命令
命令1 | 命令2 | 命令3 | …
管道符将命令1的STDOUT发送给命令2的STDIN,命令2的STDOUT发送到命令3的STDIN
这里需要注意的是:
- 所有命令会在当前shell进程的子shell进程中执行
- 也就是说管道符 | 在传递出参数的同时还会fork出子进程来运行接下来的命令
这条代码就是用到了管道 | 新开子进程的原理实现的,这个是关键
函数体中的内容
- 估计看出来是函数,各位看官当时就明白这个函数是怎么运行的了
- 我们不妨继续拆一拆函数体
: | : &
- : 执行命令冒号 :
- : | 将执行冒号 :命令的结果,也就是0,通过管道传给冒号 :命令作为参数
- & 放在后台运行
- 这样看这个代码也没什么问题
关键在于函数名和内部命令的名字是相同的,才会发生自己调用自己的死循环
从头拆一遍这段代码
#!/bin/bash
:(){ ## 定义一个名为冒号 :的函数
:|: & ## 不断通过管道在后台传递给自己重复执行
}; ## 函数结束
: ## 执行冒号: 函数
- 这就是传说中的fork() Bomb
组合成炸弹
- 各位看官看到这也许就疑问了,不过是管道传个0嘛,又没有循环
- 这有怎么就成了炸弹了么?
我们先来看一下度娘的定义
fork炸弹(fork bomb)在计算机领域中是一种利用系统调用fork(或其他等效的方式)进行的阻断服务攻击。
光是:|: &命令本身,没什么问题,执行一次后就退出了
光是:(){};:一个奇奇怪怪叫做冒号:的函数也没啥问题,名字奇怪而已
但当这两个看起来奇奇怪怪的东西组合在一起,就产生了不可思议的后果
我们来看看执行的过程:
- 执行冒号 : 函数
- 函数执行过程中不断将函数输出的结果传给自己
- 接收到结果的同时再次调用自身开启新的进程并并放到后台执行
- 不断地开启新的子进程,占用系统资源
本质上,创建一个函数,该函数每次调用都会调用两次,并且没有任何方法可以终止自身。它会一直加倍,直到你用完系统资源。
运行此脚本对服务器产生的影响
fork炸弹以极快的速度创建大量进程(进程数呈以2为底数的指数增长趋势),并以此消耗系统分配予进程的可用空间使进程表饱和,而系统在进程表饱和后就无法运行新程序,除非进程表中的某一进程终止;但由于fork炸弹程序所创建的所有实例都会不断探测空缺的进程槽并尝试取用以创建新进程,因而即使在某进程终止后也基本不可能运行新进程。
fork炸弹生成的子程序在消耗进程表空间的同时也会占用CPU和内存,从而导致系统与现有进程运行速度放缓,响应时间也会随之大幅增加,以致于无法正常完成任务,从而使系统的正常运作受到严重影响。
实践
[12:14:25 root@C8-88[ ~]#:(){ :|: & };:
[1] 2260
[12:16:07 root@C8-88[ ~]#ps aux | grep 2260
- 执行完服务器直接死机了
以上是关于简述 :(){ :|: & };: 代码的意义的主要内容,如果未能解决你的问题,请参考以下文章
线代&NumPy第八章 - 特征值和特征向量 | Eigenvalue and Eigenvector | 简述并提供代码