• 9
  • Sep

“盗梦空间(Inception)”刚刚在国内上映那会(2010年9月1日)我就去看了,确实是一部好电影。后来还在网上看到说盗梦空间就像是一层层的“函数调用”,确实很形象(见此:Inception:一场层层调用的函数大战)。

今天看推时,突然看到一条,说是Shell有个变量:$SHLVL,是查看当前Shell的级别(Level)。我立马一试,果然如此,简直就是Inception的翻版!

我们在使用Shell(以Bash为例)的过程中,实际上就是一个Inception的过程:执行一个脚本的时候,并不是在当前的Shell环境下执行的,而是另起一个Shell,在里面执行,执行完毕后,销毁它,并回到当前的Shell。

一般情况下,我们的Shell都是第一层,如图:

Inception Terminal 01

如何进入下一层呢?只需要不停地执行自身(bash)即可,如图,执行了三次bash后,成功进入了第四层Shell:

Inception Terminal 02

与Inception一样的是,想要回到前一层,必须得执行“exit”完成任务,或者被“kill”。假如很不幸第一层被kill掉的话,后面的N层梦境都没有了(父进程退出,子进程也遭殃)。

问题来了:Shell到底支持几层梦境呢?在看了Bash的源码后,我发现它竟然支持1000层!在Bash源码的variables.c,约4680行左右中有这样的定义:

if (shell_level < 0)
  shell_level = 0;
else if (shell_level > 1000)
{
  internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
  shell_level = 1;
}

也就是说,超过1000层后,又回到第1层,也就是现实世界了?

于是我就想进入1000层看看,如何方便的进入第1000层Shell呢?for loop一次次套一个bash命令肯定是行不通的,因为在进入下一层Shell后,上层的Shell的命令就不再对这层起作用了。

我想了很多方法都行不通,最后只好去请教神通广大、无所不能的华主席(@shellexy)。华主席就是牛逼,马上就给了一个解决方案,用Pygtk来Inception至N层!

原理是这样的:

使用GTK+的vte库来创建一个虚拟终端,通过vte.Terminal的paste_clipboard方法,不停地注入“bash \n”,于是该Terminal就能按照你想要的方式进入N层梦境了。源代码如下:

#!/usr/bin/python

import vte, gtk

window = gtk.Window()
window.set_default_size(480, 320)
window.set_title('Inception')
window.connect('destroy', lambda *w: gtk.main_quit())

sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

term = vte.Terminal()
term.set_emulation('xterm')

sw.add(term)
window.add(sw)
window.show_all()

term.fork_command()
clipboard = gtk.Clipboard()

for i in range(1000):
    clipboard.set_text('echo $SHLVL \n bash \n')
    term.paste_clipboard()

gtk.main()

代码很简单,基本上不需要讲解。关键的便是clipboard的剪贴版操作及terminal的粘贴动作。

不过,如果你想真正进入第1000层,可是有点难的。这要取决于你的电脑是否有够强劲的CPU,是否有足够的内存——当然还有你是不是有足够的耐心。

在我的电脑上,我跑了很久……基本上是这篇日志开始写的时候开始跑的,一直到日志写完,还只有进入第三百多层,接下来每一层的进入都会花很多的时间,可以说是越来越慢。同时,我的物理内存(2GB)已经消耗完毕,开始烧SWAP了。

来两张图吧,壮观的“盗梦终端”:每一层都在“do_wait”下一层的完成……

Inception Terminal 03

pstree命令的效果:bash-bash-bash……实在是显示不过来了。

Inception Terminal 04

写完这篇日志时,我已经Inception至四百多层了,SWAP大概还有1700MB,看似可行,不过同时我的机器反应速度也越来越慢了,我还是先把日志发表了吧~

操作系统的世界,还真是跟现实相当类似啊。当然操作系统世界要比当前人类世界领先多了,比如fork(),哈哈。

我在想,要不大家来一个Inception至一千层的比赛,看谁的机器比较给力,能在最短的时间进入呢?

UPDATE-2010-09-10

发现rem1x兄提供的利用.bashrc进行Inception的方式相当棒。在此补充一下:

在$HOME目录下建立一个inception.sh,内容为:

echo "Level $SHLVL"
if [ $SHLVL -eq 999 ]; then
	echo "e n d: `date +%H:%m:%S`"
else
	bash
fi

然后再往 $HOME/.bashrc 中加入这么一段:

if [ "$SHLVL" -eq 1 ]; then
	echo "start: `date +%H:%m:%S`"
fi
	echo "Level $SHLVL"
	./inception.sh

接下来开启一下全新的终端,然后它便开始不停地Inception至下一层了!

» You can leave a comment.

4 Pingbacks

