⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unix

📁 Unix编程常见问题解答
💻
📖 第 1 页 / 共 5 页
字号:
                              itimers的函数,使用“man 
                              setitimer”确认你的系统支持),你可以用它们自己撺一<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              个‘usleep()’。(参见BSD源程序的‘usleep()’以便知道怎样做)<BR><BR>&nbsp;&nbsp; 
                              * 
                              如果你有POSIX实时(realtime)支持,那会有一个‘nanosleep()’函数。<BR><BR>众观以上方法,‘select()’可能是移植性最好的(直截了当说,它经常比<BR>‘usleep()’或基于itimer的方法更有效)。但是,在睡眠中捕获信号的做法会有<BR>所不同;基于不同应用,这可以成为或不成为一个问题。<BR><BR>无论你选择哪条路,意识到你将受到系统计时器分辨率的限制是很重要的(一<BR>些系统允许设置非常短的时间间隔,而其他的系统有一个分辨率,比如说10毫<BR>秒,而且总是将所有设置时间取整到那个值)。而且,关于‘sleep()’,你设置<BR>的延迟只是最小值(译者注:实际延迟的最小值);经过这段时间的延迟,会有<BR>一个中间时间间隔直到你的进程重新被调度到。<BR><BR>1.4&nbsp;&nbsp;我怎样得到一个更细分时间单位的alarm函数版本?<BR>==================================================<BR><BR>当今Unix系统倾向于使用‘setitimer()’函数实现闹钟,它比简单的‘alarm()’函<BR>数具有更高的分辨率和更多的选择项。一个使用者一般需要首先假设‘alarm()’<BR>和‘setitimer(ITIMER_REAL)’可能是相同的底层计时器,而且假设同时使用两<BR>种方法会造成混乱。<BR><BR>Itimers可被用于实现一次性或重复信号;而且一般有3种不同的计时器可以用:<BR><BR>`ITIMER_REAL'<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;计数真实(挂钟)时间,然后发送‘SIGALRM’信号<BR><BR>`ITIMER_VIRTUAL'<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;计数进程虚拟(用户中央处理器)时间,然后发送‘SIGVTALRM’信号<BR><BR>`ITIMER_PROF'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              计数用户和系统中央处理器时间,然后发送‘SIGPROF’信号;它供解释器<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              用来进行梗概处理(profiling)<BR><BR>然而itimers不是许多标准的一部份,尽管它自从4.2BSD就被提供。POSIX实时标<BR>准的扩充定义了类似但不同的函数。<BR><BR>1.5 
                              父子进程如何通信?<BR>======================<BR><BR>一对父子进程可以通过正常的进程间通信的办法(管道,套接字,消息队列,共<BR>享内存)进行通信,但也可以通过利用它们作为父子进程的相互关系而具有的一<BR>些特殊方法。<BR><BR>一个最显然的方法是父进程可以得到子进程的退出状态。<BR><BR>因为子进程从它的父进程继承文件描述符,所以父进程可以打开一个管道的两端,<BR>然后fork,然后父进程关闭管道这一端,子进程关闭管道另一端。这正是你从你的<BR>进程调用‘popen()’函数运行另一个程序所发生的情况,也就是说你可以向<BR>‘popen()’返回的文件描述符进行写操作而子进程将其当作自己的标准输入,或<BR>者你可以读取这个文件描述符来看子进程向标准输出写了什么。(‘popen()’函数<BR>的mode参数定义你的意图(译者注:mode=“r”为读,mode=“w”为写);如果你<BR>想读写都做,那么你可以并不困难地用管道自己做到)<BR><BR>而且,子进程继承由父进程用mmap函数映射的匿名共享内存段(或者通过映射特<BR>殊文件‘/dev/zero’);这些共享内存段不能从无关的进程访问。<BR><BR>1.6 
                              我怎样去除僵死进程?<BR>========================<BR><BR>1.6.1 
                              何为僵死进程?<BR>--------------------<BR><BR>当一个程序创建的子进程比父进程提前结束,内核仍然保存一些它的信息以便父<BR>进程会需要它 
                              - 
                              比如,父进程可能需要检查子进程的退出状态。为了得到这些信<BR>息,父进程调用‘wait()’;当这个调用发生,内核可以丢弃这些信息。<BR><BR>在子进程终止后到父进程调用‘wait()’前的时间里,子进程被称为‘僵死进程’<BR>(‘zombie’)。(如果你用‘ps’,这个子进程会有一个‘Z’出现在它的状态区<BR>里指出这点。)即使它没有在执行,它仍然占据进程表里一个位置。(它不消耗其<BR>它资源,但是有些工具程序会显示错误的数字,比如中央处理器的使用;这是<BR>因为为节约空间进程表的某些部份与会计数据(accounting 
                              info)是共用(overlaid)的。)<BR><BR>这并不好,因为进程表对于进程数有固定的上限,系统会用光它们。即使系统没<BR>有用光 
                              ,每一个用户可以同时执行的进程数有限制,它总是小于系统的限制。<BR>顺便说一下,这也正是你需要总是 
                              检查‘fork()’是否失败的一个原因。<BR><BR>如果父进程未调用wait函数而终止,子进程将被‘init’进程收管,它将控制子进<BR>程退出后必须的清除工作。(‘init’是一个特殊的系统程序,进程号为1 
                              - 它实际<BR>上是系统启动后运行的第一个程序),<BR><BR>1.6.2 
                              我怎样避免它们的出现?<BR>----------------------------<BR><BR>你需要却认父进程为每个子进程的终止调用‘wait()’(或者‘waitpid()’,<BR>‘wait3()’,等等); 
                              或者,在某些系统上,你可以指令系统你对子进程的退出状<BR>态没有兴趣。(译者注:在SysV系统上,可以调用signal函数,设置SIGCLD信号为<BR>SIG_IGN,系统将不产生僵死进程, 
                              详细说明参见&lt;&lt;高级编程&gt;&gt;10.7节)<BR><BR>另一种方法是*两次*‘fork()’,而且使紧跟的子进程直接退出,这样造成孙子进<BR>程变成孤儿进程(orphaned),从而init进程将负责清除它。欲获得做这个的程序,参<BR>看范例章节的函数‘fork2()’。<BR><BR>为了忽略子进程状态,你需要做下面的步骤(查询你的系统手册页以知道这是否正<BR>常工作):<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              struct sigaction 
                              sa;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              sa.sa_handler = 
                              SIG_IGN;<BR>&nbsp;&nbsp;&nbsp;&nbsp; #ifdef 
                              SA_NOCLDWAIT<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              sa.sa_flags = 
                              SA_NOCLDWAIT;<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              #else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              sa.sa_flags = 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              #endif<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              sigemptyset(&amp;sa.sa_mask);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              sigaction(SIGCHLD, &amp;sa, 
                              NULL);<BR><BR>如果这是成功的,那么‘wait()’函数集将不再正常工作;如果它们中任何一个被<BR>调用,它们将等待直到*所有*子进程已经退出,然后返回失败,并且<BR>‘errno==ECHILD’。<BR><BR>另一个技巧是捕获SIGCHLD信号,然后使信号处理程序调用‘waitpid()’或<BR>‘wait3()’。参见范例章节的完整程序。<BR><BR>1.7 
                              我怎样使我的程序作为守护程序运行?<BR>======================================<BR><BR>一个“守护程序”进程通常被定义为一个后台进程,而且它不属于任何一个终端<BR>会话,(terminal 
                              session)。许多系统服务由守护程序实施;如网络服务,打印等。<BR><BR>简单地在后台启动一个程序并非足够是这些长时间运行的程序;那种方法没有正<BR>确地将进程从启动它的终端脱离(detach)。而且,启动守护程序的普遍接受的的方<BR>法是简单地手工执行或从rc脚本程序执行(译者注:rc:runcom);并希望这个守护<BR>程序将其*自身*安置到后台。<BR><BR>这里是成为守护程序的步骤:<BR><BR>&nbsp;&nbsp;1. 
                              调用‘fork()’以便父进程可以退出,这样就将控制权归还给运行你程序的<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              命令行或shell程序。需要这一步以便保证新进程不是一个进程组头领进程(process<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              group 
                              leader)。下一步,‘setsid()’,会因为你是进程组头领进程而失败。<BR><BR>&nbsp;&nbsp;2. 
                              调用‘setsid()’ 
                              以便成为一个进程组和会话组的头领进程。由于一个控制终端<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              与一个会话相关联,而且这个新会话还没有获得一个控制终端,我们的进程没<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              有控制终端,这对于守护程序来说是一件好事。<BR><BR>&nbsp;&nbsp;3. 
                              再次调用‘fork()’所以父进程(会话组头领进程)可以退出。这意味着我们,一<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              个非会话组头领进程永远不能重新获得控制终端。<BR><BR>&nbsp;&nbsp;4. 
                              调用‘chdir("/")’确认我们的进程不保持任何目录于使用状态。不做这个会导<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              致系统管理员不能卸装(umount)一个文件系统,因为它是我们的当前工作目录。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              [类似的,我们可以改变当前目录至对于守护程序运行重要的文件所在目录]<BR><BR>&nbsp;&nbsp;5. 
                              调用‘umask(0)’以便我们拥有对于我们写的任何东西的完全控制。我们不知<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              道我们继承了什么样的umask。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              [这一步是可选的](译者注:这里指步骤5,因为守护程序不一定需要写文件)<BR><BR>&nbsp;&nbsp;6. 
                              调用‘close()’关闭文件描述符0,1和2。这样我们释放了从父进程继承的标<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              准输入,标准输出,和标准错误输出。我们没办法知道这些文描述符符可能<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              已经被重定向去哪里。注意到许多守护程序使用‘sysconf()’来确认<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              ‘_SC_OPEN_MAX’的限制。‘_SC_OPEN_MAX’告诉你每个进程能够打<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              开的最多文件数。然后使用一个循环,守护程序可以关闭所有可能的文件描<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              述符。你必须决定你需要做这个或不做。如果你认为有可能有打开的文件描<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              述符,你需要关闭它们,因为系统有一个同时打开文件数的限制。<BR><BR>&nbsp;&nbsp;7. 
                              为标准输入,标准输出和标准错误输出建立新的文件描述符。即使你不打算<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              使用它们,打开着它们不失为一个好主意。准确操作这些描述符是基于各自<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              爱好;比如说,如果你有一个日志文件,你可能希望把它作为标准输出和标<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              准错误输出打开,而把‘/dev/null’作为标准输入打开;作为替代方法,你可<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              以将‘/dev/console’作为标准错误输出和/或标准输出打开,而‘/dev/null’作<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              为标准输入,或者任何其它对你的守护程序有意义的结合方法。(译者注:一<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              般使用dup2函数原子化关闭和复制文件描述符,参见&lt;&lt;高级编程&gt;&gt;3.12节)<BR><BR>如果你的守护程序是被‘inetd’启动的,几乎所有这些步骤都不需要(或不建议<BR>采用)。在那种情况下,标准输入,标准输出和标准错误输出都为你指定为网络<BR>连接,而且‘fork()’的调用和会话的操纵不应做(以免使‘inetd’造成混乱)。只<BR>有‘chdir()’和‘umask()’这两步保持有用。<BR><BR>1.8&nbsp;&nbsp;我怎样象ps程序一样审视系统的进程?<BR>=======================================<BR><BR>你真的不该想做这个。<BR><BR>到目前为止,移植性最好的是调用‘popen(pscmd,"r")’并处理它的输出。(pscmd<BR>应当是类似SysV系统上的‘“ps 
                              -ef”’,BSD系统有很多可能的显示选项:选<BR>择一个。)<BR><BR>在范例章节有这个问题的两个完整解决方法;一个适用于SunOS 
                              4,它需要root权<BR>限执行并使用‘kvm_*’例程从内核数据结果读取信息;另一种适用于SVR4系统<BR>(包括Sun 
                              OS 
                              5),它使用‘/proc’文件系统。<BR><BR>在具有SVR4.2风格‘/proc’的系统上更简单;只要对于每一个感兴趣的进程号从<BR>文件‘/proc/进程号/psinfo’读取一个psinfo_t结构。但是,这种可能是最清晰的方<BR>法也许又是最不得到很好支持的方法。(在FreeBSD的‘/proc’上,你从<BR>‘/proc/进程号/status’读取一个半未提供文档说明(semi-undocumented)的可打印字<BR>符串;Linux有一些与其类似的东西)<BR><BR>1.9&nbsp;&nbsp;给定一个进程号,我怎样知道它是个正在运行的程序?<BR>=====================================================<BR><BR>使用‘kill()’函数,而已0作为信号代码(signal 
                              number)。<BR><BR>从这个函数返回有四种可能的结果:<BR><BR>&nbsp;&nbsp; 
                              * 
                              ‘kill()’返回0<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              - 
                              这意味着一个给定此进程号的进程退出,系统允许你向它发送信号。该进<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              程是否可以是僵死进程与不同系统有关。<BR><BR>&nbsp;&nbsp; * 
                              ‘kill()’返回-1,‘errno == 
                              ESRCH’<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              - 
                              要么不存在给定进程号的进程,要么增强的安全机制导致系统否认它的存<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              在。(在一些系统上,这个进程有可能是僵死进程。)<BR><BR>&nbsp;&nbsp; * 
                              ‘kill()’返回-1,‘errno == 
                              EPERM’<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              - 
                              系统不允许你杀死(kill)这个特定进程。这意味着要么进程存在(它又可能是<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              僵死进程),要么严格的增强安全机制起作用(比如你的进程不允许发送信号<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              给*任何人*)。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;* 
                              ‘kill()’返回-1,伴以其它‘errno’值<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              - 
                              你有麻烦了!<BR><BR>用的最多的技巧是认为调用“成功”或伴以‘EPERM’的“失败”意味着进程存<BR>在,而其它错误意味着它不存在。<BR><BR>如果你特别为提供‘/proc’文件系统的系统(或所有类似系统)写程序,一个替换<BR>方法存在:检查‘proc/进程号’是否存在是可行的。<BR><BR>1.10&nbsp;&nbsp;system函数,pclose函数,waitpid函数 
                              的返回值是什么?<BR>==========================================================<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              ‘system()’,‘pclose()’或者‘waitpid()’的返回值不象是我进程的退出值(exit<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value)(译者注:退出值指调用exit() 
                              或_exit()时给的参数)... 
                              或者退出值左移了8<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
                              位...这是怎么搞的?<BR><BR>手册页是对的,你也是对的! 
                              如果查阅手册页的‘waitpid()’你会发现进程的返回<BR>值被编码了。正常情况下,进程的返回值在高16位,而余下的位用来作其它事。<BR>如果你希望可移植,你就不能凭借这个,而建议是你该使用提供的宏。这些宏总<BR>是在‘wait()’或‘wstat’的文档中说明了。<BR><BR>为了不同目的定义的宏(在‘&lt;sys/wait.h&gt;’)包括(stat是‘waitpid()’返回的值):<BR><BR>`WIFEXITED(stat)'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              如果子进程正常退出则返回非0<BR><BR>`WEXITSTATUS(stat)'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              子进程返回的退出码<BR><BR>`WIFSIGNALED(stat)'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              如果子进程由与信号而 
                              终止则返回非0<BR><BR>`WTERMSIG(stat)'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              终止子进程的信号代码<BR><BR>`WIFSTOPPED(stat)'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              如果子进程暂停(stopped)则返回非0<BR><BR>`WSTOPSIG(stat)'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              使子进程暂停的信号代码<BR><BR>`WIFCONTINUED(stat)'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              如果状态是表示子进程继续执行则返回非0<BR><BR>`WCOREDUMP(stat)'<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              如果‘WIFSIGNALED(stat)’为非0,而如果这个进程产生一个内存映射文件<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
                              (core dump)则返回非0<BR><BR>1.11 
                              我怎样找出一个进程的存储器使用情况?<BR>=========================================<BR><BR>如果提供的话,参看‘getrusage()’手册页<BR><BR>1.12 
                              为什么进程的大小不缩减?<BR>=============================<BR><BR>当你使用‘free()’函数释放内存给堆时,几乎所有的系统都*不*减少你程序的<BR>对内存的使用。被‘free()’释放的内存仍然属于进程地址空间的一部份,并将<BR>被将来的‘malloc()’请求所重复使用。<BR><BR>如果你真的需要释放内存给系统,参看使用‘mmap()’分配私有匿名内存映射<BR>(private 
                              anonymous 
                              mappings)。当这些内存映射被取消映射时,内存真的将其释放给<BR>系统。某些‘malloc()’的实现方法(比如在GNU 
                              C库中)在允许时自动使用‘mmap()’<BR>实施大容量分配;这些内存块(blocks)随着‘free()’被释放回系统。<BR><BR>当然,如果你的程序的大小增加而你认为它不应该这样,你可能有一个‘内存泄<BR>露’(‘memory 
              

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -