China Open source community
站内导航:

 
 
 
当前位置: 首页 >> 程序设计 >> Unix编程常见问题解答
 

Unix编程常见问题解答

作者:      来源:     发表时间:2006-05-28     浏览次数:      字号:    


4.1 怎样知道我的系统有多少存储器容量?
=====================================

这是另一个‘经常未回答的问题’。在多数情况下,你不该试图去找到答案.

如果你必需得到答案,问题的答案通常是有的,但非常依赖于不同的操作系统。
例如,在Solaris中,可以用 `sysconf(_SC_PHYS_PAGES)' 和 `sysconf(_SC_PAGESIZE)';
在FreeBSD中,可以用`sysctl()'; 在linux中可以通过读取并处理`/proc/meminfo'得到
(使用该文件时需小心你的程序,它要接受历史上任何不同合法格式). 其它的操作
系统有各自的方式,我也没有意识到更多可移植的方法。

在HP-UX(9版和10版)中,可以使用如下的代码:

struct pst_static pst;

if (pstat_getstatic(&pst, sizeof(pst), (size_t) 1, 0) != -1)
{
printf(" Page Size: %lu
", pst.page_size);
printf("Phys Pages: %lu
", pst.physical_memory);
}

4.2 我怎样检查一个用户的口令?
=============================

4.2.1 我怎样得到一个用户的口令?
-------------------------------

在多数的UNIX系统中, 用户口令通常存放在`/etc/passwd'文件中. 该文件一般是
这种格式:

用户名:口令:用户号:用户组号:注释:用户目录:登录shell
(username:password:uid:gid:gecos field:home directory:login shell)

但是这些标准随着时间而不断改变, 现在的用户信息可能存放在其它机器上, 或
者说并不存放在 `/etc/passwd' 文件中。 当今系统实现也使用 `shadow' 文件保存用
户口令以及一些敏感信息. 该文件只允许有特定权限的用户读取.

为安全考虑,用户口令一般是加密的,而不是用明文表示的。

POSIX 定义了一组访问用户信息的函数. 取得一个用户信息的最快方式是使用`getpwnam()'
和`getpwuid()' 函数. 这两个函数都返回一个结构指针, 该结构包含了用户的所有信
息. `getpwnam()' 接收用户名字符串(username), `getpwuid()' 接收用户号(uid),
(`uid_t'类型在POSIX中有定义). 若调用失败则返回NULL.

但是, 正如前面所讲, 当今的操作系统都有一个shadow文件存放敏感信息,即用户口令。
有些系统当调用者用户号是超级用户时返回用户口令, 其它用户要求你使用其它方式存取
shadow文件. 这时你可以使用`getspnam()', 通过输入用户名得到一个有关用户信息的结
构. 再者, 为了能够成功的完成这些, 你需要有一定的权限. (在一些系统中, 如HP-UX和
SCO, 你可以用`getprpwnam()'代替。)

4.2.2 我怎样通过用户号得到阴影口令文件中的口令?
-----------------------------------------------

我的系统使用一组getsp*函数获得重要用户信息的. 然而, 我没有`getspuid()',
只有`getspnam()'. 我怎样做才能通过用户号获得用户信息呢?

变通方法是相对非常容易的。下面的函数可以直接放入你个人的应用函数库:

#include <stdlib.h>
#include <stdio.h>

#include <pwd.h>
#include <shadow.h>

struct spwd *getspuid(uid_t pw_uid)
{
struct spwd *shadow;
struct passwd *ppasswd;

if( ((ppasswd = getpwuid(pw_uid)) == NULL)
|| ((shadow = getspnam(ppasswd->pw_name)) == NULL))
return NULL;

return shadow;
}

现在的问题是, 有些系统在阴影文件中并不保存用户号(uid)以及其它的信息。

4.2.3 我怎样核对一个用户的口令?
-------------------------------

一个基本的问题是, 存在各种各样的认证系统, 所以口令也就并不总是象它们看上去
那样。 在传统的系统中, 使用UNIX风格的加密算法,加密算法是不同的,有些系统使
用DES(译者注:DES:Data Encryption Standard,为NIST[National Institute of
Standard and Technology]确认的标准加密算法,最新消息表明,NIST将采用一种新
的加密标准Rijndael逐步取代DES)算法,其它的系统, 如FreeBSD国际版使用MD5(译者
注:MD5是当今最为广泛使用的单项散列算法,由Ron Rivest发明,详细资料参见RFC 1321
http://www.faqs.org/rfcs/rfc1321.html)算法。

最常用的方法是使用一种单项加密算法(译者注:即单项散列[Hash]算法)。输入的
明文口令被加密,然后与文件中存放的加密口令比较。怎样加密的详细信息可以
查看`crypt()'的手册页, 这里有一个通常做法的版本:

/* 输入明文口令和加密口令, 检查是否匹配,
* 成功返回 1, 其它情况返回 0。
*/

int check_pass(const char *plainpw, const char *cryptpw)
{
return strcmp(crypt(plainpw,cryptpw), cryptpw) == 0;
}

这个函数之所以能工作是因为加密函数使用的添加(salt)字串存放在加密口令字串的前部。

*警告:* 在一些系统中, 口令加密是使用一种‘crypt()’的变体,即‘bigcrypt()’函数。


5. 编程杂技
***********

5.1 我怎样使用通配字符比较字符串?
==================================

对于它的回答依赖于你所谓‘通配字符’一词的确切含义。

有两种很不相同的概念被认定为‘通配字符’。它们是:

*文件名通配模式*(filename patterns)
这是shell用来进行文件名匹配替换的(expansion)(或称为‘globbing’)

*正则表达式*
这是供编辑器用的,比如‘grep’,等等。它是用来匹配正文,而它们正常
情况下不应用于文件名。

5.1.1 我怎样使用文件名通配模式比较字符串?
------------------------------------------

除非你不走运,你的系统应该有函数‘fnmatch()’供你进行文件名匹配。它一
般只允许Bourne Shell风格的模式。它识别‘*’,‘[...]’和‘?’,但可能不
支持在Korn和Bourne-Again shell程序下才有的更神秘(arcane)的模式。

如果你没有这个函数,那么比闭门造车更好的方法是你可以从BSD或GNU原程
序那里去抄(snarfing)一个过来。

而且,在普通的匹配实际文件名情况下,查阅‘glob()’函数,它将搜索到匹配
一个给定模式的所有存在文件。

5.1.2 我怎样使用正则表达式比较字符串?
--------------------------------------

有很多稍有句法不同的正则表达式;大部分系统起码使用两种:一种是‘ed’
程序可以识别的,有时候被记作‘基本正则表达式’,另一种是‘egrep’程序
可以识别的,记作‘扩充正则表达式’。Perl(译者注:Perl: Practical Extract and
Report Language,实用析取与报表语言)语言拥有它自己稍有不同的风格,Emacs
也是。

为了支持这么多格式,相应的有很多实现。系统一般有正则表达式匹配函数(通
常为‘regcomp()’函数和‘regexec()’函数)提供,但是要小心使用;有些系统
有超过一种实现可用,附之以不同的接口。另外,还有很多可用的软件库的实
现(顺便说一下,一般都是将一个正则表达式编译成内部形式然后再使用,因为
总是假设你有很多字符串要比较同一正则表达式。)

一个可用的软件库是‘rx’软件库,从GNU的镜像站点可以得到。它看来是正在
开发中,基于你不同的观点这是一件又好又不好的事情

5.2 什么是在程序中发送电子邮件的最好方法?
==========================================

有好几种从Unix程序发电子邮件的方法。根据不同情况最好的选择有所不同,
所以我将提供两个方法。还有第三种方法,这里没有说道,是连接本地主机的SMTP
(译者注:SMTP:Simple Mail Transfer Protocol简单邮件传输协议)端口并直接使
用SMTP协议,参见RFC 821(译者注:RFC:Request For Comments)。

5.2.1 简单方法:/bin/mail
-------------------------

对于简单应用,执行‘mail’程序已经是足够了(通常是‘/bin/mail’,但一些系统
上有可能是‘/usr/bin/mail’)。

*警告:*UCB Mail程序的一些版本甚至在非交互模式下也会执行在消息体(message
body)中以‘~!’或‘~|’打头的行所表示的命令。这可能有安全上的风险。

象这样执行:‘mail -s '标题' 收件人地址 ...’,程序将把标准输入作为消息体,
并提供缺省得消息头(其中包括已设定的标题),然后传递整个消息给‘sendmail’
进行投递。

这个范例程序在本地主机上发送一封测试消息给‘root’:

#include <stdio.h>

#define MAILPROG "/bin/mail"

int main()
{
FILE *mail = popen(MAILPROG " -s 'Test Message' root", "w");
if (!mail)
{
perror("popen");
exit(1);
}

fprintf(mail, "This is a test.
");

if (pclose(mail))
{
fprintf(stderr, "mail failed!
");
exit(1);
}
}

如果要发送的正文已经保存在一个文件中,那么可以这样做:

system(MAILPROG " -s 'file contents' root </tmp/filename");

这个方法可以扩展到更复杂的情况,但是得当心很多潜在的危险(pitfalls):

* 如果使用system()或popen(),你必须非常当心将参数括起来从而保护它们不被
错误的进行文件名匹配替换或单词分割。

* 基于用户设置数据来构造命令行是缓冲区越界错误和其它安全漏洞的普遍原
因。

* 这种方法不允许设定CC:(译者注:CC:Carbon Copy 抄送)或 BCC:(译者注:
BCC:Blind Carbon Copy:盲送,指投递地址不在消息中出现的抄送)的收件人。
(一些/bin/mail程序的版本允许,其它则不允许)

5.2.2 直接启动邮件传输代理(译者注:MTA: mail transfer agent):/usr/bin/sendmail
-------------------------------------------------------------------------------

‘mail’程序是“邮件用户代理”(Mail User Agent)的一个例子,它旨在供用户
执行以收发电子邮件,但它并不负责实际的传输。一个用来传输邮件的程序被
称为“邮件传输代理”(MTA),而在Unix系统普遍能找到的邮件传输代理被称为
‘sendmail’。也有其它在使用的邮件传输代理,比如‘MMDF’,但这些程序
通常包括一个程序来模拟‘sendmail’的普通做法。

历史上,‘sendmail’通常在‘/usr/lib’里找到,但现在的趋势是将应用库程序从
‘/usr/lib’挪出,并挪入比如‘/usr/sbin’或‘/usr/libexec’等目录。结果是,一般
总是以绝对路径启动‘sendmail’程序,而路径是由系统决定的。

为了了解‘sendmail’程序怎样工作,通常需要了解一下“信封”(envelope)的概
念。这非常类似书面信件;信封上定义这个消息投递给谁,并注明由谁发出(
为了报告错误的目的)。在信封中包含的是“消息头”和“消息体”,之间由一个
空行隔开。消息头的格式主要在RFC 822中提供;并且参见MIME 的RFC文档。(
译者注:MIME的文档包括:RFC1521,RFC1652)

有两种主要的方法使用‘sendmail’程序以生成一个消息:要么信封的收件人能
被显式的提供,要么‘sendmail’程序可被指示从消息头中推理出它们。两种方
法都有优缺点。

5.2.2.1 显式提供信封内容
.........................

消息的信封内容可在命令行上简单的设定。它的缺点在于邮件地址可能包含的
字符会造成‘system()’和‘popen() ’程序可观的以外出错(grief),比如单引号,
被括起的字符串等等。传递这些指令给shell程序并成功解释可以预见潜在的危
险。(可以将命令中任何一个单引号替换成单引号、反斜杠、单引号、单引号的
顺序组合,然后再将整个地址括上单引号。可怕,呃?)

以上的一些不愉快可以通过避开使用‘system()’或‘popen()’函数并求助于‘
fork()’和‘exec()’函数而避免。这有时不管怎样也是需要的;比如,用户
自定义的对于SIGCHLD信号的处理函数通常会打断‘pclose()’函数从而影响到
或大或小的范围。

这里是一个范例程序:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sysexits.h>
/* #include <paths.h> 如果你有的话 */

#ifndef _PATH_SENDMAIL
#define _PATH_SENDMAIL "/usr/lib/sendmail"
#endif

/* -oi 意味着 "不要视‘ .’为消息的终止符"
* 删除这个选项 ,"--" 如果使用sendmail 8版以前的版本 (并希望没有人
* 曾经使用过一个以减号开头的收件人地址)
* 你也许希望加 -oem (report errors by mail,以邮件方式报告错误)
*/

#define SENDMAIL_OPTS "-oi","--"

/* 下面是一个返回数组中的成员数的宏 */

#define countof(a) ((sizeof(a))/sizeof((a)[0]))

/* 发送由FD所包含以读操作打开的文件之内容至设定的收件人;前提是这
* 个文件中包含RFC822定义的消息头和消息体,收件人列表由NULL指针
* 标志结束;如果发现错误则返回-1,否则返回sendmail的返回值(它使用
* <sysexits.h>中提供的有意义的返回代码)
*/

int send_message(int fd, const char **recipients)
{
static const char *argv_init[] = { _PATH_SENDMAIL, SENDMAIL_OPTS };
const char **argvec = NULL;
int num_recip = 0;
pid_t pid;
int rc;
int status;

/* 计算收件人数目 */

while (recipients[num_recip])
++num_recip;

if (!num_recip)
return 0; /* 视无收件人为成功 */

/* 分配空间给参数矢量 */

argvec = malloc((sizeof char*) * (num_recip+countof(argv_init)+1));
if (!argvec)
return -1;

/* 初始化参数矢量 */

memcpy(argvec, argv_init, sizeof(argv_init));
memcpy(argvec+countof(argv_init),
recipients, num_recip*sizeof(char*));
argvec[num_recip + countof(argv_init)] = NULL;

/* 需要在此增加一些信号阻塞 */

/* 产生子进程 */

switch (pid = fork())
{
case 0: /* 子进程 */

/* 建立管道 */
if (fd != STDIN_FILENO)
dup2(fd, STDIN_FILENO);

/* 其它地方已定义 -- 关闭所有>=参数的文件描述符对应的参数 */
closeall(3);

/* 发送: */
execv(_PATH_SENDMAIL, argvec);
_exit(EX_OSFILE);

default: /* 父进程 */

free(argvec);
rc = waitpid(pid, &status, 0);
if (rc < 0)
return -1;
if (WIFEXITED(status))
return WEXITSTATUS(status);
return -1;

case -1: /* 错误 */
free(argvec);
return -1;
}
}

5.2.2.2 允许sendmail程序推理出收件人
.....................................

‘sendmail’的‘-t’选项指令‘sendmail’程序处理消息的头信息,并使用所有
包含收件人(即:‘To:’,‘Cc:’和‘Bcc:’)的头信息建立收件人列表。它的优
点在于简化了‘sendmail’的命令行,但也使得设置在消息头信息中所列以外的
收件人成为不可能。(这通常不是一个问题)

作为一个范例,以下这个程序将标准输入作为一个文件以MIME附件方式发送给
设定的收件人。为简洁起见略去了一些错误检查。这个程序需要调用‘metamail’
分发程序包的‘mimecode’程序。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
/* #include <paths.h> 如果你有的话 */

#ifndef _PATH_SENDMAIL
#define _PATH_SENDMAIL "/usr/lib/sendmail"
#endif

#define SENDMAIL_OPTS "-oi"
#define countof(a) ((sizeof(a))/sizeof((a)[0]))

char tfilename[L_tmpnam];
char command[128+L_tmpnam];

void cleanup(void)
{
unlink(tfilename);
}

int main(int argc, char **argv)
{
FILE *msg;
int i;

if (argc < 2)
{
fprintf(stderr, "usage: %s recipients...
", argv[0]);
exit(2);
}

if (tmpnam(tfilename) == NULL
|| (msg = fopen(tfilename,"w")) == NULL)
exit(2);

atexit(cleanup);

fclose(msg);
msg = fopen(tfilename,"a");
if (!msg)
exit(2);

/* 建立收件人列表 */

fprintf(msg, "To: %s", argv[1]);
for (i = 2; i < argc; i++)
fprintf(msg, ",
%s", argv[i]);
fputc('
',msg);

/* 标题 */

fprintf(msg, "Subject: file sent by mail
");

/* sendmail程序会自动添加 From:, Date:, Message-ID: 等消息头信息 */

/* MIME的处理 */

fprintf(msg, "MIME-Version: 1.0
");
fprintf(msg, "Content-Type: application/octet-stream
");
fprintf(msg, "Content-Transfer-Encoding: base64
");

/* 消息头结束,加一个空行 */

fputc('
',msg);
fclose(msg);

/* 执行编码程序 */

sprintf(command, "mimencode -b >>%s", tfilename);
if (system(command))
exit(1);

/* 执行信使程序 */

sprintf(command, "%s %s -t <%s",
_PATH_SENDMAIL, SENDMAIL_OPTS, tfilename);
if (system(command))
exit(1);

return 0;
}


6. 工具的使用
*************

6.1 我怎样调试fork函数产生的子进程?
====================================

根据可用的工具有两种不同的方法:

你的调试器(debugger)可能有允许你选择是否跟踪调用‘fork()’以后的父或子进程
的选项,对于某些目的来说那已经足够了。

替换方法是,你的调试器可能有一个选项允许你将它依附(attach)到一个正在执行
的程序。这样你可以依附调试器到一个已经开始执行的子进程。如果你不需要从
子进程一开始就开始测试,这通常已经足够。否则,你会希望在子进程的‘fork()’
调用后插入一个‘sleep()’调用,或者插入如下的循环:

{
volatile int f = 1;
while(f);
}

这样子进程将一直在此循环不往下执行直到你用调试器设定‘f’为0。

并且记住,使用调试器并非是找到你程序中错误的唯一方法;在很多Unix系统
上有一些工具程序可用来跟踪系统调用和信号,而且丰富的日志经常也是有
用的。

6.2 怎样通过其他库文件建立新的库文件?
======================================

前提是我们所说的是归档(archive)(静态)库,最简单的方法是将所有选择的库
文件使用‘ar x’命令在一个空目录里拆分成它们原始的单个目标文件(object),
然后再合并成一个。当然,文件名有可能会重复,但是如果这个库文件很大,
你也许一开始就不想将它们合并在一起。

6.3 怎样创建动态连接库(shared library)/dlls?
=============================================

创建动态连接库(shared libraries)的方法根据不同的系统有所不同。这个过程主要
分两步;第一步要求包括在动态连接库中的目标必须首先是编译好的,通常需
要某个编译选项指示这串代码是位置无关的(position-indepenent);第二步,是将
这些目标连接在一起形成一个库文件。

这里是一个演示以上道理的小程序:

/* shrobj.c 文件 */

const char *myfunc()
{
return "Hello World";
}

/* shrobj.c 结束 */

/* hello.c 文件 */

#include <stdio.h>

extern const char *myfunc();

main()
{
printf("%s
", myfunc());
return 0;
}

/* hello.c 结束 */

$ gcc -fpic -c shrobj.c
$ gcc -shared -o libshared.so shrobj.o
$ gcc hello.c libshared.so
$ ./a.out
Hello World

到目前为止,如果你希望库文件和它的创建过程是都可以移植的话,那么最好
的办法是使用GNU Libtool程序包。它是个小型的工具程序套件,这些工具程序
知道建立动态连接库的平台无关性;你可以只发布你的程序必要的部分,从而
当一个安装者配置你的软件包时,他能决定生成什么库。Libtool程序包在不支持
动态连接库的系统上也能工作得很好。它而且知道与GNU Autoconf程序和GNU
Automake程序挂钩(如果你使用这些工具来管理你程序的编译创建过程)。

如果你不想使用Libtool程序包,那么对于gcc以外的编译器,你需要按照下面所
列修改编译器参数:

AIX 3.2 使用 xlc (未证实)
取消‘-fpic’选项,以‘-bM:SRE -bE:libshared.exp’取代‘-shared’。你并且
需要创建一个名为‘libshared.exp’的文件保存一个所有输出符号(symbols to export)
的列表,比如以上的范例程序,就需要输出‘myfunc’符号。另外,在连接库
时使用‘-e _nostart’参数(在较新的AIX版本上,我相信应该将其变成‘-bnoentry’)。

SCO OpenServer 5 使用 SCO 开发系统(Development System) (未证实)
如果你使用ELF(译者注:ELF:执行与连接格式Executable and Linking Forrmat,
一种Unix可执行目标文件的格式)格式,那么共享库只能在OS5上可用,而它
需要‘-belf’选项。并以‘-Kpic’取代‘-fpic’,在连接时使用‘cc -belf -G’。

Solaris 使用 SparcWorks 编译器
以‘-pic’取代‘-fpic’,并以‘ld -G’取代‘gcc -shared’。

(鼓励大家提供更多的材料丰富上述列表)

其它要当心的问题:

* AIX和(我相信)Digital Unix不需要-fpic选项,因为所有代码都是位置无关的。

* AIX一般需要你创建一个‘输出文件’,即一个保存所有动态连接库中输出
符号的列表。一些连接器(linker)版本(可能只有SLHS连接器,是svld?)有一个
选项可以输出所有符号。

* 如果你对于连接器想使用普遍的‘-l’参数来引用你的动态连接库,你必须
理解你的系统在实际运行时是怎样寻找动态连接库的。最普通的方法是使用
‘LD_LIBRARY_PATH’环境变量,但是通常在连接时有一种其它选项可以
设定。

* 大多数实现方法是在程序内部记录所希望的动态连接库在运行时的位置。这
样把一个动态连接库从一个目录移到另一个目录将导致程序无法工作。许多
系统对于连接器有一个选项用以设定希望运行时动态连接库的位置(比如在
Solaris系统上是‘-R’连接器选项,或者是‘LD_RUN_PATH’环境变量)。

* ELF和a.out的实现方法可能有一个连接器选项‘-Bsymbolic’,它导致在库本
身内部的引用被解释好。否则,在这些系统上,所有符号的解释将推迟到最
后连接时再进行,这样在main程序中的单一函数将重载库中的对应函数。

6.4 我能更改一个动态连接库里的目标吗?
======================================

一般不行。

在大多数系统上(除了AIX),当你连接目标并形成一个动态连接库时,它就象连
接一个可执行程序;目标并不保留它们单一的特征。结果是,一般不能从一个动
态连接库里析取出或更换一个单一的目标。

6.5 我能在一个运行着的程序中生成堆栈映象吗?
============================================

一些系统提供库函数可以提取(unwinding)出堆栈,从而(比如)你可以在一个错误
处理函数中生成一个堆栈映象,但是只有一小部分系统有这些函数。

一个可能的变通方法(workaround)是让你的程序执行一个调试器调试*它自己* -
详细方法仍然根据不同系统稍有不同,但一般的概念是这样:

void dump_stack(void)
{
char s[160];

sprintf(s, "/bin/echo 'where
detach' | dbx -a %d", getpid());
system(s);

return;
}

你需要根据你不同的系统对dbx的参数和命令进行加工,或者甚至换另一个调试
器,比如‘gdb’,但这仍然是我见过的对于这种问题最普遍的解决方法。为此,
荣誉授予Ralph Corderoy。

下面列表包含在一些系统上需要用到的命令行:

大多数使用dbx的系统
`"/bin/echo 'where
detach' | dbx /path/to/program %d"'

AIX
`"/bin/echo 'where
detach' | dbx -a %d"'

IRIX
`"/bin/echo 'where
detach' | dbx -p %d"'
?
范例程序
********

捕获 SIGCHLD 信号
=================

#include <sys/types.h> /* 在任何其它 sys 下的头文件之前引用这个头文件 */
#include <sys/wait.h> /* waitpid()和一些不同的宏所需的头文件 */
#include <signal.h> /* 信号函数的头文件 */
#include <stdio.h> /* fprintf函数的头文件 */
#include <unistd.h> /* fork函数的头文件 */
void sig_chld(int); /* 我们的 SIGCHLD 信号处理函数的原形(prototype) */

int main()
{
struct sigaction act;
pid_t pid;

/* 设定sig_chld函数作为我们SIGCHLD信号的处理函数 */
act.sa_handler = sig_chld;

/* 在这个范例程序里,我们不想阻塞其它信号 */
sigemptyset(&act.sa_mask);

/*
* 我们只关心被终止的子进程,而不是被中断
* 的子进程 (比如用户在终端上按Control-Z)
*/
act.sa_flags = SA_NOCLDSTOP;

/*
* 使这些设定的值生效. 如果我们是写一个真实的应用程序,
* 我们也许应该保存这些原有值,而不是传递一个NULL。
*/
if (sigaction(SIGCHLD, &act, NULL) < 0)
{
fprintf(stderr, "sigaction failed
");
return 1;
}

/* fork */
switch (pid = fork())
{
case -1:
fprintf(stderr, "fork failed
");
return 1;

case 0: /* 是子进程,直接结束 */
_exit(7); /* 退出状态 = 7 */

default: /* 父进程 */
sleep(10); /* 给子进程完成的时间 */
}

return 0;
}

/*
* 信号处理函数 -- 只有当接收到一个SIGCHLD信号才被调用,
* 即有一个子进程终止
*/
void sig_chld(int signo)
{
int status, child_val;

/* 非阻塞地等待任何子进程结束 */
if (waitpid(-1, &status, WNOHANG) < 0)
{
/*
* 不建议在信号处理函数中调用标准输入/输出函数,
* 但在一个类似这个的玩具程序里或许没问题
*/
fprintf(stderr, "waitpid failed
");
return;
}

/*
* 我们现在有保存在‘status’变量中的子进程退出信息并可以使用
* wait.h中定义的宏对其进行操作
*/
if (WIFEXITED(status)) /* 子进程是正常退出吗? */
{
child_val = WEXITSTATUS(status); /* 获取子进程的退出状态 */
printf("child's exited normally with status %d
", child_val);
}
}

读取进程表 - SUNOS 4 版
=======================

#define _KMEMUSER
#include <sys/proc.h>
#include <kvm.h>
#include <fcntl.h>

char regexpstr[256];
#define INIT register char *sp=regexpstr;
#define GETC() (*sp++)
#define PEEKC() (*sp)
#define UNGETC(c) (--sp)
#define RETURN(pointer) return(pointer);
#define ERROR(val)
#include <regexp.h>

pid_t
getpidbyname(char *name,pid_t skipit)
{
kvm_t *kd;
char **arg;
int error;
char *p_name=NULL;
char expbuf[256];
char **freeme;
int curpid;
struct user * cur_user;
struct user myuser;
struct proc * cur_proc;


if((kd=kvm_open(NULL,NULL,NULL,O_RDONLY,NULL))==NULL){
return(-1);
}
sprintf(regexpstr,"^.*/%s$",name);
compile(NULL,expbuf,expbuf+256,'

[1] [2] [3]

编辑 webmaster

 
 
 
评论
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •  
    中国源码网 - WWW.YUANMA.ORG - 中国开放源代码社区