LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 2947|回复: 17

发帖规则(清兄弟们遵守)与论坛问答(FAQ)

[复制链接]
发表于 2003-5-17 13:48:18 | 显示全部楼层 |阅读模式
发帖前最好先查查旧帖或是google,另外也看看man页面。如果有不懂的就大胆的问,但是应该把自己标题写清楚,要问的内容也描述清楚。
兄弟们都喜欢有个清爽的论坛。那么就让大家一起来努力吧



1 不要重复发帖,
重复发帖让论坛看起来很乱,同时也不利于对这个问题进行讨论。如果你的问题被知道的人看到,那么自然会回答你的问题。如果没有人知道你的问题,那么重复也没有用。 如果问题沉了,那么可以回复"UP"等让它浮起来 。

2 帖子标题还是没有描述问题,并写清楚你想问什么。一眼能看出是什么内容的贴子的,大家讨论起来会更有热情。如果论坛中一眼看过去都是
“问一个问题”
“大家看看这个什么做”
“救命啊”

一眼看过去都是这样的帖子
讨论的热情都没了
所以还是写清楚一点吧


论坛是大家的,就由大家一起努力
 楼主| 发表于 2003-5-17 13:50:19 | 显示全部楼层
FAQ以后会整理后也发到这里来
 楼主| 发表于 2003-5-17 14:30:01 | 显示全部楼层
这是从其它地方转过来的
对大家有一些用

我希望这里也能早点出现好的回答
有自己的FAQ




1.C/C++编程
1.1: 我有问题,怎样发问,怎样查找答案
1.2:STL string 怎么转换大小写
1.3: 怎样用gcc生成动态库,用什么编译选项
1.4:怎样指定程序链接某个库的动态库或静态库 版本
1.5: 请问各UNIX平台下和编译器名
1.6: solaris下有没有C函数可以获得进程的相关信息
1.7: c++下使用<iostream>的问题(namespace[名字空间]关键字问题)
1.8: :volatile的作用是什么
1.9: 请问,用 gcc 或 ld 连接程序时,如何设置段的属性?


2.数据库编程
2.1:如何在pro*c中调用存储过程
2.2: 请问在pro*c中知道错误代码,怎么查这个代码的错误信息
2.3: AIX下编译的嵌入式DB2 C程序,删除数据库后再创建时连接不成功
2.4: 请问SOLARIS下使用OCI编程序要连什么库
2.5:请问怎样用ct-librery编程

3 图形界面编程
3.1:论坛中有人说 GTK+ 并不支持中文,是这样吗?
3.2:为什么在控件上的汉字无法显示,并伴有如下错误警告?
3.3:为什么在屏幕上输出的汉字变成了乱码?
3.4:在修改控件字体的第一步就发生了错误,为什么 gdk_font_load() 总是失败?
3.5:我不想使用宋体,怎样获得其它字体的代号?
3.6:你为什么没有说到 gettext() 及相关国际化标准?

4 UNIX系统编程
4.1 我建立共享内存之后,忘记了删除掉。请问:1)如果我不管它,它是由系统自己释放掉么
4.2 请问IPCS显示的各权限位意义
4.3 如何在unix下实现kbhit的功能?谢谢!!

8. Solaris内核编程相关问题 [本篇为转载]
8.1 Solaris内核模块中如何getcwd
8.2
8.3 如何避免一个套接字进入TIME_WAIT状态
8.4 结构在优化编译中的对齐问题
8.5
8.6 如何得到非局部变量列表
8.7
8.8 如何单独获得Solaris编译环境
8.9 如何获取Solaris内核可调参数列表
8.10
8.11 如何页边界对齐式分配内存
8.12
8.13 compile()和step()怎么用





1.1: 我有问题,怎样发问,怎样查找答案
A 许多问题可以通过GOOGLE查找答案,GOOGLE网址是www.google.com,上面的资料都很具体。另外在论坛的精华区也有不少讨论,许多可以直接解决你的问题,建议问前先在GOOGLE查找,并看看精华区。 如果已找了GOOGLE并看了精华区,但是对某些方面不理解,那么就在论坛上发问。发问时标题要清楚,并把你的问题描述清楚。许多人不会看你帖子的内容,所以在标题中写清楚问题也是很重要的。
如果要粘贴代码,那么使用发言功能中的CODE功能,这样可以保证代码不会乱,能有更多人看懂你代码。
当别人准确回答了你的问题时,应该说声谢谢,表示这个问题已回答,并表示对帮助你的人的感谢。这样会让更多的人觉得你的一个好学有礼貌的人,从而会更注意你的问题。
如果你按上面这些方法做了,并不能保证你的发言一定能回答,因为不是每个人什么问题都知道,但可以保证会有更多人关注你的问题,回答的可能性会更高。



