320
C/C++编编编编编编 1.C/C++编编 1.1: 编编编编编编编编 A 论论论论论论论论论论论论论 论论论论论论 ,, 论论论 GOOGLE 论论 论论论论论论论 ,。 论论论论论 GOOGLE论论论论论论论论论论论论 ,, 论论 论论论论论论论论论论论论论论论论 ,。,。 论论论论论论论论论 CODE 论论 论论论论论论论论论 ,,。 编编编编编编编 ,,,。一,。 论论论论论论论论论论论论论论论论论论论论论论 ,一,,,。 1.2STL string 编编编编编编编 A 论论 STL 论论论论 transform 论论 编编: string str22 = "This IS a MiXed CaSE stRINg"; transform (str22.begin(),str22.end(), str22.begin(), tolower);

C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

  • Upload
    others

  • View
    91

  • Download
    0

Embed Size (px)

Citation preview

Page 1: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

C/C++编程答疑解惑1.C/C++编程1.1: 遇到问题怎么解决

A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE 查找,并看看精华区。 如果已找了 GOOGLE 并看了精华区,但是对某些方面不理解,那么就在论坛上发问。发问时标题要清楚,并把你的问题描述清楚。许多人不会看你帖子的内容,所以在标题中写清楚问题也是很重要的。

如果要粘贴代码,那么使用发言功能中的 CODE 功能,这样可以保证代码不会乱,能有更多人看懂你代码。

当别人准确回答了你的问题时,应该说声谢谢,表示这个问题已回答,并表示对帮助你的人的感谢。这样会让更多的人觉得你的一个好学有礼貌的人,从而会更注意你的问题。

如果你按上面这些方法做了,并不能保证你的发言一定能回答,因为不是每个人什么问题都知道,但可以保证会有更多人关注你的问题,回答的可能性会更高。

1.2:STL string 怎么转换大小写

A:使用 STL 算法中的 transform 函数 代码:

Page 2: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   string str22 = "This IS a MiXed CaSE stRINg";    transform (str22.begin(),str22.end(), str22.begin(),

tolower);

1.3: 怎样生成动态库

A:创建步骤:

首先创建 object 文件,这个文件将加入通过 gcc –fPIC 参数命令加入到共享函数库里面。PIC 的意思是“位置无关代码”(Position Independent

Code)。下面是一个标准的格式: 代码:

gcc -shared -Wl,-soname,your_soname -o library_name file_list library_list

下面再给一个例子,它创建两个 object 文件(a.o 和 b.o),然后创建一个包含 a.o 和 b.o 的共享函数库。例子中”-g”和“-Wall”参数不是必须的。

代码:

gcc -fPIC -g -c -Wall a.c gcc -fPIC -g -c -Wall b.c gcc -shared -Wl,-soname,liblusterstuff.so.1 -o

liblusterstuff.so.1.0.1 a.o b.o -lc

下面是一些需要注意的地方:

Page 3: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

· 不用使用-fomit-frame-pointer 这个编译参数除非你不得不这样。虽然使用了这个参数获得的函数库仍然可以使用,但是这使得调试程序几乎没有用,无法跟踪调试。

· 使用-fPIC 来产生代码。 使用 shared 说明生成动态库,使用 soname 说明生成的库名

· 某些情况下,使用 gcc 来生成 object 文件,需要使用“-Wl,-export-

dynamic”这个选项参数。通常,动态函数库的符号表里面包含了这些动态的对象的符号。这个选项在创建 ELF 格式的文件时候,会将所有的符号加入到动态符号表中。可以参考 ld 的帮助获得更详细的说明。

star3s 补充:

对于 C++程序要使用 EXTERN “C”说明输出接口 代码:

   extern "C"    {      int soTest(int a,int b) ;    }    int soTest(int a,int b)    {      return a+b;    }

Page 4: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

1.4:怎样指定程序链接某个库的动态库或静态库版本

A:使用 Bdynamic 和-Bstatic 选项。默认情况下,假如共享库可用,连接器会使用共享库。但是-Bdynamic 和-Bstatic 提供了很好控制库的方法。它们可以决定用共享库还是用静态库。

传-Bdynamic 和-Bstatic 选项给连接器,如下操作: # gcc -o main main.o -Wl,-Bstatic \ -lfoo -Wl,-Bdynamic -lbar # gcc -o main main.o -Wl,-Bstatic 告诉连接器所有的库(象 libc 等等)都使用静态的版本。

1.5: 请问各 UNIX 平台下和编译器名

kaisakaisa : sun CC (c++) cc(c) alpha cxx(c++) cc(c) HP aCC(c++) IBM xlC (c++)

1.6: solaris 下有没有 C 函数可以获得进程的相关信息 比如:进程名、进程 pid、进程所占 CPU/Memory、开始时间、运行状态等

等。谢谢。 liupch:2003-03-19 10:03 读取/proc/进程号/psinfo 这个文件。 就用我告诉你的那个函数 ioctl(fd, PIOCPSINFO, &procinfo); 在看一下 procinfo 这个结构就知道了。

Page 5: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

1.7: c++下使用<iostream>的问题

在 c++下使用 include <iostream>后为什么编译器会报错呢 1.检查你是不是使用 g++编译器编译。gcc 编 c++程序会有问题 2.检查在 include 头文件后有没有使用 using namespace std;

新的标准 C++加了几个关键字,其中最常用的就是 namespace(名字空间)

加入这个关键字是为了避免在大程序中符号名重定义问题 同时原来的标准 c++类都包含在 std 名字空间中(如果使用 c++ 的标准

函数库,如<cstdio> 那么它们也在 std 名字空间中)

新的标准 c++头文件没有 .h,所以当你们 include 它们时要注意使用 std

名字空间.

以下一个例子 代码:

#nclude <iostream> #include <string> using namespace std;  //在 include 所有标准 c++头文件后 main() {     cout<<"hello world"<<endl; }

1.8 :volatile 的作用是什么

volatile 的本意是“易变的”

Page 6: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

由于访问寄存器的速度要快过 RAM,所以编译器一般都会作减少存取外部RAM 的优化。

一般说来,volatile 用在如下的几个地方: 1、中断服务程序中修改的供其它程序检测的变量需要加 volatile; 2、多任务环境下各任务间共享的标志应该加 volatile; 3、存储器映射的硬件寄存器通常也要加 volatile 说明,因为每次对它的读

写都可能由不同意义; 另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个

标志读了一半被打断了重写),在 1 中可以通过关中断来实现,2 中可以禁止任务调度,3 中则只能依靠硬件的良好设计了。 简单点: 就是该变量会以编译器无法预知的方式发生变化,请编译器不要做优化

(所有的编译器的优化均假设编译器知道变量的变化规律)

1.9: 请问,用 gcc 或 ld 连接程序时,如何设置段的属性?

幽灵兵:

为了实现共享数据,这个方法要比其它 进程/线程 的数据同步方法更 快,更方便,更灵活,而且是所有方法中最简单的。 比如我在 Win 下这样做: 1. 创建个 DLL 专门用于数据交换 2. 在 DLL 中声明一个全局变量 Var1

Page 7: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

3. 在连接时声明 .bss 段为共享段 ( Vc Linker: /section:.bss,S | Gcc: [type] Var1 __attrbiute__

((section("shared"), shared)); ) 4. 在需要这个变量的进程中调用这个 DLL ... ... 其实就是将这个 DLL 作为一种数据池... 难道你们没用过吗? 我想将 .bss 段设置为 共享,gcc 在 NT 下可以用: int Var __attrbiute__ ((section("shared"), shared)); 在 Linux/UNIX 下怎么办呢? JohnBull :2003-04-27 Linux系统并不强调“段”的使用,因为 Linux 的设计者考虑到这个 OS

将移植到包括RISC 等多种平台上,而事实上只有 Intel 的 CPU才强调“段”的概念,其他(包括大多数 64 位)系统都是基于“页”进行设计的。

在 Linux 中,请用共享内存。

2.数据库编程2.1:如何在 pro*c 中调用存储过程

ilmare : 在嵌入式 SQL 中使用 CALL 语句调用存储过程的格式如下:

代码:

EXEC SQL CALL [schema.] [package.] stored_proc[@db_link] (arg1, ...)

[INTO :ret_var [[INDICATOR] :ret_ind]];

例子:

Page 8: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

创建一个 PL/SQL 函数 fact,该函数存储于 mathpkg 包中 代码:

EXEC SQL CREATE OR REPLACE PACKAGE BODY mathpkg as

function fact(n IN INTEGER) RETURN INTEGER AS BEGIN IF (n <= 0) then return 1; ELSE return n * fact(n - 1); END IF; END fact; END mathpkg; END-EXEC.

使用该函数 ... int num, fact; ... EXEC SQL CALL mathpkg.fact(:num) INTO :fact; ...

2.2: 请问在 pro*c 中知道错误代码,怎么查这个代码的错误信息?

mengwg : 在 sql*plus 中使用>oerr ora 1013 CHUJUN_98: sqlca 中含有中文含义 sqlca.sqlerrm.sqlerrmc 代表的是 sqlCZHI 行的

结果描述

Page 9: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

2.3: AIX 下编译的嵌入式 DB2 C 程序,删除数据库后再创建时连接不成功

预编译:embprep proc 得到 proc.bnd 和 proc.c 文件,编译没有错误发生。

编译:xlc -o proc proc.c -ldb2 编译正确,生成执行文件 proc。 执行 proc 一切正常。 因为涉及到可能的移植问题,于是测试将数据库 Emp Drop掉(db2 drop

database Emp),再重新建原来相同的数据库 Emp 和表 info,建成后执行proc,程序可正常连接数据库,可在对表 info 操作时(如 select)返回SQLCODE 错误=-805,也就是说无法对表进行操作。重新编译该程序后,运行正常!

AIX 下的 DB2 数据库不太好用,它在编译时会产生一个 PKG存放于数据库中,

并且会对它打上时间戳。如果从新建库获建表的话都会破坏时间戳,必须从新编译或 rebind,以产生新的 PKG。

hasjing:2003-03-17 12:55 AIX 下的 DB2 数据库不太好用,它在编译时会产生一个 PKG存放于数据

库中, 并且会对它打上时间戳。如果从新建库获建表的话都会破坏时间戳,必须从

新编译或 rebind,以产生新的 PKG。 时间戳的目的是为了保证 db2 数据库优化策略的一致性,而且 PKG 的内

部名称

Page 10: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

是 SQC 文件的前八个字母,所以在开发时要当心每个模块的名字前八个字母不能相同!否则会产生 PKG覆盖,会产生 818 的错误代码!

所以 IBM 的东西虽好,但是规矩太多! 在 db2环境下 ? SQL0805 可以查看错误代码!

2.4: 请问 SOLARIS 下使用 OCI 编程序要连什么库

在 Solaris8 下安装了 oracle8.1.6,并编译用 OCI 编写的程序,其中使用了很多的 oci 函数,包括Direct Path API,但联接时,提示OCIDirPathColArrayReset'等符号找不到

编译时已加入了-L/export/home/opt/oracle/8.1.6/lib -lclntsh

如果将 Direct Path 的调用去掉,则连接成功,请各位高手不吝赐教 wangz :2003-03-20

加上-lclient8 就可以了!!试试看,我这里可以

2.5:请问怎样用 ct-librery 编程

有哪位熟悉 ct-library 编程,我在写有关 socket 通讯的程序,需要和sybase 数据库交互信息。希望和高手切磋一下!!!

minsanyuan :2003-04-21 22:55 调 CT-Library 就可以了,那来的 socket呀, 如果用 socket 就不要用 ct-Library 了, 如果你很牛,可以试试两都都用

Page 11: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

1. 初始化 ct-library ct_init( ....) 2. 分配连接结构 ct_con_alloc(...) 3. 设置用户名及口令 ct_con_props(...) 4. 建立连接 ct_connect(..) 5. 断开连接 ct_exit( ... ) 6. 释放 ct_ctx_drop(...) /Sybase/sample 下有例子

3 GTK+ 编程

以下由付强提供,QQ:775341 ,感谢他把自己的学习成果写出来与大家分享 原作者声明如下 > 这些文章其实是我的学习笔记,以 FAQ 方式代问自答,希 > 望能帮初学者解决一点实际问题。 > 您可以随意修改或转载本文,但请保证内容的正确性,以 > 免误人子弟 --- 谢谢 :)

Page 12: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

3.1 问:论坛中有人说 GTK+ 并不支持中文,是这样吗?

----------------------------------------------------------- 答: GTK+ 采用 UTF-8 编码就是为了支持多字节文字,所以 GTK+

肯定支持中文,而且非常出色,你可以看到网上使用 GTK+ 编写 的中文软件数不胜数,所以没必要理会那种说法。 ( 我真的看过这类的帖子,误人子弟...呵呵... ) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>

3.2 问:为什么在控件上的汉字无法显示,并伴有如下错误警告:

"** (:1864): WARNING **: Invalid UTF8 string passed to pango_layout_set_text()" ----------------------------------------------------------- 答: GTK+ 中的字符串均采用 UTF-8 格式编码,这个提示就是告 诉您,程序中的字符串编码格式不是 UTF-8,通常这是由编辑器 造成的,例如在我的 VC7 中,默认的存档编码格式为 GB2312。 但是,除了将文件以 UTF-8 编码格式存档外,你还可以使用 glib 的转换函数在程序的运行过程中将字符串转换为 UTF-8。 例如: 将 button = gtk_button_new_with_label( "确定" );

Page 13: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

改成 button = gtk_button_new_with_label( g_locale_to_utf8("确定",4,NULL,NULL,NULL)); >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>

3.3 问:为什么在屏幕上输出的汉字变成了乱码?

----------------------------------------------------------- 答: 既然可以显示乱码,就表明与编码格式无关。 您可能已经想到了 --- 对,问题就出在字体上。 一般情况下,系统会用"Sans"作为默认字体,但这种字体中 只包含了 ASCII 码,所以汉字是无法显示的。 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>

3.4 问 : 在 修 改 控 件 字 体 的 第 一 步 就 发 生 了 错 误 ,gdk_font_load()

总是失败,为什么?

----------------------------------------------------------- 答: 首先向您提供一些建议: 1.不要用 GDK 的一些函数更改字体,因为那是 GTK+的不稳定 因素之一(至少在 2.0 以下版本中是这样的)。 2.为了获得更多的字体,应该避免使用 GdkFont类型,及相关

Page 14: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

函数,取而代之的是 Pango 库,这是 GTK+ 的一部分。 3.GdkFont 只能使用 XFont 字体,而且使用方法相对复杂, PangoFontDescription 可以使用 XFont,以及一些本地字体, 包括 Win Font, XRender Font, TrueType Font,所以您没有必 要再留恋 GdkFont 了。 4.您不需要修改所有控件,更简便的方法是在主窗口创建后立 即修改它的字体,将来创建的控件会继承它的这项属性。 例如:

代码:

window = gtk_window_new( GTK_WINDOW_TOPLEVEL ); { GtkStyle *style = gtk_rc_get_style( window ); pango_font_description_set_family( style->font_desc, "Stsong" ); gtk_widget_set_style( window, style ); }

( 这三句代码是将程序的字体设置为 "华文宋体" ) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>

3.5 问:我不想使用宋体,怎样获得其它字体的代号?

----------------------------------------------------------- 答: 部分字体的代号和它的文件名是相同的,例如黑体的代码为 "Simhei",华文宋体为"Stsong"。

Page 15: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

您还可以通过一些 Linux/Unix 下的编辑软件的字体选择框 来查找字体对应的代码。 但标准的方法是调用 Pango 库中的相应函数来查找机器上已 安装的字体。 如果想了解更多关于 Pango 的高级使用方法,请参考它自带 的 API 开发手册。 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>

3.6 问:你为什么没有说到 gettext() 及相关国际化标准? 答: 目前国内多数程序员采用这个办法汉化程序,所以我没有必 要再多说。但它不一定对每个人都合适,我通常采用自己管理语 言模块的方法实现国际化,这在很多情况下要比 gettext()更方 便。

4. 系统编程

4.1 我建立共享内存之后,忘记了删除掉。请问:1)如果我不管它,它是由系统自己释放掉么

一颗流星:

Page 16: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

我建立共享内存之后,忘记了删除掉。请问:1)如果我不管它,它是由系统自己释放掉么?那么系统管理共享内存的机制是怎样的? 2)能否手动释放?如果能,用什么语句呢?

gadfly :2003-04-11 不会自动释放,可以用 ipcrm删掉 蓝色键盘:2003-04-18

用 shmctl或者 ipcrm删除后,只有如下情况出现才会被彻底清除。 ****直到最后一个连接进程释放它*****

或者 ****没有一个进程在 attach 它******

4.2 请问 IPCS 显示的各权限位意义

一颗流星:

-C---------表示具有什么存取权? 蓝色键盘:2003-04-18

ipcs显示的结果中 标志位是共有 11 位。其中前两位表示如下: R 表示进程等待msgrcv

S 表示进程等待msgsnd

D 表明该段 IPC资源已经被删除,直到最后一个连接进程释放它(请注意这一点,很多情况下往往无法用 ipcrm正常删除 IPC资源,原因就在于此)

C 在进程 attach 时,共享内存段已经被清除

Page 17: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

- 相应的权限没有被设置 其它的 9 位表明该段 IPC资源的权限信息。这个大家都明白,但是注意 - 表明指定权限没有被设置。

4.3 如何在 unix 下实现 kbhit 的功能?谢谢!!

clilye:如何在 unix 下实现 kbhit 的功能?谢谢!! fieryfox: 2003-04-29 下面的文字摘自Unix Programming FAQ: 3.2 How can I read single characters from the terminal? ======================================

================= How can I read single characters from the terminal? My program

is always waiting for the user to press `<RETURN>'. Terminals are usually in canonical mode, where input is read in

lines after it is edited. You may set this into non-canonical mode, where

you set how many characters should be read before input is given to your

program. You also may set the timer in non-canonical mode terminals to 0,

this timer flushs your buffer at set intervals. By doing this, you can use

`getc()' to grab the key pressed immediately by the user. We use

`tcgetattr()' and `tcsetattr()' both of which are defined by POSIX to manipulate

the `termios' structure. #include <stdlib.h> #include <stdio.h> #include <termios.h> #include <string.h> static struct termios stored; void set_keypress(void) { struct termios new;

Page 18: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

tcgetattr(0,&stored); memcpy(&new,&stored,sizeof(struct termios)); /* Disable canonical mode, and set buffer size to 1 byte */ new.c_lflag &= (~ICANON); new.c_cc[VTIME] = 0; new.c_cc[VMIN] = 1; tcsetattr(0,TCSANOW,&new); return; } void reset_keypress(void) { tcsetattr(0,TCSANOW,&stored); return; } 3.3 How can I check and see if a key was pressed? ======================================

=========== How can I check and see if a key was pressed? On DOS I use the `kbhit()' function, but there doesn't seem to be an equivalent? If you set the terminal to single-character mode (see previous

answer), then (on most systems) you can use `select()' or `poll()' to test

for readability.

5. Solaris 内核编程相关问题

5.1 Solaris 内核模块中如何 getcwd

Q: 在 Solaris 7 64-bit 内核模块中如何获知一个进程的当前工作目录(cwd),getcwd

并不是一个系统调用 A: Rich Teer <[email protected]> 最好通过 u->u_cdir 获取当前工作目录(cwd)的 vnode(v节点)。但这依赖

于内核当前上

Page 19: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

下文,curproc 可能并不对应你期望的进程。 代码:

usr/include/sys/user.h typedef struct user { ... ... /* * protected by p_lock */ struct vnode * u_cdir; /* current directory */ struct vnode * u_rdir; /* root directory */

5.2 如何避免一个套接字进入 TIME_WAIT 状态

Q: 我正在写一个 unix server 程序,不是 daemon,经常需要在命令行上重启它,绝大

多数时候工作正常,但是某些时候会报告"bind: address in use",于是重启失 败。 A: Andrew Gierth <[email protected]> server 程序总是应该在调用 bind()之前设置 SO_REUSEADDR套接字选

项。至于 TIME_WAIT 状态,你无法避免,那是 TCP协议的一部分。 Q: 如何避免等待 60秒之后才能重启服务 A: Erik Max Francis <[email protected]> 使用 setsockopt,比如

Page 20: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

代码:

--------------------------------------------------------------------------  int option = 1;  if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR,

&option,                    sizeof( option ) ) < 0 )  {      die( "setsockopt" );  }  -------------------------------------------------------------------------- 

5.3 编 写 TCP/SOCK_STREAM 服 务 程 序 时 ,SO_REUSEADDR 到底什么意思?

A: 这个套接字选项通知内核,如果端口忙,但 TCP 状态位于 TIME_WAIT

,可以重用

端口。如果端口忙,而 TCP 状态位于其他状态,重用端口时依旧得到一个错误信息,

指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧

使用同一端口,此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期

望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不

Page 21: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

可能。

一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端

口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组

还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使

用 SO_REUSEADDR 选项。

5.4 在客户机/服务器编程中(TCP/SOCK_STREAM),如何理解 TCP 自动机 TIME_WAIT 状 态?

A: W. Richard Stevens <1999年逝世,享年 49岁>

下面我来解释一下 TIME_WAIT 状态,这些在<<Unix Network Programming Vol I>>

中 2.6节解释很清楚了。

MSL(最大分段生存期)指明 TCP 报文在 Internet 上最长生存时间,每个具体的 TCP实现

都必须选择一个确定的 MSL值。RFC 1122 建议是 2分钟,但 BSD 传统实现采用了 30秒。

Page 22: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

TIME_WAIT 状态最大保持时间是 2 * MSL,也就是 1-4分钟。

IP 头部有一个 TTL,最大值 255。尽管 TTL 的单位不是秒(根本和时间无关),我们仍需

假设,TTL 为 255 的 TCP 报文在 Internet 上生存时间不能超过 MSL。

TCP 报文在传送过程中可能因为路由故障被迫缓冲延迟、选择非最优路径等等,结果

发送方 TCP机制开始超时重传。前一个 TCP 报文可以称为"漫游 TCP 重复报文",后一个

TCP 报文可以称为"超时重传 TCP 重复报文",作为面向连接的可靠协议,TCP实现必须

正确处理这种重复报文,因为二者可能最终都到达。

一个通常的 TCP 连接终止可以用图描述如下:

代码:

client                     server             FIN M  close  ----------------->  (被动关闭)             ACK M+1         <-----------------             FIN N         <-----------------  close             ACK N+1 

Page 23: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

       -----------------> 

为什么需要 TIME_WAIT 状态?

假设最终的 ACK丢失,server 将重发 FIN,client 必须维护 TCP 状态信息以便可以重发

最终的 ACK,否则会发送RST,结果 server 认为发生错误。TCP实现必须可靠地终止连

接的两个方向(全双工关闭),client 必须进入 TIME_WAIT 状态,因为client 可能面

临重发最终ACK 的情形。

代码:

{  scz 2001-08-31 13:28  先调用 close()的一方会进入 TIME_WAIT 状态 } 

此外,考虑一种情况,TCP实现可能面临先后两个同样的相关五元组。如果前一个连

接处在 TIME_WAIT 状态,而允许另一个拥有相同相关五元组的连接出现,可能处理

Page 24: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

TCP 报文时,两个连接互相干扰。使用 SO_REUSEADDR 选项就需要考虑这种情况。

为什么 TIME_WAIT 状态需要保持 2MSL 这么长的时间?

如果 TIME_WAIT 状态保持时间不足够长(比如小于 2MSL),第一个连接就正常终止了。

第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二

个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让 TIME_WAIT

状态保持时间足够长(2MSL),连接相应方向上的 TCP 报文要么完全响应完毕,要么被

丢弃。建立第二个连接的时候,不会混淆。

A: 小四 <[email protected]>

在 Solaris 7 下有内核参数对应 TIME_WAIT 状态保持时间 # ndd -get /dev/tcp tcp_time_wait_interval

240000

# ndd -set /dev/tcp tcp_time_wait_interval 1000

Page 25: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

缺省设置是 240000ms,也就是 4分钟。如果用 ndd修改这个值,最小只能设置到 1000ms,

也就是 1秒。显然内核做了限制,需要 Kernel Hacking。 # echo "tcp_param_arr/W 0t0" | adb -kw /dev/ksyms /dev/mem

physmem 3b72

tcp_param_arr: 0x3e8 = 0x0

# ndd -set /dev/tcp tcp_time_wait_interval 0

我不知道这样做有什么灾难性后果,参看<<Unix 编程/应用问答中文版>

>的声明。

5.5TIME_WAIT 状态保持时间为 0 会有什么灾难性后果?在普遍的现实应用中,好象也 就是服务器不稳定点,不见得有什么灾难性后果吧?

D: [email protected]

Linux 内核源码 /usr/src/linux/include/net/tcp.h 中 #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to

successfully

* close the socket, about 60 seconds */

最好不要改为 0,改成 1。端口分配是从上一次分配的端口号+1 开始分配的,所以一般

不会有什么问题。端口分配算法在 tcp_ipv4.c 中 tcp_v4_get_port 中。

Page 26: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

5.6 结构在优化编译中的对齐问题

Q: 我正在写一个流模块,其中用到了#pragma pack(),当使用 gcc -D_KERNEL -c abc.c

ld -r -o abc abc.o

编译链接时,一切正常。为了获得 64-bit模块,我必须使用 Sun

Workshop 5.0,

结果导致系统崩溃。访问 http://docs.sun.com/htmlcoll/coll.32.8/iso-8859-1/CPPPG/

Pragmas.html#15434

上面说必须在编译链接应用程序的时候指定"-misalign",所以我用了如下命令编译

/opt/SUNWspro/bin/cc -D_KERNEL -misalign -c abc.c

/usr/ccs/bin/ld -r -o abc abc.o

但是我不知道该如何在链接时指定"-misalign"。使用的是"/usr/ccs/bin/

ld"。 A: Casper H.S. Dik - Network Security Engineer

<[email protected]>

"-misalign"仅仅用于应用程序,无法应用到内核编程中。"-misalign"使得编译

Page 27: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

获得的代码增加了一些 runtime glue,它们将指示内核模拟 unaligned

load(慢)。

作为内核编程,没有等效技术。

Q: 使用#pragma pack()是因为需要读取来自Windows客户端的报文,对端使用

#pragma pack(1)压缩了所使用的数据结构

代码:

   #pragma pack(1)     typedef struct pkt_hdr_struct     {         uint8_t  pkt_ver;         uint32_t pkt_type;         uint32_t pkt_len;     } pkt_hdr_t;     #pragma pack() 

为了采用这个结构读取网络数据,Solaris端的服务程序需要强制转换匹配该结构,

但是一旦企图读取紧接在 pkt_ver 成员之后的 pkt_type 成员,崩溃了。尝试过其他

办法,首先用一个字符指针读取第一个字节,然后指针增一,把该指针强制类型

Page 28: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

转换成( uint32_t * ),然后读取数据,依然崩溃。

此外,是否意味着无法在内核模块编程中使用#pragma pack()

A: Ed L Cashin <[email protected]>

我想你可以单独写一个 pkt_header_read()函数,单字节读取然后拼装成相应的数

据类型。如果你想避免函数调用,可以使用"inline"关键字。 A: Casper H.S. Dik - Network Security Engineer

<[email protected]>

你是否意识到 pkt_hdr_t 结构使得你必须自己转换字节序(对端是 x86平台)

我不认为#pragma pack()是最好的解决办法,考虑定义如下结构

代码:

  struct phs     {         char ver;         char type[4];         char len[4];     } 

采用 memcpy()读取数据 memcpy( &phs.type[0], &pkt.pkt_type, 4 );

A: Andrew Gabriel <[email protected]>

Page 29: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

采用字符指针是正确的,但是你犯了个错误,编写如下函数

代码:

int read_misaligned_int ( int * iptr )     {         int    i;         int    value;         char * ptr  = ( char * )iptr;         char * vptr = ( char * )&         for ( i = 0; i < sizeof( int ); i++ )         {             *vptr++ = *ptr++;         }         return( value );     } 

此外,既然你提到对端是 x86平台,可能还需要考虑字节序转换的问题 A: W. Richard Stevens <1999年逝世,享年 49岁>

代码:

/*  * return value:  *     1 big-endian  *     2 little-endian  *     3 unknow  *     4 sizeof( short ) != 2  */  static int byte_order ( void )  {      union      {          short s;          char  c[ sizeof( short ) ];      } un;      un.s = 0x0201; 

Page 30: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

    if ( 2 == sizeof( short ) )      {          if ( ( 2 == un.c[0] ) && ( 1 == un.c[1] ) )          {              puts( "big-endian" );              return( 1 );          }          else if ( ( 1 == un.c[0] ) && ( 2 == un.c[1] ) )          {              puts( "little-endian" );              return( 2 );          }          else          {              puts( "unknow" );              return( 3 );          }      }      else      {          puts( "sizeof( short ) = %d", sizeof( short ) );          return( 4 );      }      return( 3 );  }  /* end of byte_order */ 

D: CERNET 华中地区网络中心 程序设计版 集体讨论汇总 为了解决 Unix自定义结构在 GCC优化编译中对齐问题,一般解决办法是

用如下宏封装 自定义结构

代码:

#pragma pack(1) struct my_arphdr { }; #pragma pack()

Page 31: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

如果是 SPARC/Solaris,还可以这样 代码:

struct my_arphdr { } __attribute__ ((packed));

两种办法其实都可以用在 Unix系统/GCC 编译器中。 D: mbuf@smth 关于结构中字节对齐问题,相应编译器选项为 GCC/G++ : -fpack-struct Sun Workshop cc/CC: -misalign 最好不这样做,会大大降低程序效率,特别在某些架构中。应该尝试用位操

作来处理。 D: Unknown@smth GCC 可以这么解决

代码:

#ifdef __GCC__ #define PACKED __attribute__((__packed__)) #else #define PACKED #endif struct msg {    u_int16_t PACKED first;    ... };

还是 VC 简单,#include <pshpack1.h> 就搞定了 A: gfh_nuaa

Page 32: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

DEC : #pragma pack(1) SUN : #pragma pack(1) AIX : 编译时 -q align=packed HP-UX : #pragma pack 1 D: Joe Durusau 在 Visual C++ 中,使用 "-ZP1" 就可以让编译器对自定义结构进行单字

节对齐,实 际就是取消了对齐优化。 A: [email protected] 2001-12-20 13:09 1) 结构内部成员的 pack

代码:

struct foo  {      char a;      int  b __attribute__ ((packed));  }; 

2) 整个结构的 pack

代码:

struct foo  {      char a;      int  b;  }__attribute__ ((packed)); 

3) 文件范围的 pack

Page 33: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

代码:

struct foo  {      char a;      int  b;  };  ... ...

4) 编译选项的 pack

-fpack-struct

但这是最危险的做法,因为这样做可能会使库函数和你的程序对结构内成员的偏移理

解不一致。

Q: 小四 <[email protected]>

#pragma pack(push)

#pragma pack(n)

... ...

#pragma pack(pop)

push/pop 这个用法都谁支持啊 A: [email protected]

这个写法我没见过,VC 和 GCC 都是这样写的

#pragma (push, N) // 把原来 align设置压栈,并设新的 pack 为 N

Page 34: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

#pragma (pop) // align设置弹栈

5.7 如何得到非局部变量列表

Q: 什么工具可以从目标文件中提取非局部变量列表 A: Donald McLachlan <[email protected]>

最简单的就是 nm,假设你有一个目标文件(或者已链接过的可执行文件),nm -g 将显

示所有"全局"变量。下面是一个 Solaris 的例子:

代码:

--------------------------------------------------------------------------  /* gcc -o junk junk.c */  int        var1;  static int var2;  int main ( void )  {      int var3;      return( 0 );  }  /* end of main */  --------------------------------------------------------------------------  $ nm -g junk  junk:  [Index]   Value      Size    Type  Bind  Other Shndx   Name  [66]    |    133640|       0|OBJT |GLOB |0    |15     |_DYNAMIC  [61]    |    133496|       0|OBJT |GLOB |0    |13     |

_GLOBAL_OFFSET_TABLE_  [71]    |    133528|       0|OBJT |GLOB |0    |14     |

_PROCEDURE_LINKAGE_TABLE_  [69]    |         0|       0|NOTY |WEAK |0    |UNDEF  |

__deregister_frame_info  [60]    |         0|       0|NOTY |WEAK |0    |UNDEF  |

Page 35: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

__register_frame_info  [70]    |    133836|       0|OBJT |GLOB |0    |19     |_edata  [59]    |    133872|       0|OBJT |GLOB |0    |20     |_end  [58]    |    133864|       4|OBJT |GLOB |0    |20     |_environ  [72]    |     67960|       0|OBJT |GLOB |0    |12     |_etext  [67]    |    133600|       0|FUNC |GLOB |0    |UNDEF  |_exit  [75]    |     67936|      20|FUNC |GLOB |0    |11     |_fini  [64]    |     67908|      28|FUNC |GLOB |0    |10     |_init  [73]    |     67956|       4|OBJT |GLOB |0    |12     |_lib_version  [57]    |     67380|     116|FUNC |GLOB |0    |9      |_start  [62]    |    133576|       0|FUNC |GLOB |0    |UNDEF  |atexit  [68]    |    133864|       4|OBJT |WEAK |0    |20     |environ  [63]    |    133588|       0|FUNC |GLOB |0    |UNDEF  |exit  [74]    |     67784|      24|FUNC |GLOB |0    |9      |main  [65]    |    133868|       4|OBJT |GLOB |0    |20     |var1  $ 

注意到 var2 这样的"静态全局变量",由于仅仅在单个源文件中有效,nm

-g 并未显示

它。如果不指定-g 选项,将显示 var2(当然会显示更多垃圾信息)。

代码:

$ nm junk  junk:  [Index]   Value      Size    Type  Bind  Other Shndx   Name  ... ...  [65]    |    133868|       4|OBJT |GLOB |0    |20     |var1  [46]    |    133860|       4|OBJT |LOCL |0    |20     |var2  $

5.8 如何单独获得 Solaris 编译环境

Q: 我需要安装哪些包

Page 36: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

A: Seán Boran <[email protected]> 需要下列 Solaris安装包: SUNWbtool、SUNWsprot、SUNWtoo、SUNWhea、SUNWarc、SUNWli

bm、SUNWlibms

可以用 pkginfo [-l]检查是否安装了这些包 $ pkginfo SUNWbtool SUNWsprot SUNWtoo SUNWhea SUNWarc

SUNWlibm SUNWlibms system SUNWarc Archive Libraries system SUNWbtool CCS tools bundled with SunOS system SUNWhea SunOS Header Files system SUNWlibm Sun WorkShop Bundled libm system SUNWlibms Sun WorkShop Bundled shared libm system SUNWsprot Solaris Bundled tools system SUNWtoo Programming Tools $ 可以从 Solaris CD 中单独安装缺少的包(pkgadd)

象 make 这样的工具安装在/usr/ccs/bin,增加到$PATH环境变量中。但是这个 make 和

某些工具相冲突,比如 BIND,此时应该安装GNU make,确认 GNU

make 的搜索路径位于 /usr/ccs/bin/make之前。另外,$PATH环境变量中/usr/ccs/bin 应该位

于/usr/ucb之 前。

5.9 如何获取 Solaris 内核可调参数列表

Q: 谁有 Solaris 内核可调参数列表 A: Andrew Garman <[email protected]> 执行

Page 37: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

/usr/xpg4/bin/nm /platform/sun4u/kernel/unix | egrep 'OBJT \|GLOB' | more 显示结果中部分为 Solaris 内核可调参数,另外一些非可调内核参数。可以

用 ndd 获取、 设置网络相关参数。 D: scz <[email protected]> 可以考虑 /usr/ccs/bin/nm -nx /dev/ksyms | egrep 'OBJT \|GLOB' | more 不知道二者区别何在?第二个报告内容应该包含了后来动态加载内核模块

输出的符号, 第一个才对应基本内核输出的符号。

5.10 如何页边界对齐式分配内存

Q: 我希望在页边界上分配大块内存,要求普通用户、非特权进程亦能使用此技术。

在 mmap(2)手册页中没有明确表明返回地址边界对齐。它提到可以指定起始地址以

保证页边界对齐,但没有说明如果由系统选定起始地址时是否也是页边界对齐的。

MAP_ANON 并非所有系统都支持,我需要在 Solaris 2.x 上运行。 A: Andrew Gierth <[email protected]> mmap(2)即可满足要求。某些系统提供了 valloc或者memalign,但它

们的实现机制是, 分配超过请求大小的内存,然后调整之,这相当浪费。

Page 38: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

mmap(2)应该始终是页边界对齐的。 在那些不支持 MAP_ANON 的系统上,打开/dev/zero 获取句柄,传递给

mmap(2),效果 是一样的。 mmap(2)的可移植性足够好,不过"分配超过请求大小的内存并调整之"可

能更具有可 移植性。

5.11 compile()和 step()怎么用

Q: 我知道这两个函数是 Solaris 对正则表达式的支持函数,可到底怎么用呢?

代码:

A: microcat <[email protected]>  --------------------------------------------------------------------------  /* gcc -Wall -O3 -o reg reg.c -lgen */  #include <stdio.h>  #include <stdlib.h>  #include <regexpr.h>  int main ( int argc, char * argv[] )  {      char * expbuf = NULL;      if ( ( expbuf = compile( argv[1], NULL, NULL ) ) == NULL )      {          exit( EXIT_FAILURE );      }      if ( step( argv[2], expbuf ) )      {          printf( "Match at: %s\n", loc1 );      }      else 

Page 39: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

    {          printf( "No match.\n" );      }      free( expbuf );      exit( EXIT_SUCCESS );  }  /* end of main */  --------------------------------------------------------------------------  $ ./reg '^.*inetd$' '/usr/sbin/inetd'  Match at: /usr/sbin/inetd  $ 

Unix 编程/应用问答中文版

感谢 -- 感谢 C语言的发明者、Unix 操作系统的发明者、感谢全世界C 程序员创造

的 Unix 共 享传统文化圈,她是如此强大、充满禁忌、而又魅力四射。 感谢我的朋友,deepin <[email protected]>,在整个维护过程中

的支持、帮 助 和 鼓 励 。 感 谢 我 所 有 的 NSFocus 安 全 研 究 小 组 的 朋 友

(tt、yuange、security@

nsfocus.com ... ...),是他们容忍我在这个非正业上花费时间。 主要支持人员(字母顺序) -- Andrew Gierth <[email protected]> backend <[email protected]> Casper Dik <[email protected]> deepin <[email protected]> scz <[email protected]> suxm <[email protected]> tt <[email protected]> --------------------------------------------------------------------------

Page 40: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

目录 0. Unix/C 传奇问题 0.1 Dennis Ritchie 和 Ken Thompson

0.2 W. Richard Stevens 之死 1. 系统管理配置问题 1.1 如何给 SUN工作站增加 eeprom硬件口令保护 1.2 如何增加交换空间 1.3 为什么我不能在/home目录下创建子目录 1.4 如何改变一台主机的 locale

1.5 Solaris 7自动注销 1.6 一个目录拥有 setgid设置,怎么理解 1.7 非 Sun Console 上有无等价 Stop-A 的按键 1.8 如何让一个用户只能 ftp而无法 telnet 1.9 1.10 为什么 Sun工作站非要输入 boot 命令才能启动 1.11 如何让 Solaris识别新增加的硬件 1.12 2. 堆栈相关问题 2.1 如何理解 pstack 的输出信息 2.2 2.3 Solaris 中如何获取一个 C 程序的调用栈回溯 2.4 如何编程获取栈底地址 2.5 如何得到一个运行中进程的内存映像

Page 41: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

2.6 调试器如何工作的 2.7 x86/Linux 上如何处理 SIGFPE信号 3. -lelf、-lkvm、-lkstat 相关问题 3.1 如何判断可执行文件是否携带了调试信息 3.2 mprotect 如何用 3.3 mmap 如何用 3.4 getrusage 如何用 3.5 setitimer 如何用 4. 系统资源相关问题 4.1 主流Unix 操作系统上如何编程获取进程的内存、CPU利用状况 4.2 Solaris 下如何获知 CPU速率 4.3 如何编程获取 Solaris系统当前内存大小 5. 块设备相关问题 5.1 CDROM设备究竟在哪里 5.2 如何弹出光驱 5.3 如何利用超级块进行恢复工作 5.4 Solaris Root 口令忘记了 5.5 如何使用 fmthard

5.6 如何从光盘恢复 Solaris 7 的引导扇区 5.7 Solaris支持类似微软 autorun.inf 文件的功能吗 5.8 如何修改/dev/null 的属性 5.9

Page 42: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

5.10 如何自己制作 Solaris启动软盘 5.11 x86/Solaris 如何访问 FAT32分区 6. /etc/system 可调资源限制 6.1 Solaris 下如何限制每个用户可拥有的最大进程数 6.2 如何配置系统使之支持更多的伪终端 6.3 如何增加每个进程可打开文件句柄数 6.4 6.5 做了 setuid()这类调用的程序如何产生 core dump

6.6 消息队列调整 7. DNS 相关问题 7.1 如何进行 DNS 区传输 7.2 如何获知权威名字服务器 7.3 如何配置 DNS 的委托解析 7.4 如何获知 BIND 的版本号 7.5 Solaris/FreeBSD/Linux 如何指定域名解析的顺序 8. Solaris 内核编程相关问题 8.1 Solaris 内核模块中如何 getcwd 8.2 8.3 如何避免一个套接字进入 TIME_WAIT 状态 8.4 结构在优化编译中的对齐问题 8.5 8.6 如何得到非局部变量列表 8.7 8.8 如何单独获得 Solaris 编译环境

Page 43: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

8.9 如何获取 Solaris 内核可调参数列表 8.10 8.11 如何页边界对齐式分配内存 8.12 8.13 compile()和 step()怎么用 8.14 8.15 8.16 8.17 9. 图形界面相关问题 9.1 如何避免进入 Solaris 的图形界面 9.2 Solaris 7 的锁屏 9.3 如何调整键盘重复率 9.4 如何拔掉键盘继续运行 Solaris

9.5 Solaris 下如何设置显卡分辨率 9.6 Solaris 下如何设置显示刷新率 10. 网卡相关问题 10.1 如何在程序中获取本机MAC 地址 10.2 如何在 Sun工作站上安装 3块网卡 10.3 如何在 Solaris x86 上安装网卡驱动 10.4 Solaris 单网卡多 IP(以太网卡别名)

10.5 如何修改主机名(hostname)

10.6 SPARC/Solaris 2.5/2.6/7/8 下如何设置网卡 100Mb全双工 10.7 Unix/Linux/BSD 如何对抗ARP欺骗攻击 10.8 10.9

Page 44: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

10.10 10.11 x86/Solaris 如何强制设定网卡速率 10.12 Solaris/FreeBSD/Linux 如何确定网卡Capability/Speed 10.13 10.14 traceroute 是怎么实现的 11. package 相关问题 11.1 Solaris 下如何将二进制软件包安装到指定目标路径下 11.2 Solaris 下如何自己定制二进制安装包 11.3 如何恢复/usr/bin/su 的缺省安装属性 11.4 如何获知指定包与其他包之间的依赖关系 11.5 Linux 中如何知道 ifconfig属于哪个包 11.6 Solaris 下如何知道某包中有哪些文件 12. 日志相关问题 12.1 12.2 12.3 如何关闭 cron 的日志 12.4 13. 进程相关问题 13.1 如何根据进程名获得 PID 13.2 13.3 13.4 Solaris 7/8 下 ps 输出中的问号 13.5 13.6 13.7 给定一个 PID,如何知道它对应一个运行中的进程 13.8 Unix/Linux 编程中所谓"僵尸进程"指什么

Page 45: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

13.9 x86/FreeBSD 4.3-RELEASE 的 ptrace(2)手册页 13.10 Solaris 下如何知道哪个进程使用了哪个端口 13.11 x86/FreeBSD 如何快速获取指定用户拥有的进程数 14. 一些小工具的使用 14.1 14.2 14.3 只在本地文件系统上查找 14.4 15. 32-bit/64-bit 相关问题 15.1 Solaris 下如何识别当前内核版本 15.2 如何启动 Solaris 32-bit/64-bit 内核 15.3 gcc支持 64-bit 编译吗 15.4 Solaris启动时内核文件找不到了 15.5 64-bit驱动程序无法在 8 下关联,但在 7 下工作正常 16. 库相关问题 16.1 在 Solaris 7 下编写网络程序需要链接哪些库 16.2 16.3 链接过程中库的顺序 16.4 16.5 16.6 /usr/lib/ld.so.1损坏或丢失 16.7 16.8 16.9 Solaris 8 下如何配置运行时链接环境 17. 文件查看问题 17.1 如何直接查看 man 文件

Page 46: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

17.2 .tex 文件怎么读 17.3 Solaris 下怎么看.ps 文件 18. 补丁相关问题 18.1 如何根据补丁号从 Sun主站下载补丁 18.2 18.3 18.4 给 Solaris 2.6安装推荐补丁集 18.5 已知补丁号,如何最快判断系统中是否已经安装该补丁 18.6 如何安装补丁 19. 终端相关问题 19.1 如何使 Backspace 键做删除操作,而不是显示^H 19.2 19.3 如何清空 stdin 的缓冲 19.4 Linux Console 下一按错键就叫,怎么关 20. shell script 问题 20.1 如何获取一个字符串的长度 20.2 读超时自动使用缺省值 20.3 20.4 BASH 中如何得到一个字符串的子串 20.5 20.6 20.7 20.8 使用 tr 命令加密文件 20.9 有哪些命令用于查找定位 20.10 20.11 如何将大写文件名转换为小写文件名

Page 47: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

21. FreeBSD 相关问题 21.1 21.2 如何将一个 512 字节的文件写入主引导扇区 21.3 21.4 21.5 21.6 x86/FreeBSD 4.x 下不能 cp覆盖/kernel

21.7 x86/FreeBSD 下如何设置路由 21.8 21.9 什么是 locale

21.10 用 cvsup安装 vim

21.11 FreeBSD 下 vi 输入中文会显示\x??\x?? 21.12 21.13 21.14 21.15 UDMA ICRC error 是什么意思 21.16 Limiting closed port RST response 什么意思 21.17 21.18 21.19 21.20 22. Linux Kernel Programming 22.1 直接访问内存[显存]地址 22.2 23. Linux 相关问题 23.1 --------------------------------------------------------------------------

Page 48: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

0. Unix/C传奇问题

0.1 Dennis Ritchie 和 Ken Thompson

Q: 我想知道他们,为什么大家不断提到这两个名字? A: All of Unix Programmers 我们也想知道, 1969年Dennis Ritchie 和 Ken Thompson 在贝尔实验室创造性地发明

了 Unix 操作系统, 为此 1983年他们获得了图灵奖。 尽管 Ritchie 是 C 程序设计语言的发明者,但是他最喜欢的编程语言是

Alef。而 Thompson 是一名业余飞行员,曾到莫斯科驾驶过米格-29。 欢迎访问 http://cm.bell-labs.com/who/dmr/ http://cm.bell-labs.com/who/ken/

0.2 W. Richard Stevens 之死

Q: David Johns <[email protected]> 我是他的崇拜者,用 www.google.com搜索他的讣告,但这份讣告没有

提及死因,有人 知道吗? 真地仅仅是英年早逝吗? A: Nithyanandham <[email protected]> 他死于 1999/09/01,家人不想让别人知道死因。讣告位于 http://www.azstarnet.com/clips/richard_stevens.html

Page 49: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

A: joe broz <[email protected]> 似乎是一场攀岩事故,或者滑雪事故,我不确认。

1. 系统管理配置问题

1.1 如何给 SUN工作站增加 eeprom硬件口令保护

A: scz <[email protected]> man -s 1M eeprom 了解细节,要求当前是 root身份 # /usr/sbin/eeprom (显示当前 eeprom配置)

# /usr/sbin/eeprom security-mode=full ( 可选的有 command, full, none) 此时进入交互式设置口令过程,总共输入两次,如果两次口令输入不一致,

则本次设 置作废。成功设置之后除了 go 命令之外的其他 ok 状态下命令均需要口令,

包括 boot 命 令。 设置成 command 时,同样进入交互式口令输入过程。此时,除了 boot

和 go 命令之外的 其他 ok 状态下命令均需要口令。注意,如果仅仅输入 boot 命令,不需要

口令,一旦 boot 命令后面带了参数,比如 boot cdrom -s,同样需要输入口令。 如果设置成 none(缺省设置),表示去掉这种口令保护。 # /usr/sbin/eeprom security-password= (等号后面无其他字符,直接

回车)

Page 50: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

如果想改变前面设置的口令,用这条命令,同样是交互式输入过程。 # /usr/sbin/eeprom security-#badlogins=3 (缺省是 0)

设置口令输入尝试次数。 警告:如果设置了 eeprom硬件保护口令而又忘记,会带来很多麻烦,务

必小心。 一个可行的设置办法是,安全模式设置到 command而不是 full,这样至

少可以正常启 动系统。于是只要记得 root 口令或者还有其他机会获得 root权限(缓冲区

溢出?),就 可以通过设置安全模式为 none而挽救回来。 但是如果设置成 full模式却忘记了 eeprom 口令,我想首先应该打电话给

SUN 的技术支 持。如果出于某种理由你不想这样做,我不确认 eeprom 是否可以热插拔,

先用一个无 口 令 保护的 eeprom 启动系统, 然 后热插拔换上 那 个 有 口 令 保护的

eeprom,然后用 root

权限抹去 eeprom 口令。

1.2 如何增加交换空间

A: WT <[email protected]> 你无法改变分区大小,但是可以增加/删除交换文件,效果类似交换分区。

下列命令

Page 51: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

在根目录下创建一个 500MB 的交换文件,名为 swapfile # mkfile 500m /swapfile 下列命令将使之生效 # swap -a /swapfile 现在你有了额外的 500MB交换空间,为了每次重启后依旧有效,编辑/

etc/vfstab 文件 增加如下行 /swapfile - - swap - no - # swap -l 这里"-l"意味着"list",显示所有交换空间。仔细阅读"swap"和"mkfile"的

手册页。

1.3 为什么我不能在/home目录下创建子目录

Q: Solaris 7 下,root身份,当我试图在/home目录下创建子目录时,系统拒绝,为

什么? A: mohansundarraj 如 果 /etc/rc2.d/S74autofs 脚本 中 automount(1M)守护进 程 已经

mount 了/home,就是 这种现象,而这还是缺省安装后的情形。可以 # /etc/init.d/autofs stop # umount /home 然后你就可以用 root身份在/home 下创建子目录,增加文件了。为了永久

取消 autofs

特 性 , 可 以 将 /etc/rc2.d/S74autofs 脚 本 改 名 , 并 注 释 掉 /etc/

Page 52: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

auto_home、 /etc/auto_master 两个文件中的入口点。 SPARC/Solaris 的缺省用户主目录是/export/home,而不是/home。

1.4 如何改变一台主机的 locale

Q: 一台 SPARC/Solaris 8 运行在 US locale环境中,现在我们想让它运行在

IE(Ireland) locale环境中,以便可以使用欧洲日期格式,怎么办? A: Sharad Ramachandran <[email protected]> 运行 sys-unconfig,在此之前请man -s 1M sys-unconfig, A: chad schrock <[email protected]> 天啊,为了拍死一只苍蝇,你要引爆原子弹吗? 只需要做如下操作,在你的 .cshrc/.profile/.bashrc 等启动脚本中设置

$LANG环境变 量的值为 en_UK,注销,重新登录即可。为了使这个设置全局有效,修改 /etc/default/init 文件,LANG=en_UK,重启动。 -------------------------------------------------------------------------- # @(#)init.dfl 1.2 92/11/26 # # This file is /etc/default/init. /etc/TIMEZONE is a symlink to this

file. # This file looks like a shell script, but it is not. To maintain # compatibility with old versions of /etc/TIMEZONE, some shell

constructs # (i.e., export commands) are allowed in this file, but are

ignored. # # Lines of this file should be of the form VAR=value, where VAR

is one of # TZ, LANG, or any of the LC_* environment variables.

Page 53: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

# TZ=GMT+8 LANG=zh.GBK -------------------------------------------------------------------------- 参看 locale(1)和 locale(5),了解更多关于 locale 的信息。运行"locale -

a",查看 当前系统所支持的所有 locale。 A: Sun Microsystems 2001-06-12 有三种方式改变 locale。首先用"locale -a"命令确认系统中已安装的

locale 1) 从CDE登录屏幕上修改 locale

选择 options -> languages -> choose the new locale

注意,如果登录用户的初始化文件中有不同的 locale设置,将优先于系统全局 locale

设置。 2) 临时设置 locale(shell 相关的) ksh : LANG=<locale> sh : LANG=<locale> export LANG csh : setenv LANG <locale> bash: export LANG=en_US(zh.GBK) 3) vi /etc/default/init 增加如下内容 LANG=<locale> LC_ALL=<locale> 重启系统。 运行"locale"命令确认改变生效。 如果你希望使用的 locale 并未安装,参看如下文档安装 locale Solaris 8 : <<International Language Environments Guide>> Solaris 7 : <<Solaris Internationalization Guide For

Page 54: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

Developers>> Solaris 2.6: <<Solaris Internationalization Guide for

Developers>> D: scz <[email protected]> 1998-08 SPARC/Solaris 2.5 下,为了在 vi 中正确看到中文需要设置环境变量 sh LANG=C;export LANG LC_CTYPE=iso_8859_1;export LC_CTYPE csh setenv LANG zh 关于设置 LANG 这个环境变量涉及到/usr/lib/locale 下的目录权限。

1.5 Solaris 7 自动注销

Q: 怎样设置才能 30秒后自动注销 A: shridhara 不幸的是,Solaris 对此没有什么好的支持。如果正在使用 telnet 会话,或

许可以考 虑"logout"变量,参看 telnet 的手册页。一个变通的办法,使用 K-Shell,

它支持 TMOUT变量,用于指定非活动时限(以秒为单位)。比如,如果一个 shell

会话 3分钟内 不活动,则终止这个 shell 会话 $ TMOUT=180;export TMOUT 可以在用户的.profile 文件中放置该行。缺点是你只能使用 ksh。 D: scz <[email protected]> vi /etc/default/login # TIMEOUT sets the number of seconds (between 0 and 900) to

wait before # abandoning a login session. # TIMEOUT=180

Page 55: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

这里的超时设置针对登录过程,而不是登录成功后的 shell 会话超时设置。

1.6 一个目录拥有 setgid 设置,怎么理解

Q: 对一个目录做了 setgid设置,可我并没有发现这和正常情况有什么区别

A: John Riddoch <[email protected]> 在这种目录下创建新文件时将采用 setgid设置对应的属组,比如 $ ls -ld b drwxrws--- 2 jr group 512 Mar 14 17:13 b/ $ touch b/a $ ls -l b/a -rw------- 1 jr group 0 Mar 14 17:13 b/a $ id uid=178(jr) gid=10(staff) jr 的缺省组是 staff,而现在 b/a 文件属组是 group。 D: 小四 <[email protected]>

SPARC/Solaris 7 下测试 如果目录拥有 SGID设置,那么该目录下新创建的文件将继承该目录的属

组,而不是创 建者所对应的 GID。 [root@ /export/home/scz]> id uid=0(root) gid=1(other) <-- 注意当前用户的属组 [root@ /export/home/scz]> mkdir groupsgid [root@ /export/home/scz]> ls -ld groupsgid drwxr-xr-x root other groupsgid/ [root@ /export/home/scz]> chown scz:users groupsgid [root@ /export/home/scz]> chmod g+s groupsgid [root@ /export/home/scz]> ls -ld groupsgid drwxr-sr-x scz users groupsgid/ <-- 目录拥有 SGID设置 [root@ /export/home/scz]> cd groupsgid/

Page 56: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

[root@ /export/home/scz/groupsgid]> touch scz_0 [root@ /export/home/scz/groupsgid]> ls -l scz_0 -rw-r--r-- root users scz_0 <-- 注意属组变化 [root@ /export/home/scz/groupsgid]> chmod g-s ../groupsgid/ [root@ /export/home/scz/groupsgid]> ls -ld ../groupsgid/ drwxr-xr-x scz users ../groupsgid/ [root@ /export/home/scz/groupsgid]> touch scz_1 [root@ /export/home/scz/groupsgid]> ls -l scz_1 -rw-r--r-- root other scz_1 <-- 注意属组变化 [root@ /export/home/scz/groupsgid]>

1.7 非 Sun Console 上有无等价 Stop-A 的按键

A: neomilev 如果是便携机,尝试 alt/break 或者 ctrl/break。如果是 vt100终端,尝

试 F11 或者 break

1.8 如何让一个用户只能 ftp 而无法 telnet

A: 小四 <[email protected]>

修改该用户在/etc/passwd 中的 shell 为/bin/false,在/etc/shells 文件中增加

/bin/false,此时,该用户只能 ftp,telnet失败。

1.9 为什么 Sun工作站非要输入 boot命令才能启动

Q: 我有台 Sun工作站,每次开机后停在 ok 状态下,需要手工输入 boot

命令才能启动, 现在想避免这种效果,怎么办 A: /usr/sbin/eeprom auto-boot?=true

Page 57: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

/usr/sbin/eeprom auto-boot? <-- 查询 A: dengdai@SMTH 进入 OBP 状态 ok setenv auto-boot? true ok setenv boot-device disk 反之 ok setenv auto-boot? false

1.10 如何让 Solaris识别新增加的硬件

Q: 比如新增加了网卡、硬盘、光驱什么的,如何让 Solaris 意识到这种增加 A: spp(低音炮) & suxm <[email protected]>

有三种办法 a. Stop-A 进入 OBP 状态,输入 boot -r

b. sync(重复);reboot -- -r

c. touch /reconfigure;sync(重复);reboot

参 看reboot(1M)、boot(1M)、eeprom(1M)、kernel(1M)、cfgadm(1M)、psradm

(1M) 手 册页 Q: 我新增加了一块硬盘,不想 boot -r而立即生效,怎么办 A: 老大 <[email protected]> 2001-12-04 16:51

直接将第二块硬盘接上去,然后顺序执行如下命令,不用重新启动机器 modunload -i 0 drvconfig(1M) devlinks(1M) disks(1M)

Page 58: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

如果需要重新格式化、分区、创建文件系统,就继续执行 format(1M) newfs(1M)

2. 堆栈相关问题

2.1 如何理解 pstack 的输出信息

Q: 080603a7 main (1, 80479b8, 80479c0) + d53 结尾的 d53 是什么 A: Roger A. Faulkner <[email protected]> 在 代 码段绝对 地址 0x080603a7 处, main() 调 用 了 一 个 函 数 ,

0x080603a7正是 main + 0xd53,换句话说,从main()函数开始的 0xd53偏移处。

2.2 Solaris 中如何获取一个 C 程序的调用栈回溯

Q: 我想在 Solaris 2.6极其后续版本上获取一个 C 程序的调用栈回溯,类似如下输出

(10) 0x00045e08 integ + 0x408 [./two_brn.e] (11) 0x0006468c trajcem + 0x128 [./two_brn.e] (12) 0x00055490 fly_traj + 0xf58 [./two_brn.e] (13) 0x0004052c top_level + 0x14 [./two_brn.e] (14) 0x000567e4 _start + 0x34 [./two_brn.e] 这样我就可以知道当程序崩溃、死锁的时候代码执行到了何处。在 HP-UX

和 IRIX 上 可 以 利 用 U_STACK_TRACE() 和

trace_back_stack_and_print(),Solaris 上呢? Q: 有没有办法显示当前堆栈中的数据(GNU/Linux系统)?我希望自己的

Page 59: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

异常处理程序 在进程结束前 dump整个栈区(stack),以便观察到栈顶是什么函数。对于

调试意想 不到的运行时错误而言,这很重要。 A: Bjorn Reese <[email protected]> 用/usr/proc/bin/pstack [-F] <pid ...>

参 看 这 个 例 子 代 码 , http://home1.stofanet.dk/breese/debug/debug.tar.gz

Q: is there a way to access call stack information at run time from within

a program? i've been maintaining my own crude stack using __FUNCTION__

and linked lists but can't help but think there's gotta be a better way... A: Nate Eldredge <[email protected]> 这依赖于 你 的系统, 如 果 使 用 glibc 2.1 或更 新 版 本 , 可 以 使 用

backtrace()函数, 参看<execinfo.h>,其他系统可能有不同的技术支持。 注意,你所使用的办法可能是唯一能够保证跨平台使用的 A: Andrew Gabriel <[email protected]>

Consultant Software Engineer 下面是一个 backtrace()的应用举例,如果你使用 Solaris 2.4及其后续版

本,那 么这个例子可以很好的工作。很可能无法工作在 64-bit模式下,我没有尝

试过, 好像 Solaris 7 已经提供了一个类似的演示程序。还可以增加某些功能,我

没有时

Page 60: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

