ls proc $$ self fd 3 255 引发的一些琐事

我在使用bash的时候通常会利用它的自动补全功能来看看文件夹下的内容(连按两下Tab键),例如:
  
  说明Music文件夹下有这三个文件,我也就不需要提前用ls命令来确定了。
  
  但是最近我在查看当前shell(bash)的文件描述符时时却碰见一个“怪事”,当我用bash的自动补全功能查看时,显示为有0, 1, 2, 255, 3这五个文件:
  
  但是当我用ls命令来显示fd文件夹的时候,却只显示有0, 1, 2, 255这4个文件,3这个文件不存在:
  
  这是为什么呢?
  
  其实原因很简单,自动补全功能是bash内置的一个功能,而ls是系统上的一个程序,以子进程的形式独立于bash运行。所以如果bash这个自动补全功能打开了我们要补全的路径(文件夹也是文件),那么应该会获得文件描述符3,ls也是一样。但是5736这个PID是bash的,所以我们用ls的时候看不到3而用bash的自动补全功能看得到。
  
  为了证实一下这个的想法,上网查了一下相关资料,了解到bash自动补全功能本身就是一个用shell语言写的脚本,其配置在/etc/bash_completion这个文件中,其中常用的内置命令是complete ,用法为complete -F _known_hosts xvncviewer ,即当开头的命令./程序是xvncviewer 的时候,如果用户在参数上连按Tab键就会调用_known_hosts这个shell内置函数 ,例如:
  
  skx@lappy:~$ xvncviewer s[TAB]
  
  savannah.gnu.org            ssh.tardis.ed.ac.uk
  
  scratchy                    steve.org.uk
  
  security.debian.org         security-master.debian.org
  
  sun
  
  skx@lappy:~$ xvncviewer sc[TAB]
  
  我们进入/etc/bash_completion文件,查找刚刚使用的ls命令,看看它的自动补全是什么配置的:
  
  complete -F _longopt a2ps awk base64 bash bc bison cat colordiff cp csplit \
  
  cut date df diff dir du enscript env expand fmt fold gperf \
  
  grep grub head indent irb ld ldd less ln ls m4 md5sum mkdir mkfifo mknod \
  
  mv netstat nl nm objcopy objdump od paste pr ptx readelf rm rmdir \
  
  sed seq sha{,1,224,256,384,512}sum shar sort split strip sum tac tail tee \
  
  texindex touch tr uname unexpand uniq units vdir wc who
  
  可以看到,其调用的是_longopt这个内置函数,继续定位:
  
  _longopt()
  
  {
  
  local cur prev words cword split
  
  _init_completion -s || return
  
  case "${prev,,}" in
  
  --help|--usage|--version)
  
  return 0
  
  ;;
  
  --*dir*)
  
  _filedir -d
  
  return 0
  
  ;;
  
  --*file*|--*path*)
  
  _filedir
  
  return 0
  
  ;;
  
  --+([-a-z0-9_]))
  
  local argtype=$( $1 --help 2>&1 | sed -ne \
  
  "s|.*$prev\[\{0,1\}=[<[]\{0,1\}\([-A-Za-z0-9_]\{1,\}\).*|\1|p" )
  
  case ${argtype,,} in
  
  *dir*)
  
  _filedir -d
  
  return 0
  
  ;;
  
  #......省略
  
  可以看到_longopt会调用_filedir这个函数:
  
  _filedir(www.2018yulpt.com)
  
  {
  
  local i IFS=$'\n' xspec
  
  _tilde "$cur" || return 0
  
  local -a toks
  
  local quoted x tmp
  
  _quote_readline_by_ref "$cur" quoted
  
  x=$( compgen -d -- "$quoted" ) &&
  
  while read -r tmp; do
  
  toks+=( "$tmp" )
  
  done <<< "$x"
  
  if [[ "$1" != -d ]]; then
  
  # Munge xspec to contain uppercase version too
  
  # http://www.boshenyl.cn /gmane.comp.shells.bash.bugs/15294/focus=15306
  
  xspec=${1:+"!*.@($1|${1^^})"}
  
  x=$( compgen -f -X "$xspec" -- $quoted ) &&
  
  while read -r tmp; do
  
  toks+=( "$tmp" )
  
  done <<< "$x"
  
  fi
  
  # If the filter failed to produce anything, try without it if configured to
  
  [[ -n ${COMP_FILEDIR_FALLBACK:-} && \
  
  -n "$1" && "$1" != -d && ${#toks[@]} -lt 1 ]] && \
  
  x=$( compgen -f -- $quoted ) &&
  
  #......省略
  
  可以看到该函数使用了compgen这个内置命令来获取文件夹下的文件名(-f = "www.bomaoyule.cn filename"),例如:
  
  我们使用strace来追踪这个内置命令的系统调用,特别是返回文件描述符的系统调用open:
  
  通过对比可以看到compgen调用open打开了这个文件夹,而且得到了文件描述符3(前面的open都调用了close删除了它们得到的文件描述符3)。
  
  如果将compgen换成ls :
  
  对比可以看出,compgen只有一个execve ,即compgen是在bash进程中执行的,但ls有两个,第二个说明了它是作为bash的子进程运行的, 证实了我们之前的想法。
  
  如果感兴趣的话可以看看ls的源码,其中使用到了readdir opendir这两个库函数(GNU coreutils-8.29)
  
  综上,我们可以用两个图来总结。
  
  自动补全:
  
  Process: Bash
  
  +-----------------------------+
  
  |                             |
  
  |  0,1,2,255       0,1,2,3,255|
  
  |    Tab->compgen->open       |
  
  |                             |
  
  +-----------------------------+
  
  ls 命令:
  
  Process: Bash
  
  +-----------------------------+
  
  |                             |
  
  |  0,1,2,255                  |
  
  |     ls                      |
  
  |      +                      |
  
  +-----------------------------+
  
  |
  
  |
  
  | Child Process: ls
  
  +------+----------------------+
  
  |                             |
  
  |0,1,2         0,1,2,3        |
  
  |  opendir->open              |
  
  +-----------------------------+
  
  另外,如果我们将操作应用于/proc/self/文件夹也会得到一些有意思的结果:
  
  第一行我们已经讲明白了,但是第二行和第三行怎么解释呢?
  
  在man 5 proc下对这个文件夹的解释是这样的:
  
  /proc www.hjha178.com/self
  
  This  directory  refers  to  the  process  accessing  the  /proc
  
  filesystem, and is identical to the /proc directory named by the
  
  process ID of the same process.
  
  也就是说,/proc/self/反应的是当前访问文件的进程的状态数据 ,所以我们用ls /proc/self/fd/实际上是ls /proc/${PID of ls}/fd/ ,而ls会打开这个文件夹(同时获得3这个文件描述符),所以就会看到0,1,2,3这个四个文件了。但如果我们直接ls /proc/self/fd/3 ,这个时候ls的进程还没有获得3这个描述符,就尝试去打开3这个不存在的文件,所以就报错了。在CentOS的文档中提到了这个文件夹的作用:
  
  The /proc/self/ www.wmyl11.com  directory is a link to the currently running process. This allows a process to look at itself without having to know its process ID.
  
  另外提一下bash进程中的255文件描述符,这个是bash独有的一个小“trick”,其对应的文件是一个终端设备:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值