Barrelfish研究——分析hake

Barrelfish研究——分析hake

上一篇讲了Barrelfish的环境搭建。在搭建过程中,我们注意到一些步骤和通常编译Linux源码包的方法有不同之处。

类比

通常我们编译Linux源码包的步骤基本上都是先configure再make。这种包的源码管理是用的autotools。另外一种常见的源码管理工具是cmake。编译的方法是先cmake再make。以上的两种方法中,不论是configure还是cmake,它们的目的无非是进行一些平台相关的设置,源码结构的配置,然后生成相应的Makefile。有了Makefile之后,源码管理工具的任务到这里就完成了。后面的步骤基本一致。即执行make,批量(一般是用gcc)编译源码。

Barrelfish的编译过程不同在,其既不是执行configure也不是执行cmake,而是执行了hake.sh。执行过程中,也可以看到漫长的扫描过程,然后会在build目录下生成一个庞大的Makefile文件(大约十几MB)。后面同样也是执行make的过程。

在这个过程中,可以体会到hake.sh的角色,大概同autotools与cmake是相同的,即为生成Makefile。

分析hake.sh

执行hake.sh,打印所支持的选项如下:
-s|--source-dir: path to source tree (required)
-i|--install-dir: path to install directory (defaults to `pwd`)
-a|--architecture: specify archtitecture to build for (can be
    given multiple times, default architectures are
    "x86_64"
-n|--no-hake: just rebuild hake itself, don't run it (only useful
    for debugging hake)
阅读hake.sh,它主要做了以下几项工作:

第一

echo "Setting up hake build directory..."
if [ ! -f hake/Config.hs ]; then
    cp $SRCDIR/hake/Config.hs.template hake/Config.hs
    cat >> hake/Config.hs <<EOF


-- Automatically added by hake.sh. Do NOT copy these definitions to the defaults
source_dir = "$SRCDIR"
architectures = [ $ARCHS ]
install_dir = "$INSTALLDIR"
EOF
else
    echo "You already have Config.hs, leaving it as-is."
fi
通过Config的模板Config.hs.template创建一个新的Config.hs文件,并在文件末尾添加三个变量。这三个变量的值,当然是来于在自hake.sh的参数。

第二

if [ ! -f ./symbolic_targets.mk ]; then
    echo "Creating new symbolic_targets.mk file."
    cp "$SRCDIR/hake/symbolic_targets.mk" . 
else
    echo "You already have symbolic_targets.mk, leaving it as-is."
fi
创建一个新的symbolic_targets.mk文件

第三

ghc -O --make -XDeriveDataTypeable \
    -package ghc \
    -package ghc-paths \
    -o hake/hake \
    -outputdir hake \
    -i$SRCDIR/hake \
    -ihake \
    $SRCDIR/hake/Main.hs $LDFLAGS || exit 1
终于看到了ghc了。有人会问,为什么编译Barrelfish需要ghc,它在其中起什么作用?答案就在这里:ghc的作用就是编译一些hs为后缀名的Haskell源码,生成一个名为hake的程序。
第四
if [ "$RUN_HAKE" == "No" ] ; then
    echo "Not running hake as per your request."
    exit
fi
echo "Running hake..."
./hake/hake --output-filename Makefile --source-dir "$SRCDIR" || exit
注意hake.sh文件的第四个参数-n。如果你设置了这个参数,那么表示仅编译生成hake,并不执行hake。那么这个hake的程序做了哪些工作呢?

分析hake

上面说到,hake是用Haskell语言写的,其main函数所在文件名为Main.hs。如果你了解Haskell语言,那是再好不过的了。不过,如果不熟悉也没有关系,我们并不需要读懂它的每一行代码如何工作,只要知道它的大概功能就可以了。(Haskell语言是一个很值得学习的函数式编程语言,如果你未曾接触过函数式编程语言,花一点时间去了解一下Haskell是很有意义的!)

打开Main.hs文件,直奔末尾找到main函数