间了。 /* * Produce a stack trace for Solaris systems. * * Copyright (C) 1995-1998 Andrew Gabriel

<[email protected]> * Parts derived from Usenet postings of Bart Smaalders and

Casper Dik. * */ /* ......................................................................... */ #include <setjmp.h> #include <sys/types.h> #include <sys/reg.h> #include <sys/frame.h> #include <dlfcn.h> #include <errno.h> #include <unistd.h> #include <stdio.h> #if defined(sparc) || defined(__sparc) #define FLUSHWIN() asm("ta 3"); #define FRAME_PTR_INDEX 1 #define SKIP_FRAMES 0 #endif #if defined(i386) || defined(__i386) #define FLUSHWIN() #define FRAME_PTR_INDEX 3 #define SKIP_FRAMES 1 #endif #if defined(ppc) || defined(__ppc) #define FLUSHWIN() #define FRAME_PTR_INDEX 0 #define SKIP_FRAMES 2 #endif /* ......................................................................... */ static void print_address ( void * pc ) { Dl_info info; if ( dladdr( pc, &info ) == 0 ) { /* not found */ fprintf( stderr, "*** %s:0x%x\n", "??", ( unsigned int )pc ); }

Page 61: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

else { /* found */ fprintf( stderr, "*** %s:%s+0x%x\n", info.dli_fname,

info.dli_sname, ( unsigned int )pc - ( unsigned int )info.dli_saddr ); } return; } /* end of print_address */ /* ......................................................................... */ static int validaddr ( void * addr ) { static long pagemask = -1; char c; if ( pagemask == -1 ) { pagemask = ~( sysconf( _SC_PAGESIZE ) - 1 ); } addr = ( void * )( ( long )addr & pagemask ); if ( mincore( ( char * )addr, 1, &c ) == -1 && errno ==

ENOMEM ) { return 0; /* invalid */ } else { return 1; /* valid */ } } /* end of validaddr */ /* ......................................................................... */ /* * this function walks up call stack, calling print_addess * once for each stack frame, passing the pc as the argument. */ static void print_stack ( void ) { struct frame * sp; jmp_buf env; int i; int * iptr; FLUSHWIN(); setjmp( env ); iptr = ( int * )env; sp = ( struct frame * )iptr[ FRAME_PTR_INDEX ];

Page 62: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

for ( i = 0; i < SKIP_FRAMES && sp; i++ ) { if ( !validaddr( sp ) || !validaddr( &sp->fr_savpc ) ) { fprintf( stderr, "***[stack pointer corrupt]\n" ); return; } sp = ( struct frame * )sp->fr_savfp; } i = 100; /* looping check */ while ( validaddr( sp ) && validaddr( &sp->fr_savpc ) && sp-

>fr_savpc && --i ) { print_address( ( void * )sp->fr_savpc ); sp = ( struct frame * )sp->fr_savfp; } } /* end of print_stack */ /* ......................................................................... */ void backtrace( void ) { fprintf( stderr, "***backtrace...\n" ); print_stack(); fprintf( stderr, "***backtrace ends\n" ); } /* ......................................................................... */

2.3 如何编程获取栈底地址

Q: 虽然很多操作系统的用户进程栈底地址固定,但是我需要写一个可广泛移植C 程序

获取这个栈底地址。 A: tt <[email protected]> 2001-06-02 19:40 假设堆栈(stack)向低地址方向增长,则所谓栈底指堆栈(stack)最高地址 x86/Linux 栈底是 0xc0000000( 栈底往低地址的 4 个字节总是零 ) SPARC/Solaris 7/8 栈底是 0xffbf0000( 栈底往低地址的 4 个字节总是零

)

Page 63: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

SPARC/Solaris 2.6 栈底是 0xf0000000( 栈底往低地址的 4 个字节总是零 )

x86/FreeBSD 栈底是 0xbfc00000( 栈底往低地址的 4 个字节总是零 ) x86/NetBSD 1.5 栈底是 0xbfbfe000

x86/OpenBSD 2.8 栈底是 0xdfbfe000 D: jonah 对于 NetBSD 1.5,栈底是 0xbfc00000。根据源码,最高用户地址是

0xbfbfe000,因为 最后 4MB(2^22)的最后两页(0x2000 字节,一页 4096 字节)保留用做 U

区,但是目前不再 使用这块内存。因此,0xbfbfe000才是真正的栈底。 tt 在 OpenBSD 2.8 上测试结果,栈底是 0xdfbfe000,注意和 NetBSD

1.5 相差很大。 A: tt <[email protected]> -------------------------------------------------------------------------- /* * gcc -Wall -O3 -o gstack gstack.c * * A simple example to get the current stack bottom address * warning3 <[email protected]> * 2001-06-01 * * Modified by scz <[email protected]> * 2001-06-02 */ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <setjmp.h> typedef void Sigfunc ( int ); /* for signal handlers */ Sigfunc * signal ( int signo, Sigfunc * func );

Page 64: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

static Sigfunc * Signal ( int signo, Sigfunc * func ); static char * get_stack_bottom ( void ); static void segfault ( int signo ); static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump = 0; static Sigfunc *seg_handler; static Sigfunc *bus_handler; /* for xxxBSD */ Sigfunc * signal ( int signo, Sigfunc * func ) { struct sigaction act, oact; act.sa_handler = func; sigemptyset( &act.sa_mask ); act.sa_flags = 0; if ( sigaction( signo, &act, &oact ) < 0 ) { return( SIG_ERR ); } return( oact.sa_handler ); } /* end of signal */ static Sigfunc * Signal ( int signo, Sigfunc * func ) /* for our

signal() funct ion */ { Sigfunc * sigfunc; if ( ( sigfunc = signal( signo, func ) ) == SIG_ERR ) { exit( EXIT_FAILURE ); } return( sigfunc ); } /* end of Signal */ static char * get_stack_bottom ( void ) { volatile char *c; /* for autovar, must be volatile */ seg_handler = Signal( SIGSEGV, segfault ); bus_handler = Signal( SIGBUS, segfault ); c = ( char * )&c; if ( sigsetjmp( jmpbuf, 1 ) != 0 ) { Signal( SIGSEGV, seg_handler ); Signal( SIGBUS, bus_handler ); return( ( char * )c ); } canjump = 1; /* now sigsetjump() is OK */ while ( 1 )

Page 65: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

{ *c = *c; c++; } return( NULL ); } /* end of get_stack_bottom */ static void segfault ( int signo ) { if ( canjump == 0 ) { return; /* unexpected signal, ignore */ } canjump = 0; siglongjmp( jmpbuf, signo ); /* jump back to main, don't return */ } /* end of segfault */ int main ( int argc, char * argv[] ) { fprintf( stderr, "Current stack bottom is at 0x%p\n",

get_stack_bottom() ); return( EXIT_SUCCESS ); } /* end of main */ -------------------------------------------------------------------------- D: scz <[email protected]> 2001-06-03 00:38 W. Richard Stevens 在 <<Advanced Programming in the UNIX

Environment>>中详细 介绍了 setjmp/longjmp 以及 sigsetjmp/siglongjmp 函数。 这个程序的原理很简单,不断向栈底方向取值,越过栈底的地址访问会导

致 SIGSEGV

信号,然后利用长跳转回到主流程报告当前 c值,自然对应栈底。 tt测试表明,在 x86/FreeBSD 中导致 SIGBUS信号。据 jonah 报告,不

仅仅是 FreeBSD, NetBSD 以及 OpenBSD 系统中上述程序越界访问也导致 SIGBUS信号,

而不是 SIGSEGV

Page 66: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

信号。 非局部转移,比如函数间转移的时候考虑使用 setjmp/longjmp。但是如果

涉及到信号 句柄与主流程之间的转移,就不能使用 longjmp 了。当捕捉到信号进入信

号句柄,此 时当前信号被自动加入进程的信号屏蔽字中,阻止后来产生的这种信号干

扰此信号句 柄。如果用 longjmp跳出信号句柄,此时进程的信号屏蔽字状态未知,有

些系统做了 保存恢复,有些系统没有做。根据 POSIX.1,此时应该使用 sigsetjmp/

siglongjmp 函 数。下面来自 SPARC/Solaris 7 的 setjmp(3C) -------------------------------------------------------------------------- #include <setjmp.h> int setjmp ( jmp_buf env ); int sigsetjmp ( sigjmp_buf env, int savemask ); void longjmp ( jmp_buf env, int val ); void siglongjmp ( sigjmp_buf env, int val ); -------------------------------------------------------------------------- 如果 savemask 非 0,sigsetjmp 在 env 中保存进程当前信号屏蔽字,相

应 siglongjmp 回 来的时候从 env 中恢复信号屏蔽字。 数据类型 sig_atomic_t由ANSI C 定义,在写时不会被中断。它意味着这

种变量在具有 虚存的系统上不会跨越页边界,可以用一条机器指令对其存取。这种类型的

Page 67: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

变量总是 与 ANSI类型修饰符 volatile 一并出现,防止编译器优化带来的不确定状

态。 在 longjmp/siglongjmp 中,全局、静态变量保持不变,声明为 volatile

的自动变量也 保持不变。 无 论 是否使 用 了 编 译优化开 关 , 为 了 保 证广泛兼容 性 , 都 应该在

get_stack_bottom() 中声明 c 为 volatile变量。 注意这里,必须使用长跳转,而不能从信号句柄中直接返回。因为导致信号

SIGSEGV、 SIGBUS分发的语句始终存在,直接从信号句柄中返回主流程,将回到引

发信号的原指 令处,而不是下一条指令(把这种情况理解成异常,而不是中断),于是立

即导致下一 次信号分发,出现广义上的死循环,所谓程序僵住。可以简单修改上述程序

不利用 长跳转,简单对一个全局变量做判断决定是否继续循环递增 c,程序最终

僵住;如果 在信号句柄中输出调试信息,很容易发现这个广义上的无限循环。 D: scz <[email protected]> 2001-06-03 00:40 在 x86/Linux系统中用如下命令可以确定栈区所在 # cat /proc/1/maps <-- 观察 1 号进程 init

Page 68: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

... ... bfffe000-c0000000 rwxp fffff000 00:00 0 # 在 SPARC/Solaris 7 中用/usr/proc/bin/pmap 命令确定栈区所在 # /usr/proc/bin/pmap 1 <-- 观察 1 号进程 init ... ... FFBEC000 16K read/write/exec [ stack ] # 16KB == 0x4000,0xFFBEC000 + 0x4000 == 0xFFBF0000

与前面 tt介绍的 SPARC/Solaris 7/8 栈底是 0xffbf0000( 栈底往低地址的 4 个字节总是零

) 相符合。 此外,在 SPARC/Solaris 7 下,可以这样验证之 # /usr/ccs/bin/nm -nx /dev/ksyms | grep "|_userlimit" [7015] |0x0000100546f8|0x000000000008|OBJT |GLOB |0 |ABS |

_userlimit [8051] |0x000010054700|0x000000000008|OBJT |GLOB |0 |ABS

|_userlimit32 # echo "_userlimit /J" | adb -k /dev/ksyms /dev/mem physmem 3b72 _userlimit: _userlimit: ffffffff80000000 # skd64 0x000010054700 8 byteArray [ 8 bytes ] ----> 0000000000000000 00 00 00 00 FF BF 00 00 # ~~~~~~~~~~~ 对于 32-bit 应用程序来说,这是用户 空间上限 如 果 编 译 64-bit 应 用 程 序 , 用户空 间 上限是 _userlimit , 也 就 是

0xffffffff80000000 # /opt/SUNWspro/SC5.0/bin/cc -xarch=v9 -O -o gstack gstack.c # ./gstack Current stack bottom is at 0xffffffff80000000 #

Page 69: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

对于 SPARC/Solaris 2.6 32-bit kernel mode # echo "_userlimit /X" | adb -k /dev/ksyms /dev/mem physmem 3d24 _userlimit: _userlimit: f0000000 #

2.4 如何得到一个运行中进程的内存映像

A: Sun Microsystems 1998-03-30 有些时候必须得到一个运行中进程的内存映像而不能停止该进程,Solaris

系统了这 样的工具,gcore 为运行中进程创建一个 core 文件。假设我的 bash 进程

号是 5347 # gcore 5347 gcore: core.5347 dumped # file core.5347 core.5347: ELF 32-位 MSB core 文件 SPARC 版本 1,来自'bash' # 注意,只能获取属主是你自己的进程的内存映像,除非你是 root。 2.6 调试器如何工作的 Q: 我想在一个自己编写的程序中单步运行另外一个程序,换句话说,那是

一个调试 器,该如何做? A: Erik de Castro Lopo <[email protected]> 这是一个操作系统相关的问题。最一般的回答是使用 ptrace()系统调用,

尽管我 不确认究竟这有多么普遍。Linux man手册上说 SVr4、SVID EXT、AT&T、

X/OPEN

Page 70: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

和 BSD 4.3 都支持它。 为了使用 ptrace(),你的程序应该调用 fork(),然后在子进程中做如下调

用: ptrace( PTRACE_TRACEME, 0, 0, 0 ); 接下来调用 exec()家族的函数执行你最终企图跟踪的程序。 为了单步进入子进程,在父进程中调用: ptrace( PTRACE_SINGLESTEP, 0, 0, 0 ); 还有一些其他函数做恢复/设置寄存器、内存变量一类的工作。 GDB 的源代码足以回答这个问题。 2.7 x86/Linux 上如何处理 SIGFPE信号 Q: 参看如下程序 -------------------------------------------------------------------------- /* * gcc -Wall -pipe -O3 -o sigfpe_test_0 sigfpe_test_0.c * * 注意与下面的编译效果进行对比,去掉优化开关-O3 * * gcc -Wall -pipe -o sigfpe_test_0 sigfpe_test_0.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <setjmp.h> /* * for signal handlers */ typedef void Sigfunc ( int ); Sigfunc * signal ( int signo, Sigfunc *func ); static Sigfunc * Signal ( int signo, Sigfunc *func ); static void on_fpe ( int signo ); Sigfunc * signal ( int signo, Sigfunc *func ) {

Page 71: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

struct sigaction act, oact; act.sa_handler = func; sigemptyset( &act.sa_mask ); act.sa_flags = 0; if ( signo == SIGALRM ) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */ #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */ #endif } if ( sigaction( signo, &act, &oact ) < 0 ) { return( SIG_ERR ); } return( oact.sa_handler ); } /* end of signal */ static Sigfunc * Signal ( int signo, Sigfunc *func ) { Sigfunc *sigfunc; if ( ( sigfunc = signal( signo, func ) ) == SIG_ERR ) { perror( "signal" ); exit( EXIT_FAILURE ); } return( sigfunc ); } /* end of Signal */ static void on_fpe ( int signo ) { fprintf( stderr, "here is on_fpe\n" ); return; } /* end of on_fpe */ int main ( int argc, char * argv[] ) { unsigned int i; Signal( SIGFPE, on_fpe ); i = 51211314 / 0; /*

Page 72: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

* 另外,增加这行后,再次对比有-O3 和无-O3 的效果 * * fprintf( stderr, "i = %#X\n", i ); */ return( EXIT_SUCCESS ); } /* end of main */ -------------------------------------------------------------------------- 有-O3、无-O3,以及有无最后那条 fprintf()语句,效果上有差别,自行对

比。如果 输出"here is on_fpe",则会发现永不停止。 D: 小四 <[email protected]> 2001-12-14 18:25

为了便于讨论,约定两个名词,中断和异常。这里中断指最常规的中断,比如 int指

令带来的软中断。异常的典型代表有除 0 错。区别在于,发生异常时,x86

架构上 CPU

将当前 EIP(指向引发异常的指令)压栈,发生中断时,x86架构上 CPU 将当前 EIP 的后一

个地址(指向引发中断的指令的后一条指令)压栈。在异常处理代码中,如果认为能够 从灾难中恢复,可以不修改被压栈的 EIP,从而返回到引发异常的指令处。

更多细节 请查看 Intel手册。 这些是从前DOS 下残留的汇编知识,不过也快忘光了,刚才又找元宝宝确

认了一下。 在上述代码中,on_fpe()直接返回了,导致再次触发异常,所以无休止输

Page 73: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

出。事实上 在所有的计算器处理程序中,都会对 SIGFPE信号做相应处理,前些日子

看 yacc/lex 的 时候又碰上过。正确的做法是,利用远跳转转移,让开引发异常的指令。 代码修改如下 -------------------------------------------------------------------------- /* * gcc -Wall -pipe -O3 -o sigfpe_test_1 sigfpe_test_1.c * * 注意与下面的编译效果进行对比,去掉优化开关-O3 * * gcc -Wall -pipe -o sigfpe_test_1 sigfpe_test_1.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <setjmp.h> /* * for signal handlers */ typedef void Sigfunc ( int ); Sigfunc * signal ( int signo, Sigfunc *func ); static Sigfunc * Signal ( int signo, Sigfunc *func ); static void on_fpe ( int signo ); static sigjmp_buf jmpbuf; static volatile sig_atomic_t canjump = 0; Sigfunc * signal ( int signo, Sigfunc *func ) { struct sigaction act, oact; act.sa_handler = func; sigemptyset( &act.sa_mask ); act.sa_flags = 0; if ( signo == SIGALRM ) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */

Page 74: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

#endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */ #endif } if ( sigaction( signo, &act, &oact ) < 0 ) { return( SIG_ERR ); } return( oact.sa_handler ); } /* end of signal */ static Sigfunc * Signal ( int signo, Sigfunc *func ) { Sigfunc *sigfunc; if ( ( sigfunc = signal( signo, func ) ) == SIG_ERR ) { perror( "signal" ); exit( EXIT_FAILURE ); } return( sigfunc ); } /* end of Signal */ static void on_fpe ( int signo ) { if ( canjump == 0 ) { return; /* unexpected signal, ignore */ } canjump = 0; fprintf( stderr, "here is on_fpe\n" ); siglongjmp( jmpbuf, signo ); /* jump back to main, don't return */ return; } /* end of on_fpe */ int main ( int argc, char * argv[] ) { unsigned int i; if ( sigsetjmp( jmpbuf, 1 ) != 0 ) { fprintf( stderr, "c u later\n" ); return( EXIT_SUCCESS ); } /*

Page 75: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

* now sigsetjump() is OK */ canjump = 1; Signal( SIGFPE, on_fpe ); i = 51211314 / 0; /* * 另外,增加这行后,再次对比有-O3 和无-O3 的效果 * * fprintf( stderr, "i = %#X\n", i ); */ return( EXIT_SUCCESS ); } /* end of main */ -------------------------------------------------------------------------- 关于-O3 的讨论,对 gcc 编译器熟悉的朋友请继续,呵,我对 Linux 下的

这此东西,实 在缺乏兴趣。

3. -lelf、-lkvm、-lkstat 相关问题

3.1 如何判断可执行文件是否携带了调试信息

Q: 某些时候需要知道编译可执行文件时是否携带了调试信息(比如是否指定了-g 编译

选项)。检查可执行文件中是否包含".stab" elf section,".stab" section

用于 保存相关调试信息。 A: Sun Microsystems 2000-05-15 下面这个脚本演示如何判断可执行文件是否携带调试信息 -------------------------------------------------------------------------- #! /bin/sh # # Script that test whether or not a given file has been built for

Page 76: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

# debug (-g option specified in the compilation) if [ $# -le 0 ] then echo "Usage: $1 filename" exit 1 fi if [ ! -f $1 ] then echo "File $1 does not exist" exit 1 fi /usr/ccs/bin/dump -hv $1 | /bin/egrep -s '.stab$' if [ $? -eq 0 ] then echo "File '$1' has been built for debug" exit 0 else echo "File '$1' has not been built for debug" exit 1 fi -------------------------------------------------------------------------- 如果对 ELF 文件格式不熟悉,理解上述代码可能有点困难,参看 http://www.digibel.org/~tompy/hacking/elf.txt ,这是 1.1 版的 ELF

文件格式规范。

3.2 mprotect 如何用

A: 小四 <[email protected]> # truss prtconf 2>&1 | grep sysconf sysconfig(_CONFIG_PAGESIZE) = 8192 sysconfig(_CONFIG_PHYS_PAGES) = 16384 # 由此可知当前系统页尺寸是 8192 字节。 -------------------------------------------------------------------------- /* * gcc -Wall -g -ggdb -static -o mtest mtest.c */ #include <stdio.h>

Page 77: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

#include <stdlib.h> #include <errno.h> #include <sys/mman.h> int main ( int argc, char * argv[] ) { char *buf; char c; /* * 分配一块内存,拥有缺省的 rw-保护 */ buf = ( char * )malloc( 1024 + 8191 ); if ( !buf ) { perror( "malloc" ); exit( errno ); } /* * Align to a multiple of PAGESIZE, assumed to be a power of two */ buf = ( char * )( ( ( unsigned int )buf + 8191 ) & ~8191 ); c = buf[77]; buf[77] = c; printf( "ok\n" ); /* * Mark the buffer read-only. * * 必须保证这里 buf 位于页边界上,否则mprotect()失败,报告无效参数 */ if ( mprotect( buf, 1024, PROT_READ ) ) { perror( "\nmprotect" ); exit( errno ); } c = buf[77]; /* * Write error, program dies on SIGSEGV */ buf[77] = c; exit( 0 ); } /* end of main */ -------------------------------------------------------------------------- $ ./mtest

Page 78: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

ok 段错误 (core dumped) <-- 内存保护起作用了 $

3.3 mmap 如何用

A: 小四 <[email protected]>

下面写一个完成文件复制功能的小程序,利用 mmap(2),而不是标准文件 I/O 接口。

-------------------------------------------------------------------------- /* * gcc -Wall -O3 -o copy_mmap copy_mmap.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* for memcpy */ #include <strings.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define PERMS 0600 int main ( int argc, char * argv[] ) { int src, dst; void *sm, *dm; struct stat statbuf; if ( argc != 3 ) { fprintf( stderr, " Usage: %s <source> <target>\n", argv[0] ); exit( EXIT_FAILURE ); } if ( ( src = open( argv[1], O_RDONLY ) ) < 0 ) { perror( "open source" ); exit( EXIT_FAILURE ); }

Page 79: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

/* 为了完成复制,必须包含读打开,否则mmap()失败 */ if ( ( dst = open( argv[2], O_RDWR | O_CREAT | O_TRUNC,

PERMS ) ) < 0 ) { perror( "open target" ); exit( EXIT_FAILURE ); } if ( fstat( src, &statbuf ) < 0 ) { perror( "fstat source" ); exit( EXIT_FAILURE ); } /* * 参看前面 man手册中的说明,mmap()不能用于扩展文件长度。所以这

里必须事 * 先扩大目标文件长度,准备一个空架子等待复制。 */ if ( lseek( dst, statbuf.st_size - 1, SEEK_SET ) < 0 ) { perror( "lseek target" ); exit( EXIT_FAILURE ); } if ( write( dst, &statbuf, 1 ) != 1 ) { perror( "write target" ); exit( EXIT_FAILURE ); } /* 读的时候指定 MAP_PRIVATE 即可 */ sm = mmap( 0, ( size_t )statbuf.st_size, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, src, 0 ); if ( MAP_FAILED == sm ) { perror( "mmap source" ); exit( EXIT_FAILURE ); } /* 这里必须指定 MAP_SHARED 才可能真正改变静态文件 */ dm = mmap( 0, ( size_t )statbuf.st_size, PROT_WRITE, MAP_SHARED, dst, 0 );

Page 80: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

if ( MAP_FAILED == dm ) { perror( "mmap target" ); exit( EXIT_FAILURE ); } memcpy( dm, sm, ( size_t )statbuf.st_size ); /* * 可以不要这行代码 * * msync( dm, ( size_t )statbuf.st_size, MS_SYNC ); */ return( EXIT_SUCCESS ); } /* end of main */ -------------------------------------------------------------------------- mmap()好处是处理大文件时速度明显快于标准文件 I/O,无论读写,都少

了一次用户 空间与内核空间之间的复制过程。操作内存还便于设计、优化算法。 文件 I/O 操作/proc/self/mem 不存在页边界对齐的问题。至少 Linux 的

mmap()的最后一 个形参 offset 并未强制要求页边界对齐,如果提供的值未对齐,系统自动

向上舍入到 页边界上。 malloc()分配得到的地址不见得对齐在页边界上 /proc/self/mem 和/dev/kmem 不同。root 用户打开/dev/kmem 就可以

在用户空间访问到 内核空间的数据,包括偏移 0处的数据,系统提供了这样的支持。 显然代码段经过/proc/self/mem 可写映射后已经可写,无须 mprotect()

介入。 D: scz <[email protected]>

Page 81: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

Solaris 2.6 下参看 getpagesize(3C)手册页,关于如何获取页大小,一般是 8192。

Linux 下参看 getpagesize(2)手册页,一般是 4096。

3.4 getrusage 如何用

A: 小四 <[email protected]>

在 SPARC/Solaris 2.6/7 下结论一致,只支持了 ru_utime 和 ru_stime

成员,其他成员 被设置成 0。修改头文件后在 FreeBSD 4.3-RELEASE 上测试,则不只支

持 ru_utime 和 ru_stime 成员。从 FreeBSD 的 getrusage(2)手册页可以看到,这个函数

源自 4.2 BSD。 如此来说,至少对于 SPARC/Solaris 2.6/7,getrusage(3C)并无多大意

义。 3.5 setitimer 如何用 D: scz <[email protected]> 为什么要学习使用 setitimer(2),因为 alarm(3)属于被淘汰的定时器技术。

A: 小四 <[email protected]>

下面是个 x86/FreeBSD 4.3-RELEASE 下的例子 -------------------------------------------------------------------------- /* * File : timer_sample.c * Author : Unknown (Don't ask me anything about this program) * Complie : gcc -Wall -pipe -O3 -o timer_sample timer_sample.c

Page 82: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

* Platform : x86/FreeBSD 4.3-RELEASE * Date : 2001-09-18 15:18 */ /

************************************************************************

* * * Head File * * * *****************************************************************

*******/ #include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <signal.h> /

************************************************************************

* * * Macro * * * *****************************************************************

*******/ typedef void Sigfunc ( int ); /* for signal handlers */ /

************************************************************************

* * * Function Prototype * * * *****************************************************************

*******/ static void Atexit ( void ( *func ) ( void ) ); static void init_signal ( void ); static void init_timer ( void ); static void on_alarm ( int signo ); static void on_terminate ( int signo ); static int Setitimer ( int which, const struct itimerval *value, struct itimerval *ovalue ); Sigfunc * signal ( int signo, Sigfunc *func ); static Sigfunc * Signal ( int signo, Sigfunc *func ); static void terminate ( void ); /

*********************************************************************

Page 83: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

*** * * * Static Global Var * * * *****************************************************************

*******/ /

************************************************************************/

static void Atexit ( void ( *func ) ( void ) ) { if ( atexit( func ) != 0 ) { perror( "atexit" ); exit( EXIT_FAILURE ); } return; } /* end of Atexit */ /* * 初始化信号句柄 */ static void init_signal ( void ) { int i; Atexit( terminate ); for ( i = 1; i < 9; i++ ) { Signal( i, on_terminate ); } Signal( SIGTERM, on_terminate ); Signal( SIGALRM, on_alarm ); return; } /* end of init_signal */ static void init_timer ( void ) { struct itimerval value; value.it_value.tv_sec = 1; value.it_value.tv_usec = 0; value.it_interval = value.it_value; Setitimer( ITIMER_REAL, &value, NULL ); } /* end of init_timer */ static void on_alarm ( int signo ) {

Page 84: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

static int count = 0; /* * 演示用,这很危险 */ fprintf( stderr, "count = %u\n", count++ ); return; } static void on_terminate ( int signo ) { /* * 这次我们使用 atexit()函数 */ exit( EXIT_SUCCESS ); } /* end of on_terminate */ static int Setitimer ( int which, const struct itimerval *value, struct itimerval *ovalue ) { int ret; if ( ( ret = setitimer( which, value, ovalue ) ) < 0 ) { perror( "setitimer" ); exit( EXIT_FAILURE ); } return( ret ); } /* end of Setitimer */ Sigfunc * signal ( int signo, Sigfunc *func ) { struct sigaction act, oact; act.sa_handler = func; sigemptyset( &act.sa_mask ); act.sa_flags = 0; if ( signo == SIGALRM ) { #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */ #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */ #endif

Page 85: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

} if ( sigaction( signo, &act, &oact ) < 0 ) { return( SIG_ERR ); } return( oact.sa_handler ); } /* end of signal */ static Sigfunc * Signal ( int signo, Sigfunc *func ) { Sigfunc *sigfunc; if ( ( sigfunc = signal( signo, func ) ) == SIG_ERR ) { perror( "signal" ); exit( EXIT_FAILURE ); } return( sigfunc ); } /* end of Signal */ static void terminate ( void ) { fprintf( stderr, "\n" ); return; } /* end of terminate */ int main ( int arg, char * argv[] ) { init_signal(); init_timer(); while ( 1 ) { /* * 形成阻塞,降低CPU 占用率 */ getchar(); } return( EXIT_SUCCESS ); } /* end of main */ /

************************************************************************/

-------------------------------------------------------------------------- D: scz <[email protected]> 讨论一个问题。getchar()的作用是降低CPU 占用率,可用 top 命令查看。

Page 86: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

timer_sample.c 中换用 ITIMER_PROF/SIGPROF 后,你会发现上述程序无输出,我据此

认为 getchar()形成的阻塞不计算在进程虚拟时钟中,也不认为系统正在为进程利益 而运行。 如果进一步将 getchar()去掉,直接一个 while()无限循环,即使换用 ITIMER_PROF/SIGPROF,程序还是有输出。不过 top 命令查看的结果让

你吐血,CPU 几 乎无空闲。 D: scz <[email protected]> setitimer( ITIMER_REAL, &value, NULL )导致分发 SIGALRM信号,

如果同时使用 alarm(),势毕造成冲突。此外注意 sleep()、pause()等函数带来的冲突。

4. 系统资源相关问题

4.1 主流 Unix操作系统上如何编程获取进程的内存 、CPU利用状况

Q: Solaris 下如何编程获知 CPU 占用率和内存占用信息呢,可移植吗? Q: 我想写个程序遍历当前运行中的活动进程,Solaris 提供相应系统调用

了吗 A: Nicholas Dronen <[email protected]> 不可移植。man -s 4 proc,man -s 3k kstat

Page 87: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

如果不是编程,可以用 top、mpstat、vmstat、sar(1)等等,还有 /usr/ucb/ps -aux,对于 Solaris 来说,后者更直接精炼,top 不是标准

配置。 # /usr/bin/prstat (Solaris 8 prstat(1M)手册页) # /usr/ucb/ps -aux | head (Solaris 2.x) Q: 主流 Unix 操 作系统上 如何编 程 获 取 进 程 的 内存、 CPU 利用 状 况 ,

AIX、HP、SUN process memory usage process cpu time usage A: Nate Eldredge <[email protected]> man -s 3C getrusage D: 小四 <[email protected]>

在 SPARC/Solaris 2.6/7 下结论一致,只支持了 ru_utime 和 ru_stime

成员,其他成员 被设置成 0。FreeBSD 4.3-RELEASE 上测试,则不只支持 ru_utime 和

ru_stime 成员。从 FreeBSD 的 getrusage(2)手册页可以看到,这个函数源自 4.2 BSD。 至少对于 SPARC/Solaris 2.6/7,getrusage(3C)并无多大意义。 A: Robert Owen Thomas <[email protected]> 对于 Solaris,可以利用 procfs 接口,下面的例子获取指定进程的内存占

用情况 -------------------------------------------------------------------------- /* * @(#)memlook.c 1.0 10 Nov 1997 * Robert Owen Thomas [email protected] * memlook.c -- A process memory utilization reporting tool. * * gcc -Wall -O3 -o memlook memlook.c */

Page 88: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

#pragma ident "@(#)memlook.c 1.0 10 Nov 1997 Robert Owen Thomas [email protected]"

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/signal.h> #include <sys/syscall.h> #include <sys/procfs.h> #include <sys/param.h> #include <unistd.h> #include <fcntl.h> int counter = 10; int showUsage ( const char * ); void getInfo ( int, int ); int main ( int argc, char * argv[] ) { int fd, pid, timeloop = 0; char pidpath[BUFSIZ]; /* /usr/include/stdio.h: #define BUFSIZ

1024 */ switch ( argc ) { case 2: break; case 3: timeloop = atoi( argv[2] ); break; default: showUsage( argv[0] ); break; } /* end of switch */ pid = atoi( argv[1] ); sprintf( pidpath, "/proc/%-d", pid ); /* -表示向左靠 */ /* * /proc/1/是目录,但在这种用法中,就是直接打开目录,不是打开文件 */ if ( ( fd = open( pidpath, O_RDONLY ) ) < 0 ) { perror( pidpath ); exit( 1 ); } if ( 0 < timeloop )

Page 89: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

{ for ( ; ; ) { getInfo( fd, pid ); sleep( timeloop ); } } getInfo( fd, pid ); close( fd ); exit( 0 ); } /* end of main */ int showUsage ( const char * progname ) { fprintf( stderr, "%s: usage: %s < PID > [time delay]\n",

progname, progname ); exit( 3 ); } /* end of showUsage */ void getInfo ( int fd, int pid ) { prpsinfo_t prp; prstatus_t prs; if ( ioctl( fd, PIOCPSINFO, &prp ) < 0 ) { perror( "ioctl" ); exit( 5 ); } if ( ioctl( fd, PIOCSTATUS, &prs ) < 0 ) { perror( "ioctl" ); exit( 7 ); } if ( counter > 9 ) { fprintf( stdout, "PID\tIMAGE\t\tRSS\t\tHEAP\t\tSTACK\n" ); counter = 0; } fprintf( stdout, "%u\t%-9u\t%-9u\t%-15u\t%-15u\n", pid, ( unsigned int )prp.pr_bysize, ( unsigned int )prp.pr_byrssize, ( unsigned int )prs.pr_brksize, ( unsigned int )prs.pr_stksize ); counter++; } /* end of getInfo */ --------------------------------------------------------------------------

Page 90: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

4.2 Solaris 下如何获知 CPU速率

A: Philip Brown <[email protected]> psrinfo -v psrinfo | grep on-line | wc -l 简单给出 CPU 数目 A: scz <[email protected]> # /usr/platform/`uname -i`/sbin/prtdiag -v # /usr/platform/`uname -m`/sbin/prtdiag -v # /usr/bin/netstat -k cpu_info0 A: Tony Walton <[email protected]> 如果你装了 Sun Workshop,还可以尝试 fpversion 命令 # /opt/SUNWspro/bin/fpversion A SPARC-based CPU is available. CPU's clock rate appears to be approximately 266.1 MHz. Kernel says CPU's clock rate is 270.0 MHz. Kernel says main memory's clock rate is 90.0 MHz. Sun-4 floating-point controller version 0 found. An UltraSPARC chip is available. FPU's frequency appears to be approximately 277.1 MHz. Use "-xtarget=ultra2i -xcache=16/32/1:256/64/1" code-

generation option. Hostid = 0x80BC3CB3. #

4.3 如何编程获取 Solaris 系统当前内存大小

Q: 如何编程(或者有什么现成命令)获取 Solaris系统当前内存大小? A: Nithyanandham <[email protected]> 几个现成命令 /usr/platform/`uname -m`/sbin/prtdiag -v | grep Memory prtconf -v | grep Memory 如果装了 GNU top,也可以直接用 top 命令看到。 D: scz <[email protected]> truss prtconf 的输出中有如下内容 sysconfig(_CONFIG_PAGESIZE) = 8192 sysconfig(_CONFIG_PHYS_PAGES) = 16384

Page 91: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

Memory size: 128 Megabytes # /usr/ccs/bin/nm -nx /dev/ksyms | grep "|sysconfig$" 10626] |0x0000100ec110|0x0000000001bc|FUNC |GLOB |0 |ABS

|sysconfig # find /usr/include -type f -name "*.h" | xargs grep -l

_CONFIG_PAGESIZE /usr/include/sys/sysconfig.h # vi -R /usr/include/sys/sysconfig.h /* * cmd values for _sysconfig system call. * WARNING: This is an undocumented system call, * therefore future compatibility can not * guaranteed. */ #define _CONFIG_PAGESIZE 6 /* system page size */ #define _CONFIG_PHYS_PAGES 26 /* phys mem installed in

pages */ 参看 sysconf(3C)手册页。 _SC_PAGESIZE _SC_PAGE_SIZE _SC_PHYS_PAGES A: Casper Dik <[email protected]> -------------------------------------------------------------------------- /* * Program to determine the size installed physical memory on

Suns. * * Casper Dik. */ #define MEGABYTE 0x00100000 #define MAXMEM 0x7ff00000 #define THEMEM "/dev/mem" #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <unistd.h> int main ( int argc, char * argv[] ) { int fd = open( THEMEM, O_RDONLY ); char c; unsigned long pos, mapstart = 0; int totmb = 0; if ( fd == -1 )

Page 92: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

{ perror( THEMEM ); exit( 1 ); } for ( pos = 0; pos < MAXMEM; pos += MEGABYTE ) { if (lseek( fd, pos, 0 ) == -1 ) { perror( "lseek" ); exit( 1 ); } if ( read( fd, &c, 1 ) == -1 ) { int size = ( pos - mapstart ) / MEGABYTE; if ( size != 0 ) { printf( "found %3d MB starting at 0x%p\n", size, ( void * )mapst art ); totmb += size; } mapstart = pos + MEGABYTE; /* start of next possible mapping

*/ } } printf( "Total memory size: %d MB\n", totmb ); exit( 0 ); } -------------------------------------------------------------------------- 由于需要读访问/dev/mem,普通用户用户无法使用该程序。

5. 块设备相关问题

5.1 CDROM 设备究竟在哪里

Q: 为了 mount光驱,需要哪些包 A: SUNWvolr SUNWcstl SUNWcstlx D: Dennis Clarke <[email protected]> 1) su - root 2) /etc/init.d/volmgt stop 3) ls -1 /dev/dsk/c*s2

Page 93: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

4) mount -F hsfs -o ro /dev/dsk/c0t6d0s2 /cdrom 或者 1) /etc/init.d/volmgt stop 2) /etc/init.d/volmgt start

常见问题网址1、关于指针的问题 2、http://www.chinaunix.net/forum/viewtopic.php?t=52887

3、2、关于行缓存 4、http://www.chinaunix.net/forum/viewtopic.php?t=51199

5、3、exit、_exit()、atexit 以及 return 和 main 函数隐式返回的区别 6、http://www.chinaunix.net/forum/viewtopic.php?t=62916

7、4、ESQL(內嵌式 SQL)简述 8、http://www.chinaunix.net/forum/viewtopic.php?t=59492

9、5、fork、exec系列与 system、popen 区别 10、 http://www.chinaunix.net/forum/viewtopic.php?

t=62446 11、 6、如何将“字符串”转换为“浮点数” 12、 http://www.chinaunix.net/forum/viewtopic.php?

t=62074 13、 7、Socket 问题汇总 14、 socket 连接问问题 15、 http://www.chinaunix.net/forum/viewtopic.php?

t=60921

Page 94: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

16、 接受和发送报文 17、 http://www.chinaunix.net/forum/viewtopic.php?

t=59264 18、 短连接的问题 TIME_WAIT 状态问题 19、 http://www.chinaunix.net/forum/viewtopic.php?

t=59456 20、 bind 的问题 21、 http://www.chinaunix.net/forum/viewtopic.php?

t=54290 22、 UDP 编程相关问题 23、 http://www.chinaunix.net/forum/viewtopic.php?

t=51499 24、 网络编程中字符打印的问题 25、 http://www.chinaunix.net/forum/viewtopic.php?

t=51369 26、 TCP SOCKET 编程的读写函数使用规则 27、 http://www.chinaunix.net/forum/viewtopic.php?

t=51362 28、 SOCKET 编程中字节转化的问题 29、 http://www.chinaunix.net/forum/viewtopic.php?

t=49814 30、 TCP socket 编程中客户和服务器是怎么连接的。 31、 http://www.chinaunix.net/forum/viewtopic.php?

t=47847 32、 *******网络编程基础( 讨论版 V2 )********

33、 http://www.chinaunix.net/forum/viewtopic.php?

Page 95: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

t=44645 34、 讨论 bind()的一个问题 35、 http://www.chinaunix.net/forum/viewtopic.php?

t=46779 36、 select 的用法 37、 http://www.chinaunix.net/forum/viewtopic.php?

t=58606 38、 8、标准 c怎么实现数字到字符转换 39、 http://www.chinaunix.net/forum/viewtopic.php?

t=58206 40、 9、用 getenv()/putenv()实现 setenv()

41、 http://www.chinaunix.net/forum/viewtopic.php? t=58382

42、 10、dbx源码调试工具快速上手Z

43、 http://www.chinaunix.net/forum/viewtopic.php? t=59496

44、 11、c 和 c++的初学者的入门程序 45、 http://www.chinaunix.net/forum/viewtopic.php?

t=55594 46、 12、深度探索C++对象模型 47、 http://www.chinaunix.net/forum/viewtopic.php?

t=59107 48、 13、phread 解读(五)----系列 49、 http://www.chinaunix.net/forum/viewtopic.php?

t=58765 50、 http://www.chinaunix.net/forum/viewtopic.php?

t=58762

Page 96: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

51、 http://www.chinaunix.net/forum/viewtopic.php? t=58762

52、 http://www.chinaunix.net/forum/viewtopic.php? t=58759

53、 http://www.chinaunix.net/forum/viewtopic.php? t=58758

54、 14、关于 VI 编辑器的 TAB设置 55、 http://www.chinaunix.net/forum/viewtopic.php?

t=54691 56、 15、如何实现,输入一个字符串,然后退出循环 57、 http://www.chinaunix.net/forum/viewtopic.php?

t=53577 58、 16、数组地址的问题 59、 http://www.chinaunix.net/forum/viewtopic.php?

t=52347 60、 17、怎么查看共享内存的 ipcs 输出信息 61、 http://www.chinaunix.net/forum/viewtopic.php?

t=51861 62、 18、使用共享内存 shmat()的权限问题 63、 http://www.chinaunix.net/forum/viewtopic.php?

t=51658 64、 19、signal 函数可放在程序的什么地方? 65、 http://www.chinaunix.net/forum/viewtopic.php?

t=23866&postdays=0&postorder=asc&start=15 66、 20、IPC 的用处 67、 http://www.chinaunix.net/forum/viewtopic.php?

t=49960

Page 97: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

68、 21、IEEE 标准对于 fork 和 pthread_create 的描述 69、 http://www.chinaunix.net/forum/viewtopic.php?

t=50171 70、 22、有关 libftp 的移植问题 71、 http://www.chinaunix.net/forum/viewtopic.php?

t=47306 72、 23、malloc分配的内存的生命周期 73、 http://www.chinaunix.net/forum/viewtopic.php?

t=47544 74、 24、如何判断键盘输入为 F1,F2,F3,上下键,左右键等 75、 http://www.chinaunix.net/forum/viewtopic.php?

t=45859 76、 25、core dump系列分析之 1

77、 http://www.chinaunix.net/forum/viewtopic.php? t=68536

Unix 编程常见问题Unix 编程常见问题解答(FAQ / Frequently Asked Questions)(v1.37)

(中文版 v0.1.0)

关于这篇“常见问题解答”

************************

这篇“常见问题解答” 由 Patrick Horgan自一九九六年五月开始起草;因其历经

Page 98: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

数月未复更新,我从而接手编辑。我已经将其内容稍做重新安排并加入一些新的内

容;我仍然认为它仍处于“有待开发建设”中。

请将批评,建议,增补,更正或其它意见发给维护者,电子邮件地址: [email protected]

这篇文档的超文本版(英文)在 WEB 上可以获得。主站点设在

“http://www.erlenstar.demon.co.uk/unix/faq_toc.html”。

美国镜像站点设在“http://www.whitefang.com/unix/faq_toc.html”。

这篇文档可以用 FTP 的方式自主机 rtfm.mit.edu 和其众多镜像站点的news.answers 归

档中找到(译者注:URL 是 ftp://rtfm.mit.edu/pub/faqs/unix-faq/

programmer/faq)。

它的官方归档名是“unix-faq/programmer/faq”。其他将网络新闻组*.answers归档的

服务器也会在目录“comp.unix.programmer”下存放这篇文档。

其他信息资源未于此一一列出。读者可在新闻组 comp.unix.programmer

每周定期发

Page 99: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

布的[READ ME FIRST]中找到其他“常见问题”,书籍,原代码等资源的的连接。

关于管理新闻组的小问题等等也能在其中找到;而我只想在将这篇文档中特别讨

论问题和回答。

所有提供的资料已经经过维护者编辑,所有错误或疏忽是我的责任,跟提供者无

关。

这篇“常见问题解答”现在以 Texinfo资源格式维护;我使用“makeinfo”程序将其

转换成供新闻组阅读的原始字符文件格式,并使用“texi2html”程序将其转换成

HTML 格式。

版权所有:1997,1998, 1999, 2000 Andrew Gierth. 这篇文档允许通过新闻组或

电子邮件方式的分发,也允许在 news.answers 归档的镜像 FTP或WWW站点归档存

Page 100: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

放,并保证提供所有维持该文档更新应付出的努力。(本许可能够以个人为单位取

消)未经维护者许可,不允许将该文档以其他任何方式发表,无论是书面,WWW,

光盘,或在其他任何媒体。

内容提供者名单,无先后次序:

问题目录 ********

(译者:这里我有意保留原文以便于查询)

1. Process Control 进程控制

  1.1 Creating new processes: fork() 创建新进程:fork 函数

    1.1.1 What does fork() do? fork 函数干什么?

    1.1.2 What's the difference between fork() and vfork()? fork 函数 与 vfork 函数的区别在哪里?

    1.1.3 Why use _exit rather than exit in the child branch of a fork? 为何在一个 fork 的子进程分支中使用_exit 函数而不使用 exit 函数?

  1.2 Environment variables 环境变量

Page 101: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

    1.2.1 How can I get/set an environment variable from a program? 我怎样在程序中获得/设置环境变量?

    1.2.2 How can I read the whole environment? 我怎样读取整个环境变量表?

  1.3 How can I sleep for less than a second? 我怎样睡眠小于一秒?

  1.4 How can I get a finer-grained version of alarm()? 我怎样得到一个更细分时间单位的 alarm 函数版本(译者注:希望 alarm 的时间小于一秒)?

  1.5 How can a parent and child process communicate? 父子进程如何通信?

  1.6 How do I get rid of zombie processes? 我怎样去除僵死进程?

    1.6.1 What is a zombie? 何为僵死进程?

    1.6.2 How do I prevent them from occuring? 我怎样避免它们的出现?

  1.7 How do I get my program to act like a daemon? 我怎样使我的程序作为守护程序运行?

  1.8 How can I look at process in the system like ps does? 我怎样象 ps 程序一样审视系统的进程?

Page 102: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

  1.9 Given a pid, how can I tell if it's a running program? 给定一个进程号(译者注:pid: process ID),我怎样知道它是个正在运行的程序?

  1.10 What's the return value of system/pclose/waitpid? system函数,pclose 函数,waitpid 函数 的返回值是什么?

  1.11 How do I find out about a process' memory usage? 我怎样找出一个进程的存储器使用情况?

  1.12 Why do processes never decrease in size? 为什么进程的大小不缩减?

  1.13 How do I change the name of my program (as seen by `ps')? 我怎样改变我程序的名字(即“ps”看到的名字)?

  1.14 How can I find a process' executable file? 我怎样找到进程的相应可执行文件?

    1.14.1 So where do I put my configuration files then? 那么,我把配置文件放在哪里呢?

  1.15 Why doesn't my process get SIGHUP when its parent dies? 为何父进程死时,我的进程未得到 SIGHUP信号?

  1.16 How can I kill all descendents of a process? 我怎样杀死一个进程的所有派生进程?

2. General File handling (including pipes and sockets) 一般文件操作(包括管道和套接字)

Page 103: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

  2.1 How to manage multiple connections? 怎样管理多个连接?

    2.1.1 How do I use select()? 我怎样使用 select()?

    2.1.2 How do I use poll()? 我怎样使用 poll() ?

    2.1.3 Can I use SysV IPC at the same time as select or poll? 我是否可以将 SysV 进程间通信 (译者注:IPC: Interprocess

Communications) 与 select或 poll 同

时使用?   2.2 How can I tell when the other end of a connection shuts

down? 我怎么知道连接的另一端已关闭?

  2.3 Best way to read directories? 读目录的最好方法?

  2.4 How can I find out if someone else has a file open? 我怎么知道其他人已经打开一个文件?

  2.5 How do I `lock' a file? 我怎样锁定一个文件?   2.6 How do I find out if a file has been updated by another

process? 我怎么知道一个文件是否已被其他进程更新?

  2.7 How does the `du' utility work? “du”工具程序是怎么工作的?

  2.8 How do I find the size of a file? 我怎么知道一个文件的大小?

Page 104: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

  2.9 How do I expand `~' in a filename like the shell does? 我怎样象 shell 程序一样将一个文件名中含有的“~”展开?

  2.10 What can I do with named pipes (FIFOs)? 我能用有名管道(FIFOs)(译者注:FIFO: First In First Oout)干什么?

    2.10.1 What is a named pipe? 什么是有名管道?

    2.10.2 How do I create a named pipe? 我怎样创建一个有名管道?

    2.10.3 How do I use a named pipe? 我怎样使用一个有名管道?

    2.10.4 Can I use a named pipe across NFS? 我能基于网络文件系统(译者注:NFS:Network File System)使用有名管道吗?

    2.10.5 Can multiple processes write to the pipe simultaneously? 多个进程能否同时向这个管道写执行写操作?

    2.10.6 Using named pipes in applications 在应用程序中使用有名管道。

3. Terminal I/O 终端输入/输出(I/O:input/output)

  3.1 How can I make my program not echo input? 我怎样使我的程序不回射输入?

Page 105: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

  3.2 How can I read single characters from the terminal? 我怎样从终端读取单个字符?

  3.3 How can I check and see if a key was pressed? 我怎样检查是否一个键被摁下?

  3.4 How can I move the cursor around the screen? 我怎样将光标在屏幕里移动?

  3.5 What are pttys? pttys(pttys:Pseudo-teletypes)是什么?

  3.6 How to handle a serial port or modem? 怎样控制一个串行口和调制解调器(译者注:modem: modulate-demodulate)

    3.6.1 Serial device names and types 串行设备和类型

    3.6.2 Setting up termios flags 设置 termios 的标志位       3.6.2.1 c_iflag

      3.6.2.2 c_oflag

      3.6.2.3 c_cflag

      3.6.2.4 c_lflag

      3.6.2.5 c_cc

4. System Information 系统信息

  4.1 How can I tell how much memory my system has? 我怎样知道我的系统有多少存储器容量?

Page 106: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

  4.2 How do I check a user's password? 我怎样检查一个用户的口令?

    4.2.1 How do I get a user's password? 我怎样得到一个用户的口令?

    4.2.2 How do I get shadow passwords by uid? 我怎样通过用户号(译者注:uid: User ID)得到阴影口令文件中的口令?

    4.2.3 How do I verify a user's password? 我怎样核对一个用户的口令?

5. Miscellaneous programming 编程杂技

  5.1 How do I compare strings using wildcards? 我怎样使用通配字符比较字符串?

    5.1.1 How do I compare strings using filename patterns? 我怎样使用文件名通配模式比较字符串?

    5.1.2 How do I compare strings using regular expressions? 我怎样使用正则表达式比较字符串?

  5.2 What's the best way to send mail from a program? 什么是在程序中发送电子邮件的最好方法?

    5.2.1 The simple method: /bin/mail 简单方法:/bin/mail

Page 107: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

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

      5.2.2.1 Supplying the envelope explicitly 显式提供收件人信息

      5.2.2.2 Allowing sendmail to deduce the recipients 允许sendmail 程序根据邮件内容分析出收件人

6. Use of tools 工具的使用

  6.1 How can I debug the children after a fork? 我怎样调试 fork 函数产生的子进程?

  6.2 How to build library from other libraries? 怎样通过其他库文件建立新的库文件?

  6.3 How to create shared libraries / dlls? 怎样创建动态连接库/dlls?

  6.4 Can I replace objects in a shared library? 我能更改一个动态连接库里的目标吗?

  6.5 How can I generate a stack dump from within a running program? 我能在一个运行着的程序中生成堆栈映象吗?

1. 进程控制

***********

Page 108: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

1.1 创建新进程:fork 函数

========================

1.1.1 fork 函数干什么? ----------------------

     #include <sys/types.h>

     #include <unistd.h>

     pid_t fork(void);

‘fork()’函数用于从已存在进程中创建一个新进程。新进程称为子进程,而原进程称为

父进程。你可以通过检查‘fork()’函数的返回值知道哪个是父进程,哪个是子进程。父

进程得到的返回值是子进程的进程号,而子进程则返回 0。以下这个范例程序说明它的基本

功能:      pid_t pid;

     switch (pid = fork())

     {

     case -1:

         /* 这里 pid 为-1,fork 函数失败 */

         /* 一些可能的原因是 */

Page 109: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         /* 进程数或虚拟内存用尽 */

         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()’语句,但是上面的形式是

一个有用的惯用方法。

知道子进程自父进程继承什么或未继承什么将有助于我们。下面这个名单会因为

Page 110: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

不同 Unix 的实现而发生变化,所以或许准确性有了水份。请注意子进程得到的是

这些东西的 *拷贝*,不是它们本身。

由子进程自父进程继承到:

   * 进程的资格(真实(real)/有效(effective)/已保存(saved) 用户号(UIDs)和组号(GIDs))

   * 环境(environment)

   * 堆栈

   * 内存

   * 打开文件的描述符(注意对应的文件的位置由父子进程共享,这会引起含糊情况)

   * 执行时关闭(close-on-exec) 标志 (译者注:close-on-exec 标志可通过 fnctl()对文件描

     述符设置,POSIX.1 要求所有目录流都必须在 exec 函数调用时关闭。更详细说明,

     参见<<UNIX环境高级编程>> W. R. Stevens, 1993, 尤晋元等译(以下简称<<高级编

Page 111: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     程>>), 3.13节和 8.9节)

   * 信号(signal)控制设定

   * nice值 (译者注:nice值由 nice 函数设定,该值表示进程的优先级,数值越小,优

     先级越高)

   * 进程调度类别(scheduler class) (译者注:进程调度类别指进程在系统中被调度时所

     属的类别,不同类别有不同优先级,根据进程调度类别和 nice值,进程调度程序可计

     算出每个进程的全局优先级(Global process prority),优先级高的进程优先执行)

   * 进程组号

   * 对话期 ID(Session ID) (译者注:译文取自<<高级编程>>,指:进程所属的对话期

     (session)ID, 一个对话期包括一个或多个进程组, 更详细说明参见<

<高级编程>>

     9.5节)

Page 112: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   * 当前工作目录

   * 根目录 (译者注:根目录不一定是“/”,它可由 chroot 函数改变)

   * 文件方式创建屏蔽字(file mode creation mask (umask)) (译者注:译文取自<<高级编

     程>>,指:创建新文件的缺省屏蔽字)

   * 资源限制

   * 控制终端

子进程所独有:

   * 进程号

   * 不同的父进程号(译者注:即子进程的父进程号与父进程的父进程号不同,父进

     程号可由 getppid 函数得到)

   * 自己的文件描述符和目录流的拷贝(译者注:目录流由 opendir 函数创建,因其为

     顺序读取,顾称“目录流”)

   * 子进程不继承父进程的进程,正文(text),数据和其它锁定内存(memory locks)

Page 113: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     (译者注:锁定内存指被锁定的虚拟内存页,锁定后,不允许内核将其在必要时

     换出(page out),详细说明参见<<The GNU C Library Reference

Manual>> 2.2 版,

     1999, 3.4.2节)

   * 在 tms 结构中的系统时间(译者注:tms 结构可由 times 函数获得,它保存四个数据

     用于记录进程使用中央处理器(CPU:Central Processing Unit)的时间,包括:用户时

     间,系统时间,用户各子进程合计时间,系统各子进程合计时间)

   * 资源使用(resource utilizations)设定为 0

   * 阻塞信号集初始化为空集(译者注:原文此处不明确,译文根据 fork 函数手册页

     稍做修改)

   * 不继承由 timer_create 函数创建的计时器

   * 不继承异步输入和输出

1.1.2 fork 函数 与 vfork 函数的区别在哪里里?

Page 114: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

-------------------------------------------

有些系统有一个系统调用‘vfork()’,它最初被设计成‘fork()’的较少额外支出

(lower-overhead)版本。因为‘fork()’包括拷贝整个进程的地址空间,所以非常

“昂贵”,这个‘vfork()’函数因此被引入。(在 3.0BSD 中)(译者注:BSD:

Berkeley Software Distribution)

*但是*,自从‘vfork()’被引入,‘fork()’的实现方法得到了很大改善,最值得

注意的是“写操作时拷贝”(copy-on-write)的引入,它是通过允许父子进程可访问

相同物理内存从而伪装(fake)了对进程地址空间的真实拷贝,直到有进程改变内

存中数据时才拷贝。这个提高很大程度上抹杀了需要‘vfork()’的理由;事实上,

一大部份系统完全丧失了‘vfork()’的原始功能。但为了兼容,它们仍然提供

Page 115: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

‘vfork()’函数调用,但它只是简单地调用‘fork()’,而不试图模拟所有‘vfork()’

的语义(semantics, 译文取自<<高级编程>>,指定义的内容和做法)。

结论是,试图使用任何‘fork()’和‘vfork()’的不同点是*很*不明智的。事实上,

可能使用‘vfork()’根本就是不明智的,除非你确切知道你想*干什么*。

两者的基本区别在于当使用‘vfork()’创建新进程时,父进程将被暂时阻塞,而

子进程则可以借用父进程的地址空间。这个奇特状态将持续直到子进程要么退

出,要么调用‘execve()’,至此父进程才继续执行。

这意味着一个由‘vfork()’创建的子进程必须小心以免出乎意料地改变父进程的

变量。特别的,子进程必须不从包含‘vfork()’调用的函数返回,而且必须不调

用‘exit()’(如果它需要退出,它需要使用‘_exit()’;事实上,对于使用正常

‘fork()’创建的子进程这也是正确的)(译者注:参见 1.1.3)

Page 116: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

1.1.3 为何在一个 fork 的子进程分支中使用_exit 函数而不使用 exit 函数?

-----------------------------------------------------------------

‘exit()’与‘_exit()’有不少区别在使用‘fork()’,特别是‘vfork()’时变得很

突出。

‘exit()’与‘_exit()’的基本区别在于前一个调用实施与调用库里用户状态结构

(user-mode constructs)有关的清除工作(clean-up),而且调用用户自定义的清除程序

(译者注:自定义清除程序由 atexit 函数定义,可定义多次,并以倒序执行),相对

应,后一个函数只为进程实施内核清除工作。

在由‘fork()’创建的子进程分支里,正常情况下使用‘exit()’是不正确的,这是

因为使用它会导致标准输入输出(译者注:stdio: Standard Input

Output)的缓冲区被

Page 117: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

清空两次,而且临时文件被出乎意料的删除(译者注:临时文件由 tmpfile

函数创建

在系统临时目录下,文件名由系统随机生成)。在 C++程序中情况会更糟,因为静

态目标(static objects)的析构函数(destructors)可以被错误地执行。(还有一些特殊情

况,比如守护程序,它们的*父进程*需要调用‘_exit()’而不是子进程;适用于绝

大多数情况的基本规则是,‘exit()’在每一次进入‘main’函数后只调用一次。)

在由‘vfork()’创建的子进程分支里,‘exit()’的使用将更加危险,因为它将影响

*父*进程的状态。

1.2 环境变量

============

1.2.1  如何从程序中获得/设置环境变量? --------------------------------------

获得一个环境变量可以通过调用‘getenv()’函数完成。

Page 118: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     #include <stdlib.h>

     char *getenv(const char *name);

设置一个环境变量可以通过调用‘putenv()’函数完成。      #include <stdlib.h>

     int putenv(char *string);

变量 string 应该遵守"name=value"的格式。已经传递给 putenv 函数的字符串*不*能够被

释放或变成无效,因为一个指向它的指针将由‘putenv()’保存。这意味着它必须是

在静态数据区中或是从堆(heap)分配的。如果这个环境变量被另一个‘putenv()’的

调用重新定义或删除,上述字符串可以被释放。

/* 译者增加:

因为 putenv()有这样的局限,在使用中经常会导致一些错

误,GNU libc 中还包括了两个 BSD风格的函数:

#include <stdlib.h>

int setenv(const char *name, const char *value, int replace);

void unsetenv(const char *name);

Page 119: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

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));

Page 120: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

           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");

     }

Page 121: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

现在假设你想创建一个新的环境变量,变量名为‘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”的格式相同)。

Page 122: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

这个数组以一个‘空’(NULL)指针标记结束。这里是一个打印当前环境变量列表

的小程序(类似‘printenv’)。      #include <stdio.h>

     extern char **environ;

     int main()

     {

         char **ep = environ;

         char *p;

         while ((p = *ep++))

             printf("%s\n", p);

         return 0;

     }

一般情况下,‘envrion’变量作为可选的第三个参数传递给‘main()’;就是说,

上面的程序可以写成:      #include <stdio.h>

     int main(int argc, char **argv, char **envp)

     {

         char *p;

         while ((p = *envp++))

             printf("%s\n", p);

Page 123: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         return 0;

     }

虽然这种方法被广泛的操纵系统所支持(译者注:包括DOS),这种方法事实上并

没有被 POSIX(译者注:POSIX: Portable Operating System Interace)

标准所定义。(一

般的,它也比较没用)

1.3 我怎样睡眠小于一秒?

========================

在所有 Unix 中都有的‘sleep()’函数只允许以秒计算的时间间隔。如果你想要更

细化,那么你需要寻找替换方法:

   * 许多系统有一个‘usleep()’函数

   * 你可以使用‘select()’或‘poll()’,并设置成无文件描述符并试验;一个普

     遍技巧是基于其中一个函数写一个‘usleep()’函数。(参见comp.unix.questions

     FAQ 的一些例子)

Page 124: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   * 如果你的系统有 itimers(很多是有的)(译者注:setitimer 和getitimer 是两个操作

     itimers 的函数,使用“man setitimer”确认你的系统支持),你可以用它们自己撺一

     个‘usleep()’。(参见BSD源程序的‘usleep()’以便知道怎样做)

   * 如果你有 POSIX实时(realtime)支持,那会有一个‘nanosleep()’函数。

众观以上方法,‘select()’可能是移植性最好的(直截了当说,它经常比

‘usleep()’或基于 itimer 的方法更有效)。但是,在睡眠中捕获信号的做法会有

所不同;基于不同应用,这可以成为或不成为一个问题。

无论你选择哪条路,意识到你将受到系统计时器分辨率的限制是很重要的(一

些系统允许设置非常短的时间间隔,而其他的系统有一个分辨率,比如说10毫

秒,而且总是将所有设置时间取整到那个值)。而且,关于‘sleep()’,你设置

Page 125: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

的延迟只是最小值(译者注:实际延迟的最小值);经过这段时间的延迟,会有

一个中间时间间隔直到你的进程重新被调度到。

1.4  我怎样得到一个更细分时间单位的 alarm 函数版本?

==================================================

当今Unix系统倾向于使用‘setitimer()’函数实现闹钟,它比简单的‘alarm()’函

数具有更高的分辨率和更多的选择项。一个使用者一般需要首先假设‘alarm()’

和‘setitimer(ITIMER_REAL)’可能是相同的底层计时器,而且假设同时使用两

种方法会造成混乱。

Itimers 可被用于实现一次性或重复信号;而且一般有 3种不同的计时器可以用:

`ITIMER_REAL'

      计数真实(挂钟)时间,然后发送‘SIGALRM’信号 `ITIMER_VIRTUAL'

Page 126: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

      计数进程虚拟(用户中央处理器)时间,然后发送‘SIGVTALRM’信号 `ITIMER_PROF'

     计数用户和系统中央处理器时间,然后发送‘SIGPROF’信号;它供解释器

     用来进行梗概处理(profiling)

然而 itimers 不是许多标准的一部份,尽管它自从 4.2BSD 就被提供 。POSIX实时标

准的扩充定义了类似但不同的函数。

1.5 父子进程如何通信?

======================

一对父子进程可以通过正常的进程间通信的办法(管道,套接字,消息队列,共

享内存)进行通信,但也可以通过利用它们作为父子进程的相互关系而具有的一

些特殊方法。

一个最显然的方法是父进程可以得到子进程的退出状态。

因为子进程从它的父进程继承文件描述符,所以父进程可以打开一个管道的两端,

Page 127: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

然后 fork,然后父进程关闭管道这一端,子进程关闭管道另一端。这正是你从你的

进程调用‘popen()’函数运行另一个程序所发生的情况,也就是说你可以向

‘popen()’返回的文件描述符进行写操作而子进程将其当作自己的标准输入,或

者你可以读取这个文件描述符来看子进程向标准输出写了什么。(‘popen()’

函数

的 mode 参数定义你的意图(译者注:mode=“r”为读,mode=“w”为写);如果你

想读写都做,那么你可以并不困难地用管道自己做到)

而且,子进程继承由父进程用 mmap 函数映射的匿名共享内存段(或者通过映射特

殊文件‘/dev/zero’);这些共享内存段不能从无关的进程访问。

1.6 我怎样去除僵死进程?

========================

1.6.1 何为僵死进程? --------------------

Page 128: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

当一个程序创建的子进程比父进程提前结束,内核仍然保存一些它的信息以便父

进程会需要它 - 比如,父进程可能需要检查子进程的退出状态。为了得到这些信

息,父进程调用‘wait()’;当这个调用发生,内核可以丢弃这些信息。

在子进程终止后到父进程调用‘wait()’前的时间里,子进程被称为‘僵死进程’

(‘zombie’)。(如果你用‘ps’,这个子进程会有一个‘Z’出现在它的状态区

里指出这点。)即使它没有在执行,它仍然占据进程表里一个位置。(它不消耗其

它资源,但是有些工具程序会显示错误的数字,比如中央处理器的使用;这是

因为为节约空间进程表的某些部份与会计数据(accounting info)是共用(overlaid)的。)

这并不好,因为进程表对于进程数有固定的上限,系统会用光它们。即使系统没

有用光 ,每一个用户可以同时执行的进程数有限制,它总是小于系统的限制。

Page 129: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

顺便说一下,这也正是你需要总是 检查‘fork()’是否失败的一个原因。

如果父进程未调用 wait 函数而终止,子进程将被‘init’进程收管,它将控制子进

程退出后必须的清除工作。(‘init’是一个特殊的系统程序,进程号为 1 - 它实际

上是系统启动后运行的第一个程序),

1.6.2 我怎样避免它们的出现? ----------------------------

你需要却认父进程为每个子进程的终止调用‘wait()’(或者‘waitpid()’,

‘wait3()’,等等); 或者,在某些系统上,你可以指令系统你对子进程的退出状

态没有兴趣。(译者注:在 SysV系统上,可以调用 signal 函数,设置SIGCLD信号为

SIG_IGN,系统将不产生僵死进程, 详细说明参见<<高级编程>>10.7

节)

另一种方法是*两次*‘fork()’,而且使紧跟的子进程直接退出,这样造成孙子进

Page 130: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

程变成孤儿进程(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(&sa.sa_mask);

         sigaction(SIGCHLD, &sa, NULL);

如果这是成功的,那么‘wait()’函数集将不再正常工作;如果它们中任何一个被

调用,它们将等待直到*所有*子进程已经退出,然后返回失败,并且

‘errno==ECHILD’。

Page 131: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

另一个技巧是捕获 SIGCHLD信号,然后使信号处理程序调用‘waitpid()’或

‘wait3()’。参见范例章节的完整程序。

1.7 我怎样使我的程序作为守护程序运行?

======================================

一个“守护程序”进程通常被定义为一个后台进程,而且它不属于任何一个终端

会话,(terminal session)。许多系统服务由守护程序实施;如网络服务,打印等。

简单地在后台启动一个程序并非足够是这些长时间运行的程序;那种方法没有正

确地将进程从启动它的终端脱离(detach)。而且,启动守护程序的普遍接受的的方

法是简单地手工执行或从 rc脚本程序执行(译者注:rc:runcom);并希望这个守护

程序将其*自身*安置到后台。

这里是成为守护程序的步骤:

Page 132: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

  1. 调用‘fork()’以便父进程可以退出,这样就将控制权归还给运行你程序的

     命令行或 shell 程序。需要这一步以便保证新进程不是一个进程组头领进程(process

     group leader)。下一步,‘setsid()’,会因为你是进程组头领进程而失败。

  2. 调用‘setsid()’ 以便成为一个进程组和会话组的头领进程。由于一个控制终端

     与一个会话相关联,而且这个新会话还没有获得一个控制终端,我们的进程没

     有控制终端,这对于守护程序来说是一件好事。

  3. 再次调用‘fork()’所以父进程(会话组头领进程)可以退出。这意味着我们,一

     个非会话组头领进程永远不能重新获得控制终端。

  4. 调用‘chdir("/")’确认我们的进程不保持任何目录于使用状态。不做这个会导

     致系统管理员不能卸装(umount)一个文件系统,因为它是我们的当前工作目录。

Page 133: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     [类似的,我们可以改变当前目录至对于守护程序运行重要的文件所在目录]

  5. 调用‘umask(0)’以便我们拥有对于我们写的任何东西的完全控制。我们不知

     道我们继承了什么样的 umask。

     [这一步是可选的](译者注:这里指步骤 5,因为守护程序不一定需要写文件)

  6. 调用‘close()’关闭文件描述符 0,1 和 2。这样我们释放了从父进程继承的标

     准输入,标准输出,和标准错误输出。我们没办法知道这些文描述符符可能

     已经被重定向去哪里。注意到许多守护程序使用‘sysconf()’来确认

     ‘_SC_OPEN_MAX’的限制。‘_SC_OPEN_MAX’告诉你每个进程能够打

     开的最多文件数。然后使用一个循环,守护程序可以关闭所有可能的文件描

     述符。你必须决定你需要做这个或不做。如果你认为有可能有打开的文件描

Page 134: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     述符,你需要关闭它们,因为系统有一个同时打开文件数的限制。

  7. 为标准输入,标准输出和标准错误输出建立新的文件描述符。即使你不打算

     使用它们,打开着它们不失为一个好主意。准确操作这些描述符是基于各自

     爱好;比如说,如果你有一个日志文件,你可能希望把它作为标准输出和标

     准错误输出打开,而把‘/dev/null’作为标准输入打开;作为替代方法,你可

     以将‘/dev/console’作为标准错误输出和/或标准输出打开,而‘/dev/null’作

     为标准输入,或者任何其它对你的守护程序有意义的结合方法。(译者注一

     般使用 dup2 函数原子化关闭和复制文件描述符,参见<<高级编程>

>3.12节)

如果你的守护程序是被‘inetd’启动的,几乎所有这些步骤都不需要(或不建议

Page 135: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

采用)。在那种情况下,标准输入,标准输出和标准错误输出都为你指定为网络

连接,而且‘fork()’的调用和会话的操纵不应做(以免使‘inetd’造成混乱)。只

有‘chdir()’和‘umask()’这两步保持有用。

1.8  我怎样象 ps 程序一样审视系统的进程?

=======================================

你真的不该想做这个。

到目前为止,移植性最好的是调用‘popen(pscmd,"r")’并处理它的输出。(pscmd

应当是类似 SysV系统上的‘“ps -ef”’,BSD系统有很多可能的显示选项:选

择一个。)

在范例章节有这个问题的两个完整解决方法;一个适用于 SunOS 4,它需要 root权

限执行并使用‘kvm_*’例程从内核数据结果读取信息;另一种适用于SVR4系统

(包括 Sun OS 5),它使用‘/proc’文件系统。

Page 136: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

在具有 SVR4.2风格‘/proc’的系统上更简单;只要对于每一个感兴趣的进程号从

文件‘/proc/进程号/psinfo’读取一个 psinfo_t 结构。但是,这种可能是最清晰的方

法也许又是最不得到很好支持的方法。(在 FreeBSD 的‘/proc’上,你从

‘/proc/进程号/status’读取一个半未提供文档说明(semi-

undocumented)的可打印字

符串;Linux 有一些与其类似的东西)

1.9  给定一个进程号,我怎样知道它是个正在运行的程序?

=====================================================

使用‘kill()’函数,而已 0 作为信号代码(signal number)。

从这个函数返回有四种可能的结果:

   * ‘kill()’返回 0

       - 这意味着一个给定此进程号的进程退出,系统允许你向它发送信号。该进

         程是否可以是僵死进程与不同系统有关。

Page 137: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   * ‘kill()’返回-1,‘errno == ESRCH’

       - 要么不存在给定进程号的进程,要么增强的安全机制导致系统否认它的存

         在。(在一些系统上,这个进程有可能是僵死进程。)

   * ‘kill()’返回-1,‘errno == EPERM’

       - 系统不允许你杀死(kill)这个特定进程。这意味着要么进程存在(它又可能是

         僵死进程),要么严格的增强安全机制起作用(比如你的进程不允许发送信号

         给*任何人*)。

    * ‘kill()’返回-1,伴以其它‘errno’值

       - 你有麻烦了!

用的最多的技巧是认为调用“成功”或伴以‘EPERM’的“失败”意味着进程存

在,而其它错误意味着它不存在。

如果你特别为提供‘/proc’文件系统的系统(或所有类似系统)写程序,一个替换

Page 138: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

方法存在:检查‘proc/进程号’是否存在是可行的。

1.10  system 函数,pclose 函数,waitpid 函数 的返回值是什么?

==========================================================

     ‘system()’,‘pclose()’或者‘waitpid()’的返回值不象是我进程的退出值(exit

        value)(译者注:退出值指调用 exit() 或_exit()时给的参数)... 或者退出值左移了 8

       位...这是怎么搞的?

手册页是对的,你也是对的! 如果查阅手册页的‘waitpid()’你会发现进程的返回

值被编码了。正常情况下,进程的返回值在高 16 位,而余下的位用来作其它事。

如果你希望可移植,你就不能凭借这个,而建议是你该使用提供的宏。这些宏总

是在‘wait()’或‘wstat’的文档中说明了。

为了不同目的定义的宏(在‘<sys/wait.h>’)包括(stat 是‘waitpid()’返回的值):

Page 139: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

`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

Page 140: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

1.11 我怎样找出一个进程的存储器使用情况?

=========================================

如果提供的话,参看‘getrusage()’手册页

1.12 为什么进程的大小不缩减?

=============================

当你使用‘free()’函数释放内存给堆时,几乎所有的系统都*不*减少你程序的

对内存的使用。被‘free()’释放的内存仍然属于进程地址空间的一部份,并将

被将来的‘malloc()’请求所重复使用。

如果你真的需要释放内存给系统,参看使用‘mmap()’分配私有匿名内存映射

(private anonymous mappings)。当这些内存映射被取消映射时,内存真的将其释放给

系统。某些‘malloc()’的实现方法(比如在 GNU C 库中)在允许时自动使用‘mmap()’

实施大容量分配;这些内存块(blocks)随着‘free()’被释放回系统。

Page 141: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

当然,如果你的程序的大小增加而你认为它不应该这样,你可能有一个‘内存泄

露’(‘memory leak’)- 即在你的的程序中有缺陷(bug)导致未用的内存没释放。

1.13 我怎样改变我程序的名字(即“ps”看到的名字)?

=================================================

在 BSD风格的系统中,‘ps’程序实际上审视运行进程的地址空间从而找到当前

的‘argv[]’,并显示它。这使得程序可以通过简单的修改‘argv[]’以改变它的

名字。

在 SysV风格的系统中,命令的名字和参数的一般头 80 字节是存放在进程的 u-区(

u-area), 所以不能被直接修改。可能有一个系统调用用来修改它(不象是这样),

但是其它的话,只有一个方法就是实施一个‘exec()’,或者些内核内存(危险,

而且只有 root才有可能)。

Page 142: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

一些系统(值得注意的是 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’变量的查

Page 143: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

询来查找这个程序。但是,不能保证成功,因为有可能执行程序时‘argv[0]’是

一些任意值,也不排除这个可执行文件在执行后可能已经被更名或删除的情况。

如果所有你想做的只是能打印一个和错误消息一起出现的合适的名字,那么最好

的方法在‘main()’函数中将‘argv[0]’的值保存在全局变量中以供整个程序使

用。虽然没有保证说‘argv[0]’的值总是有意义,但在大多数情况下它是最好的

选择。

人们询问这个问题的最普通原因是意图定位他们程序的配置文件。这被认为是

不好的形式;包含可执行文件的目录应当*只*包含可执行文件,而且基于管理的

要求经常试图将配置文件放置在和可执行文件不同的文件系统。

试图做这个的一个比较不普通但更正规的理由是允许程序调用‘exec()’执行它

Page 144: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

自己;这是一种用来完全重新初始化进程(比如被用于一些‘sendmail’的版本)的

办法(比如当一个守护程序捕获一个‘SIGHUP’信号)。

1.14.1 那么,我把配置文件放在哪里里呢?

为配置文件安排正确的目录总是取决于你使用的 Unix系统的特点;

‘/var/opt/PACKAGE’,‘/usr/local/lib’,‘/usr/local/etc’,或者任何其它一

些可能的地方。用户自定义的配置文件通常是在‘$HOME’下的以“.”开始的隐藏文件(

比如‘$HOME/.exrc’)。

从一个在不同系统上都能使用的软件包(package)的角度看,它通常意味着任何站

点范围(sitewide)的配置文件的位置有个已设定的缺省值,可能情况是使用一个在

配置脚本程序里的‘--prefix’选项(Autoconf 脚本程序集做这个工作)。你会希望允

许这个缺省值在程序执行时被一个环境变量重载。(如果你没使用配置脚本程序,

Page 145: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

那么在编译时,将这个位置缺省值作为‘-D’选项放入项目文件(Makefile),或者

将其放入一个‘config.h’头文件,或做其它类似的工作)

--

用户自定义配置需要放置于一个在‘$HOME’下的文件名“.”打头的文件,或者

在需要多个配置文件时,建立文件名“.”打头的子目录。(在列目录时,文件名以

“.”打头的文件或目录缺省情况下被忽略。)避免在‘$HOME’建立多个文件因

为这会造成非常杂乱的情况。当然,你也应该允许用户通过一个环境变量重载这个

位置。即使不能找到某个用户的配置文件,程序仍应当以适宜的方式执行。

1.15 为何父进程死时,我的进程未得到 SIGHUP 信号?

===============================================

因为本来就没有设想是这样做的。

Page 146: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

‘SIGHUP’是一个信号,它按照惯例意味着“终端线路被挂断”。它与父进程

无关,而且通常由 tty驱动程序产生(并传递给前台的进程组)。

但是,作为会话管理系统(session management system)的一部份,确切说有两种情况

下‘SIGHUP’会在一个进程死时发送出:

   * 当一个终端设备与一个会话相关联,而这个会话的会话首领进程死时,

     ‘SIGHUP’被发送至这个终端设备的所有前台进程组。

   * 当一个进程死去导致一个进程组变成孤儿,而且该进程组里一个或多个进程

     处于*暂停*状态时,那么‘SIGHUP’和‘SIGCONT’被发送至这个孤儿进程

     组的所有成员进程。(一个孤儿进程组是指在该进程组中没有一个成员进程的

     父进程属于和该进程组相同的会话的其它进程组。)

Page 147: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

1.16 我怎样杀死一个进程的所有派生进程?

=======================================

没有一个完全普遍的方法来做这个。虽然你可以通过处理‘ps’的输出确定进

程间的相互关系,但因为它只表示系统的一瞬间的状态(snapshot)所以并不可靠。

但是,如果你启动一个子进程,而它可能生成它自己的子进程,而你意图一次杀

死整个生成的事务(job),解决方法是将最先启动的子进程置于一个新的进程组,

当你需要时杀死整个进程组。

建议为创建进程组而使用的函数是‘setpgid()’。在可能情况下,使用这个函数

而不使用‘setpgrp()’,因为后一个在不同系统中有所不同(在一些系统上‘setgrp();’

等同于‘setpgid(0,0);’,在其它系统上,‘setpgrp()’和‘setpgid()’相同)。

参见范例章节的事务-控制范例程序。

Page 148: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

放置一个子进程于其自身的进程组有一些影响。特别的,除非你显式地将该进程

组放置于前台,它将被认为是一个后台事务并具有以下结果:

   * 试图从终端读取的进程将被‘SIGTTIN’信号暂停。

   * 如果设置终端模式‘tostop’,那么试图向终端写的进程将被‘SIGTTOU’

     信号暂停。(试图改变终端模式也导致这个结果,且不管当前‘tostop’

是否

     设置)

   * 子进程将不会收到从终端发出的键盘信号(比如‘SIGINT’或‘SIGQUIT’)

在很多应用程序中输入和输出总会被重定向,所以最显著的影响将是丧失键盘

信号。父进程需要安排程序起码捕获‘SIGINIT’和‘SIGQUIT’(可能情况下

还有‘SIGTERM’),并在需要情况下清除后台事务。

2. 一般文件操作(包括管道和套接字)

*********************************

Page 149: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

请同时参考套接字 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()本质上来讲做的是同一件事,只是完成的方法不一样。两者都

Page 150: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

通过检验一组文件描述符来检测是否有特定的时间将在上面发生并在一定的时间

内等待其发生。

[重要事项:无论 select()还是 poll()都不对普通文件起很大效用,它们着重用

于套接口(socket)、管道(pipe)、伪终端(pty)、终端设备(tty)和其他一些字符

设备,但是这些操作都是系统相关(system-dependent)的。]

2.2.1 我如何使用 select()函数? ------------------------------

select()函数的接口主要是建立在一种叫'fd_set'类型的基础上。它('fd_set')

是一组文件描述符(fd)的集合。由于 fd_set类型的长度在不同平台上不同,因此

应该用一组标准的宏定义来处理此类变量:     fd_set set;

    FD_ZERO(&set);       /* 将 set 清零 */

    FD_SET(fd, &set);    /* 将 fd 加入 set */

Page 151: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

    FD_CLR(fd, &set);    /* 将 fd从 set 中清除 */

    FD_ISSET(fd, &set);  /* 如果 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);

其中:

Page 152: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

nfds :      需要检查的文件描述符个数,数值应该比是三组 fd_set 中最大数

            更大,而不是实际文件描述符的总数。

readset:    用来检查可读性的一组文件描述符。

writeset:   用来检查可写性的一组文件描述符。

exceptset:  用来检查意外状态的文件描述符。(注:错误并不是意外状态)

timeout:    NULL指针代表无限等待,否则是指向 timeval 结构的指针,代表最

            长等待时间。(如果其中 tv_sec 和 tv_usec 都等于 0, 则文件描述符

            的状态不被影响,但函数并不挂起)

函数将返回响应操作的对应操作文件描述符的总数,且三组数据均在恰当位置被

修改,只有响应操作的那一些没有修改。接着应该用 FD_ISSET宏来查找返回的文

件描述符组。

这里是一个简单的测试单个文件描述符可读性的例子:

Page 153: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     int isready(int fd)

     {

         int rc;

         fd_set fds;

         struct timeval tv;

   

         FD_ZERO(&fds);

         FD_SET(fd,&fds);

         tv.tv_sec = tv.tv_usec = 0;

   

         rc = select(fd+1, &fds, NULL, NULL, &tv);

         if (rc < 0)

           return -1;

   

         return FD_ISSET(fd,&fds) ? 1 : 0;

     }

当然如果我们把 NULL指针作为 fd_set 传入的话,这就表示我们对这种操作的发生

不感兴趣,但 select() 还是会等待直到其发生或者超过等待时间。

[译者注:在 Linux 中,timeout指的是程序在非 sleep 状态中度过的时间,而不是

Page 154: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

实际上过去的时间,这就会引起和非 Linux平台移植上的时间不等问题。移植问

题还包括在 System V风格中 select()在函数退出前会把 timeout设为未定义的

NULL 状态,而在 BSD 中则不是这样,Linux 在这点上遵从 System V,因此在重复利

用 timeout指针问题上也应该注意。]

2.1.2 我如何使用 poll()? ------------------------

poll()接受一个指向结构'struct pollfd'列表的指针,其中包括了你想测试的

文件描述符和事件。事件由一个在结构中事件域的比特掩码确定。当前的结构在

调用后将被填写并在事件发生后返回。在 SVR4(可能更早的一些版本)中的

"poll.h"文件中包含了用于确定事件的一些宏定义。事件的等待时间精确到毫秒

(但令人困惑的是等待时间的类型却是 int),当等待时间为 0 时,poll()函数立即

Page 155: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

返回,-1则使 poll()一直挂起直到一个指定事件发生。下面是 pollfd 的结构。

     struct pollfd {

         int fd;        /* 文件描述符 */

         short events;  /* 等待的事件 */

         short revents; /* 实际发生了的事件 */

     };

于 select()十分相似,当返回正值时,代表满足响应事件的文件描述符的个数,

如果返回 0则代表在规定事件内没有事件发生。如发现返回为负则应该立即查看

errno,因为这代表有错误发生。

如果没有事件发生,revents 会被清空,所以你不必多此一举。

这里是一个例子:

   /* 检测两个文件描述符,分别为一般数据和高优先数据。如果事件发生

      则用相关描述符和优先度调用函数 handler(),无时间限制等待,直到

      错误发生或描述符挂起。*/

Page 156: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   

   #include <stdlib.h>

   #include <stdio.h>

 

   #include <sys/types.h>

   #include <stropts.h>

   #include <poll.h>

 

   #include <unistd.h>

   #include <errno.h>

   #include <string.h>

 

   #define NORMAL_DATA 1

   #define HIPRI_DATA 2

 

   int poll_two_normal(int fd1,int fd2)

   {

       struct pollfd poll_list[2];

       int retval;

 

       poll_list[0].fd = fd1;

       poll_list[1].fd = fd2;

       poll_list[0].events = POLLIN|POLLPRI;

       poll_list[1].events = POLLIN|POLLPRI;

 

Page 157: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

       while(1)

       {

           retval = poll(poll_list,(unsigned long)2,-1);

           /* retval 总是大于 0或为-1,因为我们在阻塞中工作 */

 

           if(retval < 0)

           {

               fprintf(stderr,"poll 错误: %s\n",strerror(errno));

               return -1;

           }

   

           if(((poll_list[0].revents&POLLHUP) == POLLHUP) ||

              ((poll_list[0].revents&POLLERR) == POLLERR) ||

              ((poll_list[0].revents&POLLNVAL) == POLLNVAL) ||

              ((poll_list[1].revents&POLLHUP) == POLLHUP) ||

              ((poll_list[1].revents&POLLERR) == POLLERR) ||

              ((poll_list[1].revents&POLLNVAL) == POLLNVAL))

             return 0;

 

           if((poll_list[0].revents&POLLIN) == POLLIN)

             handle(poll_list[0].fd,NORMAL_DATA);

           if((poll_list[0].revents&POLLPRI) == POLLPRI)

             handle(poll_list[0].fd,HIPRI_DATA);

           if((poll_list[1].revents&POLLIN) == POLLIN)

Page 158: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

             handle(poll_list[1].fd,NORMAL_DATA);

           if((poll_list[1].revents&POLLPRI) == POLLPRI)

             handle(poll_list[1].fd,HIPRI_DATA);

       }

   }

2.1.3 我是否可以同时使用 SysV IPC 和 select()/poll()? ---------------------------------------------------

*不能。* (除非在 AIX 上,因为它用一个无比奇怪的方法来实现这种组合)

一般来说,同时使用 select()或 poll()和 SysV 消息队列会带来许多麻烦 。SysV

IPC 的对象并不是用文件描述符来处理的,所以它们不能被传递给select()和

poll()。这里有几种解决方法,其粗暴程度各不相同:

   - 完全放弃使用 SysV IPC。 :-)

   - 用 fork(),然后让子进程来处理 SysV IPC,然后用管道或套接口和父进程

     说话。父进程则使用 select()。

   - 同上,但让子进程用 select(),然后和父亲用消息队列交流。

Page 159: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   - 安排进程发送消息给你,在发送消息后再发送一个信号。*警告*:要做好

     这个并不简单,非常容易写出会丢失消息或引起死锁的程序。

……另外还有其他方法。

2.2 我如何才能知道和对方的连接被终止?

======================================

如果你在读取一个管道、套接口、FIFO 等设备时,当写入端关闭连接时,你将会

得到一个文件结束符(EOF)(read()返回零字节读取)。如果你试图向一个管道或

套接口写入,当读取方关闭连接,你将得到一个 SIGPIPE 的信号,它会使进程终

止除非指定处理方法。(如果你选择屏蔽或忽略信号,write()会以 EPIPE 错误退

出。)

2.3 什么是读取目录的最好方法?

==============================

Page 160: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

历史上曾有过许多不同的目录读取方法,但目前你应该使用 POSIX.1 标准的

<dirent.h>接口。

opendir()函数打开一个指定的目录;readdir()将目录以一种标准的格式读入;

closedir()关闭描述符。还有一些其他如 rewinddir()、telldir()和seekdir()

等函数,相信不难理解。

如果你想用文件匹配符('*','?'),那么你可以使用大多数系统中都存在的glob()

函数,或者可以查看 fnmatch()函数来得到匹配的文件名,或者用 ftw()

来遍历整

个目录树。

2.4 我如何才能知道一个文件被另外进程打开?

==========================================

这又是一个“经常不被回答的问题”,因为一般来说你的程序不会关心文件是否

Page 161: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

正被别人打开。如果你需要处理文件的并发操作,那你应该使用咨询性文件锁。

一般来说要做到这点很难,像 fuser或 lsof 这样可以告诉你文件使用情况的工具

通过解析内核数据来达到目的,但这种方法十分不健康!而且你不能从你的程序

中调用它们来获取信息,因为也许当它们执行完成之后,文件的使用状况在瞬间

又发生了变化,你无法保证这些信息的正确。

2.5 我如何锁住一个文件?

========================

有三种不同的文件锁,这三种都是“咨询性”的,也就是说它们依靠程序之间的

合作,所以一个项目中的所有程序封锁政策的一致是非常重要的,当你的程序需

要和第三方软件共享文件时应该格外地小心。

有些程序利用诸如 FIlENAME.lock 的文件锁文件,然后简单地测试此类文件是否

Page 162: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

存在。这种方法显然不太好,因为当产生文件的进程被杀后,锁文件依然存在,

这样文件也许会被永久锁住。UUCP 中把产生文件的进程号 PID存入文件,但这样做

仍然不保险,因为 PID 的利用是回收型的。

这里是三个文件锁函数:      flock();

     lockf();

     fcntl();

flock()是从BSD 中衍生出来的,但目前在大多数 UNIX系统上都能找到,在单个主

机上 flock()简单有效,但它不能在 NFS 上工作。Perl 中也有一个有点让人迷惑的

flock()函数,但却是在 perl 内部实现的。

fcntl()是唯一的符合 POSIX 标准的文件锁实现,所以也是唯一可移植的。它也同

时是最强大的文件锁——也是最难用的。在 NFS 文件系统上,fcntl()请求会被递

Page 163: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

交给叫 rpc.lockd 的守护进程,然后由它负责和主机端的 lockd 对话,和flock()

