天天看点

制作initrd(2):update-initramfs和mkinitramfs脚本分析

    前一篇文章<制作initrd(1):向initrd内部更新驱动模块>提到更新initrd.img镜像时需要运行update-initramfs命令。起初以为是二进制文件,网上胡乱搜索一通发现update-initramfs和mkinitramfs两个命令同为脚本文件,既然是shell脚本那必须得分析内容并备忘。

root@ubuntu:~# file `which update-initramfs`
/usr/sbin/update-initramfs: POSIX shell script, ASCII text executable
root@ubuntu:~# file `which mkinitramfs`
/usr/sbin/mkinitramfs: POSIX shell script, ASCII text executable      

    总的来说,编译内核的最后一步执行make install时会调用update-initramfs,update-initramfs继而调用mkinitramfs生成initrd.img。因此,mkinitramfs是核心脚本,他的作用是啥?如果你有手工做过initrd.img的经历,一定记得这是一个往临时initrd目录copy文件的繁琐过程,mkinitramfs则用脚本替代了手工操作(真伟大,还把人逼失业了!):1).在临时initrd目录下构建FHS规定的文件系统;2).按/etc/initramfs-tools/module和/etc/modules文件的配置,往lib/modules/目录拷贝模块,同时生成模块依赖文件modules.dep,以后内核启动后会从initramfs中(initrd.img被解压到内存中)按模块依赖关系modprobe模块;3).拷贝/etc/initramfs-tools/scripts和/usr/share/initramfs-tools/scripts下的配置文件到conf/目录下,以后内核启动,创建第一个进程init(initrd.img根目录下init.sh文件)会从conf/*读取配置,按一定的顺序加载模块/执行程序;4).模块的加载离不开modprobe工具集,因此需要拷贝modprobe工具集及其他工具到initrd目录结构下,同时解决这些工具的依赖关系(依赖的so文件的路径);5).所有步骤完成,调用cpio和gzip工具打包压缩临时initrd目录结构。

    上面是mkinitramfs整体流程的概括,下面直接贴注释后脚本,如注释有误请在留言栏告诉我,谢谢~

    首先是update-initramfs脚本,仅注释了执行update-initramfs -u更新initrd.img时的脚本执行流。脚本的开始定义了一些变量,然后跳去执行L400+之后的while getopts... do--按命令参数设置不同的运行模式。之后,按运行模式,进入不同的函数,以执行update-initramfs -u为例,进入update函数。 

#!/bin/sh

STATEDIR=/var/lib/initramfs-tools
BOOTDIR=/boot
CONF=/etc/initramfs-tools/update-initramfs.conf
USETRIGGERS=true
mode=""
version=""
update_initramfs=yes
backup_initramfs=no

set -e
#CONF存在且可读 则source ${CONF}中的设置 即source <span style="font-family: Arial, Helvetica, sans-serif;">/etc/initramfs-tools/update-initramfs.conf文件里的变量</span>

[ -r ${CONF} ] && . ${CONF}

case "$DPKG_MAINTSCRIPT_PACKAGE" in
linux-image-*)
  #INITRAMFS_TOOLS_KERNEL_HOOK 长度为0
  if [ -z "$INITRAMFS_TOOLS_KERNEL_HOOK" ]; then
    # kernel maintainer script called us directly; ignore
    # it and let the hook script handle it instead
    echo "update-initramfs: deferring update (hook will be called later)"
    exit 0
  fi
  ;;
?*)
  if     $USETRIGGERS           \ #USETRIGGERS=true
    && [ $# = 1 ]           \ #参数数量==1
    && [ x"$1" = x-u ]          \ #第一个参数是-u
    && dpkg-trigger --check-supported 2>/dev/null
  then
    if dpkg-trigger --no-await update-initramfs; then
      echo "update-initramfs: deferring update (trigger activated)"
      exit 0
    fi
  fi
  ;;
esac
#开始定义一些函数 先搜索getopts,定位到那继续
usage()
{
  if [ -n "${1:-}" ]; then
    printf "${*}\n\n" >&2
  fi
  cat >&2 << EOF
Usage: ${0} [OPTION]...

Options:
 -k [version] Specify kernel version or 'all'
 -c   Create a new initramfs
 -u   Update an existing initramfs
 -d   Remove an existing initramfs
 -t   Take over a custom initramfs with this one
 -b   Set alternate boot directory
 -v   Be verbose
 -h   This message

EOF
  exit 1
}

# chroot check
chrooted()
{
  # borrowed from udev's postinst
  if [ "$(stat -c %d/%i /)" = "$(stat -Lc %d/%i /proc/1/root 2>/dev/null)" ]; then
    # the devicenumber/inode pair of / is the same as that of
    # /sbin/init's root, so we're *not* in a chroot and hence
    # return false.
    return 1
  fi
return 0
}

mild_panic()
{
  if [ -n "${1:-}" ]; then
    printf "${*}\n" >&2
  fi
  exit 0
}

panic()
{
  if [ -n "${1:-}" ]; then
    printf "${*}\n" >&2
  fi
  exit 1
}

verbose()
{
  if [ "${verbose}" = 1 ]; then
    printf "${*}\n"
  fi
}

version_exists()
{
  [ -e "${STATEDIR}/${1}" ] && [ -e "${initramfs}" ]
  return $?
}

set_initramfs()
{
  initramfs="${BOOTDIR}/initrd.img-${version}"
}


# backup initramfs while running
backup_initramfs()
{
  [ ! -r "${initramfs}" ] && return 0
  initramfs_bak="${initramfs}.dpkg-bak"
  [ -r "${initramfs_bak}" ] && rm -f "${initramfs_bak}"
  ln -f "${initramfs}" "${initramfs_bak}" \
    || cp -a "${initramfs}" "${initramfs_bak}"
  verbose "Keeping ${initramfs_bak}"
}

# keep booted initramfs
backup_booted_initramfs()
{
  initramfs_bak="${initramfs}.dpkg-bak"

  # first time run thus no backup
  [ ! -r "${initramfs_bak}" ] && return 0

  # chroot with no /proc
  [ ! -r /proc/uptime ] && rm -f "${initramfs_bak}" && return 0

  # no kept backup wanted
  [ "${backup_initramfs}" = "no" ] && rm -f "${initramfs_bak}" && return 0

  # no backup yet
  if [ ! -r "${initramfs}.bak" ]; then
    mv -f ${initramfs_bak} "${initramfs}.bak"
    verbose "Backup ${initramfs}.bak"
    return 0
  fi

  # keep booted initramfs
  boot_initramfs=
  uptime_days=$(awk '{printf "%d", $1 / 3600 / 24}' /proc/uptime)
  if [ -n "$uptime_days" ]; then
    boot_initramfs=$(find "${initramfs}.bak" -mtime +${uptime_days})
  fi
  if [ -n "${boot_initramfs}" ]; then
    mv -f "${initramfs_bak}" "${initramfs}.bak"
    verbose "Backup ${initramfs}.bak"
    return 0
  fi
  verbose "Removing current backup ${initramfs_bak}"
  rm -f ${initramfs_bak}
}

# nuke generated copy
remove_initramfs_bak()
{
  [ -z "${initramfs_bak:-}" ] && return 0
  rm -f "${initramfs_bak}"
  verbose "Removing ${initramfs_bak}"
}


generate_initramfs()
{
  echo "update-initramfs: Generating ${initramfs}"
  OPTS="-o"
  if [ "${verbose}" = 1 ]; then
    OPTS="-v ${OPTS}"
  fi
  #if分支其实做这两件事
  #mkinitramfs -o /boot/initrd.img-`uname -r`.new  ${version}
  #mv /boot/initrd.img-`uname -r`.new /boot/initrd.img-`uname -r`
  
  if mkinitramfs ${OPTS} "${initramfs}.new" "${version}"; then
    mv -f "${initramfs}.new" "${initramfs}"
    set_sha1
  else
    mkinitramfs_return="$?"
    remove_initramfs_bak
    rm -f "${initramfs}.new"
    if [ "$mkinitramfs_return" = "2" ]; then
      # minversion wasn't met, exit 0
      exit 0
    fi
    echo "update-initramfs: failed for ${initramfs} with $mkinitramfs_return." >&2
    exit $mkinitramfs_return
  fi
}

# lilo call
run_lilo()
{
  # show lilo errors on failure
  if ! lilo -t  > /dev/null 2>&1 ; then
    echo "ERROR lilo fails for new ${initramfs}:" >&2
    echo
    lilo -t
  fi
  lilo
}

# Invoke bootloader
run_bootloader()
{
  # invoke policy conformant bootloader hooks
  if [ -d /etc/initramfs/post-update.d/ ]; then
    run-parts --arg=${version} --arg=${initramfs} \
      /etc/initramfs/post-update.d/
    return 0
  fi
}

compare_sha1()
{
  sha1sum "${initramfs}" | diff "${STATEDIR}/${version}" - >/dev/null 2>&1
  return $?
}

# Note that this must overwrite so that updates work.
set_sha1()
{
  sha1sum "${initramfs}" > "${STATEDIR}/${version}"
}

delete_sha1()
{
  rm -f "${STATEDIR}/${version}"
}

# ro /boot is not modified
ro_boot_check()
{
  # check irrelevant inside of a chroot
  if [ ! -r /proc/mounts ] || chrooted; then
    return 0
  fi

  boot_opts=$(awk '/boot/{if ((match($4, /^ro/) || match($4, /,ro/)) \
    && $2 == "/boot") print "ro"}' /proc/mounts)
  if [ -n "${boot_opts}" ]; then
    echo "WARNING: /boot is ro mounted."
    echo "update-initramfs: Not updating ${initramfs}"
    exit 0
  fi
}

get_sorted_versions()
{
  version_list=""

  for gsv_x in "${STATEDIR}"/*; do
    gsv_x="$(basename "${gsv_x}")"
    if [ "${gsv_x}" = '*' ]; then
      return 0
    fi
    worklist=""
    for gsv_i in $version_list; do
      if dpkg --compare-versions "${gsv_x}" '>' "${gsv_i}"; then
        worklist="${worklist} ${gsv_x} ${gsv_i}"
        gsv_x=""
      else
        worklist="${worklist} ${gsv_i}"
      fi
    done
    if [ "${gsv_x}" != "" ]; then
      worklist="${worklist} ${gsv_x}"
    fi
    version_list="${worklist}"
  done

  verbose "Available versions: ${version_list}"
}

set_current_version()
{
  if [ -f /boot/initrd.img-`uname -r` ]; then
    version=`uname -r`
  fi
}

set_linked_version()
{
  linktarget=
  if [ -e /initrd.img ] && [ -L /initrd.img ]; then
    linktarget="$(basename "$(readlink /initrd.img)")"
  fi

  if [ -e /boot/initrd.img ] && [ -L /boot/initrd.img ]; then
    linktarget="$(basename "$(readlink /boot/initrd.img)")"
  fi

  if [ -z "${linktarget}" ]; then
    return
  fi

  version="${linktarget##initrd.img-}"
}

set_highest_version()
{
  get_sorted_versions
  if [ -z "${version_list}" ]; then
    version=
    return
  fi
  set -- ${version_list}
  version=${1}
}

create()
{
  if [ -z "${version}" ]; then
    usage "Create mode requires a version argument"
  fi

  set_initramfs

  if [ "${takeover}" = 0 ]; then
    if version_exists "${version}"; then
      panic "Cannot create version ${version}: already exists"
    fi

    if [ -e "${initramfs}" ]; then
      panic "${initramfs} already exists, cannot create."
    fi
  fi

  generate_initramfs
}

update()
{
  #update_initramfs定义在/etc/initramfs-tools/update_initramfs.conf文件中
  #在本文件开始处被包含进来,值为update_initramfs=yes
  if [ "${update_initramfs}" = "no" ]; then
    echo "update-initramfs: Not updating initramfs."
    exit 0
  fi

  if [ -z "${version}" ]; then
    set_highest_version
  fi

  if [ -z "${version}" ]; then
    set_linked_version
  fi

  if [ -z "${version}" ]; then
    #等效version=`uname -r`
    set_current_version
  fi

  if [ -z "${version}" ]; then
    verbose "Nothing to do, exiting."
    exit 0
  fi
  #initramfs="/boot/initrd.img-`uname -r`"
  set_initramfs

  ro_boot_check

  altered_check

  backup_initramfs
  #准备调用mkinitramfs,进入函数generate_initramfs
  generate_initramfs
  #如果系统中存在/etc/initramfs/post-update.d/,就调用函数run_bootloader,ubuntu12.04并不存在这个目录
  run_bootloader

  backup_booted_initramfs
}

delete()
{
  if [ -z "${version}" ]; then
    usage "Delete mode requires a version argument"
  fi

  set_initramfs

  if [ "${takeover}" = 0 ]; then
    if [ ! -e "${initramfs}" ]; then
      panic "Cannot delete ${initramfs}, doesn't exist."
    fi

    if ! version_exists "${version}"; then
      panic "Cannot delete version ${version}: Not created by this utility."
    fi
  fi

  altered_check

  echo "update-initramfs: Deleting ${initramfs}"

  delete_sha1

  rm -f "${initramfs}" "${initramfs}.bak"
}

# Check for update mode on existing and modified initramfs
altered_check()
{
  # No check on takeover
  [ "${takeover}" = 1 ] && return 0
  if [ ! -e "${initramfs}" ]; then
    mild_panic "${initramfs} does not exist. Cannot update."
  fi
  if ! compare_sha1; then
    echo "update-initramfs: ${initramfs} has been altered." >&2
    mild_panic "update-initramfs: Cannot update. Override with -t option."
  fi
}

# Defaults
verbose=0
yes=0
# We default to takeover=1 in Ubuntu, but not Debian
takeover=1

##
#可选参数-k和-b后面带其他参数
#参数存放到flag中,供case判断
while getopts "k:cudyvtb:h?" flag; do
  case "${flag}" in
  k)
    #-k 后面有值,则保存在OPTARG(-k kernel version)
    version="${OPTARG}"
    ;;
  c)
    mode="c"
    ;;
  d)
    mode="d"
    ;;
  u)
    mode="u"
    ;;
  v)
    verbose="1"
    ;;
  y)
    yes="1"
    ;;
  t)
    takeover="1"
    ;;
  b)
    BOOTDIR="${OPTARG}"
    if [ ! -d "${BOOTDIR}" ]; then
      echo "Error: ${BOOTDIR} is not a directory." >&2
      exit 1
    fi
    ;;
  h|?)
    usage
    ;;
  esac
done
#常见的获取参数的最后一项的方法
shift $((${OPTIND} - 1))

if [ $# -ne 0 ]; then
  echo "Invalid argument for option -k." >&2
  usage
fi

# Validate arguments
#${mode}为空
if [ -z "${mode}" ]; then
  usage "You must specify at least one of -c, -u, or -d."
fi

#如果输入-k all 或者/etc/initramfs-tools/update-initramfs中的update_initramfs的值为all
if [ "${version}" = "all" ] \
  || ( [ "${update_initramfs}" = "all" ] && [ -z "${version}" ] ); then
  : FIXME check for --yes, and if not ask are you sure
  get_sorted_versions
  if [ -z "${version_list}" ]; then
    verbose "Nothing to do, exiting."
    exit 0
  fi

  OPTS="-b ${BOOTDIR}"
  if [ "${verbose}" = "1" ]; then
    OPTS="${OPTS} -v"
  fi
  if [ "${takeover}" = "1" ]; then
    OPTS="${OPTS} -t"
  fi
  if [ "${yes}" = "1" ]; then
    OPTS="${OPTS} -y"
  fi
  for u_version in ${version_list}; do
    verbose "Execute: ${0} -${mode} -k \"${u_version}\" ${OPTS}"
    "${0}" -${mode} -k "${u_version}" ${OPTS}
  done
  exit 0
fi

#只讨论update-initramfs -u的情况
case "${mode}" in
  c)
    create
    ;;
  d)
    delete
    ;;
  u)
    #-u 则调用update函数
    update
    ;;
esac      

    如果你耐心够好,看到这,差不多也该跟着执行流进入到mkinitramfs脚本。update函数的结尾依次执行generate_initramfs函数和mkinitramfs命令。就此进入mkinitramfs脚本。mkinitramfs脚本开始处也是定义一些变量,然后include两个重要的辅助脚本/usr/share/initramfs-tools/scripts/functions和/usr/share/initramfs-tools/hook-functions。一些重要的函数定义在这两个脚本中。mkinitramfs在这两个脚本的辅助下完成了前述临时initrd目录的制作

#!/bin/sh

umask 0022
export PATH='/usr/bin:/sbin:/bin'

# Defaults
keep="n"
CONFDIR="/etc/initramfs-tools"
verbose="n"
#如果/bin/busybox文件夹存在 变量BUSYBOXDIR=/bin
test -e /bin/busybox && BUSYBOXDIR=/bin
test -e /usr/lib/initramfs-tools/bin/busybox && BUSYBOXDIR=/usr/lib/initramfs-tools/bin
export BUSYBOXDIR

OPTIONS=`getopt -o c:d:ko:r:v -n "$0" -- "$@"`

# Check for non-GNU getopt
if [ $? != 0 ] ; then echo "W: non-GNU getopt" >&2 ; exit 1 ; fi

eval set -- "$OPTIONS"
#update-initramfs调用mkinitramfs时命令为
#mkinitramfs -o /boot/initrd.img-`uname -r`.new ${version},因此进入-o分支
while true; do
  case "$1" in
  -c)
    compress="$2"
    shift 2
    ;;
  -d)
    CONFDIR="$2"
    shift 2
    if [ ! -d "${CONFDIR}" ]; then
      echo "${0}: ${CONFDIR}: Not a directory" >&2
      exit 1
    fi
    ;;
  -o)
    #outfile=/boot/initrd.img-`uname -r`.new
    outfile="$2"
    shift 2
    ;;
  -k)
    keep="y"
    shift
    ;;
  -r)
    ROOT="$2"
    shift 2
    ;;
  -v)
    verbose="y"
    shift
    ;;
  --)
    shift
    break
    ;;
  *)
    echo "Internal error!" >&2
    exit 1
    ;;
  esac
done

# For dependency ordered mkinitramfs hook scripts.
#引用这两处的文件 这是一些帮助函数
. /usr/share/initramfs-tools/scripts/functions
. /usr/share/initramfs-tools/hook-functions

#引用/etc/initramfs-tools/initramfs.conf 这个文件有关于本地启动或者nfs启动的信息
. "${CONFDIR}/initramfs.conf"
EXTRA_CONF=''
#/usr/share/initramfs-tools/conf.d/目录下为空
#/etc/initramfs-tools/initramfs.conf/conf.d/ 目录下只有resume文件
for i in /usr/share/initramfs-tools/conf.d/* ${CONFDIR}/conf.d/*; do
  [ -e $i ] && EXTRA_CONF="${EXTRA_CONF} $(basename $i \
    | grep '^[[:alnum:]][[:alnum:]\._-]*$' | grep -v '\.dpkg-.*$')";
done
# FIXME: deprecated those settings on mkinitramfs run
#    these conf dirs are for boot scripts and land on initramfs
for i in ${EXTRA_CONF}; do
  if [ -e  ${CONFDIR}/conf.d/${i} ]; then
    . ${CONFDIR}/conf.d/${i}
  elif [ -e  /usr/share/initramfs-tools/conf.d/${i} ]; then
    . /usr/share/initramfs-tools/conf.d/${i}
  fi
done

# source package confs
for i in /usr/share/initramfs-tools/conf-hooks.d/*; do
  if [ -e "${i}" ]; then
    . "${i}"
  fi
done
#UMASK 非空 不过没找到何处设置了这个变量
if [ -n "${UMASK:-}" ]; then
  umask "${UMASK}"
fi
#outfile为空 也不成立
if [ -z "${outfile}" ]; then
  usage
fi

#生成新的空的initrd.img-`uname -r`文件
touch "$outfile"
outfile="$(readlink -f "$outfile")"

# And by "version" we really mean path to kernel modules
# This is braindead, and exists to preserve the interface with mkinitrd
if [ ${#} -ne 1 ]; then
  version="$(uname -r)"
else
  version="${1}"
fi

# Check that we're using a new enough kernel version, first for ourselves,
# then for each of the hooks, which can have a MINKVER variable defined
check_minkver ${version}
check_minkver ${version} /usr/share/initramfs-tools/hooks
check_minkver ${version} ${CONFDIR}/hooks

case "${version}" in
#/lib/modules/*/目录下除了/以外的文件,这是指除了该目录下除了子目录以外的所有文件?
/lib/modules/*/[!/]*)
  ;;
/lib/modules/[!/]*)
  version="${version#/lib/modules/}"
  version="${version%%/*}"
  ;;
esac

case "${version}" in
*/*)
  echo "$PROG: ${version} is not a valid kernel version" >&2
  exit 1
  ;;