34 Comments

  1. 妳好蛋疼。。。

  2. 偷懒一下……

    jimmy@jimmy:~$ export SHLVL=1000
    jimmy@jimmy:~$ bash
    bash: warning: shell level (1001) too high, resetting to 1
    jimmy@jimmy:~$

  3. 加在.bashrc里面:

    if [[ $SHLVL -le 1000 ]];then
    bash
    fi

    我看我6G内存能撑得住么

  4. rem1x

    inception.sh:

    echo "Level $SHLVL"
    if [ $SHLVL -eq 999 ]; then
    echo "e n d: `date +%H:%m:%S`"
    else
    bash
    fi

    -------------华丽分割线--------------
    我的猥琐方法......

    .bashrc :

    if [ "$SHLVL" -eq 1 ]; then
    echo "start: `date +%H:%m:%S`"
    fi
    echo "Level $SHLVL"
    ./inception.sh

    -------------输出分割线---------------

    start: 22:09:34
    Level 1
    Level 2
    Level 3
    ...
    ...
    ...
    Level 999
    e n d: 22:09:58

  5. 也忒能折腾了。。。1G内存就不做测试了。。。

  6. ttplay

    fedora13 不慢啊 (偶2G 的内存) 运行完,发现少了620M 内存

    $ time python 1.py
    real 0m49.350s
    user 0m2.348s
    sys 0m0.331s

    不过要注意ulimit -u,默认1024, 不设置会提示资源不可用,设置成3000就可以了

  7. 我一直用SHLVL判断是source还是bash。

  8. 钢盅郭子

    "当然操作系统世界要比当前人类世界领先多了"这话可不对,人类世界可以制造OS,OS可以制造人类世界吗?
    每一个宇宙分支不就是执行了一次fork()吗

  9. LaoLiulaoliu

    楼主的机子或者程序有问题,我手动进入328层shell的时候,没有用什么CPU,内存也才250M。进入1000层也消耗不了多少吧。
    基本上进入多少层shell就相当于开了多少个terminal

  10. landy

    用个省资料的终端会快些吗?在字符模式下能达到应该快些吧。

  11. BOYPT

    单纯想耗内存的话这样快点,一样开了很多bash,但是$SHLVL数值不增加

    #!/bin/bash
    inc (){
    (sleep 0.1;inc)
    }
    inc

    真想死的把sleep也去掉……

  12. ETiV

    创建一个电子表格文档, A1写bash, 拖动操作点至第999行. 全选, 复制, 粘贴到终端.

    [root@SERVER /home/web]# bash
    [root@SERVER /home/web]# bash
    [root@SERVER /home/web]# echo $SHLVL
    999
    [root@SERVER /home/web]#

  13. ETiV

    last pid: 20843; load averages: 0.00, 0.00, 0.00 up 6+00:21:31 18:25:09
    1070 processes:1 running, 1069 sleeping
    CPU: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 100% idle
    Mem: 1082M Active, 580M Inact, 620M Wired, 128K Cache, 809M Buf, 5435M Free

    8G内存表示压力不大.

  14. crackevil

    直接递归bash脚本都行啊,几乎不占资源的。的确到了1000就重1开始了。

  15. 真够疯狂的啊。。。

  16. thorne

    我是从图卦上看来的
    图片水印是个好东西
    如此我才晓得Tualatrix有了新文章

  17. 那个 pygtk 脚本慢的原因是因为操作是阻塞的,
    主席开个子线程,控制下时间间隔,就能改善了

  18. 也就是说,超过1000层后,又回到第1层,也就是现实世界了?

    这个表述是错误的。第1001层虽然叫做第1 层,但是和你开始时候的第1 层是不一样的。俺在俺的机器上试过了。

  19. 成了硬件性能测试脚本了……

  20. jiapj

    Mac OS 上进到250层就不行了好像, 我的方法就是在.bash_profile里加了一条:
    bash -l
    echo $SHLVL

  21. ych_tiger

    [tiger@xx_239 ~]$ export SHLVL=1001
    [tiger@xx_239 ~]$ echo $SHLVL
    1001
    [tiger@xx_239 ~]$ bash
    bash: warning: shell level (1002) too high, resetting to 1
    [tiger@xx_239 ~]$ echo $SHLVL
    1

    突破1001

  22. liu6104

    牛人一个

  23. prown

    靠,太猛鸟。
    不过这里的1001应该不是第一层吧

  24. 如果1000以后不是真正的第一层……这么说来Inception里男主回到现实以后的现实仍然不是真正的现实??!

  25. windylcx

    有意思!

  26. ptpt52

    盗梦终端:如何进入第1000层梦境

    #!/bin/bash
    #this will go to lvl 1000 dream!
    if test $SHLVL -lt 999; then
    echo level $((SHLVL+1))
    bash -f $0
    fi
    echo level $((SHLVL+1))
    bash

    保存为 bash1000,直接运行即可 进入1000层梦境

Leave a Comment