不同,fcntl()可以实现记录层上的封锁。

lockf()只是一个简化了的 fcntl()文件锁接口。

无论你使用哪一种文件锁,请一定记住在锁生效之前用 sync 来更新你所有的文件

输入/输出。       lock(fd);

      write_to(some_function_of(fd));

      flush_output_to(fd); /* 在去锁之前一定要冲洗输出 */

      unlock(fd);

      do_something_else;   /* 也许另外一个进程会更新它 */

      lock(fd);

      seek(fd, somewhere); /* 因为原来的文件指针已不安全 */

      do_something_with(fd);

      ...

一些有用的 fcntl()封锁方法(为了简洁略去错误处理):

     #include <fcntl.h>

     #include <unistd.h>

   

Page 164: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     read_lock(int fd)   /* 整个文件上的一个共享的文件锁 */

     {

         fcntl(fd, F_SETLKW, file_lock(F_RDLCK, SEEK_SET));

     }

   

     write_lock(int fd)  /* 整个文件上的一个排外文件锁 */

     {

         fcntl(fd, F_SETLKW, file_lock(F_WRLCK, SEEK_SET));

     }

   

     append_lock(int fd) /* 一个封锁文件结尾的锁,

                            其他进程可以访问现有内容 */

     {

         fcntl(fd, F_SETLKW, file_lock(F_WRLCK, SEEK_END));

     }

前面所用的 file_lock 函数如下:      struct flock* file_lock(short type, short whence)

     {

         static struct flock ret ;

         ret.l_type = type ;

         ret.l_start = 0 ;

         ret.l_whence = whence ;

Page 165: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         ret.l_len = 0 ;

         ret.l_pid = getpid() ;

         return &ret ;

     }

2.6 我如何能发现一个文件已由另外一个进程更新?

==============================================

这又几乎是一个经常不被回答的问题,因为问这个问题的人通常期待能有一个系

统级的告示来反映当前目录或文件被修改,但没有什么保证移植性的实现方法,

IRIX 有一个非标准的功能用来监测文件操作,但从未听说在其他平台上也有相类

似的功能。

一般来说,你能尽的最大努力就是用 fstat()函数,通过监视文件的mtime 和 ctime

你能得知文件什么时候被修改了,或者被删除/连接/改名了,听起来很复杂,所

以你应该反思一下为什么你要做这些。

Page 166: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

2.7 请问 du 是怎样工作的?

========================

du只简单地用 stat()(更准确地说是用 lstat()函数)遍历目录结构中的每个文件

和目录,并将它们所占用的磁盘块加在一起。

如果你想知道其中细节,总是这么一句话:“读下源代码吧,老兄!”

BSD(FreeBSD、NetBSD 和 OpenBSD)的源代码在这些发行的 FTP网站的源码目录里,

GNU 版本的源码当然可以在任何一个 GNU镜像站点中找到——前提是你自己懂得如

何解包。

2.8 我如何得到一个文件的长度?

==============================

用 stat()或在文件打开后用 fstat()。

这两个调用会将文件信息填入一个结构中, 其中你能找到诸如文件主人、属性、

大小、最后访问时间、最后修改时间等所有关于此文件的东西。

Page 167: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

下面的程序大体示范如何用 stat()得到文件大小。      #include <stdlib.h>

     #include <stdio.h>

   

     #include <sys/types.h>

     #include <sys/stat.h>

   

     int get_file_size(char *path,off_t *size)

     {

       struct stat file_stats;

   

       if(stat(path,&file_stats))

         return -1;

   

       *size = file_stats.st_size;

       return 0;

     }

2.9 我如何像 shell里一样扩展在文件名里的'~'?

============================================

'~'的标准用法如下:如果单独使用或者后面跟一个'/',那么'~'就被当作当前

Page 168: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

用户的 home目录,[译者注:事实上'~'就被替换为$HOME环境变量],如果'~'后

直接跟一个用户名,则被替换的就是那个用户的 home目录。如果没有合适的匹

配,则 shell 不会做任何改动。

请注意,有可能一些文件的确是以'~'打头的,不分青红皂白地将'~'替换会使你

的程序无法打开这些文件。一般来说,从 shell 通过命令行或环境变量传递入程

序的文件名不须要进行替换,因为 shell 已经替你做好,而程序自己生成的、用

户输入的,或从配置文件中读取的却应该进行替换。

这里是一段用标准 string类的 C++实现:    string expand_path(const string& path)

   {

       if (path.length() == 0 || path[0] != '~')

         return path;

 

       const char *pfx = NULL;

       string::size_type pos = path.find_first_of('/');

Page 169: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

 

       if (path.length() == 1 || pos == 1)

       {

           pfx = getenv("HOME");

           if (!pfx)

           {

               // 我们想替换"~/",但$HOME却没有设置                struct passwd *pw = getpwuid(getuid());

               if (pw)

                 pfx = pw->pw_dir;

           }

       }

       else

       {

           string user(path,1,(pos==string::npos) ? string::npos : pos-1);

           struct passwd *pw = getpwnam(user.c_str());

           if (pw)

             pfx = pw->pw_dir;

       }

       // 如果我们不能找到能替换的选择,则将 path返回  

       if (!pfx)

         return path;

Page 170: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

 

       string result(pfx);

 

       if (pos == string::npos)

         return result;

 

       if (result.length() == 0 || result[result.length()-1] != '/')

         result += '/';

 

       result += path.substr(pos+1);

 

       return result;

   }

2.10 有名管道(FIFO)能做什么?

=============================

2.10.1 什么是有名管道? -----------------------

有名管道是一个能在互不相关进程之间传送数据的特殊文件。一个或多个进程向

内写入数据,在另一端由一个进程负责读出。有名管道是在文件系统中可见的,

也就是说 ls 可以直接看到。(有名管道又称 FIFO,也就是先入先出。)

Page 171: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

有名管道可以将无关的进程联系起来,而无名的普通管道一般只能将父子进程联

系起来——除非你很努力地去尝试——当然也能联系两个无关进程。有名管道是

严格单向的,尽管在一些系统中无名管道是双向的。

2.10.2 我如何建立一个有名管道? -------------------------------

在 shell 下交互地建立一个有名管道,你可以用 mknod或mkfifo 命令。在有些系统

中,mknod 产生的文件可能在/etc目录下,也就是说,可能不在你的目录下出现,

所以请查看你系统中的 man手册。[译者注:在 Linux 下,可以看一下fifo(4)]

要在程序中建立一个有名管道:

   /* 明确设置 umask,因为你不知道谁会读写管道 */

   umask(0);

   if (mkfifo("test_fifo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP))

   {

       perror("mkfifo");

       exit(1);

Page 172: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   }

也可以使用 mknod。[译者注:在 Linux 下不推荐使用 mknod,因为其中有许多臭虫

在 NFS 下工作更要小心,能使用 mkfifo 就不要用 mknod,因为mkfifo()是 POSIX.1

标准。]

   /* 明确设置 umask,因为你不知道谁会读写管道 */

   umask(0);

   if (mknod("test_fifo",

              S_IFIFO | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,

              0))

   {

       perror("mknod");

       exit(1);

   }

2.10.3 我如何使用一个有名管道? -------------------------------

使用有名管道十分简单:你如同使用一个普通文件一样打开它,用 read()

Page 173: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

write()进行操作。但对管道使用 open()时可能引起阻塞,下面一些常用规律可

以参考。

   * 如果你同时用读写方式(O_RDWR)方式打开,则不会引起阻塞。

   * 如果你用只读方式(O_RDONLY)方式打开,则 open()会阻塞一直到有写方打

     开管道,除非你指定了 O_NONBLOCK,来保证打开成功。

   * 同样以写方式(O_WRONLY)打开也会阻塞到有读方打开管道,不同的是如果

     O_NONBLOCK被指定 open()会以失败告终。

当对有名管道进行读写的时,注意点和操作普通管道和套接字时一样:当写入方

关闭连接时 read()返回 EOF,如果没有听众write()会得到一个 SIGPIPE

的信号,

对此信号进行屏蔽或忽略会引发一个 EPIPE 错误退出。

2.10.4 能否在 NFS 上使用有名管道? --------------------------------

Page 174: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

不能,在 NFS协议中没有相关功能。(你可能可以在一个 NFS 文件系统中用有名管

道联系两个同在客户端的进程。)

2.10.5 能否让多个进程同时向有名管道内写入数据? -----------------------------------------------

如果每次写入的数据少于 PIPE_BUF 的大小,那么就不会出现数据交叉的情况。但

由于对写入的多少没有限制,而 read()操作会读取尽可能多的数据,因此你不能

知道数据到底是谁写入的。

PIPE_BUF 的大小根据 POSIX 标准不能小于 512,一些系统里在<limits.h>中被定

义,[译者注:Linux 中不是,其值是 4096。]这可以通过 pathconf()或fpathconf()

对单独管道进行咨询得到。

2.10.6 有名管道的应用 ---------------------

    “我如何时间服务器和多个客户端的双向交流?”

Page 175: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

一对多的形式经常出现,只要每次客户端向服务器发出的指令小于PIPE_BUF,它

们就可以通过一个有名管道向服务器发送数据。客户端可以很容易地知道服务器

传发数据的管道名。

但问题在于,服务器不能用一个管道来和所有客户打交道。如果不止一个客户在

读一个管道,是无法确保每个客户都得到自己对应的回复。

一个办法就是每个客户在向服务器发送信息前都建立自己的读入管道,或让服务

器在得到数据后建立管道。使用客户的进程号(pid)作为管道名是一种常用的方

法。客户可以先把自己的进程号告诉服务器,然后到那个以自己进程号命名的管

道中读取回复。

3. 终端输入/输出

****************

Page 176: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

3.1 我怎样使我的程序不回射输入?

================================

     我怎样能使我的程序不回射输入,就象登录时询问我的口令时那样?

有一个简单方法,也有一个稍微复杂点的方法:

简单方法是使用‘getpass()’函数,它几乎能在所有 Unix系统上找到。它以一个

给定的字符串参数作为提示符(prompt)。它读取输入直到读到一个‘EOF’

或换

行符(译者注:‘EOF’用‘^d’输入,而换行符为‘^m’或回车)然后返回一个

指向位于静态内存区包含键入字符的字符串指针。(译者注:字符串不包含换行

符)

复杂一点的方法是使用‘tcgetattr()’函数和‘tcsetattr()’函数,两个函数都使用

一个‘struct termios’结构来操纵终端。下面这两段程序应当能设置回射状态和

不回射状态。

Page 177: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     #include <stdlib.h>

     #include <stdio.h>

     #include <termios.h>

     #include <string.h>

     static struct termios stored_settings;

     void echo_off(void)

     {

         struct termios new_settings;

         tcgetattr(0,&stored_settings);

         new_settings = stored_settings;

         new_settings.c_lflag &= (~ECHO);

         tcsetattr(0,TCSANOW,&new_settings);

         return;

     }

     void echo_on(void)

     {

         tcsetattr(0,TCSANOW,&stored_settings);

         return;

     }

两段程序使用到的都是在 POSIX 标准定义的。

3.2 我怎样从终端读取单个字符?

==============================

     我怎样从终端读取单个字符?我的程序总是要等着用户按回车。

Page 178: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

终端通常在标准(canonical)模式,在此模式输入总是经编辑后以行读入。你可以

设置终端为非标准(non-canonical)模式,而在此模式下你可以设置在输入传递给

你的程序前读入多少字符。你也可以设定非标准模式的计时器为 0,这个计时器

根据设定的时间间隔清空你的缓冲区。这样做使你可以使用‘getc()’函数立即

获得用户的按键输入。我们使用的‘tcgetattr()’函数和‘tcsetattr()’函数都

是在 POSIX 中定义用来操纵‘termios’结构的。      #include <stdlib.h>

     #include <stdio.h>

     #include <termios.h>

     #include <string.h>

     static struct termios stored_settings;

     void set_keypress(void)

     {

         struct termios new_settings;

         tcgetattr(0,&stored_settings);

         new_settings = stored_settings;

Page 179: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         /* Disable canonical mode, and set buffer size to 1 byte */

         new_settings.c_lflag &= (~ICANON);

         new_settings.c_cc[VTIME] = 0;

         new_settings.c_cc[VMIN] = 1;

         tcsetattr(0,TCSANOW,&new_settings);

         return;

     }

     void reset_keypress(void)

     {

         tcsetattr(0,TCSANOW,&stored_settings);

         return;

     }

3.3 我怎样检查是否一个键被摁下?

================================

    我怎样检查是否一个键被摁下?在 DOS 上我用‘kbhit()’函数,但是在UNIX

    上看来没有相同作用的函数?

如果你设定了终端为单一字符模式(参见上一个问题解答),那么(在大多数系统)

上你可以使用‘select()’函数或‘poll()’函数测试输入是否可读。

3.4 我怎样将光标在屏幕里移动?

==============================

Page 180: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     我怎样将光标在屏幕里移动?我想不用 curses而做全屏编辑。(译者注curses

     是一个 C/C++编程工具库,它提供编程者许多函数调用,在不用关心终端类型

     的情况下操纵终端的显示)。

不开玩笑,你也许不应该想去做这个。Curses工具库知道怎样控制不同终端类型

所表现出的奇特的东西(oddities);当然 termcap/terminfo 数据会告诉你任何终端类型

具有的这些奇特东西,但你可能会发现正确把握所有这些奇特组合是一件艰巨的

工作。(译者注:在 Linux系统上,termcap 数据位于/etc/termcap,而terminfo 数据位于

/usr/share/terminfo 下按不同终端类型首字母存放的不同文件,目前终端类型数已逾

两千种)

但是,你坚决要把你自己搞的手忙脚乱(getting your hands dirty),那么去研究一下

Page 181: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

‘termcap’的函数集,特别是‘tputs()’,‘tparm()’和‘tgoto()’函数。

3.5 pttys 是什么?

=================

Pseudo-teletypes(pttys, ptys,或其它不同的缩写)是具有两部份的伪设备(pseudo-devices):

一部份为“主人”一边,你可以认为是一个‘用户’,另一部份是“仆人”一边,

它象一个标准的 tty设备一样工作。

它们之所以存在是为了提供在程序控制下的一种模拟串行终端行为的方法。比

如,‘telnet’在远端系统使用一个伪终端;服务器的远端登录 shell 程序只是从“仆

人”一边的 tty设备期待着得到操作行为,而在“主人”一边的伪终端由一个守护程

序控制,同时守护程序将所有数据通过网络转发。pttys 也被其它程序使用比如

‘xterm’,‘expect’,‘script’,‘screen’,‘emacs’和其它很多程序。

Page 182: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

3.6 怎样控制一个串行口和调制解调器?

====================================

Unix系统下对于串行口的控制很大程度上受串行终端传统的使用影响。以往,

需要不同 ioctls 函数调用的组合和其它黑客行为才能控制一个串行设备的正确操

作,不过值得庆幸的是,POSIX 在这个方面的标准化作了一些努力。

如果你使用的系统不支持‘<termios.h>’头文件,‘tcsetattr()’和其它相关函数,

那么你只能到其它地方去找点资料(或将你的老古董系统升级一下)。

但是不同的系统仍然有显著的区别,主要是在设备名,硬件流控制的操作,和

调制解调器的信号方面。(只要可能,尽量让设备驱动程序去做握手(handshaking)

工作,而不要试图直接操纵握手信号。)

打开和初始华串行设备的基本步骤是:

    * 调用‘open()’函数打开设备;而且可能需要使用特定标志作为参数:

Page 183: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

    `O_NONBLOCK'

          除非使用这个标志,否则打开一个供拨入(dial-in)或由调制解调器控制的设

          备会造成‘open()’调用被阻塞直到线路接通(carrier is

present)。一个非

          阻塞的打开操作给你在需要时令调制解调器控制失效的机会。(参见下面的

          CLOCAL)

    `O_NOCTTY'

          在自 4.4BSD演化的系统上这个标志是多余的,但在其它系统上它控制串行

          设备是否成为会话的控制终端。在大多数情况下你可能不想获得一个控制

          终端,所以就要设置这个标志,但是也有例外情况。

   * 调用‘tcgetattr()’函数获得当前设备模式。虽然有人会经常取消(ignore)得到的

      大多数或全部初始设定,但它仍然不失为一个初始化‘struct

termios’结构的

      便利方法。

Page 184: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   * 设置 termios 结构里‘c_iflag’,‘c_oflag’,‘c_flag’,‘c_lfag’

和‘c_cc’

      为合适的值。(参见下面部分。)

   * 调用‘cfsetispeed()’和‘cfsetospeed()’设定设想的波特率。很少系统允许你

     设置不同的输入和输出速度,所以普通规律是你需要设成同一个设想的值。

   * 调用‘tcsetattr()’设定设备模式。

   * 如果你是用‘O_NONBLOCK’标志打开的端口,你可能希望调用‘fcntl()’

     函数将‘O_NONBLOCK’标志重新设置成关闭。因为不同系统看来对是否

     由非阻塞打开的端口对今后的‘read()’调用造成影响有不同的处理;所以

     最好显式地设置好。

一旦你打开并设置了端口,你可以正常地调用‘read()’函数和‘write()’

函数。

Page 185: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

注意到‘read()’函数的行为将受到你调用‘tcsetattr()’函数时的标志参数设定

的控制。

‘tcflush()’,‘tcdrain()’,‘tcsendbreak()’和‘tcflow()’是其它一些你应当

注意的函数。

当你使用完端口想要关闭时,要注意一些系统上特别恶心的小危险;如果有任何

输出数据等待被写到设备(比如输出流被硬件或软件握手而暂停),你的进程将因

为‘close()’函数调用而被挂起(hang)直到输出数据排空,而且这时的进程是*不

可杀的*(unkillably)。所以调用‘tcflush()’函数丢弃待发数据可能是个明智之举。

(在我的经验中,在 tty设备上被阻塞的输出数据是造成不可杀进程最普通的原因。)

3.6.1 串行设备和类型 -------------------- 不同系统用于串行端口设备的设备名大相径庭。以下是不同系统的一些例子

Page 186: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

   * ‘/dev/tty[0-9][a-z]’作为直接访问设备,而      ‘/dev/tty[0-9][A-Z]’ 作为调制解调器控制设备(比如 SCO Unix)

   * ‘/dev/cua[0-9]p[0-9]’作为直接访问设备,‘/dev/cul[0-9]p[0-9]’

作为拨出设       备,而‘/dev/ttyd[0-9]p[0-9]’作为拨入设备(比如 HP-UX)

   * ‘/dev/cua[a-z][0-9]’作为拨出设备而‘/dev/tty[a-z][0-9]’作为拨入设备(比如

      FreeBSD) 是否正确地同所用设备名交互,并在任何硬件握手信号线上产生相应的效

果是 与系统,配置和硬件有关的,但是差不多总是遵循下面这些规则(假设硬件

是 RS-232 DTE):    - 对于任何设备的一个成功打开操作应当设置 DTR 和 RTS

   - 一个对于由调制解调器控制或供拨入的设备的阻塞打开操作将等待DCD(并且

     可能 DSR 和/或CTS 也需要)被设置,通常在设置 DTR/RTS之后。    - 如果一个对于拨出设备的打开操作正巧赶上一个对于相应拨入设备的

打开操作      因为等待线路接通而阻塞,那么打开拨出的操作*也许*造成打开拨入的

操作完

Page 187: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     成,但*也许也不*造成。一些系统为拨入和拨出端口实现一个简单的共享方案,

     当拨出端口在使用时,拨入端口被有效地设置成睡眠状态(“put to

sleep”);其      它系统不这样做,在这种系统上为避免竞争(contention)问题,需要

外部的协助才      能使拨入和拨出共享端口(比如 UUCP锁定文件的使用)。 3.6.2 设置 termios 的标志位 ------------------------- 这里是对你在使用你自己打开的串行设备时设置 termios 标志的一些提示

(即与你使 用已存在的控制 tty 相反) 3.6.2.1 c_iflag ............... 你也许希望将*所有*‘c_iflag’的标志位设成 0,除非你希望使用软件流控

制(ick), 在这种情况下你设置‘IXON’和‘IXOFF’。(译者注:有三个标志控制流控

制: IXON,IXOFF ,和 IXANY,如果 IXON被设置,那么 tty 输入队列的软件

流控制 被设置。当程序无法跟上输入队列的速度,tty 传输一个 STOP 字符,而当

输入队 列差不多空时发送 START 字符。如果 IXON被设置,那么 tty 输出队列的

Page 188: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

软件流控 制被设置。当 tty 所连接的设备跟不上输出速度,tty 将阻塞程序对 tty 的

写操作。如果 IXANY被设置,那么一旦 tty从设备收到任何字符,被暂定的输出将继续 -

译自 SCO

Unix 网 上 文 档 http://uw7doc.sco.com/SDK_sysprog/CTOC-

TermDevCntl.html,“TTY flow

control”章节,第五,六段) 3.6.2.2 c_oflag ............... 大部分‘c_oflag’的标志位是为了能使对于慢终端的输出可以正常工作所

做的这 样或那样的黑客行为,由此,一些较新的系统认为几乎所有这些标志位已

经过 时从而摈弃了它们(特别是所有血淋淋(gory)的输出排列对齐(output-

padding)选项)。 如同‘c_iflag’,将它设置成全 0 对于大部分应用程序来说是合理的。 3.6.2.3 c_cflag ............... 当设置字符的大小时,记住首先使用‘CSIZE’屏蔽,比如设置 8 位字符,

需要:          attr.c_cflag &= ~CSIZE;          attr.c_cflag |= CS8; 在‘c_cflag’里的其它你有可能需要设置为*真*的标志包括‘CREAD’和 ‘HUPCL’。

Page 189: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

如果你需要产生偶校验,那么设置‘PARENB’并清除‘PARODD’;如果你

需要产生奇校验,那么同时设置‘PARENB’和‘PARODD’。如果你根本不 想设置校验,那么确认清除‘PARENB’。 清除‘CSTOPB’ ,除非你真需要产生两个停止位。 设置硬件流控制的标志可能也能在‘c_cflag’中找到,但它们不是被标准

化的(

这是一个遗憾) 3.6.2.4 c_lflag ............... 大部分应用程序可能需要关闭‘ICANON’(标准状态,即基于行的,并进

行输 入处理),‘ECHO’和‘ISIG’。 ‘IEXTEN’是个更复杂的问题。如果你不把它关闭,具体实现允许你作一些

非 标准的事情(比如在‘c_cc’中定义增加的控制字符)从而可能导致不可预料

的接 果,但是在一些系统上,你可能需要保持‘IEXTEN’标志为真以得到一些

有用 的特征,比如硬件流控制。 3.6.2.5 c_cc ............ 这 是 一 个 包括输 入 中带有特殊含 义 字 符 的 数组。 这 些 字 符被命 名 为

‘VINTR’,

Page 190: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

‘VSTOP’等等;这些名字是这个数组的索引。 (这些字符中的两个其实根本不是字符,而是当‘ICANON’被关闭时对于 ‘read()’函数行为的控制;它们是‘VMIN’和‘VTIME’。)

这些索引名字经常被提及的方式会让人以为它们是实在的变量,比如“设置

VMIN 为 1” 其实意味着“设置 c_cc[VMIN]为 1”。这种简写是有用的并且只是 偶尔引起误会。 ‘c_cc’的很多变量位置只有当其它标志被设定时才会用到。 只有‘ICANON’被设置,才用到以下变量:      ‘VEOF’,‘VEOL’,‘VERASE’,‘VKILL’(如果定义了而且      ‘IEXTEN’被设定,那么‘VEOL2’,‘VSTATUS’和‘VWERASE’

        也用到)

只有‘ISIG’被设置,才用到以下变量:      ‘VINTR’,‘VQUIT’,‘VSUSP’(如果定义了而且‘IEXTEN’被设定,        那么‘VDSUSP’也用到)

只有‘IXON’或‘IXOFF’被设置,才用到以下变量:      ‘VSTOP’,‘VSTART’

只有‘ICANON’被取消,才用到以下变量:      ‘VMIN’,‘VTIME’

不同系统实现会定义增加的‘c_cc’变量。谨慎的做法是在设定你希望使用的值

Page 191: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

以前,使用‘_POSIX_VDISABLE’初始化这些变量(常量‘NCCS’提供这个数 组的大小)

‘VMIN’和‘VTIME’(根据不 同 的实现方 法 , 它 们 有 可 能 和‘VEOF’和‘VEOL’

分享相同两个变量)具有以下含义。‘VTIME’的值(如果不为 0)总是被解释为以十 分之一秒为单位的计时器)(译者注:VTIME变量是一个字节长,所以 1 表

示 0.1秒, 最大为 255,表示 25.5秒) `c_cc[VMIN] > 0, c_cc[VTIME] > 0'      只要输入已经有 VMIN 字节长,或者输入至少有一个字符而在读取最后

一个字      符之前VTIME 已经过期,或者被信号中断,‘read()’将返回。 `c_cc[VMIN] > 0, c_cc[VTIME] == 0'      只要输入已经有 VMIN 字节长,或者被信号中断,‘read()’将返回。否

则,将      无限等待下去。 `c_cc[VMIN] == 0, c_cc[VTIME] > 0'      只要有输入‘read()’就返回;如果 VTIME 过期却没有数据,它会返回

没有读      到字符。(这和调制解调器挂断时的文件结束标志有一点冲突;使用 1 作

为 VMIN,

Page 192: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     调用‘alarm()’或‘select()’函数并给定超时参数可以避免这个问题。) `c_cc[VMIN] == 0, c_cc[VTIME] == 0'       ‘read()’总是立刻返回;如果没有数据则返回没有读到字符。(与上面的

问题         相同)

4. 系统信息

***********

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)      {

Page 193: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         printf(" Page Size: %lu\n", pst.page_size);          printf("Phys Pages: %lu\n", 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()' 接收

Page 194: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

用户号(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))

Page 195: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         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]算法)。

输入的 明文口令被加密,然后与文件中存放的加密口令比较。怎样加密的详细信息

可以

Page 196: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