esac

# Check userspace and kernel support for compressed initramfs images
if [ -z "${compress:-}" ]; then
  #COMPRESS=gzip gzip压缩
  compress=${COMPRESS}
else
  COMPRESS=${compress}
fi

if ! command -v "${compress}" >/dev/null 2>&1; then
  compress=gzip
  [ "${verbose}" = y ] && \
    echo "No ${COMPRESS} in ${PATH}, using gzip"
  COMPRESS=gzip
fi

#/boot/config-${version}匹配以config_rd_${COMPRESS%p}开头
if ! `grep -q -i ^config_rd_${COMPRESS%p} /boot/config-${version}` ; then
  compress=gzip
  [ "${verbose}" = y ] && \
    echo "linux-2.6 misses ${COMPRESS} support, using gzip"
fi

[ "${compress}" = lzop ] && compress="lzop -9"
[ "${compress}" = xz ] && compress="xz -8 --check=crc32"

if [ -d "${outfile}" ]; then
  echo "${outfile} is a directory" >&2
  exit 1
fi
#要编译内核的版本
MODULESDIR="/lib/modules/${version}"

if [ ! -e "${MODULESDIR}" ]; then
  echo "WARNING: missing ${MODULESDIR}"
  echo "Device driver support needs thus be built-in linux image!"