1.2:STL string 怎么转换大小写
A:使用STL算法中的transform 函数

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <string>
  4. #include <cctype>

  5. using namespace std;

  6. int main(void)
  7. {
  8.         string s = "This is a TEST string.";
  9.         // original
  10.         cout << "orig: " << s << endl;

  11.         // to lower case
  12.         transform (s.begin(), s.end(), s.begin(), (int(*)(int))tolower);
  13.         cout << "lower: " << s << endl;

  14.         // to upper case
  15.         transform (s.begin(), s.end(), s.begin(), (int(*)(int))toupper);
  16.         cout  << "upper: " << s << endl;

  17.         return 0;
  18. }
复制代码

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


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

· 不用使用-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;
   }





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这个结构就知道了。


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的本意是“易变的”
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。
简单点:
就是该变量会以编译器无法预知的方式发生变化,请编译器不要做优化(所有的编译器的优化均假设编译器知道变量的变化规律)


1.9: 请问,用 gcc 或 ld 连接程序时,如何设置段的属性?
幽灵兵:
为了实现共享数据,这个方法要比其它 进程/线程 的数据同步方法更
快,更方便,更灵活,而且是所有方法中最简单的。
比如我在 Win 下这样做:
1. 创建个 DLL 专门用于数据交换
2. 在 DLL 中声明一个全局变量 Var1
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.1:如何在pro*c中调用存储过程
ilmare :
在嵌入式SQL中使用CALL 语句调用存储过程的格式如下:
代码:

EXEC SQL CALL [schema.] [package.] stored_proc[@db_link] (arg1, ...)
[INTO :ret_var [[INDICATOR] :ret_ind]];


例子:
创建一个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行的结果描述




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的内部名称
是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了,
如果你很牛,可以试试两都都用
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方式代问自答,希
> 望能帮初学者解决一点实际问题。
> 您可以随意修改或转载本文,但请保证内容的正确性,以
> 免误人子弟 --- 谢谢 :)


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( "确定" );
改成
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类型,及相关
函数,取而代之的是 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"。
您还可以通过一些 Linux/Unix 下的编辑软件的字体选择框
来查找字体对应的代码。
但标准的方法是调用 Pango库中的相应函数来查找机器上已
安装的字体。
如果想了解更多关于 Pango的高级使用方法,请参考它自带
的 API 开发手册。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

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


4. 系统编程
4.1 我建立共享内存之后,忘记了删除掉。请问:1)如果我不管它,它是由系统自己释放掉么
一颗流星:
我建立共享内存之后,忘记了删除掉。请问: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时,共享内存段已经被清除
- 相应的权限没有被设置

其它的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;

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.








8. Solaris内核编程相关问题

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

A: Rich Teer <rich@rite-group.com>
最好通过u->u_cdir获取当前工作目录(cwd)的vnode(v节点)。但这依赖于内核当前上
下文,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 */



8.3 如何避免一个套接字进入TIME_WAIT状态
Q: 我正在写一个unix server程序,不是daemon,经常需要在命令行上重启它,绝大
多数时候工作正常,但是某些时候会报告"bind: address in use",于是重启失
败。

A: Andrew Gierth <andrew@erlenstar.demon.co.uk>
server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。至于
TIME_WAIT状态,你无法避免,那是TCP协议的一部分。

Q: 如何避免等待60秒之后才能重启服务

A: Erik Max Francis <max@alcyone.com>

使用setsockopt,比如
代码:

--------------------------------------------------------------------------  
int option = 1;  

if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option,  
                  sizeof( option ) ) < 0 )  
{  
    die( "setsockopt" );  
}  
--------------------------------------------------------------------------  



Q: 编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?
A: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用
端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,
指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧
使用同一端口,此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期
望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不
可能。

一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端
口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组
还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使
用 SO_REUSEADDR 选项。