查看`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’,等等。它是用来匹配正文,而它们

Page 197: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

正常      情况下不应用于文件名。 5.1.1 我怎样使用文件名通配模式比较字符串? ------------------------------------------ 除非你不走运,你的系统应该有函数‘fnmatch()’供你进行文件名匹配。

它一 般只允许 Bourne Shell风格的模式。它识别‘*’,‘[...]’和‘?’,但可

能不 支持在 Korn 和 Bourne-Again shell 程序下才有的更神秘(arcane)的模

式。 如果你没有这个函数,那么比闭门造车更好的方法是你可以从 BSD或

GNU原程 序那里去抄(snarfing)一个过来。 而且,在普通的匹配实际文件名情况下,查阅‘glob()’函数,它将搜索到

匹配 一个给定模式的所有存在文件。 5.1.2 我怎样使用正则表达式比较字符串? -------------------------------------- 有很多稍有句法不同的正则表达式;大部分系统起码使用两种:一种是

‘ed’

程序可以识别的,有时候被记作‘基本正则表达式’,另一种是‘egrep’

程序 可以识别的,记作‘扩充正则表达式’。Perl(译者注:Perl: Practical

Page 198: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

Extract and Report Language,实用析取与报表语言)语言拥有它自己稍有不同的风

格,Emacs

也是。 为了支持这么多格式,相应的有很多实现。系统一般有正则表达式匹配函数

(通 常为‘regcomp()’函数和‘regexec()’函数)提供,但是要小心使用;有

些系统 有超过一种实现可用,附之以不同的接口。另外,还有很多可用的软件库的

实 现(顺便说一下,一般都是将一个正则表达式编译成内部形式然后再使用,

因为 总是假设你有很多字符串要比较同一正则表达式。)

一个可用的软件库是‘rx’软件库,从GNU 的镜像站点可以得到。它看来是正在

开发中,基于你不同的观点这是一件又好又不好的事情 :-)

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

==========================================

有好几种从Unix 程序发电子邮件的方法。根据不同情况最好的选择有所不同,

所以我将提供两个方法。还有第三种方法,这里没有说道,是连接本地主机

Page 199: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

的 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",

Page 200: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

"w");          if (!mail)          {              perror("popen");              exit(1);          }          fprintf(mail, "This is a test.\n");          if (pclose(mail))          {              fprintf(stderr, "mail failed!\n");              exit(1);          }      } 如果要发送的正文已经保存在一个文件中,那么可以这样做:          system(MAILPROG " -s 'file contents' root

</tmp/filename"); 这 个 方 法 可 以扩展到 更复杂的 情 况 , 但 是 得当心很 多潜在 的危险

(pitfalls):    * 如果使用 system()或 popen(),你必须非常当心将参数括起来从而保

护它们不被      错误的进行文件名匹配替换或单词分割。    * 基于用户设置数据来构造命令行是缓冲区越界错误和其它安全漏洞的

普遍原      因。    * 这种方法不允许设定 CC:(译者注:CC:Carbon Copy 抄送)或

BCC:(译者注:      BCC:Blind Carbon Copy:盲送,指投递地址不在消息中出现的抄

送)的收件人。      (一些/bin/mail 程序的版本允许,其它则不允许)

Page 201: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

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)的概 念。这非常类似书面信件;信封上定义这个消息投递给谁,并注明由谁发出

( 为了报告错误的目的)。在信封中包含的是“消息头”和“消息体”,之间

由一个

Page 202: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

空行隔开。消息头的格式主要在 RFC 822 中提供;并且参见MIME 的 RFC

文档。(

译者注:MIME 的文档包括:RFC1521,RFC1652)

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

种方 法都有优缺点。 5.2.2.1 显式提供信封内容 ......................... 消息的信封内容可在命令行上简单的设定。它的缺点在于邮件地址可能包含

的 字符会造成‘system()’和‘popen() ’程序可观的以外出错(grief),比如

单引号, 被括起的字符串等等。传递这些指令给 shell 程序并成功解释可以预见潜在

的危 险。(可以将命令中任何一个单引号替换成单引号、反斜杠、单引号、单引号

的 顺序组合,然后再将整个地址括上单引号。可怕,呃?)

以上的一些不愉快可以通过避开使用‘system()’或‘popen()’函数并求助于‘

fork()’和‘exec()’函数而避免。这有时不管怎样也是需要的;比如,用户

Page 203: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

自定义的对于 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指针

Page 204: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

      * 标志结束;如果发现错误则返回-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:   /* 子进程 */

Page 205: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

             /* 建立管道 */              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’的命令行,但也使得设置在消息头信息中所列

以外的 收件人成为不可能。(这通常不是一个问题)

Page 206: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

作为一个范例,以下这个程序将标准输入作为一个文件以 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...\n", 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);

Page 207: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         /* 建立收件人列表 */          fprintf(msg, "To: %s", argv[1]);          for (i = 2; i < argc; i++)              fprintf(msg, ",\n\t%s", argv[i]);          fputc('\n',msg);          /* 标题 */          fprintf(msg, "Subject: file sent by mail\n");          /* sendmail 程序会自动添加 From:, Date:, Message-ID: 等消息

头信息 */

         /* MIME 的处理 */          fprintf(msg, "MIME-Version: 1.0\n");          fprintf(msg, "Content-Type: application/octet-stream\n");          fprintf(msg, "Content-Transfer-Encoding: base64\n");          /* 消息头结束,加一个空行 */          fputc('\n',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. 工具的使用

*************

Page 208: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

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

==================================== 根据可用的工具有两种不同的方法: 你的调试器(debugger)可能有允许你选择是否跟踪调用‘fork()’以后的

父或子进程 的选项,对于某些目的来说那已经足够了。 替换方法是,你的调试器可能有一个选项允许你将它依附(attach)到一个

正在执行 的程序。这样你可以依附调试器到一个已经开始执行的子进程。如果你不需

要从 子进程一开始就开始测试,这通常已经足够。否则,你会希望在子进程的

‘fork()’

调用后插入一个‘sleep()’调用,或者插入如下的循环:      {          volatile int f = 1;          while(f);      } 这样子进程将一直在此循环不往下执行直到你用调试器设定‘f’为 0。 并且记住,使用调试器并非是找到你程序中错误的唯一方法;在很多 Unix

系统 上有一些工具程序可用来跟踪系统调用和信号,而且丰富的日志经常也是

有 用的。

Page 209: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

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

====================================== 前提是我们所说的是归档(archive)(静态)库,最简单的方法是将所有选择

的库 文件使用‘ar x’命令在一个空目录里拆分成它们原始的单个目标文件

(object), 然后再合并成一个。当然,文件名有可能会重复,但是如果这个库文件很大

你也许一开始就不想将它们合并在一起。

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

=============================================

创建动态连接库(shared libraries)的方法根据不同的系统有所不同。这个过程主要 分两步;第一步要求包括在动态连接库中的目标必须首先是编译好的,通

常需 要某个编译选项指示这串代码是位置无关的(position-indepenent);第

二步,是将 这些目标连接在一起形成一个库文件。 这里是一个演示以上道理的小程序:      /* shrobj.c 文件 */      const char *myfunc()      {

Page 210: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         return "Hello World";      }      /* shrobj.c 结束 */

     /* hello.c 文件 */      #include <stdio.h>      extern const char *myfunc();      main()      {          printf("%s\n", 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 程序挂钩(如果你使用这些工具来管理你程序的编译创建过程)。

Page 211: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

如果你不想使用 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’。

Page 212: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

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

其它要当心的问题:    * AIX 和(我相信)Digital Unix 不需要-fpic 选项,因为所有代码都是位

置无关的。    * AIX 一般需要你创建一个‘输出文件’,即一个保存所有动态连接库

中输出      符号的列表。一些连接器(linker)版本(可能只有 SLHS 连接器,是

svld?)有一个      选项可以输出所有符号。    * 如果你对于连接器想使用普遍的‘-l’参数来引用你的动态连接库,你

必须      理解你的系统在实际运行时是怎样寻找动态连接库的。最普通的方法是

使用      ‘LD_LIBRARY_PATH’环境变量,但是通常在连接时有一种其它选项可

以      设定。    * 大多数实现方法是在程序内部记录所希望的动态连接库在运行时的位

置。这      样把一个动态连接库从一个目录移到另一个目录将导致程序无法工作。

许多      系统对于连接器有一个选项用以设定希望运行时动态连接库的位置(比

如在

Page 213: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     Solaris系统上是‘-R’连接器选项,或者是‘LD_RUN_PATH’环境变量)。

   * ELF 和 a.out 的实现方法可能有一个连接器选项‘-Bsymbolic’,它导致在库本

     身内部的引用被解释好。否则,在这些系统上,所有符号的解释将推迟到最

     后连接时再进行,这样在 main 程序中的单一函数将重载库中的对应函数。

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

====================================== 一般不行。 在大多数系统上(除了 AIX),当你连接目标并形成一个动态连接库时,它

就象连 接一个可执行程序;目标并不保留它们单一的特征。结果是,一般不能从一

个动 态连接库里析取出或更换一个单一的目标。

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

============================================

一些系统提供库函数可以提取(unwinding)出堆栈,从而(比如)你可以在一个错误

Page 214: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

处理函数中生成一个堆栈映象,但是只有一小部分系统有这些函数。 一个可能的变通方法(workaround)是让你的程序执行一个调试器调试*它

自己* -

详细方法仍然根据不同系统稍有不同,但一般的概念是这样:      void dump_stack(void)      {          char s[160];          sprintf(s, "/bin/echo 'where\ndetach' | dbx -a %d",

getpid());          system(s);          return;      } 你需要根据你不同的系统对 dbx 的参数和命令进行加工,或者甚至换另一

个调试 器,比如‘gdb’,但这仍然是我见过的对于这种问题最普遍的解决方法。

为此, 荣誉授予Ralph Corderoy。 下面列表包含在一些系统上需要用到的命令行: 大多数使用 dbx 的系统     `"/bin/echo 'where\ndetach' | dbx /path/to/program %d"' AIX      `"/bin/echo 'where\ndetach' | dbx -a %d"' IRIX      `"/bin/echo 'where\ndetach' | dbx -p %d"'   ? 范例程序 ******** 捕获 SIGCHLD 信号 =================      #include <sys/types.h>  /* 在任何其它 sys 下的头文件之前引用

Page 215: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

这个头文件 */

     #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)          {

Page 216: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

             fprintf(stderr, "sigaction failed\n");              return 1;          }              /* fork */          switch (pid = fork())          {          case -1:              fprintf(stderr, "fork failed\n");              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)          {              /*               * 不建议在信号处理函数中调用标准输入/输出函数,               * 但在一个类似这个的玩具程序里或许没问题

Page 217: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

              */              fprintf(stderr, "waitpid failed\n");              return;          }              /*           * 我们现在有保存在‘status’变量中的子进程退出信息并可以使用           * wait.h 中定义的宏对其进行操作           */          if (WIFEXITED(status))                /* 子进程是正常退出吗? */          {              child_val = WEXITSTATUS(status); /* 获取子进程的退出状态

*/              printf("child's exited normally with status %d\n",

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;

Page 218: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         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,'\0');              while(cur_proc=kvm_nextproc(kd)){              curpid = cur_proc->p_pid;              if((cur_user=kvm_getu(kd,cur_proc))!=NULL){                  error=kvm_getcmd(kd,cur_proc,cur_user,&arg,NULL);                  if(error==-1){                      if(cur_user->u_comm[0]!='\0'){                          p_name=cur_user->u_comm;                      }                  }                  else{                      p_name=arg[0];                  }              }              if(p_name){                  if(!strcmp(p_name,name)){                      if(error!=-1){                          free(arg);                      }                      if(skipit!=-1 && ourretval==skipit){                          ourretval=-1;                      }                      else{                          close(fd);                          break;                      }                      break;                  }                  else{

Page 219: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

                     if(step(p_name,expbuf)){                          if(error!=-1){                              free(arg);                          }                          break;                      }                  }              }              if(error!=-1){                  free(arg);              }              p_name=NULL;          }          kvm_close(kd);          if(p_name!=NULL){              return(curpid);          }          return (-1);      } 读取进程表 - SYSV 版 ====================      pid_t      getpidbyname(char *name,pid_t skipit)      {          DIR  *dp;          struct dirent *dirp;          prpsinfo_t retval;          int fd;          pid_t ourretval=-1;              if((dp=opendir("/proc"))==NULL){              return -1;          }          chdir("/proc");          while((dirp=readdir(dp))!=NULL){              if(dirp->d_name[0]!='.'){                  if((fd=open(dirp->d_name,O_RDONLY))!=-1){                      if(ioctl(fd,PIOCPSINFO,&retval)!=-1){                          if(!strcmp(retval.pr_fname,name)){                              ourretval=(pid_t)atoi(dirp->d_name);                              if(skipit!=-1 && ourretval==skipit){                                  ourretval=-1;                              }

Page 220: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

                             else{                                  close(fd);                                  break;                              }                          }                      }                      close(fd);                  }              }          }          closedir(dp);          return ourretval;      } 读取进程表 - AIX 4.2 版 =======================      #include <stdio.h>      #include <procinfo.h>          int getprocs(struct procsinfo *, int, struct fdsinfo *,                   int, pid_t *, int);          pid_t getpidbyname(char *name, pid_t *nextPid)      {        struct procsinfo  pi;        pid_t             retval = (pid_t) -1;        pid_t             pid;            pid = *nextPid;            while(1)        {          if(getprocs(&pi, sizeof pi, 0, 0, &pid, 1) != 1)            break;              if(!strcmp(name, pi.pi_comm))          {            retval = pi.pi_pid;            *nextPid = pid;            break;          }        }            return retval;

Page 221: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     }          int main(int argc, char *argv[])      {        int   curArg;        pid_t pid;        pid_t nextPid;            if(argc == 1)        {          printf("syntax: %s <program> [program ...]\n",argv[0]);          exit(1);        }            for(curArg = 1; curArg < argc; curArg++)        {          printf("Process IDs for %s\n", argv[curArg]);              for(nextPid = 0, pid = 0; pid != -1; )            if((pid = getpidbyname(argv[curArg], &nextPid)) != -1)              printf("\t%d\n", pid);        }      } 使用 popen 函数和 ps 命令读取进程表 ===============================      #include <stdio.h>      /* FILE, sprintf, fgets, puts */      #include <stdlib.h>     /* atoi, exit, EXIT_SUCCESS */      #include <string.h>     /* strtok, strcmp */      #include <sys/types.h>  /* pid_t */      #include <sys/wait.h>   /* WIFEXITED, WEXITSTATUS */          char *procname(pid_t pid)      {         static char line[133], command[80], *linep, *token, *cmd;         FILE *fp;         int status;             if (0 == pid) return (char *)0;             sprintf(command, "ps -p %d 2>/dev/null", pid);         fp = popen(command, "r");         if ((FILE *)0 == fp) return (char *)0;    

Page 222: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

        /* 读取标题行 */         if ((char *)0 == fgets(line, sizeof line, fp))         {            pclose(fp);            return (char *)0;         }             /* 从标题栏分析出命令名所在列。          * (BSD风格的系统将指示命令的"COMMAND"字符串放在第 5列,

SysV 好象将          * 指示命令的“CMD”或“COMMAND”字符串放在第 4列)          */         for (linep = line; ; linep = (char *)0)         {            if ((char *)0 == (token = strtok(linep, " \t\n")))            {               pclose(fp);               return (char *)0;            }            if (0 == strcmp("COMMAND", token) || 0 ==

strcmp("CMD", token))            { /*  我们找到 COMMAND 所在列 */               cmd = token;               break;            }         }             /* 读取 ps(1) 输出行 */         if ((char *)0 == fgets(line, sizeof line, fp))         {            pclose(fp);            return (char *)0;         }             /* 抓COMMAND 标题下面的词 ... */         if ((char *)0 == (token = strtok(cmd, " \t\n")))

Page 223: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

        {            pclose(fp);            return (char *)0;         }             status = pclose(fp);         if (!WIFEXITED(status) || 0 != WEXITSTATUS(status))           return (char *)0;             return token;      }          int main(int argc, char *argv[])      {         puts(procname(atoi(argv[1])));         exit(EXIT_SUCCESS);      } 守护程序工具函数 ================      #include <unistd.h>      #include <stdlib.h>      #include <fcntl.h>      #include <signal.h>      #include <sys/types.h>      #include <sys/wait.h>      #include <errno.h>          /* closeall() -- 关闭所有>=给定值的文件描述符 */          void closeall(int fd)      {          int fdlimit = sysconf(_SC_OPEN_MAX);              while (fd < fdlimit)            close(fd++);      }          /* daemon() - 将进程从用户端脱离并消失进入后台,若失败返回-1,       * 但是在那种情况下你只能退出,因为我们可能已经生成了子进程。

Page 224: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

      * 这是基于 BSD 的版本,所以调用方需负责类似 umask 等等其它的工作。

      */          /* 相信在所有 Posix系统上都能工作 */          int daemon(int nochdir, int noclose)      {          switch (fork())          {              case 0:  break;              case -1: return -1;              default: _exit(0);          /* 原进程退出 */          }              if (setsid() < 0)               /* 不应该失败 */            return -1;              /* 如果你希望将来获得一个控制 tty,则排除(dyke)以下的 switch

语句 */

         /* -- 正常情况不建议用于守护程序 */              switch (fork())          {              case 0:  break;              case -1: return -1;              default: _exit(0);          }              if (!nochdir)            chdir("/");              if (!noclose)          {              closeall(0);              open("/dev/null",O_RDWR);

Page 225: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

             dup(0); dup(0);          }              return 0;      }          /* fork2() -- 类似 fork 函数,但子进程立刻变成孤儿进程       *            (当它退出时不产生僵死进程)

      * 返回 1 给父进程,不是任何有意义的进程号.

      * 父进程不能使用 wait 函数等待子进程结束 (它们是无关的).       */          /* 这个版本假设你没有捕获和忽略 SIGCHLD信号. */

     /* 如果你有设定,则不管怎样应使用 fork 函数 */          int fork2()      {          pid_t pid;          int rc;          int status;              if (!(pid = fork()))          {              switch (fork())              {                case 0:  return 0;                case -1: _exit(errno);    /* 假设错误码都小于 256 */                default: _exit(0);              }          }              if (pid < 0 || waitpid(pid,&status,0) < 0)            return -1;              if (WIFEXITED(status))            if (WEXITSTATUS(status) == 0)              return 1;

Page 226: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

           else              errno = WEXITSTATUS(status);          else            errno = EINTR;  /* 唉,类似这个 :-) */              return -1;      } 一个使用以上函数的范例程序:      #include <sys/types.h>      #include <sys/socket.h>      #include <netinet/in.h>      #include <stdio.h>      #include <stdlib.h>      #include <syslog.h>      #include <errno.h>          int daemon(int,int);      int fork2(void);      void closeall(int);          #define TCP_PORT 8888          void errexit(const char *str)      {          syslog(LOG_INFO, "%s failed: %d (%m)", str, errno);          exit(1);      }          void errreport(const char *str)      {          syslog(LOG_INFO, "%s failed: %d (%m)", str, errno);      }          /* 实际的子进程在此. */          void run_child(int sock)      {          FILE *in = fdopen(sock,"r");          FILE *out = fdopen(sock,"w");          int ch;    

Page 227: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         setvbuf(in, NULL, _IOFBF, 1024);          setvbuf(out, NULL, _IOLBF, 1024);              while ((ch = fgetc(in)) != EOF)            fputc(toupper(ch), out);              fclose(out);      }          /* 这是守护程序的主要工作 -- 侦听连接并生成子进程 */          void process()      {          struct sockaddr_in addr;          int addrlen = sizeof(addr);          int sock = socket(AF_INET, SOCK_STREAM, 0);          int flag = 1;          int rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,                              &flag, sizeof(flag));              if (rc < 0)            errexit("setsockopt");              addr.sin_family = AF_INET;          addr.sin_port = htons(TCP_PORT);          addr.sin_addr.s_addr = INADDR_ANY;              rc = bind(sock, (struct sockaddr *) &addr, addrlen);          if (rc < 0)            errexit("bind");              rc = listen(sock, 5);          if (rc < 0)            errexit("listen");              for (;;)          {              rc = accept(sock, (struct sockaddr *) &addr, &addrlen);                  if (rc >= 0)                switch (fork2())                {                  case 0:  close(sock); run_child(rc); _exit(0);

Page 228: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

                 case -1: errreport("fork2"); close(rc); break;                  default: close(rc);                }          }      }          int main()      {          if (daemon(0,0) < 0)          {              perror("daemon");              exit(2);          }              openlog("test", LOG_PID, LOG_DAEMON);              process();              return 0;      } 调制解调器控制范例程序 ======================      /* 发出一些简单调制解调器命令       * 需要串行设备的设备名 (最好是拨出设备,

      * 或者是非调制解调器控制设备) 作为它唯一的参数.

      * 如果你没有可共使用的拨出设备, 那么以 CFLAGS_TO_SET 取代CLOCAL。

      */          #include <stdio.h>      #include <stdlib.h>      #include <fcntl.h>      #include <unistd.h>      #include <sys/types.h>      #include <sys/time.h>      #include <sys/ioctl.h>   /* 也许需要;和系统有关 */      #include <termios.h>

Page 229: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     #include <errno.h>      #include <string.h>      #include <ctype.h>          #define CFLAGS_TO_SET (CREAD | HUPCL)      #define CFLAGS_TO_CLEAR (CSTOPB | PARENB | CLOCAL)          enum flowmode { NoFlow, HardFlow, SoftFlow };          /* 和系统有关 */      #define CFLAGS_HARDFLOW (CRTSCTS)              #define EXAMPLE_BAUD B19200      #define EXAMPLE_FLOW HardFlow              static void die(const char *msg)      {          fprintf(stderr, "%s\n", msg);          exit(1);      }          static int close_and_complain(int fd, const char *msg, int err)      {          fprintf(stderr, "%s: %s\n", msg, strerror(err));          if (fd >= 0)              close(fd);          errno = err;          return -1;      }              int open_port(const char *name, speed_t baud, enum

flowmode flow)      {          int flags;          struct termios attr;              int fd = open(name, O_RDWR | O_NONBLOCK |

O_NOCTTY);              if (fd < 0)

Page 230: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

             return close_and_complain(-1, "open", errno);              /* 设定一些不明确是否敏感的值 */              if (tcgetattr(fd, &attr) < 0)              return close_and_complain(fd, "tcgetattr", errno);              /* 无特殊输入或输出处理 */              attr.c_iflag = (flow == SoftFlow) ? (IXON | IXOFF) : 0;          attr.c_oflag = 0;              /* 设定 8 位字符宽和一些杂项控制模式 */              attr.c_cflag &= ~(CSIZE | CFLAGS_TO_CLEAR |

CFLAGS_HARDFLOW);          attr.c_cflag |= (CS8 | CFLAGS_TO_SET);          if (flow == HardFlow)              attr.c_cflag |= CFLAGS_HARDFLOW;              /* 本机模式 */              attr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG);              /* 特殊字符 -- 许多已被先前的设定取消 */              {              int i;      #ifdef _POSIX_VDISABLE              attr.c_cc[0] = _POSIX_VDISABLE;      #else              attr.c_cc[0] = fpathconf(fd, _PC_VDISABLE);      #endif              for (i = 1; i < NCCS; i++)                  attr.c_cc[i] = attr.c_cc[0];          }              attr.c_cc[VSTART] = 0x11;          attr.c_cc[VSTOP] = 0x13;

Page 231: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

             /* 对 read()函数的计时控制 */              attr.c_cc[VMIN] = 1;          attr.c_cc[VTIME] = 0;              /* 波特律 */              cfsetispeed(&attr, baud);          cfsetospeed(&attr, baud);              /* 写入设定 */              if (tcsetattr(fd, TCSANOW, &attr) < 0)              return close_and_complain(fd, "tcsetattr", errno);              /* 如果系统记住了先前的 O_NONBLOCK设定,就取消它 */              flags = fcntl(fd, F_GETFL, 0);          if (flags < 0)              return close_and_complain(fd, "fcntl(GETFL)", errno);          if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0)              return close_and_complain(fd, "fcntl(SETFL)", errno);              return fd;      }          /* 一些简单的计时工具函数 */          /* 向*TV 加 SECS 和 USECS */          static void timeradd(struct timeval *tv, long secs, long usecs)      {          tv->tv_sec += secs;          if ((tv->tv_usec += usecs) >= 1000000)          {              tv->tv_sec += tv->tv_usec / 1000000;              tv->tv_usec %= 1000000;

Page 232: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         }      }          /* 设定 *RES = *A - *B, 返回结果的符号 */          static int timersub(struct timeval *res,                          const struct timeval *a, const struct timeval *b)      {          long sec = a->tv_sec - b->tv_sec;          long usec = a->tv_usec - b->tv_usec;              if (usec < 0)              usec += 1000000, --sec;              res->tv_sec = sec;          res->tv_usec = usec;              return (sec < 0) ? (-1) : ((sec == 0 && usec == 0) ? 0 : 1);      }              /* 这个函数不试图处理非正常的字符串 (比如 ababc)

      * 超时以微妙计       * 一个更通常的做法是使用 alarm()函数处理超时.

      * 这个函数为简便起见不使用信号处理并试图提供一种替换方法       */          int expect(int fd, const char *str, int timeo)      {          int matchlen = 0;          int len = strlen(str);          struct timeval now,end,left;          fd_set fds;          char c;              gettimeofday(&end, NULL);          timeradd(&end, timeo/1000, timeo%1000);              while (matchlen < len)

Page 233: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         {              gettimeofday(&now, NULL);              if (timersub(&left, &end, &now) <= 0)                  return -1;                  FD_ZERO(&fds);              FD_SET(fd, &fds);              if (select(fd+1, &fds, NULL, NULL, &left) <= 0)                  return -1;                  if (read(fd, &c, 1) != 1)                  return -1;                  if (isprint((unsigned char)c) || c == '\n' || c == '\r')                  putchar(c);              else                  printf("\\x%02x", c);                  if (c == str[matchlen])                  ++matchlen;              else                  matchlen = 0;          }              return 0;      }              int main(int argc, char **argv)      {          int fd;          unsigned char c;              if (argc < 2)              die("no port specified");              setvbuf(stdout, NULL, _IONBF, 0);              fd = open_port(argv[1], EXAMPLE_BAUD, EXAMPLE_FLOW);          if (fd < 0)              die("cannot open port");              write(fd, "AT\r", 3);          if (expect(fd, "OK", 5000) < 0)

Page 234: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         {              write(fd, "AT\r", 3);              if (expect(fd, "OK", 5000) < 0)              {                  tcflush(fd, TCIOFLUSH);                  close(fd);                  die("no response to AT");              }          }              write(fd, "ATI4\r", 5);          expect(fd, "OK", 10000);              putchar('\n');              tcflush(fd, TCIOFLUSH);          close(fd);              return 0;      } 事务控制范例程序 ================      /* 生成前台/后台事务的函数 */          #include <stdio.h>      #include <unistd.h>      #include <stdlib.h>      #include <fcntl.h>      #include <signal.h>      #include <sys/types.h>      #include <sys/wait.h>      #include <errno.h>             /* 一些下面的函数会因为无法定位控制 tty 和调用方不在前台而失败。       * 第一种情况时,我们假设一个前台程序会有为标准输入,标准输出

或标准错误输出打开的 ctty,       * 而如果没有则返回 ENOTTY。

Page 235: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

      * 第二种情况时,除 foreground_self()函数的特殊情况以外,       * 若一个非前台程序打算输出一些东西到前台,我们返回 EPERM。       * (也许想得太多了)       */              /* 为给定的 pgrp安排一个终端 (打开一个 ctty) .

      * 这个 tcsetpgrp()外壳程序只是因为 POSIX 中特别错误(bogusity)

的地方而需要;       * 遵照标准的系统在一个非前台进程调用 tcsetpgrp 函数时传递

SIGTTOU       * 信号(差不多总是这样)。这是虚假的一致性之于一般想法的胜利。       */          int assign_terminal(int ctty, pid_t pgrp)      {          sigset_t sigs;          sigset_t oldsigs;          int rc;              sigemptyset(&sigs);          sigaddset(&sigs,SIGTTOU);          sigprocmask(SIG_BLOCK, &sigs, &oldsigs);              rc = tcsetpgrp(ctty, pgrp);              sigprocmask(SIG_SETMASK, &oldsigs, NULL);              return rc;      }              /* 类似 fork 函数,但做事务控制。如果新建立的进程放在前台则设 fg

为真。

Page 236: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

      * (这样隐式地将调用方进程放置到后台,所以做完这个后要当心 tty

的输入/输出)

      * 设定 pgrp 为-1 以创建一个新事务,在此情况下返回的进程号即是新事务的进程组号,

      * 或者设定一个同一会话中存在的事务(一般只用来启动管道操作的第二个或第二个以后

      * 的进程)。       */          pid_t spawn_job(int fg, pid_t pgrp)      {          int ctty = -1;          pid_t pid;              /* 如果生成一个*新*的前台事务,起码要求标准输入,标准输出或           * 标准错误输出的其中一个指向的是控制 tty,并且当前进程在前台。

          * 只有当在存在事务中开始一个新前台进程时才检查控制中的 tty。           * 一个没有控制 tty 的会话只能有后台事务。           */              if (fg)          {              pid_t curpgrp;                  if ((curpgrp = tcgetpgrp(ctty = 2)) < 0                  && (curpgrp = tcgetpgrp(ctty = 0)) < 0                  && (curpgrp = tcgetpgrp(ctty = 1)) < 0)                  return errno = ENOTTY, (pid_t)-1;                  if (pgrp < 0 && curpgrp != getpgrp())                  return errno = EPERM, (pid_t)-1;

Page 237: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         }              switch (pid = fork())          {              case -1: /* fork失败 */                  return pid;                  case 0: /* 子进程 */                      /* 建立新进程组, 如果需要则将我们放到前台                   * 不知道如果 setpgid 函数调用失败该怎么办(“不会发生”)                   */                      if (pgrp < 0)                      pgrp = getpid();                      if (setpgid(0,pgrp) == 0 && fg)                      assign_terminal(ctty, pgrp);                      return 0;                  default: /* 父进程 */                      /* 这里也建立自进程组. */                      if (pgrp < 0)                      pgrp = pid;                      setpgid(pid, pgrp);                      return pid;          }              /*不会执行到这里*/      }        

Page 238: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

     /* 用 SIGNO 表示的信号杀死 PGRP 表示的事务 */          int kill_job(pid_t pgrp, int signo)      {          return kill(-pgrp, signo);      }              /* 中断 PGRP 表示的事务 */          int suspend_job(pid_t pgrp)      {          return kill_job(pgrp, SIGSTOP);      }              /* 继续在后台执行 PGRP 表示的事务 */          int resume_job_bg(pid_t pgrp)      {          return kill_job(pgrp, SIGCONT);      }              /* 继续在前台执行 PGRP 表示的事务 */          int resume_job_fg(pid_t pgrp)      {          pid_t curpgrp;          int ctty;              if ((curpgrp = tcgetpgrp(ctty = 2)) < 0              && (curpgrp = tcgetpgrp(ctty = 0)) < 0              && (curpgrp = tcgetpgrp(ctty = 1)) < 0)              return errno = ENOTTY, (pid_t)-1;              if (curpgrp != getpgrp())              return errno = EPERM, (pid_t)-1;              if (assign_terminal(ctty, pgrp) < 0)

Page 239: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

             return -1;              return kill_job(pgrp, SIGCONT);      }              /* 将我们自己放置到前台,比如在中断一个前台事务之后调用       */          int foreground_self()      {          pid_t curpgrp;          int ctty;              if ((curpgrp = tcgetpgrp(ctty = 2)) < 0              && (curpgrp = tcgetpgrp(ctty = 0)) < 0              && (curpgrp = tcgetpgrp(ctty = 1)) < 0)              return errno = ENOTTY, (pid_t)-1;              return assign_terminal(ctty, getpgrp());      }              /* closeall() - 关闭所有>=给定 FD 的文件描述符 */          void closeall(int fd)      {          int fdlimit = sysconf(_SC_OPEN_MAX);              while (fd < fdlimit)              close(fd++);      }              /* 类似 system()函数,但将给定的命令作为后台事务执行,返回

shell 进程       * 的进程号(并且也是这个事务的进程组号,适用于 kill_job 等等)。       * 如果参数 INFD,OUTFD或 ERRFD 为非 NULL,则打开一个管道和

Page 240: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

一个文件描述       * 符保存与该管道有关的父进程端,然后在子进程中将被从定向

到/dev/null。       * 并且在子进程中关闭所有>2 的文件描述符(一个经常过份估计的工

作)       */          pid_t spawn_background_command(const char *cmd,                                     int *infd, int *outfd, int *errfd)      {          int nullfd = -1;          int pipefds[3][2];          int error = 0;              if (!cmd)              return errno = EINVAL, -1;              pipefds[0][0] = pipefds[0][1] = -1;          pipefds[1][0] = pipefds[1][1] = -1;          pipefds[2][0] = pipefds[2][1] = -1;              if (infd && pipe(pipefds[0]) < 0)              error = errno;          else if (outfd && pipe(pipefds[1]) < 0)              error = errno;          else if (errfd && pipe(pipefds[2]) < 0)              error = errno;              if (!error && !(infd && outfd && errfd))          {              nullfd = open("/dev/null",O_RDWR);              if (nullfd < 0)                  error = errno;          }              if (!error)          {              pid_t pid = spawn_job(0, -1);              switch (pid)

Page 241: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

             {                  case -1: /* fork失败 */                      error = errno;                      break;                      case 0: /* 子进程 */                          dup2(infd ? pipefds[0][0] : nullfd, 0);                      dup2(outfd ? pipefds[1][1] : nullfd, 1);                      dup2(errfd ? pipefds[2][1] : nullfd, 2);                      closeall(3);                          execl("/bin/sh","sh","-c",cmd,(char*)NULL);                          _exit(127);                      default: /* 父进程 */                          close(nullfd);                      if (infd)                          close(pipefds[0][0]), *infd = pipefds[0][1];                      if (outfd)                          close(pipefds[1][1]), *outfd = pipefds[1][0];                      if (errfd)                          close(pipefds[2][1]), *errfd = pipefds[2][0];                          return pid;              }          }              /* 只在错误时执行到这里 */              {              int i,j;              for (i = 0; i < 3; ++i)                  for (j = 0; j < 2; ++j)                      if (pipefds[i][j] >= 0)                          close(pipefds[i][j]);          }    

Page 242: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         if (nullfd >= 0)              close(nullfd);              return errno = error, (pid_t) -1;      }              /*---------------------------------------*/      /* 这里是使用上述函数一个小例子.         */          pid_t bgjob = -1;      volatile int signo = 0;          #ifndef WCOREDUMP       /* 如果没有 WCOREDUMP, 你也许会希望在你的平台上为它设置一

个准确的定义        * (这通常是(status & 0x80) 但也不总是这样),或者就赌没有 core

dumps(        * 就象这个程序所做)        */      # define WCOREDUMP(status) (0)      #endif          int check_children()      {          pid_t pid;          int status;          int count = 0;              while ((pid = waitpid(-1, &status, WNOHANG |

WUNTRACED)) > 0)          {              if (pid == bgjob && !WIFSTOPPED(status))                  bgjob = -1;                  ++count;                  if (WIFEXITED(status))                  fprintf(stderr,"Process %ld exited with return code

Page 243: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

%d\n",                          (long)pid, WEXITSTATUS(status));              else if (WIFSIGNALED(status))                  fprintf(stderr,"Process %ld killed by signal %d%s\n",                          (long)pid, WTERMSIG(status),                          WCOREDUMP(status) ? " (core dumped)" : "");              else if (WIFSTOPPED(status))                  fprintf(stderr,"Process %ld stopped by signal %d\n",                          (long)pid, WSTOPSIG(status));              else                  fprintf(stderr,"Unexpected status - pid=%ld,

status=0x%x\n",                          (long)pid, status);          }              return count;      }              void sighandler(int sig)      {          if (sig != SIGCHLD)              signo = sig;      }              int main()      {          struct sigaction act;          int sigcount = 0;              act.sa_handler = sighandler;          act.sa_flags = 0;          sigemptyset(&act.sa_mask);          sigaction(SIGINT,&act,NULL);          sigaction(SIGQUIT,&act,NULL);          sigaction(SIGTERM,&act,NULL);          sigaction(SIGTSTP,&act,NULL);          sigaction(SIGCHLD,&act,NULL);              fprintf(stderr,"Starting background job 'sleep 60'\n");          bgjob = spawn_background_command("sleep 60", NULL,

NULL, NULL);          if (bgjob < 0)

Page 244: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

         {              perror("spawn_background_command");              exit(1);          }          fprintf(stderr,"Background job started with id %ld\n",

(long)bgjob);          while (bgjob >= 0)          {              if (signo)              {                  fprintf(stderr,"Signal %d caught\n", signo);                  if (sigcount++)                      kill_job(bgjob, SIGKILL);                  else                  {                      kill_job(bgjob, SIGTERM);                      kill_job(bgjob, SIGCONT);                  }              }                  if (!check_children())                  pause();          }              fprintf(stderr,"Done - exiting\n");          return 0;      } ======================================

==================

其他代码

输入密码回显*号代码

Zhutr: 2003-04-18 #include <termios.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> int getpassword(char *passwd); main() {

Page 245: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

char passwd[64]; if(getpassword(passwd)==-1) { printf("error"); exit(1); } printf("%s\n",passwd); return(0); } int getpassword(char *passwd) { int fd = -1, i=0, termpid; struct termios term, termsave; char str[200]; char tmp; strcpy(str,"/dev/tty"); if ((fd = open(str, O_RDWR | O_NOCTTY )) < 0) return -1; sprintf(str, "Please input the password:"); write(fd, str, strlen(str)); tcgetattr(fd, &term); tcgetattr(fd, &termsave); term.c_lflag &= ~(ICANON|ECHO|ISIG); tcsetattr(fd, TCSANOW, &term); read(fd, &tmp, 1); while(tmp!='\n') { passwd[i] = tmp; i++; tcsetattr(fd, TCSANOW, &termsave); write(fd,"*",1); tcsetattr(fd, TCSANOW, &term); read(fd, &tmp, 1); } tcsetattr(fd, TCSANOW, &termsave); passwd[i]='\0'; write(fd,"\n",1); return 0; } superhoo: 2003-05-16 #include <stdio.h> #include <termios.h> #include <unistd.h> #include <sys/time.h> static struct termios inital_settings , new_settings;

Page 246: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

static int peek_character=-1; void init_keyboard(void); void close_keyboard(void); int kbhit(void); int readch(void); void delayusec(int usec); int main() { int ch=0; init_keyboard(); while(ch != 'q' ){ //printf("looping \n"); delayusec(100); if(kbhit()){ ch=readch(); printf("You hit %x\n",ch); } } close_keyboard(); exit(0); } void init_keyboard(void) { tcgetattr(0,&inital_settings); new_settings = inital_settings; new_settings.c_lflag &= ~ICANON; new_settings.c_lflag &= ~ECHO; new_settings.c_lflag &= ~ISIG; new_settings.c_cc[VMIN]=1; new_settings.c_cc[VTIME]=0; tcsetattr(0,TCSANOW,&new_settings); } void close_keyboard(void) { tcsetattr(0,TCSANOW,&inital_settings); } int kbhit(void) { char ch; int nread; if(peek_character != -1) return 1; new_settings.c_cc[VMIN]=0; tcsetattr(0,TCSANOW,&new_settings);

Page 247: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

nread=read(0,&ch,1); new_settings.c_cc[VMIN]=1; tcsetattr(0,TCSANOW,&new_settings); if(nread==1) { peek_character=ch; return 1; } return 0; } int readch(void) { char ch; if(peek_character != -1) { ch=peek_character; peek_character = -1; return ch; } read(0,&ch,1); return ch; } void delayusec(int usec) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = usec; select(0, NULL, NULL, NULL, &tv); }

exit 与_exit atexit 用法

exit 函数与_exit 函数区别:这两个函数都是用于正常终止一个程序,_exit

立即进入内核,exit 做一些清除处理(包括调用执行各终止处理程序,关闭所有标准 IO流等),然后进入内核

exit 会对所有流调用 fclose,所以会造成文件的刷新(多数情况下这是我们所需要的).

Page 248: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

atexit 用于登记终止处理程序.一个进程最多可以登记 32 个函数,这些函数由 exit自动调用,调用顺序与登记顺序相反(LIFO),函数类型必须为 void

(void),同一函数可以被登记多次,同时也会被调用多次

c\c++几本经典书籍下载连接

提供者:rdd 2003-05-11

1、 C++ Primer 中文版 ftp://210.25.133.20/newbook/c++primer.zip 介绍: 感谢 [email protected] 网友 C++ Primer 的第三版结合了 Stanley Lippman 的实践经验和 Josée

Lajoie 对于 ANSI/ISO 标准 C++的深入理解。这本指导书的第三版已经被重新改写过,以便更加精确地讲述标准 C++的特性和用法。在本书中,对于 C++

初学者特别有价值的是一些来自真实世界中的程序例子,它们说明了泛型程序(generic program)的设计、面向对象程序的设计、模板的用法,以及使用标准C++进行程序设计的方方面面。而且,本书也在适当的地方讲到了一些习惯用法以及效率指导。

2、Effective C++ & More Effective C++ 英文版下载 http://www.fmdstudio.net/book/ec_mec.zip 中文下载 http://fengyy.vip.sina.com/images/C/effectiveCpp.rar 3、C++编程思想 http://www.vckbase.com/tools/downtools.asp?id=41 介绍: 本书作者根据自己学习C++的亲身体会及多年教学经验,用简单

的例子和简练的叙述讲解 C++编程,别具特色。 全书共分十八章,内容涉及对

Page 249: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器类、多重继承、异常处理和运行时类型识别。 本书作为正式教材和自学用书均非常优秀,作为程序设计者的参考用书亦极为合适。

4、More Effective C++中文 http://palms.myrice.com/tutorial/MoreECpp/index.htm 5、STL Programmer's Guide http://fengyy.vip.sina.com/images/C/stlguide.rar

*ptr++ 和 *(ptr++)是一样的么

陶深: 2003-05-10

其实* 与++的优先级是一样的 在*和++同时出现的情况下依据从右到左的原则 但 ptr++又先做在加一 . 所以 *ptr++ 的意思是先取 ptr 的指针值,然后在指针值后移指向下个元素 但*ptr++的结果还是*ptr 只有到下个循环是*ptr 的值才是后个元素 伪造一个 HTTP 包,然后把客户机访问的任何站点定向到一个页面 cbchen: 2003-06-22 源码和问题都由 cbchen 解决,源码中是有点小 bug 就是首问校验和问题 我想伪造一个 HTTP 包,然后把客户机访问的任何站点定向到一个页面,但

是这个 TCP 包头的校验和怎么也不对,哪位能帮我看看?这个校验的算法当 TCP

数据区为空的时候是正确的,但是当填入数据的时候就不对了....

Page 250: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

TCP包头校验的代码?(TCP 数据区不为空)

是我程序里面的一个计算长度的弄错了。。。校验算法没有问题的。 在此提醒各位的是,TCP伪头部的长度是 12+TCPHDR+数据区的长度 void send_tcp_htm(int sockfd,struct sockaddr_in *addr) { u_char htmlbuf[]="HTTP1.1 200 OK\r\nServer: IIS SERVER\r\

nData:WED,18 JUN 2003 00:25:09 GMT\r\tContent-Type:text/html\r\nAccept-Range:bytes\r\nLast_Modified:Mon,16 Jun 2003 11:35:55 GMT\r\nContent_Length:187\r\n\r\n<html>\r\nhello\r\n</html>\r\n"; //伪造的 html 数据

int bufsize=sizeof(htmlbuf); struct send_tcp { struct iphdr ip; struct tcphdr tcp; } send_tcp; struct pseudo_header //tcp伪头部 { unsigned int source_address; unsigned int dest_address; unsigned char placeholder; unsigned char protocol; unsigned short tcp_length; struct tcphdr tcp; u_char html[bufsize]; } pseudo_header; int tcp_socket; struct sockaddr_in sin; int sinlen; u_char *str; str=(u_char *)&send_tcp; /* form ip packet */ send_tcp.ip.ihl = 5; send_tcp.ip.version = 4; send_tcp.ip.tos = 0; send_tcp.ip.tot_len = htons(40+sizeof(htmlbuf)); send_tcp.ip.frag_off = 0; send_tcp.ip.ttl = 64;

Page 251: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

send_tcp.ip.protocol = IPPROTO_TCP; send_tcp.ip.check = 0; send_tcp.ip.saddr = iprecv->ip_dst.s_addr; send_tcp.ip.daddr = addr->sin_addr.s_addr; /* form tcp packet */ send_tcp.tcp.dest = addr->sin_port; send_tcp.tcp.source = tcprecv->dest; send_tcp.tcp.ack_seq = htonl(ntohl(tcprecv->seq)+len); send_tcp.tcp.res1 = 1; send_tcp.tcp.doff = 5; send_tcp.tcp.fin = 0; send_tcp.tcp.syn = 0; send_tcp.tcp.rst = 0; send_tcp.tcp.psh = 1; send_tcp.tcp.ack = 1; send_tcp.tcp.urg = 0; send_tcp.tcp.res2 = 0; send_tcp.tcp.window = htons(512); send_tcp.tcp.check = 0; send_tcp.tcp.urg_ptr = 0; send_tcp.tcp.seq = tcprecv->ack_seq; /* set fields that need to be changed */ send_tcp.ip.id = 0 ; send_tcp.tcp.check = 0; send_tcp.ip.check = 0; /* calculate the ip checksum */ send_tcp.ip.check=in_cksum((unsigned short *)&send_tcp.ip,

20); /* set the pseudo header fields */ pseudo_header.source_address = send_tcp.ip.saddr; pseudo_header.dest_address = send_tcp.ip.daddr; pseudo_header.placeholder = 0; pseudo_header.protocol = IPPROTO_TCP; pseudo_header.tcp_length = htons(20); bcopy((char *)&send_tcp.tcp, (char *)&pseudo_header.tcp, 20);//

将 send_tcp 的数据拷如 pseudo_header 中的 tcp bcopy(htmlbuf, (char *)&pseudo_header.html, sizeof(htmlbuf)); send_tcp.tcp.check = in_cksum((unsigned short

*)&pseudo_header, sizeof(pseudo_header)); bcopy(htmlbuf,(u_char *)(str+40),sizeof(htmlbuf)); printf("size:%d\n",sizeof(htmlbuf)); if(sendto(sockfd, str, sizeof(htmlbuf)+40, 0, (struct sockaddr

*)addr,sizeof(struct sockaddr))<0)printf("sendto error!\n");

Page 252: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

} in_cksum(unsigned short *addr, int len) { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer =0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return(answer); } 如果使用的库或是 glibc 没有安装到标准目录下什么办 蓝色键盘 : 2003-06-19

配置环境变量 LD_LIBRARY_PATH指定库路径 comp.lang.c FAQ fieryfox: 2003-06-21 英文的,对 c/c++语言本身有什么不懂的可以看看,或是对什么用法不熟的

也可以看,推荐 http://www.plethora.net/~seebs/faqs/c-iaq.html http://www.eskimo.com/~scs/C- faq /top.html 连接时报`__ctype_b'没有找到 viacocha: 2003-06-22 我用 google 查了,ctype_b 是<ctype.h>里定义的一个函数,我看到

