📄 975.html
字号:
如果‘WIFSIGNALED(stat)’为非0,而如果这个进程产生一个内存映射文件<br>
(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 leak’)- 即在你的的程序中有缺陷(bug)导致未用的内存没释放。<br>
<br>
1.13 我怎样改变我程序的名字(即“ps”看到的名字)?<br>
=================================================<br>
<br>
在BSD风格的系统中,‘ps’程序实际上审视运行进程的地址空间从而找到当前<br>
的‘argv[]’,并显示它。这使得程序可以通过简单的修改‘argv[]’以改变它的<br>
名字。<br>
<br>
在SysV风格的系统中,命令的名字和参数的一般头80字节是存放在进程的u-区(<br>
u-area), 所以不能被直接修改。可能有一个系统调用用来修改它(不象是这样),<br>
但是其它的话,只有一个方法就是实施一个‘exec()’,或者些内核内存(危险,<br>
而且只有root才有可能)。<br>
<br>
一些系统(值得注意的是Solaris)可以有‘ps’的两种不同版本,一种是在<br>
‘/usr/bin/ps’拥有SysV的行为,而另一种在‘/usr/ucb/ps’拥有BSD的行为。在<br>
这些系统中,如果你改变‘argv[]’,那么BSD版的‘ps’将反映这个变化,而<br>
SysV版将不会。<br>
<br>
检查你的系统是否有一个函数‘setproctitle()’。<br>
<br>
1.14 我怎样找到进程的相应可执行文件?<br>
=====================================<br>
<br>
这个问题可以作为‘常见未回答问题’(‘Frequently Unanswered Questions’)的一<br>
个好候选,因为事实上提出这个问题经常意味着程序的设计有缺陷。:)<br>
<br>
你能作的‘最佳猜测’(‘best guess’)是通过审视‘argv[0]’的值而获得。如果<br>
它包括一个‘/’,那么它可能是可执行程序的绝对或相对(对于在程序开始时的<br>
当前目录而言)路径。如果不包括,那么你可以仿效shell对于‘PATH’变量的查<br>
询来查找这个程序。但是,不能保证成功,因为有可能执行程序时‘argv[0]’是<br>
一些任意值,也不排除这个可执行文件在执行后可能已经被更名或删除的情况。<br>
<br>
如果所有你想做的只是能打印一个和错误消息一起出现的合适的名字,那么最好<br>
的方法在‘main()’函数中将‘argv[0]’的值保存在全局变量中以供整个程序使<br>
用。虽然没有保证说‘argv[0]’的值总是有意义,但在大多数情况下它是最好的<br>
选择。<br>
<br>
人们询问这个问题的最普通原因是意图定位他们程序的配置文件。这被认为是<br>
不好的形式;包含可执行文件的目录应当*只*包含可执行文件,而且基于管理的<br>
要求经常试图将配置文件放置在和可执行文件不同的文件系统。<br>
<br>
试图做这个的一个比较不普通但更正规的理由是允许程序调用‘exec()’执行它<br>
自己;这是一种用来完全重新初始化进程(比如被用于一些‘sendmail’的版本)的<br>
办法(比如当一个守护程序捕获一个‘SIGHUP’信号)。<br>
<br>
1.14.1 So where do I put my configuration files then?<br>
-----------------------------------------------------<br>
1.14.1 那么,我把配置文件放在哪里里呢?<br>
<br>
为配置文件安排正确的目录总是取决于你使用的Unix系统的特点;<br>
‘/var/opt/PACKAGE’,‘/usr/local/lib’,‘/usr/local/etc’,或者任何其它一<br>
些可能的地方。用户自定义的配置文件通常是在‘$HOME’下的以“.”开始的隐藏文件(<br>
比如‘$HOME/.exrc’)。<br>
<br>
从一个在不同系统上都能使用的软件包(package)的角度看,它通常意味着任何站<br>
点范围(sitewide)的配置文件的位置有个已设定的缺省值,可能情况是使用一个在<br>
配置脚本程序里的‘--prefix’选项(Autoconf 脚本程序集做这个工作)。你会希望允<br>
许这个缺省值在程序执行时被一个环境变量重载。(如果你没使用配置脚本程序,<br>
那么在编译时,将这个位置缺省值作为‘-D’选项放入项目文件(Makefile),或者<br>
将其放入一个‘config.h’头文件,或做其它类似的工作)<br>
<br>
--<br>
<br>
用户自定义配置需要放置于一个在‘$HOME’下的文件名“.”打头的文件,或者<br>
在需要多个配置文件时,建立文件名“.”打头的子目录。(在列目录时,文件名以<br>
“.”打头的文件或目录缺省情况下被忽略。)避免在‘$HOME’建立多个文件,因<br>
为这会造成非常杂乱的情况。当然,你也应该允许用户通过一个环境变量重载这个<br>
位置。即使不能找到某个用户的配置文件,程序仍应当以适宜的方式执行。<br>
<br>
1.15 为何父进程死时,我的进程未得到SIGHUP信号?<br>
===============================================<br>
<br>
因为本来就没有设想是这样做的。<br>
<br>
‘SIGHUP’是一个信号,它按照惯例意味着“终端线路被挂断”。它与父进程<br>
无关,而且通常由tty驱动程序产生(并传递给前台的进程组)。<br>
<br>
但是,作为会话管理系统(session management system)的一部份,确切说有两种情况<br>
下‘SIGHUP’会在一个进程死时发送出:<br>
<br>
* 当一个终端设备与一个会话相关联,而这个会话的会话首领进程死时,<br>
‘SIGHUP’被发送至这个终端设备的所有前台进程组。<br>
<br>
* 当一个进程死去导致一个进程组变成孤儿,而且该进程组里一个或多个进程<br>
处于*暂停*状态时,那么‘SIGHUP’和‘SIGCONT’被发送至这个孤儿进程<br>
组的所有成员进程。(一个孤儿进程组是指在该进程组中没有一个成员进程的<br>
父进程属于和该进程组相同的会话的其它进程组。)<br>
<br>
1.16 我怎样杀死一个进程的所有派生进程?<br>
=======================================<br>
<br>
没有一个完全普遍的方法来做这个。虽然你可以通过处理‘ps’的输出确定进<br>
程间的相互关系,但因为它只表示系统的一瞬间的状态(snapshot)所以并不可靠。<br>
<br>
但是,如果你启动一个子进程,而它可能生成它自己的子进程,而你意图一次杀<br>
死整个生成的事务(job),解决方法是将最先启动的子进程置于一个新的进程组,<br>
当你需要时杀死整个进程组。<br>
<br>
建议为创建进程组而使用的函数是‘setpgid()’。在可能情况下,使用这个函数<br>
而不使用‘setpgrp()’,因为后一个在不同系统中有所不同(在一些系统上‘setgrp();’<br>
等同于‘setpgid(0,0);’,在其它系统上,‘setpgrp()’和‘setpgid()’相同)。<br>
<br>
参见范例章节的事务-控制范例程序。<br>
<br>
放置一个子进程于其自身的进程组有一些影响。特别的,除非你显式地将该进程<br>
组放置于前台,它将被认为是一个后台事务并具有以下结果:<br>
<br>
* 试图从终端读取的进程将被‘SIGTTIN’信号暂停。<br>
<br>
* 如果设置终端模式‘tostop’,那么试图向终端写的进程将被‘SIGTTOU’<br>
信号暂停。(试图改变终端模式也导致这个结果,且不管当前‘tostop’是否<br>
设置)<br>
<br>
* 子进程将不会收到从终端发出的键盘信号(比如‘SIGINT’或‘SIGQUIT’)<br>
<br>
在很多应用程序中输入和输出总会被重定向,所以最显著的影响将是丧失键盘<br>
信号。父进程需要安排程序起码捕获‘SIGINIT’和‘SIGQUIT’(可能情况下,<br>
还有‘SIGTERM’),并在需要情况下清除后台事务。<br>
<br>
<br>
2. 一般文件操作(包括管道和套接字)<br>
*********************************<br>
<br>
请同时参考套接字FAQ,在<br>
http://www.lcg.org/sock-faq/<br>
<br>
2.1 如何管理多个连接?<br>
======================<br>
“我想同时监控一个以上的文件描述符(fd)/连接(connection)/流(stream),<br>
应该怎么办?”<br>
<br>
使用 select() 或 poll() 函数。<br>
<br>
注意:select() 在BSD中被引入,而poll()是SysV STREAM流控制的产物。因此,<br>
这里就有了平台移植上的考虑:纯粹的BSD系统可能仍然缺少poll(),而早一些<br>
的SVR3系统中可能没有select(),尽管在SVR4中将其加入。目前两者都是POSIX.<br>
1g标准,(译者注:因此在linux上两者都存在)<br>
<br>
select()和poll()本质上来讲做的是同一件事,只是完成的方法不一样。两者都<br>
通过检验一组文件描述符来检测是否有特定的时间将在上面发生并在一定的时间<br>
内等待其发生。<br>
<br>
[重要事项:无论select()还是poll()都不对普通文件起很大效用,它们着重用<br>
于套接口(socket)、管道(pipe)、伪终端(pty)、终端设备(tty)和其他一些字符<br>
设备,但是这些操作都是系统相关(system-dependent)的。]<br>
<br>
2.2.1 我如何使用select()函数?<br>
------------------------------<br>
select()函数的接口主要是建立在一种叫'fd_set'类型的基础上。它('fd_set')<br>
是一组文件描述符(fd)的集合。由于fd_set类型的长度在不同平台上不同,因此<br>
应该用一组标准的宏定义来处理此类变量:<br>
<br>
fd_set set;<br>
FD_ZERO(&set); /* 将set清零 */<br>
FD_SET(fd, &set); /* 将fd加入set */<br>
FD_CLR(fd, &set); /* 将fd从set中清除 */<br>
FD_ISSET(fd, &set); /* 如果fd在set中则真 */<br>
<br>
在过去,一个fd_set通常只能包含少于等于32个文件描述符,因为fd_set其实只<br>
用了一个int的比特矢量来实现,在大多数情况下,检查fd_set能包括任意值的<br>
文件描述符是系统的责任,但确定你的fd_set到底能放多少有时你应该检查/修<br>
改宏FD_SETSIZE的值。*这个值是系统相关的*,同时检查你的系统中的select()<br>
的man手册。有一些系统对多于1024个文件描述符的支持有问题。[译者注:<br>
linux就是这样的系统!你会发现sizeof(fd_set)的结果是128(*8 =<br>
FD_SETSIZE=1024) 尽管很少你会遇到这种情况。]<br>
<br>
select的基本接口十分简单:<br>
<br>
int select(int nfds, fd_set *readset, fd_set *writeset,<br>
fd_set *exceptset, struct timeval *timeout);<br>
其中:<br>
nfds : 需要检查的文件描述符个数,数值应该比是三组fd_set中最大数<br>
更大,而不是实际文件描述符的总数。<br>
readset: 用来检查可读性的一组文件描述符。<br>
writeset: 用来检查可写性的一组文件描述符。<br>
exceptset: 用来检查意外状态的文件描述符。(注:错误并不是意外状态)<br>
timeout: NULL指针代表无限等待,否则是指向timeval结构的指针,代表最<br>
长等待时间。(如果其中tv_sec和tv_usec都等于0, 则文件描述符<br>
的状态不被影响,但函数并不挂起)<br>
<br>
函数将返回响应操作的对应操作文件描述符的总数,且三组数据均在恰当位置被<br>
修改,只有响应操作的那一些没有修改。接着应该用FD_ISSET宏来查找返回的文<br>
件描述符组。<br>
<br>
这里是一个简单的测试单个文件描述符可读性的例子:<br>
<br>
int isready(int fd)<br>
{<br>
int rc;<br>
fd_set fds;<br>
struct timeval tv;<br>
<br>
FD_ZERO(&fds);<br>
FD_SET(fd,&fds);<br>
tv.tv_sec = tv.tv_usec = 0;<br>
<br>
rc = select(fd+1, &fds, NULL, NULL, &tv);<br>
if (rc < 0)<br>
return -1;<br>
<br>
return FD_ISSET(fd,&fds) ? 1 : 0;<br>
}<br>
<br>
当然如果我们把NULL指针作为fd_set传入的话,这就表示我们对这种操作的发生<br>
不感兴趣,但select() 还是会等待直到其发生或者超过等待时间。<br>
<br>
[译者注:在linux中,timeout指的是程序在非sleep状态中度过的时间,而不是<br>
实际上过去的时间,这就会引起和非linux平台移植上的时间不等问题。移植问<br>
题还包括在System V风格中select()在函数退出前会把timeout设为未定义的<br>
NULL状态,而在BSD中则不是这样,linux在这点上遵从System V,因此在重复利<br>
用timeout指针问题上也应该注意。]<br>
<br>
2.1.2 我如何使用poll()?<br>
------------------------<br>
poll()接受一个指向结
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -