Unix编程常见问题解答.doc_第1页
Unix编程常见问题解答.doc_第2页
Unix编程常见问题解答.doc_第3页
Unix编程常见问题解答.doc_第4页
Unix编程常见问题解答.doc_第5页
已阅读5页,还剩115页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Unix 编程常见问题解答编程常见问题解答 目目 录录 1进程控制进程控制 4 1 1创建新进程 FORK函数 4 1 1 1fork函数干什么 4 1 1 2fork函数 与 vfork函数的区别在哪里里 8 1 1 3为何在一个fork的子进程分支中使用 exit函数而不使用exit函数 9 1 2环境变量 10 1 2 1如何从程序中获得 设置环境变量 10 1 2 2我怎样读取整个环境变量表 13 1 3我怎样睡眠小于一秒 15 1 4我怎样得到一个更细分时间单位的ALARM函数版本 16 1 5父子进程如何通信 16 1 6我怎样去除僵死进程 17 1 6 1何为僵死进程 17 1 6 2我怎样避免它们的出现 18 1 7我怎样使我的程序作为守护程序运行 19 1 8我怎样象PS程序一样审视系统的进程 22 1 9给定一个进程号 我怎样知道它是个正在运行的程序 23 1 10SYSTEM函数 PCLOSE函数 WAITPID函数 的返回值是什么 24 1 11我怎样找出一个进程的存储器使用情况 26 1 12为什么进程的大小不缩减 26 1 13我怎样改变我程序的名字 即 PS 看到的名字 26 1 14我怎样找到进程的相应可执行文件 27 1 14 1我把配置文件放在哪里里呢 28 1 15为何父进程死时 我的进程未得到 SIGHUP 信号 29 1 16我怎样杀死一个进程的所有派生进程 30 2一般文件操作一般文件操作 包括管道和套接字包括管道和套接字 31 2 1如何管理多个连接 31 2 1 1我如何使用select 函数 32 2 1 2我如何使用poll 34 2 1 3我是否可以同时使用SysV IPC和select poll 37 2 2我如何才能知道和对方的连接被终止 37 2 3什么是读取目录的最好方法 38 2 4我如何才能知道一个文件被另外进程打开 38 2 5我如何锁住一个文件 39 2 6我如何能发现一个文件已由另外一个进程更新 41 2 7请问DU是怎样工作的 41 2 8我如何得到一个文件的长度 42 2 9我如何像SHELL里一样扩展在文件名里的 43 2 10有名管道 FIFO 能做什么 45 2 10 1什么是有名管道 45 2 10 2我如何建立一个有名管道 45 2 10 3我如何使用一个有名管道 46 2 10 4能否在NFS上使用有名管道 47 2 10 5能否让多个进程同时向有名管道内写入数据 47 2 10 6有名管道的应用 47 3终端输入终端输入 输出输出 48 3 1我怎样使我的程序不回射输入 48 3 2我怎样从终端读取单个字符 49 3 3我怎样检查是否一个键被摁下 51 3 4我怎样将光标在屏幕里移动 51 3 5PTTYS是什么 52 3 6怎样控制一个串行口和调制解调器 52 3 6 1串行设备和类型 55 3 6 2设置termios的标志位 56 4系统信息系统信息 60 4 1怎样知道我的系统有多少存储器容量 60 4 2我怎样检查一个用户的口令 61 4 2 1我怎样得到一个用户的口令 61 4 2 2我怎样通过用户号得到阴影口令文件中的口令 62 4 2 3我怎样核对一个用户的口令 63 5编程杂技编程杂技 65 5 1我怎样使用通配字符比较字符串 65 5 1 1我怎样使用文件名通配模式比较字符串 65 5 1 2我怎样使用正则表达式比较字符串 66 5 2什么是在程序中发送电子邮件的最好方法 66 5 2 1简单方法 bin mail 67 5 2 2直接启动邮件传输代理 译者注 MTA mail transfer agent usr bin sendmail 69 6工具的使用工具的使用 76 6 1我怎样调试FORK函数产生的子进程 76 6 2怎样通过其他库文件建立新的库文件 77 6 3怎样创建动态连接库 SHARED LIBRARY DLLS 77 6 4我能更改一个动态连接库里的目标吗 80 6 5我能在一个运行着的程序中生成堆栈映象吗 81 1 进程控制进程控制 1 1 创建新进程 创建新进程 fork 函数函数 1 1 1 fork 函数干什么 函数干什么 include include pid t fork void fork 函数用于从已存在进程中创建一个新进程 新进程称为子进程 而原 进程称为父进程 你可以通过检查 fork 函数的返回值知道哪个是父进程 哪个是子进程 父进程得到的返回值是子进程的进程号 而子进程则返回0 以 下这个范例程序说明它的基本功能 pid t pid switch pid fork case 1 这里pid为 1 fork函数失败 一些可能的原因是 进程数或虚拟内存用尽 perror The fork failed break case 0 pid为0 子进程 这里 我们是孩子 要做什么 但是做完后 我们需要做类似下面 exit 0 default pid大于0 为父进程得到的子进程号 printf Child s pid is d n pid 当然 有人可以用 if else 语句取代 switch 语句 但是上面的形式 是 一个有用的惯用方法 知道子进程自父进程继承什么或未继承什么将有助于我们 下面这个名单会因 为 不同Unix的实现而发生变化 所以或许准确性有了水份 请注意子进程得到的 是 这些东西的 拷贝 不是它们本身 由子进程自父进程继承到 进程的资格 真实 real 有效 effective 已保存 saved 用户号 UIDs 和组号 GIDs 环境 environment 堆栈 内存 打开文件的描述符 注意对应的文件的位置由父子进程共享 这会引起含 糊情况 执行时关闭 close on exec 标志 译者注 close on exec标志可通过fnctl 对文件描 述符设置 POSIX 1要求所有目录流都必须在exec函数调用时关闭 更详 细说明 参见 W R Stevens 1993 尤晋元等译 以下简称 3 13节和8 9节 信号 signal 控制设定 nice值 译者注 nice值由nice函数设定 该值表示进程的优先级 数值越 小 优 先级越高 进程调度类别 scheduler class 译者注 进程调度类别指进程在系统中被 调度时所 属的类别 不同类别有不同优先级 根据进程调度类别和nice值 进程调 度程序可计 算出每个进程的全局优先级 Global process prority 优先级高的进程优先 执行 进程组号 对话期ID Session ID 译者注 译文取自 指 进程所属的 对话期 session ID 一个对话期包括一个或多个进程组 更详细说明参见 9 5节 当前工作目录 根目录 译者注 根目录不一定是 它可由chroot函数改变 文件方式创建屏蔽字 file mode creation mask umask 译者注 译文取自 指 创建新文件的缺省屏蔽字 资源限制 控制终端 子进程所独有 进程号 不同的父进程号 译者注 即子进程的父进程号与父进程的父进程号不 同 父进 程号可由getppid函数得到 自己的文件描述符和目录流的拷贝 译者注 目录流由opendir函数创建 因其为 顺序读取 顾称 目录流 子进程不继承父进程的进程 正文 text 数据和其它锁定内存 memory locks 译者注 锁定内存指被锁定的虚拟内存页 锁定后 不允许内核将其在 必要时 换出 page out 详细说明参见 2 2版 1999 3 4 2节 在tms结构中的系统时间 译者注 tms结构可由times函数获得 它保存四 个数据 用于记录进程使用中央处理器 CPU Central Processing Unit 的时间 包 括 用户时 间 系统时间 用户各子进程合计时间 系统各子进程合计时间 资源使用 resource utilizations 设定为0 阻塞信号集初始化为空集 译者注 原文此处不明确 译文根据fork函数手 册页 稍做修改 不继承由timer create函数创建的计时器 不继承异步输入和输出 1 1 2 fork 函数函数 与与 vfork 函数的区别在哪里里 函数的区别在哪里里 有些系统有一个系统调用 vfork 它最初被设计成 fork 的较少额外支 出 lower overhead 版本 因为 fork 包括拷贝整个进程的地址空间 所以非 常 昂贵 这个 vfork 函数因此被引入 在3 0BSD中 译者注 BSD Berkeley Software Distribution 但是 自从 vfork 被引入 fork 的实现方法得到了很大改善 最值 得注意的是 写操作时拷贝 copy on write 的引入 它是通过允许父子进程可 访问相同物理内存从而伪装 fake 了对进程地址空间的真实拷贝 直到有进程改 变内存中数据时才拷贝 这个提高很大程度上抹杀了需要 vfork 的理由 事实上 一大部份系统完全丧失了 vfork 的原始功能 但为了兼容 它们仍然提供 vfork 函数调用 但它只是简单地调用 fork 而不试图模拟所有 vfork 的语义 semantics 译文取自 指定义的内容和做法 结论是 试图使用任何 fork 和 vfork 的不同点是 很 不明智的 事实 上 可能使用 vfork 根本就是不明智的 除非你确切知道你想 干什么 两者的基本区别在于当使用 vfork 创建新进程时 父进程将被暂时阻塞 而 子进程则可以借用父进程的地址空间 这个奇特状态将持续直到子进程要么退 出 要么调用 execve 至此父进程才继续执行 这意味着一个由 vfork 创建的子进程必须小心以免出乎意料地改变父进程 的 变量 特别的 子进程必须不从包含 vfork 调用的函数返回 而且必须不 调 用 exit 如果它需要退出 它需要使用 exit 事实上 对于使用正常 fork 创建的子进程这也是正确的 译者注 参见1 1 3 1 1 3 为何在一个为何在一个 fork 的子进程分支中使用的子进程分支中使用 exit 函数而不使用函数而不使用 exit 函数 函数 exit 与 exit 有不少区别在使用 fork 特别是 vfork 时变得 很 突出 exit 与 exit 的基本区别在于前一个调用实施与调用库里用户状态结 构 user mode constructs 有关的清除工作 clean up 而且调用用户自定义的清除程 序 译者注 自定义清除程序由atexit函数定义 可定义多次 并以倒序执行 相 对 应 后一个函数只为进程实施内核清除工作 在由 fork 创建的子进程分支里 正常情况下使用 exit 是不正确的 这 是因为使用它会导致标准输入输出 译者注 stdio Standard Input Output 的缓冲 区被清空两次 而且临时文件被出乎意料的删除 译者注 临时文件由tmpfile函 数创建在系统临时目录下 文件名由系统随机生成 在C 程序中情况会更 糟 因为静态目标 static objects 的析构函数 destructors 可以被错误地执行 还 有一些特殊情况 比如守护程序 它们的 父进程 需要调用 exit 而不是 子进程 适用于绝大多数情况的基本规则是 exit 在每一次进入 main 函数后只调用一次 在由 vfork 创建的子进程分支里 exit 的使用将更加危险 因为它将 影响 父 进程的状态 1 2 环境变量环境变量 1 2 1 如何从程序中获得如何从程序中获得 设置环境变量 设置环境变量 获得一个环境变量可以通过调用 getenv 函数完成 include char getenv const char name 设置一个环境变量可以通过调用 putenv 函数完成 include int putenv char string 变量string应该遵守 name value 的格式 已经传递给putenv函数的字符串 不 能够被释放或变成无效 因为一个指向它的指针将由 putenv 保存 这意味 着它必须是在静态数据区中或是从堆 heap 分配的 如果这个环境变量被另一个 putenv 的调用重新定义或删除 上述字符串可以被释放 译者增加 因为putenv 有这样的局限 在使用中经常会导致一些错 误 GNU libc 中还包括了两个BSD风格的函数 include int setenv const char name const char value int replace void unsetenv const char name setenv unsetenv 函数可以完成所有putenv 能做的事 setenv 可以不受指针 限制地向环境变量中添加新值 但传入参数不能为空 NULL 当replace为0 时 如 果环境变量中已经有了name项 函数什么也不做 保留原项 否则原项被覆 盖 unsetenv 是用来把name项从环境变量中删除 注意 这两个函数只存在在BSD 和GNU 库中 其他如SunOS系统中不包括它们 因此将会带来一些兼容问题 我们可 以用 getenv putenv 来实现 int setenv const char name const char value int replace char envstr if name NULL value NULL return 1 if getenv name NULL envstr char malloc strlen name strlen value 2 sprintf envstr s s name value if putenv envstr return 1 return 0 记住环境变量是被继承的 每一个进程有一个不同的环境变量表拷贝 译者 注 从core文件中我们可以看出这一点 结果是 你不能从一个其他进程改变当 前 进程的环境变量 比如shell进程 假设你想得到环境变量 TERM 的值 你需要使用下面的程序 char envvar envvar getenv TERM printf The value for the environment variable TERM is if envvar printf s n envvar else printf not set n 现在假设你想创建一个新的环境变量 变量名为 MYVAR 值为 MYVAL 以下是你将怎样做 static char envbuf 256 sprintf envbuf MYVAR s MYVAL if putenv envbuf printf Sorry putenv couldn t find the memory for s n envbuf Might exit or something here if you can t live without it 1 2 2 我怎样读取整个环境变量表 我怎样读取整个环境变量表 如果你不知道确切你想要的环境变量的名字 那么 getenv 函数不是很有 用 在这种情况下 你必须更深入了解环境变量表的存储方式 全局变量 char envrion 包含指向环境字符串指针数组的指针 每一个 字 符串的形式为 NAME value 译者注 和putenv 中的 string 的格式 相同 这个数组以一个 空 NULL 指针标记结束 这里是一个打印当前环境变量列 表 的小程序 类似 printenv include extern char environ int main char ep environ char p while p ep printf s n p return 0 一般情况下 envrion 变量作为可选的第三个参数传递给 main 就是 说 上面的程序可以写成 include int main int argc char argv char envp char p while p envp printf s n p return 0 虽然这种方法被广泛的操纵系统所支持 译者注 包括DOS 这种方法事实上 并 没有被POSIX 译者注 POSIX Portable Operating System Interace 标准所定 义 一 般的 它也比较没用 1 3 我怎样睡眠小于一秒 我怎样睡眠小于一秒 在所有Unix中都有的 sleep 函数只允许以秒计算的时间间隔 如果你想要 更 细化 那么你需要寻找替换方法 许多系统有一个 usleep 函数 你可以使用 select 或 poll 并设置成无文件描述符并试验 一 个普遍技巧是基于其中一个函数写一个 usleep 函数 参见 comp unix questions FAQ 的一些例子 如果你的系统有itimers 很多是有的 译者注 setitimer和getitimer是两个操 作itimers的函数 使用 man setitimer 确认你的系统支持 你可以用它们自己 撺一个 usleep 参见BSD源程序的 usleep 以便知道怎样做 如果你有POSIX实时 realtime 支持 那会有一个 nanosleep 函数 众观以上方法 select 可能是移植性最好的 直截了当说 它经常比 usleep 或基于itimer的方法更有效 但是 在睡眠中捕获信号的做法会有 所不同 基于不同应用 这可以成为或不成为一个问题 无论你选择哪条路 意识到你将受到系统计时器分辨率的限制是很重要的 一 些系统允许设置非常短的时间间隔 而其他的系统有一个分辨率 比如说10毫 秒 而且总是将所有设置时间取整到那个值 而且 关于 sleep 你设置 的延迟只是最小值 译者注 实际延迟的最小值 经过这段时间的延迟 会有 一个中间时间间隔直到你的进程重新被调度到 1 4 我怎样得到一个更细分时间单位的我怎样得到一个更细分时间单位的 alarm 函数版本 函数版本 当今Unix系统倾向于使用 setitimer 函数实现闹钟 它比简单的 alarm 函 数具有更高的分辨率和更多的选择项 一个使用者一般需要首先假设 alarm 和 setitimer ITIMER REAL 可能是相同的底层计时器 而且假设同时使用 两 种方法会造成混乱 Itimers可被用于实现一次性或重复信号 而且一般有3种不同的计时器可以用 ITIMER REAL 计数真实 挂钟 时间 然后发送 SIGALRM 信号 ITIMER VIRTUAL 计数进程虚拟 用户中央处理器 时间 然后发送 SIGVTALRM 信号 ITIMER PROF 计数用户和系统中央处理器时间 然后发送 SIGPROF 信号 它供解释 器 用来进行梗概处理 profiling 然而itimers不是许多标准的一部份 尽管它自从4 2BSD就被提供 POSIX实时 标 准的扩充定义了类似但不同的函数 1 5 父子进程如何通信 父子进程如何通信 一对父子进程可以通过正常的进程间通信的办法 管道 套接字 消息队列 共 享内存 进行通信 但也可以通过利用它们作为父子进程的相互关系而具有的一 些特殊方法 一个最显然的方法是父进程可以得到子进程的退出状态 因为子进程从它的父进程继承文件描述符 所以父进程可以打开一个管道的两 端 然后fork 然后父进程关闭管道这一端 子进程关闭管道另一端 这正是 你从你的进程调用 popen 函数运行另一个程序所发生的情况 也就是说你 可以向 popen 返回的文件描述符进行写操作而子进程将其当作自己的标准 输入 或者你可以读取这个文件描述符来看子进程向标准输出写了什 么 popen 函数的mode参数定义你的意图 译者注 mode r 为 读 mode w 为写 如果你想读写都做 那么你可以并不困难地用管道自 己做到 而且 子进程继承由父进程用mmap函数映射的匿名共享内存段 或者通过映射 特 殊文件 dev zero 这些共享内存段不能从无关的进程访问 1 6 我怎样去除僵死进程 我怎样去除僵死进程 1 6 1 何为僵死进程 何为僵死进程 当一个程序创建的子进程比父进程提前结束 内核仍然保存一些它的信息以便 父 进程会需要它 比如 父进程可能需要检查子进程的退出状态 为了得到这些 信 息 父进程调用 wait 当这个调用发生 内核可以丢弃这些信息 在子进程终止后到父进程调用 wait 前的时间里 子进程被称为 僵死进 程 zombie 如果你用 ps 这个子进程会有一个 Z 出现在它的状态区 里指出这点 即使它没有在执行 它仍然占据进程表里一个位置 它不消耗 其 它资源 但是有些工具程序会显示错误的数字 比如中央处理器的使用 这是 因为为节约空间进程表的某些部份与会计数据 accounting info 是共用 overlaid 的 这并不好 因为进程表对于进程数有固定的上限 系统会用光它们 即使系统 没 有用光 每一个用户可以同时执行的进程数有限制 它总是小于系统的限制 顺便说一下 这也正是你需要总是 检查 fork 是否失败的一个原因 如果父进程未调用wait函数而终止 子进程将被 init 进程收管 它将控制子 进 程退出后必须的清除工作 init 是一个特殊的系统程序 进程号为1 它实 际上是系统启动后运行的第一个程序 1 6 2 我怎样避免它们的出现 我怎样避免它们的出现 你需要却认父进程为每个子进程的终止调用 wait 或者 waitpid wait3 等等 或者 在某些系统上 你可以指令系统你对子进程的退 出状态没有兴趣 译者注 在SysV系统上 可以调用signal函数 设置SIGCLD 信号为SIG IGN 系统将不产生僵死进程 详细说明参见 10 7 节 另一种方法是 两次 fork 而且使紧跟的子进程直接退出 这样造成孙子 进程变成孤儿进程 orphaned 从而init进程将负责清除它 欲获得做这个的程 序 参看范例章节的函数 fork2 为了忽略子进程状态 你需要做下面的步骤 查询你的系统手册页以知道这是否 正常工作 struct sigaction sa sa sa handler SIG IGN ifdef SA NOCLDWAIT sa sa flags SA NOCLDWAIT else sa sa flags 0 endif sigemptyset sigaction SIGCHLD 如果这是成功的 那么 wait 函数集将不再正常工作 如果它们中任何一个 被调用 它们将等待直到 所有 子进程已经退出 然后返回失败 并且 errno ECHILD 另一个技巧是捕获SIGCHLD信号 然后使信号处理程序调用 waitpid 或 wait3 参见范例章节的完整程序 1 7 我怎样使我的程序作为守护程序运行 我怎样使我的程序作为守护程序运行 一个 守护程序 进程通常被定义为一个后台进程 而且它不属于任何一个终 端 会话 terminal session 许多系统服务由守护程序实施 如网络服务 打印 等 简单地在后台启动一个程序并非足够是这些长时间运行的程序 那种方法没有 正 确地将进程从启动它的终端脱离 detach 而且 启动守护程序的普遍接受的的 方法是简单地手工执行或从rc脚本程序执行 译者注 rc runcom 并希望这个 守护程序将其 自身 安置到后台 这里是成为守护程序的步骤 1 调用 fork 以便父进程可以退出 这样就将控制权归还给运行你程序的 命令行或shell程序 需要这一步以便保证新进程不是一个进程组头领进程 process group leader 下一步 setsid 会因为你是进程组头领进程而失 败 2 调用 setsid 以便成为一个进程组和会话组的头领进程 由于一个控 制终端与一个会话相关联 而且这个新会话还没有获得一个控制终端 我们的 进程没有控制终端 这对于守护程序来说是一件好事 3 再次调用 fork 所以父进程 会话组头领进程 可以退出 这意味着我 们 一个非会话组头领进程永远不能重新获得控制终端 4 调用 chdir 确认我们的进程不保持任何目录于使用状态 不做这个 会导致系统管理员不能卸装 umount 一个文件系统 因为它是我们的当前工作 目录 类似的 我们可以改变当前目录至对于守护程序运行重要的文件所在目 录 5 调用 umask 0 以便我们拥有对于我们写的任何东西的完全控制 我们 不知道我们继承了什么样的umask 这一步是可选的 译者注 这里指步骤5 因为守护程序不一定需要写文 件 6 调用 close 关闭文件描述符0 1和2 这样我们释放了从父进程继承的 标 准输入 标准输出 和标准错误输出 我们没办法知道这些文描述符符可 能已经被重定向去哪里 注意到许多守护程序使用 sysconf 来确认 SC OPEN MAX 的限制 SC OPEN MAX 告诉你每个进程能够打 开的最多文件数 然后使用一个循环 守护程序可以关闭所有可能的文件描 述符 你必须决定你需要做这个或不做 如果你认为有可能有打开的文件描 述符 你需要关闭它们 因为系统有一个同时打开文件数的限制 7 为标准输入 标准输出和标准错误输出建立新的文件描述符 即使你不打 算使用它们 打开着它们不失为一个好主意 准确操作这些描述符是基于各自 爱好 比如说 如果你有一个日志文件 你可能希望把它作为标准输出和标准 错误输出打开 而把 dev null 作为标准输入打开 作为替代方法 你可以将 dev console 作为标准错误输出和 或标准输出打开 而 dev null 作 为标准输入 或者任何其它对你的守护程序有意义的结合方法 译者注 一 般使用dup2函数原子化关闭和复制文件描述符 参见 3 12节 如果你的守护程序是被 inetd 启动的 几乎所有这些步骤都不需要 或不建议 采用 在那种情况下 标准输入 标准输出和标准错误输出都为你指定为网络 连接 而且 fork 的调用和会话的操纵不应做 以免使 inetd 造成混乱 只有 chdir 和 umask 这两步保持有用 1 8 我怎样象我怎样象 ps 程序一样审视系统的进程 程序一样审视系统的进程 你真的不该想做这个 到目前为止 移植性最好的是调用 popen pscmd r 并处理它的输 出 pscmd 应当是类似SysV系统上的 ps ef BSD系统有很多可能的显示选项 选 择一个 在范例章节有这个问题的两个完整解决方法 一个适用于SunOS 4 它需要root 权限执行并使用 kvm 例程从内核数据结果读取信息 另一种适用于SVR4 系统 包括Sun OS 5 它使用 proc 文件系统 在具有SVR4 2风格 proc 的系统上更简单 只要对于每一个感兴趣的进程号 从文件 proc 进程号 psinfo 读取一个psinfo t结构 但是 这种可能是最清晰 的方法也许又是最不得到很好支持的方法 在FreeBSD的 proc 上 你从 proc 进程号 status 读取一个半未提供文档说明 semi undocumented 的可打 印字符串 Linux有一些与其类似的东西 1 9 给定一个进程号 我怎样知道它是个正在运行的程序 给定一个进程号 我怎样知道它是个正在运行的程序 使用 kill 函数 而已0作为信号代码 signal number 从这个函数返回有四种可能的结果 kill 返回0 这意味着一个给定此进程号的进程退出 系统允许你向它发送信号 该进 程是否可以是僵死进程与不同系统有关 kill 返回 1 errno ESRCH 要么不存在给定进程号的进程 要么增强的安全机制导致系统否认它 的存 在 在一些系统上 这个进程有可能是僵死进程 kill 返回 1 errno EPERM 系统不允许你杀死 kill 这个特定进程 这意味着要么进程存在 它又 可能是 僵死进程 要么严格的增强安全机制起作用 比如你的进程不允许发 送信号 给 任何人 kill 返回 1 伴以其它 errno 值 你有麻烦了 用的最多的技巧是认为调用 成功 或伴以 EPERM 的 失败 意味着进程 存 在 而其它错误意味着它不存在 如果你特别为提供 proc 文件系统的系统 或所有类似系统 写程序 一个替 换 方法存在 检查 proc 进程号 是否存在是可行的 1 10 system 函数 函数 pclose 函数 函数 waitpid 函数函数 的返回值的返回值 是什么 是什么 system pclose 或者 waitpid 的返回值不象是我进程的退 出值 exit value 译者注 退出值指调用exit 或 exit 时给的参数 或者退出 值左移了8 位 这是怎么搞的 手册页是对的 你也是对的 如果查阅手册页的 waitpid 你会发现进程的返 回 值被编码了 正常情况下 进程的返回值在高16位 而余下的位用来作其它 事 如果你希望可移植 你就不能凭借这个 而建议是你该使用提供的宏 这些宏 总 是在 wait 或 wstat 的文档中说明了 为了不同目的定义的宏 在 包括 stat是 waitpid 返回的值 WIFEXITED stat 如果子进程正常退出则返回非0 WEXITSTATUS stat 子进程返回的退出码 WIFSIGNALED stat 如果子进程由与信号而 终止则返回非0 WTERMSIG stat 终止子进程的信号代码 WIFSTOPPED stat 如果子进程暂停 stopped 则返回非0 WSTOPSIG stat 使子进程暂停的信号代码 WIFCONTINUED stat 如果状态是表示子进程继续执行则返回非0 WCOREDUMP stat 如果 WIFSIGNALED stat 为非0 而如果这个进程产生一个内存映射 文件 core dump 则返回非0 1 11 我怎样找出一个进程的存储器使用情况 我怎样找出一个进程的存储器使用情况 如果提供的话 参看 getrusage 手册页 1 12 为什么进程的大小不缩减 为什么进程的大小不缩减 当你使用 free 函数释放内存给堆时 几乎所有的系统都 不 减少你程序的 对内存的使用 被 free 释放的内存仍然属于进程地址空间的一部份 并将 被将来的 malloc 请求所重复使用 如果你真的需要释放内存给系统 参看使用 mmap 分配私有匿名内存映射 private anonymous mappings 当这些内存映射被取消映射时 内存真的将其释 放给 系统 某些 malloc 的实现方法 比如在GNU C库中 在允许时自动使用 mmap 实施大容量分配 这些内存块 blocks 随着 free 被释放回系统 当然 如果你的程序的大小增加而你认为它不应该这样 你可能有一个 内存 泄 露 memory leak 即在你的的程序中有缺陷 bug 导致未用的内存没释 放 1 13 我怎样改变我程序的名字我怎样改变我程序的名字 即即 ps 看到的名字看到的名字 在BSD风格的系统中 ps 程序实际上审视运行进程的地址空间从而找到当 前 的 argv 并显示它 这使得程序可以通过简单的修改 argv 以改变它 的 名字 在SysV风格的系统中 命令的名字和参数的一般头80字节是存放在进程的u 区 u area 所以不能被直接修改 可能有一个系统调用用来修改它 不象是这 样 但是其它的话 只有一个方法就是实施一个 exec 或者些内核内存 危 险 而且只有root才有可能 一些系统 值得注意的是Solaris 可以有 ps 的两种不同版本 一种是在 usr bin ps 拥有SysV的行为 而另一种在 usr ucb ps 拥有BSD的行为 在 这些系统中 如果你改变 argv 那么BSD版的 ps 将反映这个变化 而 SysV版将不会 检查你的系统是否有一个函数 setproctitle 1 14 我怎样找到进程的相应可执行文件 我怎样找到进程的相应可执行文件 这个问题可以作为 常见未回答问题 Frequently Unanswered Questions 的 一 个好候选 因为事实上提出这个问题经常意味着程序的设计有缺陷 你能作的 最佳猜测 best guess 是通过审视 argv 0 的值而获得 如 果 它包括一个 那么它可能是可执行程序的绝对或相对 对于在程序开始时的 当前目录而言 路径 如果不包括 那么你可以仿效shell对于 PATH 变量的 查 询来查找这个程序 但是 不能保证成功 因为有可能执行程序时 argv 0 是 一些任意值 也不排除这个可执行文件在执行后可能已经被更名或删除的情 况 如果所有你想做的只是能打印一个和错误消息一起出现的合适的名字 那么最 好 的方法在 main 函数中将 argv 0 的值保存在全局变量中以供整个程序使 用 虽然没有保证说 argv 0 的值总是有意义 但在大多数情况下它是最好 的 选择 人们询问这个问题的最普通原因是意图定位他们程序的配置文件 这被认为是 不好的形式 包含可执行文件的目录应当 只 包含可执行文件 而且基于管理 的 要求经常试图将配置文件放置在和可执行文件不同的文件系统 试图做这个的一个比较不普通但更正规的理由是允许程序调用 exec 执行它 自己 这是一种用来完全重新初始化进程 比如被用于一些 sendmail 的版本 的 办法 比如当一个守护程序捕获一个 SIGHUP 信号 1 14 1我把配置文件放在哪里里呢 我把配置文件放在哪里里呢 为配置文件安排正确的目录总是取决于你使用的Unix系统的特点 var opt PACKAGE usr local lib usr local etc 或者任何其它一 些可能的地方 用户自定义的配置文件通常是在 HOME 下的以 开始 的隐藏文件 比如 HOME exrc 从一个在不同系统上都能使用的软件包 package 的角度看 它通常意味着任何 站 点范围 sitewide 的配置文件的位置有个已设定的缺省值 可能情况是使用一个 在 配置脚本程序里的 prefix 选项 Autoconf 脚本程序集做这个工作 你会希 望允 许这个缺省值在程序执行时被一个环境变量重载 如果你没使用配置脚本程 序 那么在编译时 将这个位置缺省值作为 D 选项放入项目文件 Makefile 或 者 将其放入一个 config h 头文件 或做其它类似的工作 用户自定义配置需要放置于一个在 HOME 下的文件名 打头的文件 或者 在需要多个配置文件时 建立文件名 打头的子目录 在列目录时 文件名 以 打头的文件或目录缺省情况下被忽略 避免在 HOME 建立多个文 件 因 为这会造成非常杂乱的情况 当然 你也应该允许用户通过一个环境变量重载 这个 位置 即使不能找到某个用户的配置文件 程序仍应当以适宜的方式执行 1 15 为何父进程死时 我的进程未得到为何父进程死时 我的进程未得到 SIGHUP 信号 信号 因为本来就没有设想是这样做的 SIGHUP 是一个信号 它按照惯例意味着 终端线路被挂断 它与父进程 无关 而且通常由tty驱动程序产生 并传递给前台的进程组 但是 作为会话管理系统 session management system 的一部份 确切说有两种 情况下 SIGHUP 会在一个进程死时发送出 当一个终端设备与一个会话相关联 而这个会话的会话首领进程死时 SIGHUP 被发送至这个终端设备的所有前台进程组 当一个进程死去导致一个进程组变成孤儿 而且该进程组里一个或多个进 程处于 暂停 状态时 那么 SIGHUP 和 SIGCONT 被发送至这个孤儿进 程组的所有成员进程 一个孤儿进程组是指在该进程组中没有一个成员进程的 父进程属于和该进程组相同的会话的其它进程组 1 16 我怎样杀死一个进程的所有派生进程 我怎样杀死一个进程的所有派生进程 没有一个完全普遍的方法来做这个 虽然你可以通过处理 ps 的输出确定进 程间的相互关系 但因为它只表示系统的一瞬间的状态 snapshot 所以并不可 靠 但是 如果你启动一个子进程 而它可能生成它自己的子进程 而你意图一次 杀死整个生成的事务 job 解决方法是将最先启动的子进程置于一个新的进程 组 当你需要时杀死整个进程组 建议为创建进程组而使用的函数是 setpgid 在可能情况下 使用这个函数 而不使用 setpgrp 因为后一个在不同系统中有所不同 在一些系统上 setgrp 等同于 setpgid 0 0 在其它系统上 setpgrp 和 setpgid 相同 参见范例章节的事务 控制范例程序 放置一个子进程于其自身的进程组有一些影响 特别的 除非你显式地将该进 程 组放置于前台 它将被认为是一个后台事务并具有以下结果 试图从终端读取的进程将被 SIGTTIN 信号暂停 如果设置终端模式 tostop 那么试图向终端写的进程将被 SIGTTOU 信号暂停 试图改变终端模式也导致这个结果 且不管当 前 tostop 是否设置 子进程将不会收到从终端发出的键盘信号 比如 SIGINT 或 SIGQUIT 在很多应用程序中输入和输出总会被重定向 所以最显著的影响将是丧失键盘 信号 父进程需要安排程序起码捕获 SIGINIT 和 SIGQUIT 可能情况 下 还有 SIGTERM 并在需要情况下清除后台事务 2 一般文件操作一般文件操作 包括管道和套接字包括管道和套接字 请同时参考套接字FAQ 在 http www lcg org sock faq 2 1 如何管理多个连接 如何管理多个连接 我想同时监控一个以上的文件描述符 fd 连接 connection 流 stream 应该怎么办 使用 select 或 poll 函数 注意 select 在BSD中被引入 而poll 是SysV STREAM流控制的产物 因 此 这里就有了平台移植上的考虑 纯粹的BSD系统可能仍然缺少poll 而早一些 的SVR3系统中可能没有select 尽管在SVR4中将其加入 目前两者都是 POSIX 1g标准 译者注 因此在Linux上两者都存在 select 和poll 本质上来讲做的是同一件事 只是完成的方法不一样 两者都 通过检验一组文件描述符来检测是否有特定的时间将在上面发生并在一定的时 间 内等待其发生 重要事项 无论select 还是poll 都不对普通文件起很大效用 它们着重用 于套接口 socket 管道 pipe 伪终端 pty 终端设备 tty 和其他一些字符 设备 但是这些操作都是系统相关 system dependent 的 2 1 1 我如何使用我如何使用 select 函数 函数 select 函数的接口主要是建立在一种叫 fd set 类型的基础上 它 fd set 是一组文件描述符 fd 的集合 由于fd set类型的长度在不同平台上不同 因此 应该用一组标准的宏定义来处理此类变量 fd set set FD ZERO 将set清零 FD SET fd 将fd加入set FD CLR fd 将fd从set中清除 FD ISSET fd 如果fd在set中则真 在过去 一个fd set通常只能包含少于等于32个文件描述符 因为fd set其实只 用了一个int的比特矢量来实现 在大多数情况下 检查fd set能包括任意值的 文件描述符是系统的责任 但确定你的fd set到底能放多少有时你应该检查 修 改宏FD SETSIZE的值 这个值是系统相关的 同时检查你的系统中的select 的man手册 有一些系统对多于1024个文件描述符的支持有问题 译者注 Linux就是这样的系统 你会发现sizeof fd set 的结果是128 8 FD SETSIZE 1024 尽管很少你会遇到这种情况 select的基本接口十分简单 int select int nfds fd set readset fd set writeset fd set exceptset struct timeval timeout 其中 nfds 需要检查的文件描述符个数 数值应该比是三组fd set中最大数 更大 而不是实际文件描述符的总数 readset 用来检查可读性的一组文件描述符 writeset 用来检查可写性的一组文件描述符 exceptset 用来检查意外状态的文件描述符 注 错误并不是意外状态 timeout NULL指针代表无限等待 否则是指向timeval结构的指针 代表最 长等待时间 如果其中tv sec和tv usec都等于0 则文件描述符 的状态不被影响 但函数并不挂起 函数将返回响应操作的对应操作文件描述符的总数 且三组数据均在恰当位置 被 修改 只有响应操作的那一些没有修改 接着应该用FD ISSET宏来查找返回的 文 件描述符组 这里是一个简单的测试单个文件描述符可读性的例子 int isready int fd int rc fd set fds struct timeval tv FD ZERO FD SET fd tv tv sec tv tv usec 0 rc select fd 1 if rc 0 return 1 return FD ISSET fd 当然如果我们把NULL指针作为fd set传入的话 这就表示我们对这种操作的发 生 不感兴趣 但select 还是会等待直到其发生或者超过等待时间 译者注 在Linux中 timeout指的是程序在非sleep状态中度过的时间 而不是 实际上过去的时间 这就会引起和非Linux平台移植上的时间不等问题 移植问 题还包括在System V风格中select 在函数退出前会把timeout设为未定义的 NULL状态 而在BSD中则不是这样 Linux在这点上遵从System V 因此在重 复利 用timeout指针问题上也应该注意 2 1 2 我如何使用我如何使用 poll poll 接受一个指向结构 struct pollfd 列表的指针 其中包括了你想测试的 文件描述符和事件 事件由一个在结构中事件域的比特掩码确定 当前的结构 在 调用后将被填写并在事件发生后返回 在SVR4 可能更早的一些版本 中的 poll h 文件中包含了用于确定事件的一些宏定义 事件的等待时间精确到毫秒 但令人困惑的是等待时间的类型却是int 当等待时间为0时 poll 函数立即 返回 1则使poll 一直挂起直到一个指定事件发生 下面是pollfd的结构 struct pollfd int fd 文件描述符 short events 等待的事件 short revents 实际发生了的事件 于select 十分相似 当返回正值时 代表满足响应事件的文件描述符的个数 如果返回0则代表在规定事件内没有事件发生 如发现返回为负则应该立即查看 errno 因为这代表有错误发生 如果没有事件发生 revents会被清空 所以你不必多此一举 这里是一个例子 检测两个文件描述符 分别为一般数据和高优先数据 如果事件发生 则用相关描述符和优先度调用函数handler 无时间限制等待 直到 错误发生或描述符挂起 include

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论