redhat 里的 bug 报告里说 gcc 2.3.X 里有一个 ctype_b 的 bug,说这个函数

Page 253: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

编译有错,没想到这么一个 BUG让我遇上了,最后 redhat 说解决方案一是重做一个 ctype_b 函数,二是等待!!

注意符号__ctype_b 对应 c语言中的符号_ctype_b ,因为编译器会在全局变量前加个_号

为什么两次 fork 可以防止僵死进程

gadfly : 2003-06-21

wangrujun 写到:

无论是 fork 1次还是 fork2次,其结果都是子进程被 init 进程领养(假设父进程先退出的)。之后子进程调用 exit,最终会被 init 进程释放它的资源,并真正死亡。

你这个假设并不是两次 fork 的与一次 fork 比较的前提。看看 APUE 的 151

页最后几行.

apue 写到:

回忆一下 8 . 5 节中有关僵死进程的讨论。如果一个进程要 f o r k

一个子进程,但不要求它等待 子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现

这一要求的诀窍是调用 f o r k

两次。程序 8 - 5 实现了这一点。

fork 两次在防止僵死方面来说,就是因为儿子进程先退出,孙子进程就被

Page 254: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

init 接管了,实际上与最初的父进程脱离了关系,就不会僵死了。 两次 fork 的其它作用就是做 daemon。原因就像无双说的用于 daemon

编写。

如何知道和共享内存关联的所有进程号

无双: 2003-06-27

fuser 命令可以 共享内存应该有文件映射吧 只要有文件映射的就可以通过 fuser 来查看 gadfly: 2003-06-28 至于共享内存的 attach 个数可以通过,shmctl(segId, IPC_STAT, &ds);

取 struct shmid_ds ds 的成员 unsigned short int shm_nattch; /* number of current attaches */ 来获得。 fuser恐怕只对文件的打开起作用 jobman: 2003-06-28 你说的问题我也遇到,共享内存有一个 nattch 字段,表示当前 有几个进程和该共享内存段连接,我想你是需要知道这是哪些进程, 可是看起来不太可能,恐怕得自己解决,因为在核心部分没有留下 相应得信息。至少我没有发现

怎样通过系统函数查看 swap 状态

gadfly :swap 可以通过 statfs 来查看吧

Page 255: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

哪里有 c/c++视频讲座下载

c/c++视频讲座下载

ecat: 2003-05-08 http://www.buct.edu.cn/itat/C++/

SOCKET 出现大量 FIN_WAIT_2 和 CLOSE_WAIT 连接的问题

Fzchh:“请教 SOCKET 出现大量 FIN_WAIT_2 和 CLOSE_WAIT 连接的问题”

我的服务端在接收到大量的 SOCKET 的短时间的连接的时候,会发现我的服务器出现我的监听断口上出现许多 FIN_WAIT_2 和 CLOSE_WAIT 的连接,导致我新的客户端不能连接。各位大哥是否能够帮我想点办法,如何限制服务端的服务和怎么比较快的释放这些端口?!

无双:2003-06-10

会不会是因为对方还没有 close造成的 使用 close 操作的话 那么会等到 socket buffer 中所有数据都发送完后才会调用 close

会不会是这个原因 stevenyi:2003-06-11

主动 close 的一方的 socket 会进入 time_wait 状态,所以你在客户端主动close 就没问题了

Page 256: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

怎样删除大量小文件

gadfly: 2003-06-10 文件太多,有的系统 rm *是会有问题的。 可以如楼上说的, 也可已如: rm [a-e]* rm [f-h]* ....... 依次删除 JohnBull: 文件太多的话,shell 会把*扩展成为超长的文件名列表,有可能超过 shell

命令行长度上限 chdonald: 这样太麻烦了,因为 rm 不能够删除太多的文件,用[a-e]也无法得知道是

否会超过,应该用 ls | xargs rm doni: echo * |xargs rm –f

信号灯使用 SEM_UNDO 的危机

chdonald: 我使用了 semop,用到了 SEM_UNDO 这个选项,目的正是想象书上所说的

可以让它在进程退出时自动还原所做操作,可是问题出现了,当我调式程序运行了 几 把之后 , 出现 "No space left on device" 的 错误 ,而且不 是 出 在semget(已经可以正确获得 SEMID),而是出在 semop,查了下 GOOGLE,大多是出在 semget 创建信号灯不够的情况下,man 了一把,发现是:

ENOSPC The limit on the number of individual processes

Page 257: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

requesting an SEM_UNDO would be exceeded. 说明使用 SEM_UNDO 是有次数限制的,最后只能舍弃 SEM_UNDO 这个本

以为是好东东的选项目. sunlan: 2003-06-23 你不会在进程退出的时候自己又对信号灯做了把 P/V 操作吧?! 如果使用了 SEM_UNDO,则在进程退出时不能显式的做 P/V 操作,而必

须由系统为你做,否则就等于做了两遍! 在多进程通过信号灯同步资源、而单个进程可能运行较长时间的情况下,不

建议使用 SEM_UNDO,否则运行进程将长时间占用资源,仅当其退出后才会释放!

在 pro*c 中怎样调用 oracle 的存储过程

临风左岸 : 2003-06-25 EXEC SQL EXECUTE BEGIN raise_salary(:emp_id, :increase); END; END-EXEC; 关于 proc 和 oci 的下栽 ccrazy: 2003-06-13 资料不错,想使用的都可以下来看 http://www.vvsoft.net/vvbksd/index.asp?typename=oracle

如何在两个进程中传递 socket描述符

fieryfox: 2003-05-26 用 ioctl 可以传递描述符。I_SENDFD 和 I_RECVFD

描述符是 OS 为每个进程维护的,其实是描述符表的入口,里边有复杂的

Page 258: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

数据结构以屏蔽文件、设备、socket 等的不同。描述符是进程内唯一的,出了该进程就没有了意义。比如都是描述符 5,对进程 A 和进程 B 其含义是完全不同的因此需要 ioctl即OS 参与才能完成描述符传递。 蓝色键盘:

能 够 实 现 进 程 之 间 传 输 描 述 子 的 方 式 很 多 , 例 如socketpair,FIFO,socket域,shm 等等。如果是父子进程之前传输的话,用管道(有名,流管道)或者 socketpair 会简单一些。

我的 socket 程序编译有问题连拉出错 蓝色键盘: 2003-05-12

对于 solaris 上面的 socket 程序的编译连接,需要做如下的补充: 1、相对于其它的 unix系统,solaris 上面 gcc 对于语法的检查要严格得多

在编译的时候要求加入必要的库文件,但是在很多的系统中可能不需要,

例如 Linux。 2、对于 socket 编程来说,solaris 上面至少需要用-lsocket(注意中间没

有空格)来指明连接 scoket 库。 3、在加入-lsocket 的情况下,编译仍然出错的话,需要加入-lnsl。例如: $gcc -c -g test.c -lsocket –lnsl getppid源码 pid_t getpppid(pid_t pid) { psinfo_t psinf; int fd; char buf[256]; sprintf(buf, "/proc/%d/psinfo", pid); fd=open(buf,0);

Page 259: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

if(fd!=-1) { read(fd, &psinf, sizeof(psinfo_t)); close(fd); } return psinf.pr_ppid; }

C 中的 restrict

A: caohongxin: 我指的 restrict 是 C99 标准中新增加的类型修饰符.

书中原话是这样的 /********************************************************* restrict 这种修饰符只适用于指针.

由 restrict修饰的指针是最初唯一对指针所指向的对象进行存取的办法,

仅当第二个指针基于第一个时,才能对对象进行存取.

因此,对对象的存取都限定于基于有 restrict修饰的指针表达式中.

由 restrict修饰的指针主要被用做函数指针,或者指向由malloc()分配的内存变量.

restrict 数据类型不改变程序的语义.

使用 restrict限制一个地址只能通过一个指针访问(就像是代码 1增加了restrict限制后编译时会报错) 另外要注意的是这个关键字只是 c99 功能 不是标准 c++中的 'Restrict' Pointers One of the new features in the recently approved C standard

C99, is the restrict pointer qualifier. This qualifier can be applied to a data pointer to indicate that, during the scope of that pointer declaration, all data accessed through it will be accessed only

Page 260: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

through that pointer but not through any other pointer. The 'restrict' keyword thus enables the compiler to perform certain optimizations based on the premise that a given object cannot be changed through another pointer. Now you're probably asking yourself, "doesn't const already guarantee that?" No, it doesn't. The qualifier const ensures that a variable cannot be changed through a particular pointer. However, it's still possible to change the variable through a different pointer. For example:

代码: void f (const int* pci, int *pi;); // is *pci immutable? { (*pi)+=1; // not necessarily: n is incremented by 1 *pi = (*pci) + 2; // n is incremented by 2 } int n; f( &n, &n);//增加 restrict 关键字是因为这里会出问题,

//如果对两个参数都使用了 restrict 关键字,那么这里编译时会报错,因为一

//个地址可以通过两个指针访问了 In this example, both pci and pi point to the same variable, n.

You can't change n's value through pci but you can change it using pi. Therefore, the compiler isn't allowed to optimize memory access for *pci by preloading n's value. In this example, the compiler indeed shouldn't preload n because its value changes three times during the execution of f(). However, there are situations in which a variable is accessed only through a single pointer. For example:

代码: FILE *fopen(const char * filename, const char * mode); The name of the file and its open mode are accessed through

unique pointers in fopen(). Therefore, it's possible to preload the values to which the pointers are bound. Indeed, the C99 standard revised the prototype of the function fopen() to the following:

/* new declaration of fopen() in <stdio.h> */ FILE *fopen(const char * restrict filename, const char * restrict mode); Similar changes were applied to the entire standard C library:

printf(), strcpy() and many other functions now take restrict pointers:

Page 261: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

代码: int printf(const char * restrict format, ...); char *strcpy(char * restrict s1, const char * restrict s2); C++ doesn't support restrict yet. However, since many C++

compilers are also C compilers, it's likely that this feature will be added to most C++ compilers too.

Pro*c 编译的 Makefile 例子 Wangz: 2003-05-14 SHELL=/bin/sh CC= /opt/ansic/bin/cc PC= proc OUTPUT = $(HOME)/bin CFLAGS= -g -I$(HOME)/include

-I$(ORACLE_HOME)/precomp/public PCFLAGS= include=$(HOME)/include parse=no \ CHAR_MAP=STRING SQLCHECK=SEMANTICS \ userid=user/pwd ORALIBS= -DDEBUG \ -I$(ORACLE_HOME)/rdbms/public \ -I$(ORACLE_HOME)/precomp/public \ -I$(ORACLE_HOME)/rdbms/demo \ -I$(ORACLE_HOME)/plsql/public \ -I$(ORACLE_HOME)/network/public \ -L$(ORACLE_HOME)/lib/ \ -L$(HOME)/lib TFLAGS= \ -lpubdb \ -lclntsh \ -lsql8 \ -Y -Qy -lc -lm .SUFFIXES: .pc .c .o all: \ a.o .pc.o: $(PC) $(PCFLAGS) $< $(CC) $(CFLAGS) -c $(<:.pc=.c) $(CC) $(CFLAGS) -o $(OUTPUT)/$* $*.o $(ORALIBS) ${TFLAGS} clean: rm *.c rm *.o

Page 262: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

shm 限制值为什么不准确

beggar: 我想测试我最大能得到多少个 shm 的 id.结果发现一个进程中可用的信号

灯最大是系统上限而不是进程上限,下 ulimit显示不符 $ sysctl kern.ipc.shmmni kern.ipc.shmseg //查看系统限制值 kern.ipc.shmmni: 192 kern.ipc.shmseg: 128 $ ./a.out Your Limits is:189 //程序的输出值 (4 个是其它进程可用) $ 无双: 2003-05-15

有的系统的实现的话可能不会实现按进程限制 而而只是限制总数目 在许多系统中对 POSIX 标准的实现就是这样的 所以并不是说只要是符合 POSIX 的就一定会这样 很多系统对 POSIX 理解都有点偏差(个人理解,不一定准确,但现在仍然觉得

可能是这个原因) beggar: 这两天看了一下 FreeBSD 关于 ipc 的源代码,也查了一些资料。结果并不

是你所说的 posix 理解偏差。“有的系统的实现的话可能不会实现按进程限制,而只是限制总数目”不知你说的是什么系统?

在 FB源文件 sysv_shm.c 中找到关于 shmseg 和 shmmni 的变量: TUNABLE_INT("kern.ipc.shmmni", &shminfo.shmmni); TUNABLE_INT("kern.ipc.shmseg", &shminfo.shmseg); 发现在 shmget 函数中没有出现检查 shminof.shmseg 的地方.只是在开

Page 263: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

头检查了一下 shmmin,shmmax,shmmni,没有对 shmseg 进行检查。代码如下 if (uap->size < shminfo.shmmin || uap->size >

shminfo.shmmax) return EINVAL; if (shm_nused >= shminfo.shmmni) /* any shmids left? */ return ENOSPC; 但在 shmat 函数中却发现了检查 shmseg 的代码 if (i >= shminfo.shmseg) return EMFILE; 编程测试 #include <sys/types.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #define MAXLIST 1000 int shmlist[MAXLIST]; char *shmpt[MAXLIST]; int main(int argc,char **argv) { int i,n; for(i=0;i<MAXLIST;i++){ if((shmlist[i]=shmget(IPC_PRIVATE,1,SHM_R|SHM_W))<0) break; if((shmpt[i]=shmat(shmlist[i],0,0))==(void*) -1) { //增加 shmat warn("break at shmat\n"); break; } printf("Your Limits is:%d\n",i); for(n=0;n<=i;n++){ shmdt(shmpt[n]); if(shmctl(shmlist[n],IPC_RMID,NULL)<0) errx(1,"error when remove share memory id.\n"); } exit(0); } 编译运行后的结果果然是 kern.ipc.shmseg设定的值 128 $ ./a.out a.out: break at shmat: Too many open files

Page 264: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

Your Limits is:128 $ 查找了很多资料,发现很多关于 shmseg 和 shmmni之间区别的翻译讲

得含糊不清,而且都不规范。大多如你所 paste 的那样 SHMSEG 每进程最大共享内存段数量 只需要 1 个段,不过缺省比这高得

多. SHMMNI 系统范围最大共享内存段数量 类似 SHMSEG + 用于其他应用

的空间 其实 shmseg真正限制的是进程可以用 shmat 连接的内存段的数量,而

不是限制用 shmget 创建内存段的数量.在 linux 及 solaris 上测试结果也是如此。 蓝色键盘: 对于楼主的这个问题,我当时在 hp-11 上做了测试,但是测试测结果无法

回答楼主的问题(因为楼主的系统我没有接触过)。的确,在 unix各个主流系统中,不管是 shm 这一块,很多地方实现的差异较大,我觉得楼主最后贴出了部分代码证明了对于 shmat 的限制(这个限制也是对于进程能够连接的共享内存段的限制),对于整个 shm 的大小应该(为了不误导人,还是用应该修饰)有限制的,这种限制或许不在那段代码中(例如可能在像核心申请 shm 的时候做了限制)。我觉得只要大家相互引导,相互探讨,问题还会逐步的明了的,如果大家都有兴趣,并且能够坚持的话,当然也要有时间。

在 unix 中的很多的东西,谁是真正的标准,谁是圣经!答案是没有。技术总在进步,每个厂商都是在遵循一定标准的前提下开发。如果我们能去对一些核心的东西做讨论,本身意味着论坛中的朋友在进步,既然是讨论,就不能保证

Page 265: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

所说的每一句话都必须天衣无缝,如果非要这样的话,我建议大家还是自己看书好了。毕竟一本好书能够出版是经过严格审批的!

C 函数可以获得进程的相关信息

Cs: 2003-03-18 The getpid() function returns the process ID of the calling

process. for other information, you can try getrusage: NAME getrusage - get information about resource utilization SYNOPSIS #include <sys/resource.h> int getrusage(int who, struct rusage *r_usage); refer to man page for detail usage of the func. Liupch: 读取/proc/进程号/psinfo 这个文件。 就用我告诉你的那个函数 ioctl(fd, PIOCPSINFO, &procinfo); 在看一下 procinfo 这个结构就知道了。 Chdonald: 2003-05-16 -------------------------------------------------------------------------------- 写了个简单的程序,不是很完善 /* Usage: proc <pid> */ #include <stdio.h> #include <unistd.h> #include <stropts.h> #include <sys/ioctl.h> #include <sys/old_procfs.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> main(int argc, char **argv) { int fd;

Page 266: C/C++编程问答 - SnowCoalsnowcoal.com/USER/0/files/201411/14168998476810.doc · Web view1.C/C++编程 1.1: 遇到问题怎么解决 A 论坛的精华区也有相应的讨论,许多可以直接解决你的问题,也可以GOOGLE查找,并看看精华区。

char path[32]; prpsinfo_t procinfo; snprintf(path,32,"/proc/%s",argv[1]); fd = open(path, O_RDONLY); printf("fd = %d\n", fd); if (fd < 0){ fprintf(stderr, "open %s error:%s\n", path, strerror(errno)); close(fd); exit(1); } ioctl(fd, PIOCPSINFO, &procinfo); printf("status=%c\n", procinfo.pr_sname); printf("zomb = %c\n", procinfo.pr_zomb); //if != 0, zomb printf("nice = %c\n", procinfo.pr_nice); printf("uid = %d\n", procinfo.pr_uid); printf("gid = %d\n", procinfo.pr_gid); printf("pid = %d\n", procinfo.pr_pid); printf("ppid = %d\n", procinfo.pr_ppid); printf("group leader = %d\n", procinfo.pr_pgrp); printf("sid = %d\n", procinfo.pr_sid); printf("start time = %d ns\n", procinfo.pr_start.tv_sec*10^9 +

procinfo.pr_sta rt.tv_nsec); printf("user and system time = %d ns\n",

procinfo.pr_time.tv_sec*10^9 + procin fo.pr_time.tv_nsec); printf("priority = %d\n", procinfo.pr_pri); //high value = high pri printf("tty = %d\n", procinfo.pr_ottydev); printf("used cpu = %d\%\n", procinfo.pr_pctcpu); printf("used mem = %d\%\n", procinfo.pr_pctmem); close(fd); }