fi
#检测模块依赖性 不存在模块依赖文件 怎生成
if [ ! -e "${MODULESDIR}/modules.dep" ]; then
  depmod ${version}
fi
#在/tmp下生成名为mkinitramfs_fb9BN这样的文件
DESTDIR="$(mktemp -d ${TMPDIR:-/tmp}/mkinitramfs_XXXXXX)" || exit 1
chmod 755 "${DESTDIR}"

# do not execute cache_run_scripts() if mounted with noexec
NOEXEC=""
#找到临时文件的挂载点 fs=/
fs=$(df -P $DESTDIR | tail -1 | awk '{print $6}')
if [ -n "$fs" ] && mount | grep -q "on $fs .*noexec" ; then
  NOEXEC=1
fi

__TMPCPIOGZ="$(mktemp ${TMPDIR:-/tmp}/mkinitramfs-OL_XXXXXX)" || exit 1

DPKG_ARCH=`dpkg --print-architecture`

# Export environment for hook scripts.
#
export MODULESDIR
export version
export CONFDIR
export DESTDIR
export DPKG_ARCH
export verbose
export MODULES
export BUSYBOX
export COMPCACHE_SIZE

# Private, used by 'catenate_cpiogz'.
export __TMPCPIOGZ

#在/tmp/mkinitramfs_fb9BNB文件夹下生成bin conf/conf.d etc lib/modules run sbin scripts /lib/modules/${version}这些文件夹
#这是在制作根文件系统
for d in bin conf/conf.d etc lib/modules run sbin scripts ${MODULESDIR}; do
  mkdir -p "${DESTDIR}/${d}"
done

# Copy the modules.order file in
if [ -f "${MODULESDIR}/modules.order" ]; then
  cp -p "${MODULESDIR}/modules.order" \
    "${DESTDIR}${MODULESDIR}/modules.order"
fi

# MODULES=list case.  Always honour.
for x in "${CONFDIR}/modules" /usr/share/initramfs-tools/modules.d/*; do
  if [ -f "${x}" ]; then
    #add_modules_from_file定义在/usr/share/initramfs-tools/hook-functions中
    #这里就是add_modules_from_file /etc/initramfs-tools/modules
    #/etc/initramfs-tools/modules中含有要添加到initrd镜像中的modules
    add_modules_from_file "${x}"
  fi
done

# 没有找到MODULES定义的文件 按注释MODULES=most
# MODULES=most is default
case "${MODULES}" in
dep)
  dep_add_modules
  ;;
most)
  #auto_add_modules仍然定义在/usr/share/initramfs-tools/hook-functions中
  auto_add_modules
  ;;
netboot)
  auto_add_modules base
  auto_add_modules net
  ;;
list)
  # nothing to add
  ;;
*)
  echo "W: mkinitramfs: unsupported MODULES setting: ${MODULES}."
  echo "W: mkinitramfs: Falling back to MODULES=most."
  auto_add_modules
  ;;
esac

# Resolve hidden dependencies
hidden_dep_add_modules

# First file executed by linux-2.6
#拷贝init文件,init是系统启动后进入根文件系统之后启动的第一个进程
cp -p /usr/share/initramfs-tools/init ${DESTDIR}/init

# add existant boot scripts
#递归搜索/usr/share/initramfs-tools/scripts/目录下所有配置文件(init-top/all_generic_ide udev...),如果文件中不含有"OPTION="字段,
#则在${DESTDIR}/scripts/目录中创建对应目录,并把文件拷贝到该目录中
for b in $(cd /usr/share/initramfs-tools/scripts/ && find . \
  -regextype posix-extended -regex '.*/[[:alnum:]\._-]+$' -type f); do
  option=$(sed '/^OPTION=/!d;$d;s/^OPTION=//;s/[[:space:]]*$//' "/usr/share/initramfs-tools/scripts/${b}")
  [ -z "${option}" ] || eval test -n \"\${$option}\" -a \"\${$option}\" != \"n\" || continue

  [ -d "${DESTDIR}/scripts/$(dirname "${b}")" ] \
    || mkdir -p "${DESTDIR}/scripts/$(dirname "${b}")"
  cp -p "/usr/share/initramfs-tools/scripts/${b}" \
    "${DESTDIR}/scripts/$(dirname "${b}")/"