main :: IO() 
main = do
    -- parse arguments; architectures default to config file
    args <- System.Environment.getArgs
    let o1 = parse_arguments args
        al = if opt_architectures o1 == [] 
             then Config.architectures 
             else opt_architectures o1
        opts = o1 { opt_architectures = al }
    if opt_usage_error opts then do
	hPutStrLn stderr usage
        exitWith $ ExitFailure 1
      else do


    -- sanity-check configuration settings
    -- this is currently known at compile time, but might not always be!
    if isJust configErrors then do
	hPutStrLn stderr $ "Error in configuration: " ++ (fromJust configErrors)
	exitWith $ ExitFailure 2    
      else do


    hPutStrLn stdout ("Source directory: " ++ opt_sourcedir opts)
    hPutStrLn stdout ("BF Source directory: " ++ opt_bfsourcedir opts)
    hPutStrLn stdout ("Install directory: " ++ opt_installdir opts)
      
    hPutStrLn stdout "Reading directory tree..."
    l <- listFilesR (opt_sourcedir opts)
    hPutStrLn stdout "Reading Hakefiles..."
    hfl <- readHakeFiles $ hakeFiles l
    hPutStrLn stdout "Writing HakeFile module..."
    modf <- openFile ("Hakefiles.hs") WriteMode
    hPutStrLn modf $ hakeModule l hfl
    hClose modf
    hPutStrLn stdout "Evaluating Hakefiles..."
    inrules <- evalHakeFiles opts l hfl
    hPutStrLn stdout "Done!"
    -- filter out rules for unsupported architectures and resolve relative paths
    let rules = 
          ([(f, resolveRelativePaths opts (fromJust (filterRuleByArch rl)) (strip_hfn opts f))
           | (f,rl) <- inrules, isJust (filterRuleByArch rl) ])
    hPutStrLn stdout $ "Generating " ++ (opt_makefilename opts) ++ " - this may take some time (and RAM)..." 
    makef <- openFile(opt_makefilename opts) WriteMode
    hPutStrLn makef $ preamble opts args
    -- let hfl2 = [ strip_hfn opts (fst h) | h <- hfl ]
    hPutStrLn makef $ makeHakeDeps opts $ map fst hfl
    hPutStrLn makef $ makeMakefile rules
    hPutStrLn makef $ makeDirectories rules
    hClose makef
    exitWith ExitSuccess
大约50行左右的代码,看懂它,就是hake所做的工作啦。

如果你用的编辑器不错,可以看到以"--"开头的行被高亮显示(vim和gedit均可),这些行是的注释。"hPutStrLn stdout"开头的行表示在终端打印。忽略这些代码。前十行的功能主要是参数的处理,得到源码路径和安装路径。然后调用listFilesR函数,它的输入是一个目录的路径,然后递归扫描其中所有的文件,输出这些文件完整路径的列表。将这个列表传递给hakeFiles函数,过滤出以"/Hakefile"结尾的文件。(回想那个hello world例子程序中的Hakefile文件)然后将这些Hakefile输入到readHakeFiles函数,即可得到每个Hakefile文件的内容。再调用hakeModule函数,对这些内容做整理,输出到一个名为Hakefiles.hs的文件中。(如果觉得有点晕了,不妨停下来,看看build目录下是否有一个Hakefiles.hs文件。打开看看,会发现,这个庞大的Hakefiles.hs文件有点类似与每个子目录中Hakefile内容的一个大汇总。)现在看到evalHakeFiles,这个函数会调用runghc,以脚本方式运行Hakefiles.hs,生成的结果是一个HRule的列表。HRule类型的定义在HakeTypes.hs文件,用来表示hake自己定义的一些语法规则。再后面hPutStrLn makef开头的几行代码,就是往Makefile中写内容了。这些内容包括一下预先定义好的preamble、编译的依赖关系、以及那些HRule解析的结果。总而言之,这时,一个完整的Makefile就生成了。

大功告成,稍微整理一下思路,想一想hake到底是一个什么东西。它是用Haskell语言写的一个程序,这个程序分析Barrelfish源码的结构,从中找出每一个子目录中的Hakefile,这些Hakefile包含一些编译选项,由hake定义的一套语法规则写成。汇总它,解析它,生成一个巨大的Makefile。把后面编译Barrelfish的任务就交给make和gcc来完成,hake就可以功成身退了。

后记

hake的意思是鳕鱼。笔者后来找到了Barrelfish官方给出的技术文档 TN-003-Hake.pdf,所叙述的工作流程与我们的分析几乎完全一致。只是在谈及hake的设计原则以及必要性方面,对文档中颇有些夸张的说法尚且不能完全赞同。不管怎样,通过对它的分析,了解了它在Barrelfish这个项目整体中所占的角色,方便了我们今后继续对这个系统的研究;并且还稍微学习了一下Haskell语言。收获还是很大的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目前主流的操作系统架构主要有以下几种: 1. 单体式架构(Monolithic Architecture):这种架构下,整个操作系统的所有功能模块都运行在同一个内核空间中,如传统的Windows和Linux操作系统。这种架构简单直接,但由于所有模块都在同一个内核空间运行,容易导致性能和稳定性问题。 2. 微内核架构(Microkernel Architecture):这种架构将操作系统的核心功能和服务进行分离,只保留最基本的功能在内核中,其他功能通过进程或者服务运行在用户态。常见的微内核操作系统有QNX和L4。 3. 外核架构(Exokernel Architecture):外核架构将内核的功能进一步裁剪,只提供硬件和资源的抽象接口,应用程序可以直接管理和控制硬件资源。这种架构提供了更高的灵活性和性能,但也增加了应用程序的复杂性。例子包括Xen和Barrelfish。 4. 虚拟化架构(Virtualization Architecture):这种架构通过虚拟化技术,在一台物理机上同时运行多个虚拟机,每个虚拟机可以独立运行一个操作系统。常见的虚拟化平台有VMware和VirtualBox。 5. 容器化架构(Containerization Architecture):这种架构将应用程序及其所有依赖项打包成一个容器,容器可以在不同的操作系统上运行,并且具有良好的隔离性和可移植性。常见的容器化平台有Docker和Kubernetes。 以上是目前主流的操作系统架构,不同的架构适用于不同的应用场景和需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值