Unix编程/应用问答中文版
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会有什么灾难性后果?在普遍的现实应用中,好象也
就是服务器不稳定点,不见得有什么灾难性后果吧?
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/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 <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 * )&value;
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
#pragma pack(1)
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这个用法都谁支持啊
这个写法我没见过,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
$
9. 图形界面相关问题
9.1 如何避免进入Solaris的图形界面
Q: 我想让console保持在字符模式下,该如何做
A: Darren Dunham <ddunham@redwood.taos.com>
这里有一份很好的FAQ,http://www.wins.uva.nl/pub/solaris/solaris2.html
下文引自http://www.science.uva.nl/pub/solaris/solaris2.html#q3.54
如何允许/禁止dtlogin?
是否启动dtlogin可以用/usr/dt/bin/dtconfig命令进行设置,不带任何参数执行该
命令,提示如下:
/usr/dt/bin/dtconfig -d (disable auto-start)
/usr/dt/bin/dtconfig -e (enable auto-start)
/usr/dt/bin/dtconfig -kill (kill dtlogin)
/usr/dt/bin/dtconfig -reset (reset dtlogin)
/usr/dt/bin/dtconfig -p (printer action update)
/usr/dt/bin/dtconfig -inetd (inetd.conf /usr/dt daemons)
/usr/dt/bin/dtconfig -inetd.ow (inetd.conf /usr/openwin daemons)
如果绝大多数时间你并不想关闭图形模式,可以在"session"菜单上选择
"command line login"。
A: <lucifer@nospam.org>
更省事的办法是
cd /etc/rc2.d
mv S99dtlogin s99dtlogin
Q: Solaris CDE窗口的启动与关闭
A: tenia@一塌糊涂 1999-11-03
用/usr/dt/bin/dtlogin
dtlogin -daemon 从命令行启动注册窗口
dtlogin -e 使系统自动启动注册窗口
dtlogin -d 取消自动启动
dtlogin -kill 杀掉注册窗口
9.2 Solaris 7的锁屏
Q: Solaris 7中哪个进程负责锁屏效应。7以前的版本,某些人在console登录后锁屏,
回家前忘记取消锁屏,我简单地杀掉xlock进程即可。但是我不知道Solaris 7中
该怎么做。看了看dtsession和dtscreen的一些东西,但是无论我杀掉二者中哪个
进程,console挂起在黑屏的无限循环中,只有鼠标光标可见
A: <buck_naked@NOiname.SPAMcom>
应该是dtscreen
9.3 如何调整键盘重复率
Q: Ultra 5 Solaris 8 如何设置键盘重复率?我想设置重复率到最大,延迟到最小。
A: Alan Coopersmith <alanc@alum.calberkeley.org>
http://soar.Berkeley.EDU/~alanc/
如果是root想对系统中所有用户做此修改,编辑/etc/dt/config/Xservers,增加
-ar1 和 -ar2选项。如果/etc/dt/config/Xservers不存在,从
/usr/dt/config/Xservers复制一份过来。
如果不是root,仅仅想修改自己的配置,用/usr/openwin/bin/accessx配置键盘和鼠
标参数。
man -M /usr/openwin/man Xsun
-ar1 milliseconds
这么多毫秒后按键开始自动重复。缺省500毫秒。参数对于x86或者PowerPC
无效。
-ar2 milliseconds
两次自动重复之间的时间间隔(毫秒单位)。缺省50毫秒。参数对于x86或者
PowerPC无效。
man -M /usr/openwin/man accessx
9.4 如何拔掉键盘继续运行Solaris
Q: 我这里的E250/E3500装了Solaris后键盘都不能拔掉,一拔掉就进入OK状态。而老
式的SparcServer 1000E是可以不要键盘运行的,不知道要在哪里设置才能够不要
键盘运行?
Q: 这里是一台Sun Ultra 5,拔掉键盘后,系统停止响应
A: J.Keil
拔掉键盘导致Ultra 5的console设备侦测到一次BREAK条件。BREAK条件将中断操作系
统,使系统进入OBP(open boot prom)监视状态。如果启动内核时使用了kadb内核调
试器,BREAK条件使系统进入kadb调试状态。
参看kbd(1)手册页,有几种办法禁止这种行为:
a. 永久办法
vi /etc/default/kbd
KEYBOARD_ABORT=disable
/usr/bin/kbd -i (不用重新启动机器)
b. 临时办法
/usr/bin/kbd -a disable
与之对应的就是
/usr/bin/kbd -a enable
c. 看BSM的时候找到的另外一种解决办法,在/etc/system文件中增加如下行
set abort_enable = 0
A: dkoleary@mediaone.net 2001-06-02 22:09
用kbd(1)命令
kbd [ enable | disable | alternate ]
enable : 允许 STOP-A
disable : 禁止 STOP-A
alternate : 允许拔掉键盘,但不禁止 STOP-A
为了使用 alternate 选项,需要安装下列补丁
Solaris 2.6 105924-10
Solaris 7 107589-03
9.5 Solaris下如何设置显卡分辨率
/etc/openwin/server/etc/OWconfig
/usr/openwin/server/etc/OWconfig
/usr/sbin/m64config -prconf -propt
/usr/sbin/m64config -res '?'
ls -l /dev/fb (一个符号链接)
ls -l /dev/fbs/m640 (一个符号链接)
prtconf -F (Return the device pathname of the console frame buffer)
m64config -res 1152x900x76 -depth 8
ls -l /dev/fbs/ffb0 (一个符号链接)
/usr/sbin/ffbconfig -prconf -propt (当前设置1152x900x76x8)
参看m64config(1M)、ffbconfig(1M)手册页
9.6 Solaris下如何设置显示刷新率
A: CERNET 华中地区网络中心 UNIX版 domyself 2001-08-16
除了m64config(1M),还有一种办法,就是进入OBP状态设置分辨率、刷新率。进入
OBP状态至少有两种方法
# sync <-- 同步文件系统,准备重启
# init 0 <-- 关闭系统后将停留在OBP状态,也就是ok提示符下
其实我们最常用的做法是Stop-A进入OBP状态
ok> show-displays
这里可以看到当前的显示设备,如果你只有一个显示设备,那么这里有两个提示,选
择a就选择了当前显示设备,选择q表示退出,你只能选择a。选择之后可以Ctrl-Y输
入那个很长的设备路径全名。
ok> dev <设备路径全名> (用Ctrl-Y输入)
选择并设置成当前结点,后面的words命令只处理当前结点,不接受指定
ok> pwd (验证当前路径是否正确)
ok> words
列出当前结点的方法名,分辨率和刷新率是作为方法提供的。比如你可能看到
r1024x768x77x24
r1152x900x76x8
注意,数字前面有一个小写的'r',表示resolution。假设我们以前的设置是
1152x900x76x8,现在想改成1024x768x77x24,用如下命令
ok> setenv output-device screen:r1024x768x77x24
ok> boot -r
再次注意,"screen:"之后指定的是方法名,也就是说前面有那个小写的'r'。你用
words看到什么就指定什么。自己估计显存大小,分辨率、刷新率、颜色深度是相互
制约的。最好不要自己调节这些参数,很容易损坏显示设备。OBP状态下设置分辨率、
刷新率的命令普遍描述如下
ok> setenv output-device <device-path>:<resolution>
上面screen是个别名,可以用devalias看到本来的设备路径全名,也就是
show-displays看到的那个设备路径全名。指定设备路径全名也可以,如果你撑着了
的话。
ok> devalias (检查设备别名)
启动之后可以用如下命令检查当前设置
# m64config -prconf | grep Current
Current resolution setting: 1024x768x77
Current depth: 24
#
警告:和调节PC机所配显示设备一样,这种调节具有破坏性,务必小心。对于高版本
的Solaris,建议使用m64config等工具调节显示刷新率,低版本Solaris才考
虑进入OBP状态设置。
A: 水木清华 humvee
x86/Solaris下可用kdmconfig
10. 网卡相关问题
10.1 如何在程序中获取本机MAC地址
Q: 如何在C代码中获取本机MAC地址,我用strace跟踪ifconfig
ioctl(4, SIOCGIFHWADDR, 0xbffffb80) = 0
ioctl(4, SIOCGIFADDR, 0xbffffb80) = 0
ioctl(4, SIOCGIFBRDADDR, 0xbffffb80) = 0
ioctl(4, SIOCGIFNETMASK, 0xbffffb80) = 0
D: Unix Programmer
用gethostname()/gethostbyname()依赖于本机的域名解析系统,比如/etc/hosts文
件、/etc/nsswitch.conf文件、/etc/resolv.conf文件。这样获取本机IP是不可靠的。
如果/etc/hosts文件中没有指定本机IP,则依赖DNS是否配置了PTR资源记录。可靠的
办法应该是strace ifconfig、truss ifconfig,实际就是照ifconfig的实现去获取
本机IP。
A: David Peter <dave.peter@eu.citrix.com>
strace是Linux下的工具,由于HP-UX 10.20的ioctl不支持SIOCGIFHWADDR,可能需要
DLPI接口或者针对/dev/lan0的NETSTAT ioctl,为了使用NETSTAT ioctl还需要重启
动,而且HP不赞成继续使用NETSTAT ioctl,HP-UX 11.00不再支持。
根据手头一个古老的工具,Digital Unix下ioctl支持SIOCRPHYSADDR。至于SGI上的
IRIX,我想可能需要一个原始套接字,比如:
s = socket( PF_RAW, SOCK_RAW, RAWPROTO_SNOOP )
D: scz <scz@nsfocus.com> 2001-11-20 11:46
SPARC/Solaris下只有root用户才可以ifconfig -a看到本机MAC地址,普通用户并不
能这样做,但可以尝试在dmesg输出中查找,由于dmesg使用的数据有可能被破坏,这
个办法并不可靠。
"arp <本机IP地址>"可以看到本机MAC地址,使用的技术实际上就是前面编程演示的
ioctl( s, SIOCGARP, &arpreq )
此外还可以用如下命令获取本机MAC地址
ndd /dev/arp arp_cache_report | grep MYADDR
10.2 如何在Sun工作站上安装3块网卡
Q: 我想在Sun工作站上安装3块网卡,怎么办
A: Santosh
请遵循如下步骤
1) 在Sun工作站上增加网卡
2) 用boot -r启动系统
3) 观察启动信息,确认每块网卡都被识别出来,比如这种信息
PCI-device: network@1,1, hme #0
SUNW,hme0 is /pci@1f,4000/network@1,1
实际中如果每块网卡都被识别出来,有hme0、hme1 和 hme2,当然1和2可能不是
这个名字。
4) 到/etc目录下创建hostname.hme0、hostname.hme1 和 hostname.hme2。在每个文
件中分别指定IP地址,编辑/etc/hosts文件增加相应入口。
5) 重启机器
10.3 如何在Solaris x86上安装网卡驱动
A: James Adkins <jadkins@peregrine.com>
不需要修改"pcn.conf"文件。开始我只是"touch /reconfigure",Solaris x86检测
到了网卡,但是"ifconfig -a"的时候只有loopback接口,于是我尝试如下步骤:
# drvconfig
# devlinks
# touch /reconfigure
重启动后一切ok
10.4 Solaris 单网卡多IP(以太网卡别名)
Q: 对于Solaris 2.5.1来说,可以在一块物理网卡上配置多个IP地址
A: Sun Microsystems 1998-03-31
下面以lance ethernet (le0) 设备为例说明
1) 编辑/etc/hosts文件
128.195.10.31 myhost
128.195.10.46 myhost2
128.195.10.78 myhost3
2) 创建/etc/hostname.le0:n文件,注意hostname.le0:0就是hostname.le0
/etc/hostname.le0 (Contains name myhost)
/etc/hostname.le0:1 (Contains name myhost2)
/etc/hostname.le0:2 (Contains name myhost3)
注意这种文件就一行内容,主机名。
3) 如果想立即生效
% ifconfig le0:1 up
% ifconfig le0:1 129.153.76.72
% ifconfig le0:1 down
Q: Solaris 8下如何给一块以太网卡赋予多个IP地址?
A: Vadim V. Kouevda <VKouevda@pcinetgw.is.bear.com>
ifconfig le0 plumb
ifconfig le0 ether 0:1:2:3:4:5
ifconfig le0:1 plumb
ifconfig le0:1 ... up
ifconfig le0:2 plumb
ifconfig le0:2 ... up
到/etc/init.d目录下修改IP地址、子网掩码等设置。
D: scz <scz@nsfocus.com>
有三个文件需要注意,/etc/rcS.d/S30rootusr.sh(/etc/init.d/rootusr)、
/etc/rc2.d/S69inet(/etc/init.d/inetinit)和/etc/rc2.d/S72inetsvc
(/etc/init.d/inetsvc)。
Q: 如何在一块物理网卡上绑定多个IP地址
A: Sun Microsystems 1997-10-27
所谓虚拟网络接口指一个物理接口多个不同IP地址,Solaris允许一个物理网络接口
对应多个逻辑接口,换句话说,即使只有一块网卡,也可以配置多个IP地址。参看
ifconfig(1M)手册页。对于Solaris 2.x,可以在一块网卡上绑定256个不同IP地址。
Sun OS 4.x(Solaris 1.x)不支持。
/usr/sbin/ndd -get /dev/ip ip_addrs_per_if
对于Solaris 2.6,通过ndd可以配置超过256(0-255)个IP地址。
/usr/sbin/ndd -set /dev/ip ip_addrs_per_if 1-8192
将这条命令增加到/etc/rc2.d/S69inet启动脚本中去。
1) 编辑/etc/hosts文件(或者nis host map),为每个虚拟接口增加条目。别忘记修
改NIS、NIS+、DNS数据库。
2) 为每个接口创建/etc/hostname.<interface:#>文件,比如/etc/hostname.le0:1、
hostname.le0:2、hostname.le0:3 ... le0:255。文件内容为单行IP地址或者主
机名。比如创建如下文件
/etc/hostname.le0:1 (不要使用le0:0,那就是le0)
/etc/hostname.le0:2
Solaris 2.5.1下最多1024个虚拟接口。每个文件内容是自己对应的虚拟接口IP地
址或者主机名。
3) 如果使用了子网,应该在/etc/netmasks中增加
network_address netmask
157.145.0.0 255.255.255.0
4) 重启系统
5) ifconfig -a验证之
某些第三方应用程序此时可能会出问题。出于安全考虑,可以
ndd -set /dev/ip ip_forwarding 0
ndd -set /dev/ip ip_strict_dst_multihoming 1
参看RFC1112 - <<Host Extensions for IP Multicasting>>。
如果因为配置虚拟接口出现不期望的路由,考虑手动"route delete"。可以增加一个
启动脚本/etc/rc2.d/S99vif,用于完成这些任务。
对于Solaris 2.6,可能还需要
ndd -set /dev/ip ip_enable_group_ifs 0 (2.6下缺省是1,7下缺省是0)
将这条命令增加到/etc/rc2.d/S69inet启动脚本中去。
10.5 如何修改主机名(hostname)
Q: Solaris 2.6下如何修改主机名(hostname)
A: Herve Poussin <poussin@partner-system.com>
需要修改如下文件
/etc/hosts
/etc/hostname.<interface>
/etc/nodename
/etc/net/*/hosts (3 files, man -s 7D ticotsord)
如果你运行在VxVM下,则应该
# vxdctl hostid <new_name>
# vxdctl init <new_name>
10.6 SPARC/Solaris 2.5/2.6/7/8下如何设置网卡100Mb全双工
Q: 我从SPARC连接3Com交换机时,总是使用半双工,如何配置网卡强行使用100Mb全
双工
A: Martin Scerri <martin_scerri@bigfoot.com>
下列回答来自"Sun管理员FAQ 12.3",并且只适合于Solaris 2.5及其以后版本,
Sun OS 4.x及其更早版本不支持hme接口。
一般网卡可以和交换机自动协商使用100Mb全双工,如果协商失败,可能看到诸如
"late collision"一类的消息,出现丢包甚至完全不能工作的现象。为了强行指定使
用某一确定的工作模式,比如100Mb FD,可以用ndd做如下操作:
# 指定操作hme0接口
ndd -set /dev/hme instance 0
# 关闭自动协商
ndd -set /dev/hme adv_autoneg_cap 0
# 打开100Mb FD支持
ndd -set /dev/hme adv_100fdx_cap 1
# 关闭100Mb HD支持
ndd -set /dev/hme adv_100hdx_cap 0
# 关闭10Mb FD支持
ndd -set /dev/hme adv_10fdx_cap 0
# 关闭10Mb HD支持
ndd -set /dev/hme adv_10hdx_cap 0
同样需要在对端(比如交换机)强行指定使用100Mb FD模式。
注意:Fast ethernet hubs 总是使用100Mb HD模式
ethernet hubs 总是使用10Mb HD模式
如果你想强行指定系统中所有hme网卡在启动时进入同一确定模式,可以在
/etc/system文件中设置,下例表示进入100Mbit FD模式:
set hme:hme_adv_autoneg_cap=0
set hme:hme_adv_100fdx_cap=1
set hme:hme_adv_100hdx_cap=0
set hme:hme_adv_10hdx_cap=0
set hme:hme_adv_10fdx_cap=0
A: Andreas.Gouder <Andreas.Gouder@t-online.de>
你可以用如下命令获取当前设置
# ndd -get /dev/hme link_mode
0 半双工
1 全双工
# ndd -get /dev/hme link_status
0 Link Down
1 Link up
# ndd -get /dev/hme link_speed
0 10Mbps
1 100Mbps
10.7 Unix/Linux/BSD如何对抗ARP欺骗攻击
Q: Solaris的静态ARP表项(arp -s)还是会被动态刷新,我只确认Linux/FreeBSD的静
态ARP表项不会被动态刷新,到底有没有稍微通用点的对抗ARP欺骗攻击的方法。
A: scz <scz@nsfocus.com>
下面以Solaris系统为例说明,其他系统大同小异。
1) 建立静态ARP表
/usr/bin/touch /etc/static_arp_entry
/usr/bin/chown root:root /etc/static_arp_entry
/usr/bin/chmod 600 /etc/static_arp_entry
编辑/etc/static_arp_entry文件,输入类似内容
192.168.8.90 00:00:00:11:11:11
/usr/sbin/arp -s -f /etc/static_arp_entry
可以在/etc/rc2.d/S69inet启动脚本中增加这条命令。参看arp(1M)、arp(7P)手册页
了解更多细节。这种技术对系统影响不大,对网络影响较大,破坏了动态ARP解析过
程。Solaris系统中,静态ARP表不会过期,必须用"arp -d"手动删除。但是,
Solaris系统的静态ARP表项可以被动态刷新,仅仅依靠静态ARP表项并不能对抗ARP欺
骗攻击,相反纵容了ARP欺骗攻击,因为虚假的静态ARP表项不会自动超时消失。当然,
可以考虑利用cron机制补救之。(增加一个crontab)
为了对抗ARP欺骗攻击,对于Solaris系统来说,应该结合"禁止相应网络接口做ARP解
析"和"使用静态ARP表"的设置
2) 禁止某个网络接口做ARP解析(对抗ARP欺骗攻击)
"/sbin/ifconfig hme0 -arp"命令将禁止hme0接口做ARP解析,hme0接口不会发送/接
收ARP报文。必须配合使用静态ARP表,否则无法完成正常网络通信。参看
ifconfig(1M)了解更多细节。假设/etc/rc2.d/S69inet启动脚本中存在如下内容
/sbin/ifconfig hme0 -arp
/usr/sbin/arp -s -f /etc/static_arp_entry
假设/etc/static_arp_entry文件内容如下
192.168.8.90 00:00:00:11:11:11
这里192.168.8.90是一台PWin98,也做静态ARP设置(因为对方不会响应ARP请求报文)
arp -s 192.168.10.6 08-00-20-a8-2e-ac
此时192.168.8.90与192.168.10.6可以正常通信,并且192.168.10.6不受ARP欺骗攻
击的影响。事实上,绝大多数Unix/Linux/BSD操作系统,都可以结合"禁止相应网络
接口做ARP解析"和"使用静态ARP表"的设置来对抗ARP欺骗攻击。对于Linux/FreeBSD
系统,因为其静态ARP表项(arp -s)不会被动态刷新,所以不需要"禁止相应网络接口
做ARP解析"即可对抗ARP欺骗攻击。
10.11 x86/Solaris如何强制设定网卡速率
Q: x86/Solaris,我如何强行指定网卡的工作状态为100Mbps full-duplex,ndd(1M)
不工作
A: CERNET 水木清华 Unix版 inc 2001-10-11 20:29
x86/Solaris下的网卡驱动不支持ndd(1M)设置网卡工作状态,要达到目的,唯一的办
法是通过driver.conf(4)指定。x86/Solaris 8的iprb(7D)手册页建议使用
ForceSpeedDuplex选项。对于其它驱动,参看如下例子
vi /kernel/drv/iprb.conf <-- 用ifconfig -a确认一下
# To force full duplex operation, uncomment the following line:
# full-duplex=1;
#
# To force half duplex operation, uncomment the following line:
# full-duplex=0;
#
# To force 10Mbps operation, uncomment the following line:
# speed=10;
#
# To force 100Mbps operation, uncomment the following line:
# speed=100;
奇怪的是iprb.conf原来没有上面的内容,elxl.conf却有。注意,不同网卡是有区别
的,我试了RealTek RTL8139/8129、3Com 3C905B TX、Intel,只有Intel的可以这样
修改,RealTek RTL8029的我不确定。
D: CERNET 水木清华 Unix版 2001-10-12 10:36
由于x86下网卡驱动不支持ndd(1M)获取网卡状态,被迫使用netstat -k
ifconfig -a 找出网络接口名
netstat -k <interface> | grep ifspeed
某些x86网卡驱动支持,某些不支持,这个办法同样适合于SPARC网卡驱动,虽然后者
可以直接使用ndd(1M)。
10.12 Solaris/FreeBSD/Linux如何确定网卡Capability/Speed
A: 小四 <scz@nsfocus.com> 2001-12-07 17:06
Solaris
# netstat -k hme0 | grep ifspeed
# ndd -get /dev/hme link_mode
0 半双工
1 全双工
# ndd -get /dev/hme link_status
0 Link Down
1 Link up
# ndd -get /dev/hme link_speed
0 10Mbps
1 100Mbps
FreeBSD用ifconfig就可以看到
status: active <-- 网线接到一个HUB上了
status: no carrier <-- 未接网线
Linux和Windows系列我到现在也不确认。
在高版本的Linux系统中net-tools包中有一个mii-tool命令,可以用于检查这些数据,
而不是溶合在ifconfig的输出中。
10.14 traceroute是怎么实现的
Q: traceroute是怎么实现的
A: 小四 <cloudsky@263.net>
traceroute(对于Windows系列是tracert)通过逐步增加TTL值的方法,发送常规IP分
组来实现。第一次发送分组时TTL为1,第一个路由器接收到该分组之后将TTL值减1,
结果为0,于是就丢弃该分组,并发回一个"TTL超时"的ICMP报文,该报文的源地址是
这第一个路由器。紧接着发送一个TTL为2的分组。注意,traceroute发送常规的UDP
报文到一个不用的UDP端口。当初考虑将路由跟踪的功能加入IP协议本身,定义一个
"路由跟踪"选项,路由器处理带有该选项的IP包时立即发回一个跟踪报文到源站点。
然而这种做法还停留在实验室阶段,因为这需要改变所有已经存在的路由器,而且在
某种程度上与端到端原则相违背。
1) 传统的Unix实现是UDP+ICMP
2) 其实从原理上TCP+ICMP也是可以的,某些Unix系统采用了这种实现
3) Windows另有一种实现,Icmp Echo Request+ICMP
11. package相关问题
11.1 Solaris下如何将二进制软件包安装到指定目标路径下
Q: pkgadd无法指定目标路径,而我不想使用缺省安装路径/usr/local
A: lisuit@水木清华 2002-01-19 14:49
pkgadd -R <basedir> ...
11.2 Solaris下如何自己定制二进制安装包
A: deepin <deepin@nsfocus.com> & scz <scz@nsfocus.com>
[root@ /tmp/tools]> mkdir srcroot
[root@ /tmp/tools]> cp -p /usr/ccs/bin/as srcroot <-- 复制那些数据源到该目录下
[root@ /tmp/tools]> cp -p /usr/ccs/bin/make srcroot
[root@ /tmp/tools]> cp -p /usr/ccs/lib/cpp srcroot
[root@ /tmp/tools]> ls -lR srcroot
-rwxr-xr-x 1 bin bin 342072 1999 1月 12 as*
-rwxr-xr-x 1 bin bin 91344 1998 4月 27 cpp*
-rwxr-xr-x 1 bin bin 273196 1998 4月 23 make*
[root@ /tmp/tools]> cd srcroot
[root@ /tmp/tools/srcroot]> find . -print | pkgproto > ../prototype
[root@ /tmp/tools/srcroot]> cd ..
[root@ /tmp/tools]> more prototype
f none cpp 0755 bin bin
f none as 0755 bin bin
f none make 0755 bin bin
[root@ /tmp/tools]> vi prototype <-- 在头部增加如下行
i pkginfo=./pkginfo
[root@ /tmp/tools]> vi pkginfo
PKG="Scz"
NAME="Scz Package Cool Tools"
VERSION="2000-01-09 14:53"
CATEGORY="application"
PSTAMP="小四"
CLASSES="none"
ARCH="Solaris 2.7 For SPARC"
VENDOR="NSFocus"
BASEDIR="/tmp/tools/opt/"
EMAIL="scz@nsfocus.com"
[root@ /tmp/tools]> pkgmk -o -f ./prototype -b /tmp/tools/srcroot
[root@ /tmp/tools]> pkgtrans -s /var/spool/pkg /tmp/tools/SczPkg Scz
[root@ /tmp/tools]> rm -rf /var/spool/pkg/Scz
[root@ /tmp/tools]> pkgadd -d SczPkg
[root@ /tmp/tools]> ls -lR opt
-rwxr-xr-x 1 bin bin 342072 1999 1月 12 as*
-rwxr-xr-x 1 bin bin 91344 1998 4月 27 cpp*
-rwxr-xr-x 1 bin bin 273196 1998 4月 23 make*
[root@ /tmp/tools]> pkginfo -l Scz
[root@ /tmp/tools]> pkgrm Scz
[root@ /tmp/tools]> rm SczPkg
11.3 如何恢复/usr/bin/su的缺省安装属性
Q: 我怀疑/usr/bin/su被修改过了,如何证实呢
A: John D Groenveld <groenvel@cse.psu.edu>
$ pkgchk -l -p /usr/bin/su
11.4 如何获知指定包与其他包之间的依赖关系
A: Vitaly Filatov <vitaly@royint.com>
详细信息查看 /var/sadm/pkg/<package>/install/depend 文件,这里指明了该包的
三个特性
P 安装前必须已经存在的包
I 与自己不兼容的包
R 依赖自己的其他包
11.5 Linux中如何知道ifconfig属于哪个包
A:
# rpm -qif /sbin/ifconfig <-- 直接查找src.rpm
11.6 Solaris下如何知道某包中有哪些文件
A: 小四 <scz@nsfocus.com> 2001-12-10 21:39
1) 已经安装了的包,对/var/sadm/install/contents文件做grep操作
# grep SUNWcsd /var/sadm/install/contents
~~~~~~~ 包名
2) 尚未安装的包,先伪安装,然后检查pkgmap文件
# pkgadd -s /var/spool/pkg -d less-340-sol7-sparc-local
# cd /var/spool/pkg/GNUless
# grep BASEDIR pkginfo <-- 确认BASEDIR目录,比如/usr/local
# vi pkgmap <-- 注意第四列
# rm -rf /var/spool/pkg/GNUless
可以写个脚本完成这些工作。
12.3 如何关闭cron的日志
Q: 有些时候cron的日志文件增长得如此之大,占用了大量磁盘空间,有什么办法彻
底关闭cron的日志吗
A: Sun Microsystems 1998-03-30
编辑/etc/default/cron,设置 CRONLOG 变量为 NO ,将关闭cron的日志
CRONLOG=NO
缺省是
CRONLOG=YES
13. 进程相关问题
13.1 如何根据进程名获得PID
Q: 我知道ps、top等命令和grep相结合可以达到这个效果,但是我想在C程序中实现
这个功能,并且我不想用system()、popen()等方式。
D: Linux提供了一个命令,pidof(8)
A: Andrew Gierth <andrew@erlenstar.demon.co.uk>
第一种办法是读取/proc接口提供的信息
--------------------------------------------------------------------------
/* gcc -Wall -O3 -o getpid getpid.c */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/procfs.h>
#include <unistd.h>
#include <stropts.h>
#include <dirent.h>
#include <fcntl.h>
static pid_t getpidbyname ( char * name, pid_t skipit )
{
DIR * dirHandle; /* 目录句柄 */
struct dirent * dirEntry; /* 单个目录项 */
prpsinfo_t prp;
int fd;
pid_t pid = -1;
if ( ( dirHandle = opendir( "/proc" ) ) == NULL )
{
return( -1 );
}
chdir( "/proc" ); /* 下面使用相对路径打开文件,所以必须进入/proc */
while ( ( dirEntry = readdir( dirHandle ) ) != NULL )
{
if ( dirEntry->d_name[0] != '.' )
{
/* fprintf( stderr, "%s\n", dirEntry->d_name ); */
if ( ( fd = open( dirEntry->d_name, O_RDONLY ) ) != -1 )
{
if ( ioctl( fd, PIOCPSINFO, &prp ) != -1 )
{
/* fprintf( stderr, "%s\n", prp.pr_fname ); */
if ( !strcmp( prp.pr_fname, name ) ) /* 这里是相对路径,而且
不带参数 */
{
pid = ( pid_t )atoi( dirEntry->d_name );
if ( skipit != -1 && pid == skipit ) /* -1做为无效pid对
待 */
{
pid = -1;
}
else /* 找到匹配 */
{
close( fd );
break; /* 跳出while循环 */
}
}
}
close( fd );
}
}
} /* end of while */
closedir( dirHandle );
return( pid );
} /* end of getpidbyname */
static void usage ( char * arg )
{
fprintf( stderr, " Usage: %s <proc_name>\n", arg );
exit( EXIT_FAILURE );
} /* end of usage */
int main ( int argc, char * argv[] )
{
pid_t pid;
if ( argc != 2 )
{
usage( argv[0] );
}
pid = getpidbyname( argv[1], -1 );
if ( pid != -1 )
{
fprintf( stderr, "[ %s ] is: <%u>\n", argv[1], ( unsigned int )pid );
exit( EXIT_SUCCESS );
}
exit( EXIT_FAILURE );
} /* end of main */
--------------------------------------------------------------------------












文章评论
共有 0 位网友发表了评论 此处只显示部分留言 点击查看完整评论页面