done
#对于/etc/initramfs-tools/scripts下的文件使用同样的操作
for b in $(cd "${CONFDIR}/scripts" && find . \
  -regextype posix-extended -regex '.*/[[:alnum:]\._-]+$' -type f); do
  option=$(sed '/^OPTION=/!d;$d;s/^OPTION=//;s/[[:space:]]*$//' "${CONFDIR}/scripts/${b}")
  [ -z "${option}" ] || eval test -n \"\${$option}\" -a \"\${$option}\" != \"n\" || continue

  [ -d "${DESTDIR}/scripts/$(dirname "${b}")" ] \
    || mkdir -p "${DESTDIR}/scripts/$(dirname "${b}")"
  cp -p "${CONFDIR}/scripts/${b}" "${DESTDIR}/scripts/$(dirname "${b}")/"
done

echo "DPKG_ARCH=${DPKG_ARCH}" > ${DESTDIR}/conf/arch.conf
#把/etc/initramfs-tools/initramfs.conf拷贝到${DESTDIR}/conf目录下,
#这个文件涉及本地启动和nfs启动,以及加载的驱动MODULES=most
cp -p "${CONFDIR}/initramfs.conf" ${DESTDIR}/conf
for i in ${EXTRA_CONF}; do
  if [ -e "${CONFDIR}/conf.d/${i}" ]; then
    copy_exec "${CONFDIR}/conf.d/${i}" /conf/conf.d
  elif [ -e "/usr/share/initramfs-tools/conf.d/${i}" ]; then
    copy_exec "/usr/share/initramfs-tools/conf.d/${i}" /conf/conf.d
  fi
