📄 975.html
字号:
<br>
* 进程号<br>
<br>
* 不同的父进程号(译者注:即子进程的父进程号与父进程的父进程号不同,父进<br>
程号可由getppid函数得到)<br>
<br>
* 自己的文件描述符和目录流的拷贝(译者注:目录流由opendir函数创建,因其为<br>
顺序读取,顾称“目录流”)<br>
<br>
* 子进程不继承父进程的进程,正文(text),数据和其它锁定内存(memory locks)<br>
(译者注:锁定内存指被锁定的虚拟内存页,锁定后,不允许内核将其在必要时<br>
换出(page out),详细说明参见<<The GNU C Library Reference Manual>> 2.2版,<br>
1999, 3.4.2节)<br>
<br>
* 在tms结构中的系统时间(译者注:tms结构可由times函数获得,它保存四个数据<br>
用于记录进程使用中央处理器(CPU:Central Processing Unit)的时间,包括:用户时<br>
间,系统时间,用户各子进程合计时间,系统各子进程合计时间)<br>
<br>
* 资源使用(resource utilizations)设定为0<br>
<br>
* 阻塞信号集初始化为空集(译者注:原文此处不明确,译文根据fork函数手册页<br>
稍做修改)<br>
<br>
* 不继承由timer_create函数创建的计时器<br>
<br>
* 不继承异步输入和输出<br>
<br>
1.1.2 fork函数 与 vfork函数的区别在哪里里?<br>
-------------------------------------------<br>
<br>
有些系统有一个系统调用‘vfork()’,它最初被设计成‘fork()’的较少额外支出<br>
(lower-overhead)版本。因为‘fork()’包括拷贝整个进程的地址空间,所以非常<br>
“昂贵”,这个‘vfork()’函数因此被引入。(在3.0BSD中)(译者注:BSD:<br>
Berkeley Software Distribution)<br>
<br>
*但是*,自从‘vfork()’被引入,‘fork()’的实现方法得到了很大改善,最值得<br>
注意的是“写操作时拷贝”(copy-on-write)的引入,它是通过允许父子进程可访问<br>
相同物理内存从而伪装(fake)了对进程地址空间的真实拷贝,直到有进程改变内<br>
存中数据时才拷贝。这个提高很大程度上抹杀了需要‘vfork()’的理由;事实上,<br>
一大部份系统完全丧失了‘vfork()’的原始功能。但为了兼容,它们仍然提供<br>
‘vfork()’函数调用,但它只是简单地调用‘fork()’,而不试图模拟所有‘vfork()’<br>
的语义(semantics, 译文取自<<高级编程>>,指定义的内容和做法)。<br>
<br>
结论是,试图使用任何‘fork()’和‘vfork()’的不同点是*很*不明智的。事实上,<br>
可能使用‘vfork()’根本就是不明智的,除非你确切知道你想*干什么*。<br>
<br>
两者的基本区别在于当使用‘vfork()’创建新进程时,父进程将被暂时阻塞,而<br>
子进程则可以借用父进程的地址空间。这个奇特状态将持续直到子进程要么退<br>
出,要么调用‘execve()’,至此父进程才继续执行。<br>
<br>
这意味着一个由‘vfork()’创建的子进程必须小心以免出乎意料地改变父进程的<br>
变量。特别的,子进程必须不从包含‘vfork()’调用的函数返回,而且必须不调<br>
用‘exit()’(如果它需要退出,它需要使用‘_exit()’;事实上,对于使用正常<br>
‘fork()’创建的子进程这也是正确的)(译者注:参见1.1.3)<br>
<br>
1.1.3 为何在一个fork的子进程分支中使用_exit函数而不使用exit函数?<br>
-----------------------------------------------------------------<br>
<br>
‘exit()’与‘_exit()’有不少区别在使用‘fork()’,特别是‘vfork()’时变得很<br>
突出。<br>
<br>
‘exit()’与‘_exit()’的基本区别在于前一个调用实施与调用库里用户状态结构<br>
(user-mode constructs)有关的清除工作(clean-up),而且调用用户自定义的清除程序<br>
(译者注:自定义清除程序由atexit函数定义,可定义多次,并以倒序执行),相对<br>
应,后一个函数只为进程实施内核清除工作。<br>
<br>
在由‘fork()’创建的子进程分支里,正常情况下使用‘exit()’是不正确的,这是<br>
因为使用它会导致标准输入输出(译者注:stdio: Standard Input Output)的缓冲区被<br>
清空两次,而且临时文件被出乎意料的删除(译者注:临时文件由tmpfile函数创建<br>
在系统临时目录下,文件名由系统随机生成)。在C++程序中情况会更糟,因为静<br>
态目标(static objects)的析构函数(destructors)可以被错误地执行。(还有一些特殊情<br>
况,比如守护程序,它们的*父进程*需要调用‘_exit()’而不是子进程;适用于绝<br>
大多数情况的基本规则是,‘exit()’在每一次进入‘main’函数后只调用一次。)<br>
<br>
在由‘vfork()’创建的子进程分支里,‘exit()’的使用将更加危险,因为它将影响<br>
*父*进程的状态。<br>
<br>
1.2 环境变量<br>
============<br>
<br>
1.2.1 如何从程序中获得/设置环境变量?<br>
--------------------------------------<br>
获得一个环境变量可以通过调用‘getenv()’函数完成。<br>
<br>
#include <stdlib.h><br>
<br>
char *getenv(const char *name);<br>
<br>
设置一个环境变量可以通过调用‘putenv()’函数完成。<br>
<br>
#include <stdlib.h><br>
<br>
int putenv(char *string);<br>
<br>
变量string应该遵守"name=value"的格式。已经传递给putenv函数的字符串*不*能够被<br>
释放或变成无效,因为一个指向它的指针将由‘putenv()’保存。这意味着它必须是<br>
在静态数据区中或是从堆(heap)分配的。如果这个环境变量被另一个‘putenv()’的<br>
调用重新定义或删除,上述字符串可以被释放。<br>
<br>
/* 译者增加:<br>
<br>
因为putenv()有这样的局限,在使用中经常会导致一些错<br>
误,GNU libc 中还包括了两个BSD风格的函数:<br>
#include <stdlib.h><br>
int setenv(const char *name, const char *value, int replace);<br>
void unsetenv(const char *name);<br>
<br>
setenv()/unsetenv()函数可以完成所有putenv()能做的事。setenv() 可以不受指针<br>
限制地向环境变量中添加新值,但传入参数不能为空(NULL)。当replace为0时,如<br>
果环境变量中已经有了name项,函数什么也不做(保留原项),否则原项被覆盖。<br>
unsetenv()是用来把name项从环境变量中删除。注意:这两个函数只存在在BSD和GNU<br>
库中,其他如SunOS系统中不包括它们,因此将会带来一些兼容问题。我们可以用<br>
getenv()/putenv()来实现:<br>
<br>
int setenv(const char *name, const char *value, int replace)<br>
{<br>
char *envstr;<br>
<br>
if (name == NULL || value == NULL)<br>
return 1;<br>
if (getenv(name) !=NULL)<br>
{<br>
envstr = (char *) malloc(strlen(name) + strlen(value) + 2);<br>
sprintf (envstr, "%s=%s", name, value);<br>
if (putenv(envstr));<br>
return 1;<br>
}<br>
return 0;<br>
}<br>
*/<br>
<br>
记住环境变量是被继承的;每一个进程有一个不同的环境变量表拷贝(译者注:<br>
从core文件中我们可以看出这一点)。结果是,你不能从一个其他进程改变当前<br>
进程的环境变量,比如shell进程。<br>
<br>
假设你想得到环境变量‘TERM’的值,你需要使用下面的程序:<br>
<br>
char *envvar;<br>
<br>
envvar=getenv("TERM");<br>
<br>
printf("The value for the environment variable TERM is ");<br>
if(envvar)<br>
{<br>
printf("%s",envvar);<br>
}<br>
else<br>
{<br>
printf("not set.");<br>
}<br>
<br>
现在假设你想创建一个新的环境变量,变量名为‘MYVAR’,值为‘MYVAL’。<br>
以下是你将怎样做:<br>
<br>
static char envbuf[256];<br>
<br>
sprintf(envbuf,"MYVAR=%s","MYVAL");<br>
<br>
if(putenv(envbuf))<br>
{<br>
printf("Sorry, putenv() couldn't find the memory for %s",envbuf);<br>
/* Might exit() or something here if you can't live without it */<br>
}<br>
<br>
1.2.2 我怎样读取整个环境变量表?<br>
--------------------------------<br>
<br>
如果你不知道确切你想要的环境变量的名字,那么‘getenv()’函数不是很有用。<br>
在这种情况下,你必须更深入了解环境变量表的存储方式。<br>
<br>
全局变量,‘char **envrion’,包含指向环境字符串指针数组的指针,每一个字<br>
符串的形式为‘“NAME=value”’(译者注:和putenv()中的“string”的格式相同)。<br>
这个数组以一个‘空’(NULL)指针标记结束。这里是一个打印当前环境变量列表<br>
的小程序(类似‘printenv’)。<br>
<br>
#include <stdio.h><br>
<br>
extern char **environ;<br>
<br>
int main()<br>
{<br>
char **ep = environ;<br>
char *p;<br>
while ((p = *ep++))<br>
printf("%s", p);<br>
return 0;<br>
}<br>
<br>
一般情况下,‘envrion’变量作为可选的第三个参数传递给‘main()’;就是说,<br>
上面的程序可以写成:<br>
<br>
#include <stdio.h><br>
<br>
int main(int argc, char **argv, char **envp)<br>
{<br>
char *p;<br>
while ((p = *envp++))<br>
printf("%s", p);<br>
return 0;<br>
}<br>
<br>
虽然这种方法被广泛的操纵系统所支持(译者注:包括DOS),这种方法事实上并<br>
没有被POSIX(译者注:POSIX: Portable Operating System Interace)标准所定义。(一<br>
般的,它也比较没用)<br>
<br>
1.3 我怎样睡眠小于一秒?<br>
========================<br>
<br>
在所有Unix中都有的‘sleep()’函数只允许以秒计算的时间间隔。如果你想要更<br>
细化,那么你需要寻找替换方法:<br>
<br>
* 许多系统有一个‘usleep()’函数<br>
<br>
* 你可以使用‘select()’或‘poll()’,并设置成无文件描述符并试验;一个普<br>
遍技巧是基于其中一个函数写一个‘usleep()’函数。(参见comp.unix.questions<br>
FAQ 的一些例子)<br>
<br>
* 如果你的系统有itimers(很多是有的)(译者注:setitimer和getitimer是两个操作<br>
itimers的函数,使用“man setitimer”确认你的系统支持),你可以用它们自己撺一<br>
个‘usleep()’。(参见BSD源程序的‘usleep()’以便知道怎样做)<br>
<br>
* 如果你有POSIX实时(realtime)支持,那会有一个‘nanosleep()’函数。<br>
<br>
众观以上方法,‘select()’可能是移植性最好的(直截了当说,它经常比<br>
‘usleep()’或基于itimer的方法更有效)。但是,在睡眠中捕获信号的做法会有<br>
所不同;基于不同应用,这可以成为或不成为一个问题。<br>
<br>
无论你选择哪条路,意识到你将受到系统计时器分辨率的限制是很重要的(一<br>
些系统允许设置非常短的时间间隔,而其他的系统有一个分辨率,比如说10毫<br>
秒,而且总是将所有设置时间取整到那个值)。而且,关于‘sleep()’,你设置<br>
的延迟只是最小值(译者注:实际延迟的最小值);经过这段时间的延迟,会有<br>
一个中间时间间隔直到你的进程重新被调度到。<br>
<br>
1.4 我怎样得到一个更细分时间单位的alarm函数版本?<br>
==================================================<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -