Linux 版 (精华区)

作  家: Beth (打网球去!) on board 'unix'
题  目: FAQ3
来  源: 哈尔滨紫丁香站
日  期: Sat Apr 12 08:43:28 1997
出  处: qlu@hitcrc.hitcrc.hit.edu.cn

 
发信人: Only (在水一方), 信区: Unix
标  题: Unix Chinese FAQ(3) (fwd)
发信站: 饮水思源 (Sun Nov 17 10:52:05 1996)


本篇文章回答以下问题;
 
       3.1) 要如何得知一个档案建立的时间?
       3.2)  在执行 rsh 的时候要怎样才能不必等远方指令执行结束就回到 shell?
       3.3)  要怎样才能截断一个档案?
       3.4)  为什么执行 find 时所使用的 {} 符号无法达到我预期的结果?
       3.5)  我要如何改变一个 symbolic link 的 permission 呢?
       3.6)  我要如何 "undelete" 一个档案?
       3.7)  一个process 要怎样侦测出自己是否在背景状态执行?
       3.8)  为什么在 Bourne shell 当中,对回圈的输出入转向无法达到预期的效果?
       3.9)  我要怎么在一个 shell script 中或在背景执行 'ftp'、'telnet'、'tip' 等
             interactive 的程式呢?
       3.10) 在 shell script 或 C 程式当中,要怎样才能找到某个程式的 process ID
             呢?
       3.11) 我要怎样经由 rsh 执行远方指令时,检查远方指令的结束状态?
       3.12) 能不能把 shell 变数传进 awk 程式当中呢?
       3.13) 要怎样才能避免在记忆体中留下zombie processes?
       3.14) 当我要从 pipe 读一行输入时,要如何才能让这行资料像是直接从键盘输
             入而非从一个大 block buffer 来的?
       3.15) 我要如何在档案名字中加入日期?
       3.16) 为什么有一 script 是用 #! ... 做为档案的开端?
 
 3.1)    我要如何得知一个档案建立的时间?
 
         很遗憾,因为档案建立的时间并未储存在任何地方,所以答案是无法得知。
         关于一个档案你只能查到最后修改的时间("ls -l"),最后读取的时间
         ("ls -lu") 与 inode 改变的使间。有一些 man pages 将最后一个时间当
         成是建立的时间,这种说法是错的。因为 mv、ln、chmod、chmod、chown、
         chgrp 等动作都会改变这个时间。
 
         若需更详尽的说明可参考 "stat(2)" 的 man page.
 
 
 3.2)    在执行 rsh 的时候要怎样才能不必等远方指令执行结束就回到 shell?
         (关于我们所讨论的 rsh,请参阅问题2.7)
 
         以下这些凭直觉想到的答案都达不到这个效果:
                 rsh machine command &
         或
                 rsh machine 'command &'
 
         例如, 执行 rsh machine 'sleep 60 &' 这个命令时,我们可以观察到:rsh并
         不会立刻结束,而是等到远方的 sleep 命令完成以后才结束,即使我们在远
         方使用背景方式执行此命令。所以要怎样才能让 rsh 在 sleep命令启动后立
         刻结束呢?
 
         答案如下-
 
         如果您在远端使用csh:
                 rsh machine -n 'command >&/dev/null </dev/null &'
 
         如果您在远端使用 sh:
                 rsh machine -n 'command >/dev/null 2>&1 </dev/null &'
 
         为什么呢?因为 "-n" 会把 rsh 的stdin接到 /dev/null,因此您可以在本地
         机器以背景方式执行整个 rsh 命令。不管是使用 -n 选项或者在指令结尾使
         用 "/dev/null",其效果都是一样的。此外,在远端机器使用的输出入转向(写
         在单引号内的部份)会让 rsh 认定此次连线可迳行结束(因为已无其他输
         入资料)。
 
         附注: 任何档案都可以用于远端机器的输出入转向,而不仅限于 /dev/null。
 
         在许多状况下,这个复杂的命令当中有很多部份都是非必要的。
 
 
 3.3)    要怎样才能截断一个档案?
 
         BSD 的函数ftruncate() 可以设定档案的长度。但是并不是每一种版本的动作
         都一样。其他 UNIX 的变种似乎也都支援其他版本的截断功能。
 
         支援 ftruncate函数的系统多半可归类为以下三种:
 
         BSD 4.2 - Ultrix, SGI, LynxOS
                 -无法使用截断功能来增加档案长度
                 -执行截断动作不会移动档案指标
 
         BSD 4.3 - SunOS, Solaris, OSF/1, HP/UX, Amiga
                 -可以用截断功能来增加档案长度
                 -执行截断动作不会移动档案指标
 
         Cray - UniCOS 7, UniCOS 8
                 -无法使用截断功能来增加档案长度
                 -执行截断动作会移动档案指标
 
         其他系统则可能在以下四个地方与众不同:
 
         F_CHSIZE - 只在SCO 上
                 -有些系统定义了F_CHSIZE 但并没有真的支援此功能
                 -动作类似BSD 4.3
 
         F_FREESP - 只在 Interative Unix 上
                 -有些系统(如Interactive Unix)定义了F_FREESP 但并没有真的支援此
                  功能
                 -动作类似BSD 4.3
 
         chsize() - QNX and SCO
                 -有些系统(如Interactive Unix)有chsize() 函数但并没有真的支援
                  此功能
                 -动作类似BSD 4.3
 
         「空空如也」-目前找不到这种系统
                 -也许会有系统完全不支援 truncate功能
 
         FAQ 维护者的注解:以下是我在几年前从网路抓到的程式,原作者已不可考,
                          不过S.Spencer Sun <spencer@ncd.com> 也贡献了一份
                          F_FREESP的功能。
 
               functions for each non-native ftruncate follow
 
               /* ftruncate emulations that work on some System V's.
                  This file is in the public domain. */
 
               #include
               #include
 
               #ifdef F_CHSIZE
               int
               ftruncate (fd, length)
                    int fd;
                    off_t length;
               {
                 return fcntl (fd, F_CHSIZE, length);
               }
               #else
               #ifdef F_FREESP
               /* The following function was written by
                  kucharsk@Solbourne.com (William Kucharski) */
 
               #include
               #include
               #include
 
               int
               ftruncate (fd, length)
                    int fd;
                    off_t length;
               {
                 struct flock fl;
                 struct stat filebuf;
 
                 if (fstat (fd, &filebuf) < 0)
                   return -1;
 
                 if (filebuf.st_size < length)
                   {
                     /* Extend file length. */
                     if (lseek (fd, (length - 1), SEEK_SET) < 0)
                       return -1;
 
                     /* Write a "0" byte. */
                     if (write (fd, "", 1) != 1)
                       return -1;
                   }
                 else
                   {
                     /* Truncate length. */
                     fl.l_whence = 0;
                     fl.l_len = 0;
                     fl.l_start = length;
                     fl.l_type = F_WRLCK;      /* Write lock on file space. */
 
         /* This relies on the UNDOCUMENTED F_FREESP argument to
            fcntl, which truncates the file so that it ends at the
            position indicated by fl.l_start.
            Will minor miracles never cease? */
                   if (fcntl (fd, F_FREESP, &fl) < 0)
                       return -1;
                   }
 
                 return 0;
               }
               #else
               int
               ftruncate (fd, length)
                    int fd;
                    off_t length;
               {
                 return chsize (fd, length);
               }
               #endif
               #endif
 
 
 3.4)    为什么执行 find 时所使用的 {} 符号无法达到我预期的结果?
 
         Find 指令有一个 -exec 的选项会针对每一个找到的档案执行一个特殊
         的指令。Find 会把出现{}的地方置换成目前找到的档案名称。因此,
         也许有一天您会使用 find 指令对每一个档案执行某个指令,或者对
         一个目录执行某个指令。
 
                 find /path -type d -exec command {}/\* \;
 
         希望 find 能依序执行以下指令:
 
                 command directory1/*
                 command directory2/*
                 ...
 
         不幸的是,find 只会展开自成一体的 {} token;如果 {} 跟其他字元相连
         的话(如:{}/*),那么find将不会以您所想的方式展开 {}, 而是转换为以
         下命令
 
                 command {}/*
                 command {}/*
                 ...
 
         也许您可以把它当成 bug, 也可以把它看成是故意设计的特异功能。但我们
         可不愿被目前这个特异功能干扰。所以要怎样避免这个问题呢?其中一种做
         法是写一个小小的 shell script,名称就叫做 ./doit 好了,其内容如下:
 
                 command "$1"/*
 
         那么您就可以把原来的命令行改写为
 
                 find /path -type d -exec ./doit {} \;
 
         如果您想省掉 ./doit 这个 shell script, 可以这么写:
 
                 find /path -type d -exec sh -c 'command $0/*' {} \;
 
         (这种写法可行的原因是 "sh -c 'command' A B C ..."指令当中,$0会展开为
         A, $1会展开为B, 依此类推)
 
         或者您也可以略施小计使用 sed 来造出您想执行的指令行:
 
                 find /path -type d -print | sed 's:.*:command &/*:' | sh
 
         如果您想减少 command 的执行次数,您可以先检查看看系统中有没有
         xargs 这个指令, xargs会从标准输入一次读取一行,并且把这些读入的资料
         合并至一个命令行内。您可以写成以下命令行:
 
                 find /path -print | xargs command
 
         这样会使以下指令执行一次或多次:
 
                 command file1 file2 file3 file4 dir1/file1 dir1/file2
 
         很不幸地,这并不是完美无缺或者万无一失的解法,输入 xargs 的文字行
         必须以换行字元结尾,所以当档案名称当中有奇怪的字元(如换行字元)时,
         xargs就会因此而混淆。
 
 
 3.5)  我要如何改变一个 symbolic link 的 permission 呢?
 
         这个问题没有意义,因为 symbolic link的 permission 根本不代表什么。
         那个 link 所指过去的档案的 permission 才有意义。
 
 
 3.6)  我要如何 "undelete" 一个档案?
 
         某年某月的某一天,要删除 "*.foo" 却一不小心打成了 "rm * .foo",
         结果发现竟把 "*" 都删除了。真的是欲哭无泪啊!可是你也只好把这当成
         是成长的代价了。
 
         当然一个称职的系统管理员应当会定期做备份。先问一问你的系统管理员看
         你不小心删除的档案是不是有备份起来。如果没有的话,嗯,继续往下看吧!
 
         不管你是不是真的要删除一个档案,当你下了 "rm" 以后,档案就不见了。
         在你 "rm" 一个档案,系统就不再记得你的档案是用了硬碟中的哪些 block
         了。更糟糕的是,当系统要用到更多的硬碟空间时,就优先取用这些刚放出
         来的 block。不过天底下没有不可能的事。理论上说,若你在下了 "rm" 后,
         马上把系统 shutdown,资料是就得回来的。不过,你得找一个对系统非常
         熟悉且肯花费数小时至数天的时间来帮你做这件事专家才行。
 
         当你不小心 "rm" 了一个档案后,第一个反应或许是为什么不用一个 alias
         或在 sh 中的 function 将 "rm"  取代掉,当你下 "rm" 只把档案搬到一个
         垃圾桶之类的地方呢?那如果不小心杀错档案就可以挽救,只是要定期清一
         清垃圾桶就好了。有两个理由。第一,大多数的人不认为这是一个好的做法。
         这么做的话你会太依赖你的 "rm",有一天到了一个正常的系统中把正常的
         "rm" 当成你的 "rm" 来用,那可能会死得很惨。第二,你会发现你花费了
         许多不必要的时间在处理垃圾桶里的东西。所以对一个初学者而言呢,用
         "rm" 的 -i选项应该就够了。
 
         如果你有大无畏的精神的话,那好吧,就给你个简单的答案。写一个名为
         "can" 的指令,功用是将档案移到垃圾桶里。在 csh(1) 中,将以下的东西
         放进 ".login" 里:
 
         alias can       'mv \!* ~/.trashcan'       # junk file(s) to trashcan
         alias mtcan     'rm -f ~/.trashcan/*'      # irretrievably empty trash
         if ( ! -d ~/.trashcan ) mkdir ~/.trashcan  # ensure trashcan exists
 
         如果你想要每次 logout 时都把垃圾桶清乾净,那就把
 
         rm -f ~/.trashcan/*
 
         进 ".logout" 里。若你用的是 sh 或是 ksh,那自己试试著写写看吧!
 
         MIT 的雅典娜计画(Project Athena)作出了一套有
         delete/undelete/expunge/purge 的软体。这套软体可以完全取代 "rm" 而又提
         供 undelete 的功能。这个软体曾 post 在 comp.sources.misc(volume 17,
         issue 023-025)。
 
 
 3.7)    一个process 要怎样侦测出自己是否在背景状态执行?
 
         首先,您是否想知道您自己是在背景状态下执行,或者在交谈状态下执行?如果
         您只是想藉此决定是否该在终端上印出提示符号之类的讯息,那么更合适的方
         法应该是检查您的标准输入是否为终端机:
 
             sh: if [ -t 0 ]; then ... fi
             C: if(isatty(0)) { ... }
 
         一般来说,您无法得知自己是否在背景状态下执行。问题的根本在于不同的 shell
         与不同的 UNIX 版本对于「前景」与「背景」的定义可能有所不同。而且在最
         常见的系统上,前景与背景都有较好的定义,程式甚至可以在背景与前景之间任
         意切换!
 
         在没有 job control 的UNIX系统上,若要把 process 放入背景状态通常是把
         SIGINT 与 SIGQUIT 忽略掉,并且把标准输入转为"/dev/null",这是由shell处
         理的。
 
         在具有 job control 功能的 UNIX 系统,若shell支援 job control 功能,那么shell
         只要把 process group ID 设成跟 terminal 所属的 PGID 不同即可把 process 切
         换至背景状态;如果要把 process 切回前景状态,只要把此 process 的 PGID 设
         成跟目前 terminal 所属的 PGID 即可。如果 shell 不支援 job control 功能,则
         不管UNIX 系统是否支援 job control 的功能,shell 对 process 的处理动作都
         是一样的(也就是忽略SIGINT 与 SIGQUIT,并且把标准输入转为"/dev/null")。
 
 
 3.8)    为什么在 Bourne shell 当中,对回圈的输出入转向无法达到预期的效果?
 
         举个例子来说好了:
 
                 foo=bar
                 while read line
                 do
                 # do something with $line
                     foo=bletch
                 done < /etc/passwd
 
                 echo "foo is now: $foo"
 
         尽管 "foo=bletch" 已经设定了 foo 的值,然而在多种系统的 Bourne shell
         上执行此 script 的时候仍会印出 "foo is now: bar"。为什么呢?因为一些
         历史因素,在 Bourne shell 当中,一个控制结构(如一个回圈,或者一个
         "if" 叙述)的重导向会造出一个新的 subshell,所以啦,在此 subshell 内
         所设定的变数当然不会影响目前 shell 的变数。
 
         POSIX 1003.2 Shell and Tools Interface 的标准委员会已防止上述的问题,
         也就是上述的例子在遵循P1003.2 标准的Bourne shells当中会印出
         "foo is now: bletch"。
 
         在一些较古老的 (以及遵循 P1003.2 标准的) Bourne shell 当中,您可以使
         用以下技巧来避免重转向的问题:
 
                 foo=bar
                 # make file descriptor 9 a duplicate of file descriptor 0 stdin);
                 # then connect stdin to /etc/passwd; the original stdin is now
                 # `remembered' in file descriptor 9; see dup(2) and sh(1)
                 exec 9<&0 < /etc/passwd
 
                 while read line
                 do
                 # do something with $line
                     foo=bletch
                 done
 
                 # make stdin a duplicate of file descriptor 9, i.e. reconnect
                 # it to the original stdin; then close file descriptor 9
                 exec 0<&9 9<&-
                 echo "foo is now: $foo"
 
         这样子不管在哪种 Bourne shell 应该都会印出 "foo is now: bletch"。
         接下来,看看以下这个例子:
 
                 foo=bar
 
                 echo bletch | read foo
 
                 echo "foo is now: $foo"
 
         这个例子在许多 Bourne shell 内都会印出 "foo is now: bar",有些则会
         印出 "foo is now: bletch"。为什么呢?一般说来,一个 pipeline 里面
         的每一个部份都是在一个 subshell 中执行。但是有些系统的里 pipeline
         的最后一行如果是如 "read" 这类的内建指令,并不会另外造出一个
         subshell。
 
         POSIX 1003.2 对这两种作法并没有硬性规定要用哪一种。所以一个 portable
         的 shell script 不应该依赖这两种作法其中的一种。
 

 3.9)  我要怎么在一个 shell script 中或在背景执行 'ftp'、'telnet'、'tip' 等
       interactive 的程式呢?
 
         这些程式要一个 terminal interface。这是shell 所无法提供的。所以这些
         无法在 shell script 里自动执行这些程式。
 
         有一个就做 'expect' 的程式,可以用来做这件事,因为它提供了
         programmable terminal interface。底下的例子是用 'expect' 来帮你 login:
 
                 # username is passed as 1st arg, password as 2nd
                 set password [index $argv 2]
                 spawn passwd [index $argv 1]
                 expect "*password:"
                 send "$password\r"
                 expect "*password:"
                 send "$password\r"
                 expect eof
 
         expect 为 telnet, rlogin,debugger 和一些没有内建 command language 的
         程式提供了一个近乎自动化的方法。Expect 里面的有一用以在玩 rogue
         (一个 Unix 中的古老游戏)时取得较佳初始情况,然后将控制权还回给使用者
         的例子。用这个 script 你就能得到『成功的一半』。
 
         再者,有一些已经写好的程式可以帮你这类与 pseudo-tty 有关的东西,所
         以你只要 script 中执行这些程式就可以帮你处理这些东西。
 
         有两个方法可以取得 'expect':
         1.送一封 email 给 library@cme.nist.gov 内容就写 "send
           pub/expect/expect.shar.Z"
         2. ftp://ftp.cme.nist.gov/pub/expect/expect.shar.Z
 
         另一个做法是用一个就 pty 4.0 曾贴在 comp.sources.unix volume25的东
         西。这个程式会提供一个 pseudo-tty session 给需要 tty 的程式用。若使用
         named pipe 配合 pty 4.0 来做上例,则看起来可能如下:
 
                #!/bin/sh
                 /etc/mknod out.$$ p; exec 2>&1
                 ( exec 4<out.$$; rm -f out.$$
                 <&4 waitfor 'password:'
                     echo "$2"
                 <&4 waitfor 'password:'
                     echo "$2"
                 <&4 cat >/dev/null
                 ) | ( pty passwd "$1" >out.$$ )
 
         上面的 'waitfor' 是简单的 C 程式,功用为等到 input 有与所等待的字串
         相同时再往下做。
 
         下面是一个更简单的做法,不过缺点是与 'passwd' 程式的互动可能无法同
         步。
 
                 #!/bin/sh
                 ( sleep 5; echo "$2"; sleep 5; echo "$2") | pty passwd "$1"
 
 
 3.10)   在 shell script 或 C 程式当中,要怎样才能找到某个程式的 process ID
         呢?
 
         在 shell script 当中:
 
         没有现成的程式可以用来查询程式名称与 process ID 之间的对应。此外,
         如果有对应的话,通常也都不太可信,因为可能会有多个 process 执行同一
         个名称的程式,而且 process 在启动之后仍可修改自己的名称。然而,如果
         您真的想要得知执行某个特定程式的所有 process, 可以利用以下命令行达
         成:
                 ps ux | awk '/name/ && !/awk/ {print $2}'
 
         您可以把 "name" 换成您想寻找的程式名称。
 
         这个命令行的基本观念是分析 ps 程式的输出,然后用 awk或grep等公用
         程式来搜寻具有特定名称的文字行,然后把这些文字行当中的 PID 栏位印
         出来。值得注意的是此例的命令行用了 "!/awk/" 以避免 awk 的 process 被
         列出来。
 
         您可能要根据您所用的 Unix 种类来调整 ps 所用的参数。
 
         在 C 语言程式里面:
 
         在 C 的程式库里面一样没有(具有可携性)的函数可以找出程式名称与
         process IDs。
 
         然而有些厂商提供函数让您能读取 Kernel 的记忆体,例如 Sun 提供了
         kvm_ 开头的函数,Data General 则提供了 dg_ 开头的函数。如果您的系
         统管理员未限定 Kernel 记忆体的读取权力的话(一般只有 super user 或
         kmem 群组里的人员才能读取 Kernel 记忆体),一般使用者也可以利用这
         些特殊函数来达到目的。然而,这些函数通常没有正式的文件说明,就算有
         的话也都写得艰深难懂,甚至会随著系统版本的更新而改变。
 
         有些厂商会提供 /proc 档案系统,此档案系统存在的方式为一个内含多个档
         案的目录。每个档名都是一个数字,对应于 process ID,您可以开启这个档
         案并且读取关于这个 process 的资讯。再次提醒一下,有时候您会因为存取
         权限的限制而无法使用这些功能,而且使用这些功能的方式也随著系统而
         变。
 
         如果您的厂商并没有提供特殊的程式库或者 /proc 来处理这些事,但是您又
         想要在 C 里面完成这些功能,那么您可能要自己在Kernel 记忆体当中费心
         搜寻。如果您想看看这些功能在某些系统上是怎么做到的,可以参考 ofiles
         的原始程式,您可以从 comp.source.sources.unix 的历年归档文章当中取
         得。(有一个称为 kstuff 的套装程式曾经在 1991 年五月发表于
         alt.sources,它可以帮您在 kernel 当中搜寻有用的资讯,您可以到
         wuarchive.wustl.edu 利用匿名 ftp 取回
         usenet/alt.sources/articles/{329{6,7,8,9},330{0,1}}.Z。)
 
 
 3.11)   我要怎样经由 rsh 执行远方指令时,检查远方指令的结束状态?
 
         以下指令行是行不通的:
 
                 rsh some-machine some-crummy-command || echo "Command failed"
 
         如果 rsh 程式本身能成功地执行,那么 rsh 程式的结束状态就是 0,但这
         也许不是您真正想要的结果。
         如果您想检查远方程式的执行状态,您可以试试Maarten Litmaath 于 1994
         年十月在 alt.sources发表的 "ersh" script,ersh 是一个呼叫 rsh 的 shell
         script,它会安排远方的机器回应远方指令的结束状态,并传回此结束状态。
 
 
 3.12)   能不能把 shell 变数传进 awk 程式当中呢?
 
         这个问题有两个可行的方法,第一个方法只是把程式当中需要用到此变数的
         地方直接展开,例如要得知您目前使用哪些 tty,可以使用:
 
         who | awk '/^'"$USER"'/ { print $2 }'                           (1)
 
         awk 程式的程式通常会用单引号括起来,因为 awk 程式里面经常会用到 $
         字元,如果使用双引号的话,shell 本身会解释这个字元。所以啦,在这种
         特殊情形下,我们想要 shell 解释 $USER 当中的 $ 字元时,就必需先用
         单引号把前半段的句子暂时括起来,然后用双引号把 $USER 括起来,再用
         单引号把随后的句子括起来。请注意,双引号在某些状况下可以略去不写,
         也就是说,可以写成:
 
         who | awk '/^'$USER'/ { print $2 }'                             (2)
 
         然而,如果 shell 变数的内容含有特殊字元或空白字元时,就不适用了。
 
         第二种把变数的设定传进 awk 的方式是利用 awk 当中一个无文件说明的
         功能,它允许您从命令列透过「假造的档案名称」来设定变数,例如:
 
         who | awk '$1 == user { print $2 }' user="$USER" -              (3)
 
         由于命令行中的变数设定是在 awk 真正处理到的时候才会生效,因此您可
         以利用这种技巧让 awk 在遇到不同档名的时候做不同的动作。例如:
 
         awk '{ program that depends on s }' s=1 file1 s=0 file2         (4)
 
         请注意有些 awk 的版本会在 BEGIN 区块执行之前,就让真实档案名称之
         前所叙述的变数设定生效,但有些不会,所以您不可以依赖其中一种。
 
         再进一步提醒,当您指定变数的设定时,如果没有指定真实的档案名称,
         awk 将不会自动从标准输入读取,所以您要在命令之后加上一个 - 参数,
         就跟 (3) 的指令行内容一样。
 
         第三种做法是使用较新版的awk (nawk),您可以在 nawk 当中直接取用环
         境变数。例如:
 
         nawk 'END { print "Your path variable is " ENVIRON["PATH"] }' /dev/null
 
 
 3.13)   要怎样才能避免在记忆体中留下zombie processes?
 
         很不幸地,对于死掉的子 process 应有的行为特性并没有办法做一般化,因
         为这些特定/特定的机制会随著 Unix 的种类不同而有所差异。
 
         首先,在各种 Unix 上面您都必需使用 wait() 来处理子 process。也就是
         说,我还没看过有一种 Unix 会自动把结束的子 process 干掉,即使您不告
         诉它该怎么做。
 
         其次,在某些从 SysV 衍生的系统当中,如果您执行了 signal(SIGCHLD,
         SIG_IGN)",(嗯,事实上应该是SIGCLD 而非SIGCHLD,但大多数新出
         炉的 SysV 系统都会在表头档当中加上 #define SIGCHLD SIGCLD),那
         么子 processes 都会自动被清除得乾乾净净,您什么事都不用做。看看这个
         方式是否可行的最佳做法就是自己在机器上试试看。如果您想试著写出具可
         携性的程式码,那么依赖这种特殊处理方式可能不是好主意。不幸的是,在
         POSIX 并不允许您这样做;把 SIGCHLD 的行匦陨璩 SIG_IGN 在
         POSIX 当中并没有定义,所以如果您要让您的程式合乎 POSIX 的要求
         时,您就不可以这样做。
 
         那么怎样才算是 POSIX 的做法呢?如同前面所述,您必需设定一个 signal
         的处理函数,然后让它去 wait。在 POSIX 当中 signal 处理函数是经由
         sigaction 设定,由于您只对终止的子 process 感兴趣,而不是那些 stopped
         的子 process,所以可以在 sa_flags 当中加上 SA_NOCLDSTOP。如果要
         wait 子 process 而本身不因此被挡 (block),可以使用 waitpid()。第一
         个参数必需是 -1 (代表 wait 任何 pid),第三个参数必需是 WNOHANG,这是
         最具可携性的做法,也是可能会成为未来最具可携性的写法。
 
         如果您的系统不支援 POSIX,那就有很多做法了。最简单的方式就是先试
         试signal(SIGCHLD, SIG_IGN) 是否可行,可以的话那就好了。如果
         SIG_IGN 无法用来强制自动收拾残骸,那么您就要自己写一个 signal 处理
         函数来收拾残骸了。要写出一个适用于每一种 Unix 的 singal 处理函数来
         做这件事是不容易的事,因为有下列不一致的地方:

         在一些 Unix 中,一个或一个以上的子 process 死时,会呼叫 SIGCHLD 的
         signal 处理函数。也就是说,如果你的 signal 处理函数只有一个 wait()
         时,并不会把所有的子 process 都收拾乾净。不过还好,我相信这类的
         Unix 都会有 wait3() 或 waitpid(),这两者都有可在 option 参数中使用
         WNOHNAG 可用来检查是否有子 process 尚待收拾。所以在一个有
         wait3()/waitpid() 的系统中,你可以一再重复使用 wait3()/waitpid()
         以确定所有的子 process 都已收拾乾净W詈檬怯 waitpid() 因为
         它在 POSIX 标准中。
 
         在一些 SysV-derived 的系统中,再 SIGCHLD 的 signal 处理函数结束后,
         若还有子 process 等待清除,还是会产生 SIGCHLD signal。 因此,在大部
         份的 SysV 系统中,在 signal 处理函数里可以假设要处理的 signal 只有一
         个,


--
※ 来源:.饮水思源 bbs.sjtu.edu.cn.[FROM: 202.112.26.40]


--
※ 来源:·哈尔滨紫丁香站 bbs1.hit.edu.cn·[FROM: qlu@hitcrc.hitcrc.hi] 
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:409.903毫秒