Files
new_install/document/intro.tex
2010-11-08 14:53:51 +08:00

892 lines
30 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
\documentclass[a4paper,12pt,titlepage]{article}
\usepackage{CJKutf8}
\usepackage[hmargin=2cm,vmargin=1.5cm,includeheadfoot]{geometry}
\usepackage{fancyhdr}
\usepackage{verbatim}
\usepackage{color}
\usepackage{framed}
\usepackage{titlesec}
\setcounter{secnumdepth}{5}
\setcounter{tocdepth}{3}
\usepackage{indentfirst}
\usepackage{parskip}
\usepackage[unicode,colorlinks=false,pdfborder={0 0 0}, bookmarksnumbered=true,bookmarksopen=true,bookmarksopenlevel=3]{hyperref}
\usepackage[11pt]{moresize} % 字体
\usepackage[T1]{fontenc}
\usepackage{fancybox} % 支持文本加框
\usepackage{array} % 制表环境
\usepackage{longtable} % 长表格环境
\usepackage{multirow} % 纵向合并单元格
\usepackage{graphics} % 插图
\usepackage{graphicx}
\usepackage{here}
\usepackage{amsmath}
\usepackage{layout}
\usepackage{textcomp}
\usepackage{colortbl}
\usepackage{titlesec,titletoc}
\usepackage{layout}
\usepackage{tikz}
\renewcommand{\baselinestretch}{1.2} % 行距
\definecolor{shadecolor}{gray}{0.90}
\begin{document}
\begin{CJK*}{UTF8}{gbsn}
\CJKcaption{UTF8}
\CJKtilde
\pdfbookmark[-1]{“新”安装程序说明}{title}
\title{“新”安装程序说明}
\date{}
\author{李志}
\maketitle
\pdfbookmark[0]{\quad}{tableofcontents}
\tableofcontents % 目录
\listoftables
\listoffigures
\clearpage
\CJKindent % 首行缩进
\section{简介}
安装程序是完全由我们公司研发部开发的。这项工作是研发部花费时间比较长的一项工作。在2008年之前安装程序就已经存在于我们的4.1版的安装光盘上了。在2008年9月到2009年2月随着4.2版的开发安装程序也在开发。这阶段工作主要是修改原来4.1版的bug并且增加了一些功能软件包分组、软件包可选安装、服务可选启动、支持制作raid磁盘分区等。从2009年2月到2009年8月以春节为界安装程序的工作主要是增加视窗界面功能以前的安装程序界面是基于curses的使用的工具是dialog。自2009年8月到2010年6月安装程序没有重大变化。
现在所提的“新”安装程序指的是从2010年6月到现在2010年10月这段时间做的工作。为什么在安装程序停止主要开发近一年之后要开发所谓“新”的安装程序呢起因在2010年1月开始的安腾Itanium和PowerPC移植工作。2010年1月电网采购招标要求投标的操作系统可以在安腾Itanium和PowerPC平台上运行。于是我开始了操作系统移植工作。在工作中对发现安装程序很难改动这是其一。其二我们的操作系统在惠普平台上很难被安装安装程序是问题根源。其三有很多安装程序的缺陷很难改彻底。深入检查后发现安装程序有以下问题
\begin{itemize}
\item 没有层次。
界面逻辑和安装逻辑混杂在一起。
\item 在不同界面中使用不同的逻辑。
最初的安装程序没有将界面显示相关的逻辑和安装操作的逻辑分开。在做视窗界面时,相关程序员延续了这一做法。为了实现两个界面,他简单的将大量原先的代码复制到一个新的为视窗界面专用的目录之中。在随后长时间的修改缺陷和开发过程中,出现了一种可笑的现象,某个缺陷在“字符界面”不出现,在“视窗界面”出现;或者相反。
\item 没有集中存储数据。
安装程序的每一个步骤都存储自己的数据到一个自己的文件。文件名不统一,文件格式也不统一。
\item 程序模块间关系复杂而混乱。
每一个安装步骤是一个脚本文件,执行的时候就是一个进程。按照执行的时间顺序,它们就是父子进程关系。但是当安装返回上一步时,原有进程并没有消失,而是产生了一个新的进程。
\item 代码罗嗦。
\item 不必要的复杂。
\item 对操作对象表达不清晰。
很难理解为什么安装程序能操作设备/dev/sda但是不能操作/dev/cciss/xxxx。
\end{itemize}
所以从2010年6月起我开始了安装程序的重构工作。称之为“新”是因为它从架构上根本改变了旧安装程序。
\section{设计思想}
“新”安装程序设计的根本是两个理念:分层和以数据为中心。代码可以分成三层:界面层、数据层、和操作层。界面层负责界面的显示。数据层负责安装操作信息的存储。操作层负责执行安装操作。运行逻辑是界面层显示界面给用户,并根据用户输入存储信息到数据层,操作层根据数据层记录的信息执行具体的安装操作。由于有了数据层,模块间关系变得清晰,界面层只负责将数据写入数据层,至于之后这些数据会被如何使用,它并不知道;操作层根据数据层记录的信息执行操作,至于数据如何产生对它并无影响。这就给调试和开发带来了方便。
同样基于以数据为中心,在界面层我也将数据和逻辑分开。我用一个数据文件记录界面信息,比如窗口有几个按钮,它们又是如何摆放的,它们的回调函数是什么。我再在其它文件中实现窗口处理的一般逻辑,比如如何显示按钮,和那些回调函数。
\section{模块划分}
首先所有的python文件都以“ri”作为前缀代表“Rocky install”。
\begin{itemize}
\item ri\_cmd.py: 包含三个一般性的函数quit、 previous、 和next。
\item ri\_data.py: 包含一系列数据结构以python class定义这些数据结构与install.xml中的数据一一对应。
\item ri\_dep.py: 用来处理软件包依赖关系。调用者依次调用~“construct\_depending()”,~“resolve\_recursive\_depending()”,~“construct\_depended()”,~“get\_extra\_depending(pkg\_list)”~就可以得到因依赖关系而引出的额外软件包。
\item ri\_func\_dep.py: 用来检查额外的功能依赖。比如某分区设置为xfs需要xfsprogs软件包。
\item ri\_inst\_cli.py: 为安装模块ri\_install.py提供cli(Command Line Interface)形式接口。
\item ri\_inst\_tk.py: 为安装模块ri\_install.py提供视窗接口通过调用Tk函数实现
\item ri\_install.py: 安装模块,执行安装操作的主控程序。
\item ri\_main.py: 主控模块。
\item ri\_oper.py: 包含一系列python class用以记录安装操作这些安装操作的执行是通过启动子进程调用相对应的shell脚本并以管道与子进程交互。ri\_oper.py被ri\_install.py调用ri\_oper.py调用ri\_install\_tk.py或ri\_install\_cli.py。
\item ri\_tk.py: 处理与tk有关的操作。
\item ri\_tk\_cmd.py: 定义interface.xml中使用的函数。命名为ri\_tk\_cmd.py是因为这些函数在interface.xml中使用时并没有限制必须用tk实现。将来也许我可以定义一个模块ri\_ncurses\_cmd.py用来实现在ncurses上的界面定义一个ri\_gtk\_cmd.py来实现在gtk上的界面或者更多。
\item ri\_widget.py: 包含一系列数据结构以python class定义这些数据结构与interface.xml中的数据一一对应。
\end{itemize}
模块间关系如图\ref{f1}
\section{数据格式说明}
数据格式是xml。xml的模板定义语言是RelaxNG。数据文件包括
\begin{itemize}
\item interface.xml: 定义界面
\item interface\_ng.xml: interface.xml的模板定义
\item install.xml: 定义安装数据
\item install\_ng.xml: install.xml的模板定义
\end{itemize}
\subsection{界面数据格式}
下面我来介绍一下界面数据的格式。interface.xml的语法是由interface\_ng.xml定义的以下详细描述interface\_ng.xml中各个数据域的含义
\begin{shaded}
\begin{tabular}{p{2.5cm}p{3cm}p{3cm}}
元素 & 属性 & 子元素 \\
interface & base\_widget & widget+ \\
& sequence & message\_box+ \\
& & top\_window* \\
& & text* \\
& & sequence+
\end{tabular}
\end{shaded}
\begin{itemize}
\item base\_widget基础控件在安装过程中的大部分时间里都被显示。每个安装步骤只是在它上面显示与安装步骤对应的控件。举例图xxx
\item sequence安装序列控制安装界面的顺序。
\end{itemize}
\subsubsection{widget}
\begin{shaded}
\begin{tabular}{p{2.5cm}p{4cm}p{4cm}}
widget & type & geometry\_management \\
& name & geometry\_location \\
& construct & variable* \\
& widget\_attribute & action \\
& & binding* \\
& & widget*
\end{tabular}
\end{shaded}
\begin{itemize}
\item type记录widget的类型ButtonListText......
\item name名字用于将来索引此widget。
\item construct记录一个函数在ri\_widget.py构造此widget时使用请参考xxx
\item widget\_attributeTk widget的属性程序在显示tk控件的时候会把这里记录的数据直接传递给tk控件的构造函数。
\end{itemize}
\vspace{.5cm}
\begin{enumerate}
\item geometry\_management是一个choice目前只有一个选择grid\_management。geometry management意思是位置管理在Tk中有三种在我的程序中只用到了一个。
\begin{shaded}
\begin{tabular}{p{4cm}p{2cm}p{3cm}}
grid\_management & rows & configure+ \\
& & columns
\end{tabular}
\end{shaded}
\begin{shaded}
\begin{tabular}{p{3cm}p{2cm}}
configure & row \\
& column \\
& weight
\end{tabular}
\end{shaded}
row和column二选一。
\vspace{.5cm}
\item geometry\_location也是一个choice目前只有一个选择grid\_location。
\begin{shaded}
\begin{tabular}{p{4cm}p{2cm}}
grid\_location & row \\
& column \\
& columnspan \\
& rowspan
\end{tabular}
\end{shaded}
全部是传给Tk相关函数的参数。
\vspace{.5cm}
\item variable
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}}
variable & name \\
& type \\
& value
\end{tabular}
\end{shaded}
用来定义Tk中的变量在程序中只用到了StringVar。
\vspace{.5cm}
\item action
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{2cm}}
action & init & scroll* \\
& quit &
\end{tabular}
\end{shaded}
\begin{itemize}
\item init声明在widget显示创建时要额外执行的函数。
\item quit声明在widget隐藏销毁时要额外执行的函数。
\end{itemize}
\vspace{.5cm}
scroll与滚动条有关Tk对滚动条的设计和其它窗口系统不同滚动条是一个单独的widget需要和相关widget进行关联。
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}}
scroll & scrolling \\
& scrolled
\end{tabular}
\end{shaded}
scrolling是一个滚动条scrolled是被滚动的widget比如一个Listbox。
\vspace{.5cm}
\item binding声明与某一个窗口事件sequence关联的函数。
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}}
binding & sequence \\
& function
\end{tabular}
\end{shaded}
\end{enumerate}
\subsubsection{message\_box}
\begin{shaded}
\begin{tabular}{p{3cm}p{2cm}}
message\_box & name \\
& type \\
& title \\
& message \\
\end{tabular}
\end{shaded}
type是以下值它们正是Tk显示不同message\_box的函数askokcancel, askquestion, askretrycancel, askyesno, showerror, showinfo, showwarning。
\subsubsection{top\_window}
\begin{shaded}
\begin{tabular}{p{3cm}p{4cm}p{4cm}}
top\_window & name & geometry\_management \\
& widget\_attribute & widget* \\
& construct &
\end{tabular}
\end{shaded}
top\_window的定义是widget的子集。我去除了top\_window用不到的属性和子元素。关于top\_window我们可以简单地把它理解为弹出窗口而message\_box是一些特殊的top\_window。
\subsubsection{text}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{2cm}}
text & key & English \\
& & Chinese
\end{tabular}
\end{shaded}
我定义text元素是为了解决中英文并且可以扩展到更多语言问题。key是可选项当它不存在时English元素内容起Key的作用。在程序和Interface.xml中我还有一个约定key中存储的内容以\#’开头。
\subsubsection{sequence}
\begin{shaded}
\begin{tabular}{p{2.5cm}p{2cm}p{2cm}}
sequence & name & widget+
\end{tabular}
\end{shaded}
注意这里的sequence与binding中的sequence意义不同那里是直接使用Tk中的词汇这里的指的是安装序列。
name的引入是最初设计的一个想法安装程序支持多个序列。虽然最终安装程序只用到了一个序列但是这个设计被保留下来。
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}}
widget & name
\end{tabular}
\end{shaded}
此widget亦非彼widget。这里只是为了用到它唯一的属性name。
\subsection{安装数据格式}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{3cm}}
install & none & serial-number \\
& & partitions \\
& & raids \\
& & mount-points \\
& & network \\
& & groups \\
& & services
\end{tabular}
\end{shaded}
\subsubsection{serial-number}
\begin{shaded}
\begin{tabular}{p{3cm}p{2cm}p{2cm}}
serial-number & none & 文本
\end{tabular}
\end{shaded}
\subsubsection{partitions}
\begin{shaded}
\begin{tabular}{p{3cm}p{2cm}p{3cm}}
partitions & unit & partition+ \\
& label &
\end{tabular}
\end{shaded}
\begin{itemize}
\item unit是磁盘大小的单位sector, MB, GB。
\item label是一个choice目前两个选择msdosgpt。这里的设计有问题label规定了所有partition但是有可能一块硬盘是msdos另一块是gpt。解决办法应该在partions和~partition之间增加一层。
\end{itemize}
\begin{shaded}
\begin{tabular}{p{3cm}p{3cm}}
partition & device \\
& start \\
& size \\
& from\_os \\
& file-system \\
& flags \\
& type
\end{tabular}
\end{shaded}
from\_os用以标识这个partition元素是由安装程序制造的还是原先就存在的。现在安装程序还没有制作分区的能力所以from\_os总是yes
其余几个属性与parted显示的数据列一一对应。
\subsubsection{raids}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{2cm}}
raids & none & raid
\end{tabular}
\end{shaded}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}}
raid & device \\
& level \\
& from\_os \\
& active \\
& spare
\end{tabular}
\end{shaded}
\begin{itemize}
\item device是raid设备名如md0。
\item level是raid级别目前只支持015。
\item from\_os用来标识此元素是安装程序制作的还是原来就存在的。
\item active标识活动的raid部件设备名。
\item spare标识空闲的raid部件设备名。
\end{itemize}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{3cm}}
active & none & component+ \\
spare & none & component+
\end{tabular}
\end{shaded}
\begin{shaded}
\begin{tabular}{p{3cm}p{2cm}p{2cm}}
component & none & 文本
\end{tabular}
\end{shaded}
\subsubsection{mount-points}
\begin{shaded}
\begin{tabular}{p{3cm}p{2cm}p{3cm}}
mount-points & none & mount-point+
\end{tabular}
\end{shaded}
\begin{shaded}
\begin{tabular}{p{3cm}p{3cm}}
mount-point & device \\
& directory \\
& file-system \\
& format \\
\end{tabular}
\end{shaded}
\begin{itemize}
\item device 设备名
\item directory目录名挂载点
\item file-system文件系统
\item format是否格式化
\end{itemize}
\subsubsection{network}
\begin{shaded}
\begin{tabular}{p{2cm}p{3cm}}
network & hostname \\
& configuration \\
& ip? \\
& mask? \\
& gateway? \\
& primary\_dns? \\
& secondary\_dns? \\
& domain?
\end{tabular}
\end{shaded}
configuration是一个choice两个值dynamic,static。
\subsubsection{groups}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{2cm}}
groups & none & group+
\end{tabular}
\end{shaded}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{3cm}}
group & name & description? \\
& install & mandatory? \\
& & optional?
\end{tabular}
\end{shaded}
install是一个choiceall, yes, no
\vspace{.5cm}
\begin{enumerate}
\item description
\begin{shaded}
\begin{tabular}{p{3cm}p{2cm}p{2cm}}
description & none & 文本
\end{tabular}
\end{shaded}
\item mandatory
\begin{shaded}
mandatory = package+
\end{shaded}
\item optional
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{2cm}}
optional & selection & package+
\end{tabular}
\end{shaded}
selection是一个choiceall, none, manual
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}}
package & name \\
& install?
\end{tabular}
\end{shaded}
install是一个choiceyes, no。
\end{enumerate}
\subsubsection{services}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}p{2cm}}
services & none & service*
\end{tabular}
\end{shaded}
\begin{shaded}
\begin{tabular}{p{2cm}p{2cm}}
service & name \\
& number \\
& script \\
& start \\
& package
\end{tabular}
\end{shaded}
start是一个choicedisable, yes, no。
\section{代码逻辑说明}
\subsection{界面相关逻辑}
\begin{figure}[H]
\centering\fontsize{8.5pt}{\baselineskip}\selectfont\input{Diagram1}
\caption{界面逻辑图}\label{f1}
\end{figure}
\subsection{安装相关逻辑}
\subsubsection{格式化分区}
swaplinux(ext3、ext2、jfs、xfs、reiserfs)fat
\verb=format_partition.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item stdin
\begin{enumerate}
\item partition分区
\item filesystem文件系统类型
\end{enumerate}
\end{itemize}
\item 输出:
\begin{itemize}
\item success:
\verb=@ format $devname success=
\end{itemize}
\item 返回值:
\begin{description}
\item[1] argument error
\item[2] deivce node doesn't exist
\item[3] filesystem no implement
\item[127] format partition utils not found
\end{description}
\item Usage
\begin{itemize}
\item 单个处理:\verb=echo "/dev/sda1 ext2" | ./format_partition.sh=
\item 批量处理:文件格式和单个处理一致
\end{itemize}
\item 说明需要处理swap分区。
\end{itemize}
\subsubsection{挂载分区}
\verb=mount_partition.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item stdin
\begin{enumerate}
\item partition块设备
\item mountpoint挂载点
\item filesystem文件系统类型
\item filsystem options文件系统选项这个为可选参数不传这个参数会有默认的选项文件系统是ext2|ext3|reiserfs会以acl的选项挂载其他则为defaults
\end{enumerate}
\end{itemize}
\item 输出:
\begin{itemize}
\item success:
\verb=@ mount $devname success=
\end{itemize}
\item 返回值:
\begin{description}
\item[1] argument error
\item[2] device node doesn't exist
\item[32] mount failure
\end{description}
\item Usage
\begin{itemize}
\item 单个处理:\verb=echo "/dev/sda1 / ext3" |./mount_partition.sh=
\item 批量处理:文件格式和单个处理一致
\end{itemize}
\item 说明不处理swap分区。
\end{itemize}
\subsubsection{安装软件包}
\verb=install_pkg.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item stdin
\begin{itemize}
\item pkgname
\end{itemize}
\item argument:
\begin{itemize}
\item \verb=[--help] =\qquad show usage
\item \verb=[-s|--source]=\qquad package source directory, if not specify, default is /Rocky/packages.
\end{itemize}
\end{itemize}
\item 输出:
\begin{itemize}
\item sucess:
\verb=@ install $pkgname success.=
\end{itemize}
\item 返回值:
\begin{description}
\item[1] argument error
\item[2] source directory \$PKG\_SOURCE\_DIR doesn't exist
\item[3] pkgname doesn't exist in source directory
\end{description}
\item Usage
\begin{itemize}
\item 单个处理:\verb=echo "acl" |./install_pkg.sh -s /Rocky/packages=
\item 批量处理:文件格式和单个处理一致
\end{itemize}
\item 说明:
\begin{enumerate}
\item 由安装程序生成/var/lib/pkg/db的空文件
\item 默认用/Rocky/packages下面的软件包如果不指定-s参数。
\end{enumerate}
\end{itemize}
\subsubsection{配置fstab}
\verb=configure_fstab.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item stdin
\begin{enumerate}
\item block devices块设备
\item mount point挂载点
\item filesystem type文件系统类型
以下参数为可选参数,如果不指定,会有默认的配置,如果指定5)那么4)也需要指定,依次类推
\item fs\_mntops ext2|ext3|reiserfs的默认配置是acl其他都是defaults。
\item fs\_freq默认配置swap是0,其他都是1
\item fs\_passno默认配置swap是0,其他都是1
\end{enumerate}
\end{itemize}
\item 输出:无
\item 返回值:
\begin{description}
\item [1] argument error
\item [2] fstab doesn't exist
\end{description}
\item Usage
\begin{itemize}
\item 单个处理:\verb=echo "/dev/hda2 /home ext3"|./configure_fstab.sh=
\item 批量处理:文件格式和单个处理一致
\end{itemize}
\item 说明:
\begin{enumerate}
\item 需要处理swap;
\item 块设备如果是swap则转成by-id其他都转成by-uuid这个是因为udev-117的udevinfo 不能查出查出raid的by-id也不能查出swap的uuid。
\end{enumerate}
\end{itemize}
\subsubsection{生成issue文件}
\verb=generate_issue.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item argument
\begin{itemize}
\item \verb=[--help] =\qquad show usage
\item \verb=[-v|--version]=\qquad version of OS
\item \verb=[-a|--arch] =\qquad architecture of OS
\item \verb=[-r|--release]=\qquad release of OS(base or security)
\item \verb=[-d|--date] =\qquad date of generate OS
\end{itemize}
\end{itemize}
\item 输出:无
\item 返回值:
\begin{description}
\item [1] argument error
\end{description}
\item 说明:
\begin{enumerate}
\item 生成\verb=$TARGET/etc/issue=文件,一般\verb+TARGET=/mnt+
\item 可以只给一个参数,但是这样其他参数则为空值。
\end{enumerate}
\end{itemize}
\subsubsection{配置网络}
\verb=configure_network.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item argument
\verb=<-t|--type> "static" or "dynamic"=
\begin{itemize}
\item static argument:
\begin{itemize}
\item \verb=<-h|--hostname> =\qquad hostname
\item \verb=<-i|--ip> =\qquad ip address
\item \verb=<-n|--netmask> =\qquad netmask
\item \verb=<-g|--gateway> =\qquad gateway
\item \verb=<-d|--domainname>=\qquad domainname
\item \verb=[-p|--pdns] =\qquad priamaryDNS
\item \verb=[-s|--sdns] =\qquad secondaryDNS
\item\verb= [-e|--device] =\qquad network device. if no configure, default is eth0.
\end{itemize}
\item dynamic argument:
\begin{itemize}
\item \verb=<-h|--hostname> =\qquad hostname
\item \verb=[-e|--device] =\qquad network device. if no configure, default is eth0.
\end{itemize}
\end{itemize}
\end{itemize}
\item 输出:无
\item 返回值:
\begin{description}
\item [1] argument error
\item [2] ip/netmask/gateway address incorrect
\item [3] hosts/resolv.conf/ifcfg-eth0/network doesn't exist
\end{description}
\item Usage
\begin{verbatim}
./configure_network.sh -t static -h QinBo -d in.linx -i 192.168.1.110
-n 255.255.255.0 -g 192.168.1.254 -p 172.16.0.254
\end{verbatim}
\item 说明:
\begin{enumerate}
\item type必须为static或者dynamic
\item 不管哪种typehostname是必须的
\item type=static的时候IP、域名、子网掩码、网关是必须的
\item device多网卡的时候可以指定用哪个网卡默认用的是eth0。
\end{enumerate}
\end{itemize}
\subsubsection{做自启服务}
\verb=mk_serv_autoboot.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item stdin
\begin{enumerate}
\item package name软件包名字自启动服务所在软件包
\item boot script启动脚本
\item boot service需要启动的服务
\item boot number启动号
\end{enumerate}
\end{itemize}
\item 输出:无
\item 返回值:
\begin{description}
\item[1] argument error
\item[2] boot script doesn't exist
\end{description}
\item Usage
\begin{itemize}
\item 单个处理:\verb=echo "netkit-base inetd rsh S310"|./mk_serv_autoboot.sh=
\item 批量处理:文件格式和单个处理一致
\end{itemize}
\item 说明:默认在\verb=/etc/rc.d/{rc3.d,rc5.d}=建立链接。
\end{itemize}
\subsubsection{拷贝kernel、modules和生成initrd}
\verb=copy_kernels.sh=
\begin{itemize}
\item 输入:无
\item 输出:无
\item 返回值:
\begin{description}
\item[1] kernel directory/modules directory/initrd.gz/makeinitrd doesn't exist
\end{description}
\item 说明:
\begin{enumerate}
\item 此脚本从/Rocky/kernels和/Rocky/modules目录下拷贝kernel和modules
\item 把/Rocky/initrd/initrd.gz拷贝到硬盘上的/boot/initrd.gz.old
\item 生成initrd调用原来的makeinitrd脚本生成。
\end{enumerate}
\end{itemize}
\subsubsection{配置bootloader配置文件}
\verb=configure_bootloader_cnf.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item arguments
\begin{itemize}
\item \verb=[--help] =\qquad show this message
\item \verb=<-t|--type> =\qquad boot loader type:grub,etc.
\item \verb=<-r|--root> =\qquad partition mount by root
\item \verb=[-b|--boot] =\qquad partition mount by boot (optional,if the boot is not mount by stand alone)
\item \verb=[-k|--kparameter]=\qquad kernel parameter
\item \verb=[-o|--osversion] =\qquad OS version
\end{itemize}
\end{itemize}
\item 输出:无
\item 返回值:
\begin{description}
\item [1] argument error
\item [2] bootloader type doesn't specify
\item [3] bootloader no implement
\item [4] bootloader configuration file doesn't exist
\end{description}
\item 说明对grub软件包里面的grub.conf.sample(\$TARGET/boot/grub/)进行相应的修改,产生新的文件:\$TARGET/boot/grub/menu.lst。
\end{itemize}
\subsubsection{安装bootloader}
\verb=install_bootloader.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item arguments
\begin{itemize}
\item \verb=[--help] =\qquad show this message
\item \verb=<-t|--type>=\qquad boot loader type:grub,etc.
\end{itemize}
\end{itemize}
\item 输出:无
\item 返回值:
\begin{description}
\item[1] argument error
\item[2] bootloader type doesn't specify
\item[3] bootloader no implement
\item[127] bootloader utils not found
\end{description}
\item 说明此脚本把grub装在第一块硬盘上面因为bios默认会从第一块硬盘启动这样bios就能找到我们的装上去的gurb从而正确引导系统。
\end{itemize}
\subsubsection{安装完成执行的脚本}
\verb=exec_finish_install.sh=
\begin{itemize}
\item 输入:
\begin{itemize}
\item stdin
\begin{itemize}
\item 脚本的名字
\end{itemize}
\end{itemize}
\item 输出:无
\item 返回值根据具体执行脚本的返回值来确定0是成功。
\item Usage
\begin{itemize}
\item 单个处理:\verb=echo "99finish_install.sh"| ./exec_finish_install.sh=
\item 批量处理:文件格式和单个处理一致
\end{itemize}
\item 说明此脚本把99finish\_install.sh拷贝到/var/finish\_install目录下然后根据 /var/finish\_install 目录下面的脚本名字前面的数字顺序,从小到大依次拷贝到硬盘系统里面执行。
99finish\_install.sh 即原来的firstboot.sh。
\end{itemize}
\subsection{接口逻辑}
\subsubsection{界面层和操作层}
界面层和操作层在进程关系上是两个进程界面层进程负责根据用户输入写入配置文件install.xml。操作层读入install.xml根据其中信息执行相应操作。
\subsubsection{操作层:控制程序、操作、和界面}
操作层有一个总控程序ri\_install.py操作程序ri\_oper.py及大量shell scripts以及界面程序ri\_install\_tk.py和ri\_install\_cli.py。它们的关系如图\ref{f2}
\begin{figure}[H]
\centering\fontsize{8.5pt}{\baselineskip}\selectfont\input{Diagram2}
\caption{接口逻辑图}\label{f2}
\end{figure}
\section{特殊情况处理}
\subsection{软件包显示界面}
有以下几个特殊之处需要处理:
\begin{itemize}
\item 软件组个数不固定无法在interface.xml中声明
\item 显示每个软件组信息的top\_window如何激活
\end{itemize}
为了解决第一个问题在interface.xml和interface\_ng.xml中为widget和top\_window增加了一个属性construct记录一个函数名。在ri\_widget.py对widget或top\_window构造时会调用这个函数如果有construct属性的话在这个函数中会对widget的结构属性、子widget等进行添加、修改、和删除操作。
为了解决第二个问题首先在ri\_tk.py中定义了一个class:SoftwarePackageWindow我认为也可以通过interface.xml定义再加上一些辅助函数也可以达到同样效果其次在ri\_tk\_cmd.py中我定义了class GroupButton和GroupCheck它们都定义了\_\_call\_\_()于是它们的实例可以作为函数使用。第三在construct属性所指向的“构造”函数中我为每个软件组建立了一个Button或者CheckButton它们的回调函数就是GroupButton或者GroupCheck的实例。
第二个问题的难点在于每个软件组需要显示不同的必选软件包和可选软件包显示逻辑本身又是一致的。比较容易想到的是定义一个函数函数有一个输入参数。但是Button的回调函数又要求是没有参数的所以引入了“函数对象”。
\subsection{安装界面}
安装界面被设计成一个单独的进程在interface.xml中没有它的定义。实际安装操作在大量的shell scripts中实现这些shell scripts每一个都是独立的程序互相没有调用关系。ri\_oper.py为每一个shell script定义一个class这些class负责调用shell script并提供调用参数还要通过管道提供输入读取输出。安装界面有一个特殊之处它提供了两种界面tk和cli。我把涉及界面显示部分一一抽出来做成函数比如显示目前安装步骤显示安装进行了百分之多少......在ri\_install\_tk.py和ri\_install\_cli.py中都定义了这些的函数。最后ri\_install.py根据输入参数决定语言环境和显示界面为众多ri\_oper实例赋予安装权重调用ri\_oper实例的install函数。
\subsection{RAID~操作}
mkraid.sh与其它脚本在调用参数和标准输入上有些差别为了降低程序复杂度专为它提供了mkraid\_wrapper.sh。目的是规范成与其它脚本相同格式的调用参数、标准输入、和标准输出。
\section{没有做的事}
\section{安装程序使用}
\subsection{几种“非主流”安装方法}
\subsubsection{远程安装}
\subsubsection{利用配置好的xml安装}
\subsubsection{纯手工安装}
\subsection{安装过程中易碰到的问题}
\appendix
\section{Python \& TK}
\section{Xml \& RelaxNG}
\clearpage
\end{CJK*}
\end{document}