done

# ROOT hardcoding
if [ -n "${ROOT:-}" ]; then
  echo "ROOT=${ROOT}" > ${DESTDIR}/conf/conf.d/root
fi

if ! command -v ldd >/dev/null 2>&1 ; then
  echo "WARNING: no ldd around - install libc-bin" >&2
  exit 1
fi
#copy_exec定义在hook-functions中
copy_exec /usr/lib/initramfs-tools/bin/wait-for-root /sbin

# module-init-tools
copy_exec /sbin/modprobe /sbin
copy_exec /sbin/rmmod /sbin
mkdir -p "${DESTDIR}/etc/modprobe.d"
#拷贝/etc/modprobe.d/下文件到${DESTDIR}/etc/modprobe.d/下
#这些文件关系到modprobe mod时的行为
cp -a /etc/modprobe.d/* "${DESTDIR}/etc/modprobe.d/"

# util-linux
copy_exec /sbin/blkid /sbin

# workaround: libgcc always needed on old-abi arm
if [ "$DPKG_ARCH" = arm ] || [ "$DPKG_ARCH" = armeb ]; then
  cp -a /lib/libgcc_s.so.1 "${DESTDIR}/lib/"
fi

run_scripts /usr/share/initramfs-tools/hooks optional
run_scripts "${CONFDIR}"/hooks optional

# cache boot run order
if [ -n "$NOEXEC" ]; then
  echo "W: TMPDIR is mounted noexec, will not cache run scripts."
else
  for b in $(cd "${DESTDIR}/scripts" && find . -mindepth 1 -type d); do
    cache_run_scripts "${DESTDIR}" "/scripts/${b#./}"
  done
fi

# generate module deps
#生成模块依赖文件
depmod -a -b "${DESTDIR}" ${version}
rm -f "${DESTDIR}/lib/modules/${version}"/modules.*map

# make sure that library search path is up to date
#拷贝/etc/ld.so.conf*到initrd目录结构中对应位置,可执行程序可能依赖非标准路径下的so文件,
#因此在此调用ldconfig -r分析/${DESTDIR}/etc/ld.so.conf*中列出的非标准搜索路径,并生成
#缓存文件加速链接速度
#ldconfig -r ROOT : 此选项改变应用程序的根目录为ROOT(是调用chroot函数实现的).选择此项时,系统默认的配置文件
#/etc/ld.so.conf,实际对应的为 ROOT/etc/ld.so.conf.如用-r /usr/zzz时,打开配置文件 /etc/ld.so.conf时,实际打开的
#是/usr/zzz/etc/ld.so.conf文件.用此选项,可以大大增加动态链接库管理的灵活性
cp -ar /etc/ld.so.conf* "$DESTDIR"/etc/
if ! ldconfig -r "$DESTDIR" ; then
  [ $(id -u) != "0" ] \
  && echo "ldconfig might need uid=0 (root) for chroot()" >&2
fi

# Apply DSDT to initramfs
if [ -e "${CONFDIR}/DSDT.aml" ]; then
  copy_exec "${CONFDIR}/DSDT.aml" /
fi

# Remove any looping or broken symbolic links, since they break cpio.
[ "${verbose}" = y ] && xargs_verbose="-t"
(cd "${DESTDIR}" && find . -type l -printf '%p %Y\n' | sed -n 's/ [LN]$//p' \
  | xargs ${xargs_verbose:-} -rL1 rm -f)

# dirty hack for armhf's double-linker situation; if we have one of
# the two known eglibc linkers, nuke both and re-create sanity
if [ "$DPKG_ARCH" = armhf ]; then
  if [ -e "${DESTDIR}/lib/arm-linux-gnueabihf/ld-linux.so.3" ] || \
     [ -e "${DESTDIR}/lib/ld-linux-armhf.so.3" ]; then
    rm -f "${DESTDIR}/lib/arm-linux-gnueabihf/ld-linux.so.3"
    rm -f "${DESTDIR}/lib/ld-linux-armhf.so.3"
    cp -aL /lib/ld-linux-armhf.so.3 "${DESTDIR}/lib/"
    ln -sf /lib/ld-linux-armhf.so.3 "${DESTDIR}/lib/arm-linux-gnueabihf/ld-linux.so.3"
  fi
fi

[ "${verbose}" = y ] && echo "Building cpio ${outfile} initramfs"
(
# work around lack of "set -o pipefail" for the following pipe:
# cd "${DESTDIR}" && find . | cpio --quiet -R 0:0 -o -H newc | gzip >"${outfile}" || exit 1
#这是创建initramfs的最后一步了 调用cpio和gzip 打包并压缩initramfs目录
exec 3>&1
eval `
  # http://cfaj.freeshell.org/shell/cus-faq-2.html
  exec 4>&1 >&3 3>&-
  cd  "${DESTDIR}"
  {
    find . 4>&-; echo "ec1=$?;" >&4
  } | {
    cpio --quiet -R 0:0 -o -H newc 4>&-; echo "ec2=$?;" >&4
  } | ${compress} >"${outfile}"
  echo "ec3=$?;" >&4
`
if [ "$ec1" -ne 0 ]; then
  echo "E: mkinitramfs failure find $ec1 cpio $ec2 $compress $ec3"
  exit "$ec1"
fi
if [ "$ec2" -ne 0 ]; then
  echo "E: mkinitramfs failure cpio $ec2 $compress $ec3"
  exit "$ec2"
fi
if [ "$ec3" -ne 0 ]; then
  echo "E: mkinitramfs failure $compress $ec3"
  exit "$ec3"
fi
) || exit 1

if [ -s "${__TMPCPIOGZ}" ]; then
  cat "${__TMPCPIOGZ}" >>"${outfile}" || exit 1
fi

if [ "${keep}" = "y" ]; then
  echo "Working files in ${DESTDIR} and overlay in ${__TMPCPIOGZ}"
else
  rm -rf "${DESTDIR}"
  rm -rf "${__TMPCPIOGZ}"
fi

exit 0      

最后贴出hook-functions中用到的几个函数的注释

force_load()
{
    manual_add_modules ${@}
    #调用mkinitramfs时设置DESTDIR=/tmp/mkinitramfs_fb9BNB---一个临时根文件系统结构,
    #把/etc/initramfs-tools/modules中读到的modules加入到/tmp/mkinitramfs_fb9BNB/conf/modules中
    #前面已经说过update-initramfs -u时会将/etc/initramfs-tools/modules文件中的模块加入到initrd
    #文件,作为引导加载modules,看来就是在这实现的
    echo "${@}" >>"${DESTDIR}/conf/modules"
}      
add_modules_from_file()
{
# Sanity check
if [ ! -e "${1}" ]; then
echo "W: add_modules_from_file: arg1='${1}' does not exist." >&2
return
fi
#从/etc/initramfs-tools/modules中读取非注释的行,模块名和参数分别读入module和args
#并调用force_load函数
grep '^[^#]' ${1} | while read module args; do
[ -n "$module" ] || continue
force_load "${module}" "${args}"
done
}      
# $1 = file to copy to ramdisk
# $2 (optional) Name for the file on the ramdisk
# Location of the image dir is assumed to be $DESTDIR
# We never overwrite the target if it exists.
#拷贝可执行程序到$DESTDIR对应目录下,同时解决可执行程序的依赖库(ldd)
copy_exec() {
local src target x nonoptlib
local libname dirname


src="${1}"
#如果$2(可执行程序所在的目录)为空 则返回$1的值 
target="${2:-$1}"
#可执行程序不存在就return,否则往下执行
[ -f "${src}" ] || return 1
if [ -d "${DESTDIR}/${target}" ]; then
# check if already copied
#目录存在并且exe存在则返回
[ -e "${DESTDIR}/$target/${src##*/}" ] && return 0
else
#目录不存在 则创建目录
[ -e "${DESTDIR}/$target" ] && return 0
#FIXME: inst_dir
mkdir -p "${DESTDIR}/${target%/*}"
fi
#拷贝host系统的exe文件到initrd目录结构中
[ "${verbose}" = "y" ] && echo "Adding binary ${src}"
cp -pL "${src}" "${DESTDIR}/${target}"
# Copy the dependant libraries
#ldd的输出形式为 xxx ==> yyy
#for循环中的第一个sed是提取第3列中的yyy
for x in $(ldd ${src} 2>/dev/null | sed -e '
/\//!d;/linux-gate/d;/ {s/.*=>[[:blank:]]*\([^[:blank:]]*\).*/\1/};s/[[:blank:]]*\([^[:blank:]]*\) (.*)/\1/' 2>/dev/null); do


# Try to use non-optimised libraries where possible.
# We assume that all HWCAP libraries will be in tls,
# sse2, vfp or neon.
nonoptlib=$(echo "${x}" | sed -e 's#/lib/\(tls\|i686\|sse2\|neon\|vfp\).*/\(lib.*\)#/lib/\2#')

if [ -e "${nonoptlib}" ]; then
x="${nonoptlib}"
fi


libname=$(basename "${x}")
dirname=$(dirname "${x}")
# FIXME inst_lib
#从host机上拷贝依赖库到initrd目录结构中
mkdir -p "${DESTDIR}/${dirname}"
if [ ! -e "${DESTDIR}/${dirname}/${libname}" ]; then
cp -pL "${x}" "${DESTDIR}/${dirname}"
[ "${verbose}" = "y" ] && echo "Adding library ${x}" || true
fi
done
}      

继续阅读