Q: 在客户机/服务器编程中(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秒。

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  
       ----------------->  


为什么需要 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 状态,而允许另一个拥有相同相关五元组的连接出现,可能处理
TCP报文时,两个连接互相干扰。使用 SO_REUSEADDR 选项就需要考虑这种情况。

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

如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。
第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二
个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT
状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被
丢弃。建立第二个连接的时候,不会混淆。

A: 小四 <scz@nsfocus.com>

在Solaris 7下有内核参数对应 TIME_WAIT 状态保持时间

# ndd -get /dev/tcp tcp_time_wait_interval
240000
# ndd -set /dev/tcp tcp_time_wait_interval 1000

缺省设置是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编程/应用问答中文版>>的声明。




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

D: rain@bbs.whnet.edu.cn

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中。



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

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/col ... /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 <Casper.Dik@Holland.Sun.Com>

"-misalign"仅仅用于应用程序,无法应用到内核编程中。"-misalign"使得编译
获得的代码增加了一些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成员,崩溃了。尝试过其他
办法,首先用一个字符指针读取第一个字节,然后指针增一,把该指针强制类型
转换成( uint32_t * ),然后读取数据,依然崩溃。

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

A: Ed L Cashin <ecashin@coe.uga.edu>

我想你可以单独写一个pkt_header_read()函数,单字节读取然后拼装成相应的数
据类型。如果你想避免函数调用,可以使用"inline"关键字。

A: Casper H.S. Dik - Network Security Engineer <Casper.Dik@Holland.Sun.Com>

你是否意识到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 <andrew@cucumber.demon.co.uk>

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

代码:

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;  
    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()



如果是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

DEC : #pragma pack(1)
SUN : #pragma pack(1)
AIX : 编译时 -q align=packed
HP-UX : #pragma pack 1

D: Joe Durusau

在 Visual C++ 中,使用 "-ZP1" 就可以让编译器对自定义结构进行单字节对齐,实
际就是取消了对齐优化。

A: law@apue.dhs.org 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

代码:

struct foo  
{  
    char a;  
    int  b;  
};  

... ...



4) 编译选项的pack

-fpack-struct

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

Q: 小四 <scz@nsfocus.com>

#pragma pack(push)
#pragma pack(n)
... ...
#pragma pack(pop)

push/pop这个用法都谁支持啊

A: law@apue.dhs.org

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

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



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

Q: 什么工具可以从目标文件中提取非局部变量列表

A: Donald McLachlan <don@mars.dgrc.crc.ca>

最简单的就是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  |__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  
$



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

Q: 我需要安装哪些包

A: Seán Boran <sean@boran.com>

需要下列Solaris安装包:
SUNWbtool、SUNWsprot、SUNWtoo、SUNWhea、SUNWarc、SUNWlibm、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之
前。

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

Q: 谁有Solaris内核可调参数列表

A: Andrew Garman <andrew_garman@ins.com>

执行

/usr/xpg4/bin/nm /platform/sun4u/kernel/unix | egrep 'OBJT \|GLOB' | more

显示结果中部分为Solaris内核可调参数,另外一些非可调内核参数。可以用ndd获取、
设置网络相关参数。

D: scz <cloudksy@263.net>

可以考虑

/usr/ccs/bin/nm -nx /dev/ksyms | egrep 'OBJT \|GLOB' | more

不知道二者区别何在?第二个报告内容应该包含了后来动态加载内核模块输出的符号,
第一个才对应基本内核输出的符号。

8.11 如何页边界对齐式分配内存
Q: 我希望在页边界上分配大块内存,要求普通用户、非特权进程亦能使用此技术。
在mmap(2)手册页中没有明确表明返回地址边界对齐。它提到可以指定起始地址以
保证页边界对齐,但没有说明如果由系统选定起始地址时是否也是页边界对齐的。
MAP_ANON并非所有系统都支持,我需要在Solaris 2.x上运行。

A: Andrew Gierth <andrew@erlenstar.demon.co.uk>

mmap(2)即可满足要求。某些系统提供了valloc或者memalign,但它们的实现机制是,
分配超过请求大小的内存,然后调整之,这相当浪费。

mmap(2)应该始终是页边界对齐的。

在那些不支持 MAP_ANON 的系统上,打开/dev/zero获取句柄,传递给mmap(2),效果
是一样的。

mmap(2)的可移植性足够好,不过"分配超过请求大小的内存并调整之"可能更具有可
移植性。
8.13 compile()和step()怎么用
Q: 我知道这两个函数是Solaris对正则表达式的支持函数,可到底怎么用呢?
代码:

A: microcat <rotm@263.net>  

--------------------------------------------------------------------------  
/* 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  
    {  
        printf( "No match.\n" );  
    }  
    free( expbuf );  
    exit( EXIT_SUCCESS );  
}  /* end of main */  
--------------------------------------------------------------------------  

$ ./reg '^.*inetd$' '/usr/sbin/inetd'  
Match at: /usr/sbin/inetd  
$
 楼主| 发表于 2003-5-17 14:30:26 | 显示全部楼层
保留为C/C++FAQ
 楼主| 发表于 2003-5-17 14:30:42 | 显示全部楼层
保留为JAVA
 楼主| 发表于 2003-5-17 14:30:54 | 显示全部楼层
保留为PERL
 楼主| 发表于 2003-5-17 14:31:07 | 显示全部楼层
保留为PHP
 楼主| 发表于 2003-5-17 14:32:09 | 显示全部楼层
弟兄们一起努力,用我们自己的智慧把上面的保留部分写好
发表于 2003-5-17 16:36:04 | 显示全部楼层
此贴应该置顶。
 楼主| 发表于 2003-5-17 16:38:34 | 显示全部楼层
好了,刚才是找不到什么置顶
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表