- 论坛徽章:
- 0
|
. 系统管理配置问题
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.10 为什么Sun工作站非要输入boot命令才能启动
1. 系统管理配置问题
1.1 如何给SUN工作站增加eeprom硬件口令保护
A: scz
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= (等号后面无其他字符,直接回车)
如果想改变前面设置的口令,用这条命令,同样是交互式输入过程。
# /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
你无法改变分区大小,但是可以增加/删除交换文件,效果类似交换分区。下列命令
在根目录下创建一个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/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
运行sys-unconfig,在此之前请man -s 1M sys-unconfig,:-)
A: chad schrock
天啊,为了拍死一只苍蝇,你要引爆原子弹吗?
只需要做如下操作,在你的.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.
#
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=
sh : LANG=
export LANG
csh : setenv LANG
bash: export LANG=en_US(zh.GBK)
3) vi /etc/default/init
增加如下内容
LANG=
LC_ALL=
重启系统。
运行"locale"命令确认改变生效。
如果你希望使用的locale并未安装,参看如下文档安装locale
Solaris 8 : >
Solaris 7 : >
Solaris 2.6: >
D: scz 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
vi /etc/default/login
# TIMEOUT sets the number of seconds (between 0 and 900) to wait before
# abandoning a login session.
#
TIMEOUT=180
这里的超时设置针对登录过程,而不是登录成功后的shell会话超时设置。
1.6 一个目录拥有setgid设置,怎么理解
Q: 对一个目录做了setgid设置,可我并没有发现这和正常情况有什么区别
A: John Riddoch
在这种目录下创建新文件时将采用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: 小四
SPARC/Solaris 7下测试
如果目录拥有SGID设置,那么该目录下新创建的文件将继承该目录的属组,而不是创
建者所对应的GID。
[root@ /export/home/scz]> id
uid=0(root) gid=1(other) 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/ cd groupsgid/
[root@ /export/home/scz/groupsgid]> touch scz_0
[root@ /export/home/scz/groupsgid]> ls -l scz_0
-rw-r--r-- root users scz_0 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
1.7 非Sun Console上有无等价Stop-A的按键
A: neomilev
如果是便携机,尝试alt/break 或者 ctrl/break。如果是vt100终端,尝试F11 或者
break
1.8 如何让一个用户只能ftp而无法telnet
A: 小四
修改该用户在/etc/passwd中的shell为/bin/false,在/etc/shells文件中增加
/bin/false,此时,该用户只能ftp,telnet失败。
1.10 为什么Sun工作站非要输入boot命令才能启动
Q: 我有台Sun工作站,每次开机后停在ok状态下,需要手工输入boot命令才能启动,
现在想避免这种效果,怎么办
A: /usr/sbin/eeprom auto-boot?=true
/usr/sbin/eeprom auto-boot?
有三种办法
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: 老大 2001-12-04 16:51
直接将第二块硬盘接上去,然后顺序执行如下命令,不用重新启动机器
modunload -i 0
drvconfig(1M)
devlinks(1M)
disks(1M)
如果需要重新格式化、分区、创建文件系统,就继续执行
format(1M)
newfs(1M)
_________________________________________________________________________________________________
2. 堆栈相关问题
2.1 如何理解pstack的输出信息
2.2
2.3 Solaris中如何获取一个C程序的调用栈回溯
2.4 如何编程获取栈底地址
2.5 如何得到一个运行中进程的内存映像
2.6 调试器如何工作的
2.7 x86/Linux上如何处理SIGFPE信号
--------------------------------------------------------------------------
2. 堆栈相关问题
2.1 如何理解pstack的输出信息
Q: 080603a7 main (1, 80479b8, 80479c0) + d53
结尾的d53是什么
A: Roger A. Faulkner
在代码段绝对地址0x080603a7处,main()调用了一个函数,0x080603a7正是
main + 0xd53,换句话说,从main()函数开始的0xd53偏移处。
2.3 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系统)?我希望自己的异常处理程序
在进程结束前dump整个栈区(stack),以便观察到栈顶是什么函数。对于调试意想
不到的运行时错误而言,这很重要。
A: Bjorn Reese
用/usr/proc/bin/pstack [-F]
参看这个例子代码,
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
这依赖于你的系统,如果使用glibc 2.1或更新版本,可以使用backtrace()函数,
参看,其他系统可能有不同的技术支持。
注意,你所使用的办法可能是唯一能够保证跨平台使用的
A: Andrew Gabriel Consultant Software Engineer
下面是一个backtrace()的应用举例,如果你使用Solaris 2.4及其后续版本,那
么这个例子可以很好的工作。很可能无法工作在64-bit模式下,我没有尝试过,
好像Solaris 7已经提供了一个类似的演示程序。还可以增加某些功能,我没有时
间了。
/*
* Produce a stack trace for Solaris systems.
*
* Copyright (C) 1995-1998 Andrew Gabriel
* Parts derived from Usenet postings of Bart Smaalders and Casper Dik.
*
*/
/* ......................................................................... */
#include
#include
#include
#include
#include
#include
#include
#include
#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%xn", "??", ( unsigned int )pc );
}
else
{
/* found */
fprintf( stderr, "*** %s:%s+0x%xn", 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 ];
for ( i = 0; i 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 endsn" );
}
/* ......................................................................... */
2.4 如何编程获取栈底地址
Q: 虽然很多操作系统的用户进程栈底地址固定,但是我需要写一个可广泛移植C程序
获取这个栈底地址。
A: tt 2001-06-02 19:40
假设堆栈(stack)向低地址方向增长,则所谓栈底指堆栈(stack)最高地址
x86/Linux 栈底是0xc0000000( 栈底往低地址的4个字节总是零 )
SPARC/Solaris 7/8 栈底是0xffbf0000( 栈底往低地址的4个字节总是零 )
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
--------------------------------------------------------------------------
/*
* gcc -Wall -O3 -o gstack gstack.c
*
* A simple example to get the current stack bottom address
* warning3
* 2001-06-01
*
* Modified by scz
* 2001-06-02
*/
#include
#include
#include
#include
#include
typedef void Sigfunc ( int ); /* for signal handlers */
Sigfunc * signal ( int signo, Sigfunc * func );
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 ) 2001-06-03 00:38
W. Richard Stevens在>中详细
介绍了setjmp/longjmp以及sigsetjmp/siglongjmp函数。
这个程序的原理很简单,不断向栈底方向取值,越过栈底的地址访问会导致SIGSEGV
信号,然后利用长跳转回到主流程报告当前c值,自然对应栈底。
tt测试表明,在x86/FreeBSD中导致SIGBUS信号。据jonah报告,不仅仅是FreeBSD,
NetBSD 以及 OpenBSD 系统中上述程序越界访问也导致SIGBUS信号,而不是SIGSEGV
信号。
非局部转移,比如函数间转移的时候考虑使用setjmp/longjmp。但是如果涉及到信号
句柄与主流程之间的转移,就不能使用longjmp了。当捕捉到信号进入信号句柄,此
时当前信号被自动加入进程的信号屏蔽字中,阻止后来产生的这种信号干扰此信号句
柄。如果用longjmp跳出信号句柄,此时进程的信号屏蔽字状态未知,有些系统做了
保存恢复,有些系统没有做。根据POSIX.1,此时应该使用sigsetjmp/siglongjmp函
数。下面来自SPARC/Solaris 7的setjmp(3C)
--------------------------------------------------------------------------
#include
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定义,在写时不会被中断。它意味着这种变量在具有
虚存的系统上不会跨越页边界,可以用一条机器指令对其存取。这种类型的变量总是
与ANSI类型修饰符volatile一并出现,防止编译器优化带来的不确定状态。
在longjmp/siglongjmp中,全局、静态变量保持不变,声明为volatile的自动变量也
保持不变。
无论是否使用了编译优化开关,为了保证广泛兼容性,都应该在get_stack_bottom()
中声明c为volatile变量。
注意这里,必须使用长跳转,而不能从信号句柄中直接返回。因为导致信号SIGSEGV、
SIGBUS分发的语句始终存在,直接从信号句柄中返回主流程,将回到引发信号的原指
令处,而不是下一条指令(把这种情况理解成异常,而不是中断),于是立即导致下一
次信号分发,出现广义上的死循环,所谓程序僵住。可以简单修改上述程序,不利用
长跳转,简单对一个全局变量做判断决定是否继续循环递增c,程序最终僵住;如果
在信号句柄中输出调试信息,很容易发现这个广义上的无限循环。
D: scz 2001-06-03 00:40
在x86/Linux系统中用如下命令可以确定栈区所在
# cat /proc/1/maps
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
#
对于SPARC/Solaris 2.6 32-bit kernel mode
# echo "_userlimit /X" | adb -k /dev/ksyms /dev/mem
physmem 3d24
_userlimit:
_userlimit: f0000000
#
2.5 如何得到一个运行中进程的内存映像
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
这是一个操作系统相关的问题。最一般的回答是使用ptrace()系统调用,尽管我
不确认究竟这有多么普遍。Linux man手册上说SVr4、SVID EXT、AT&T、X/OPEN
和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
#include
#include
#include
#include
#include
/*
* 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 )
{
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 ) 2001-12-14 18:25
为了便于讨论,约定两个名词,中断和异常。这里中断指最常规的中断,比如int指
令带来的软中断。异常的典型代表有除0错。区别在于,发生异常时,x86架构上CPU
将当前EIP(指向引发异常的指令)压栈,发生中断时,x86架构上CPU将当前EIP的后一
个地址(指向引发中断的指令的后一条指令)压栈。在异常处理代码中,如果认为能够
从灾难中恢复,可以不修改被压栈的EIP,从而返回到引发异常的指令处。更多细节
请查看Intel手册。
这些是从前DOS下残留的汇编知识,不过也快忘光了,刚才又找元宝宝确认了一下。
在上述代码中,on_fpe()直接返回了,导致再次触发异常,所以无休止输出。事实上
在所有的计算器处理程序中,都会对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
#include
#include
#include
#include
#include
/*
* 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 */
#endif
}
else
{
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */
#endif
}
if ( sigaction( signo, &act, &oact )
# 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
#include
#include
#include
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( "okn" );
/*
* 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
ok
段错误 (core dumped)
下面写一个完成文件复制功能的小程序,利用mmap(2),而不是标准文件I/O接口。
--------------------------------------------------------------------------
/*
* gcc -Wall -O3 -o copy_mmap copy_mmap.c
*/
#include
#include
#include /* for memcpy */
#include
#include
#include
#include
#include
#include
#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 n", argv[0] );
exit( EXIT_FAILURE );
}
if ( ( src = open( argv[1], O_RDONLY ) )
Solaris 2.6下参看getpagesize(3C)手册页,关于如何获取页大小,一般是8192。
Linux下参看getpagesize(2)手册页,一般是4096。
3.4 getrusage如何用
A: 小四
在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
为什么要学习使用setitimer(2),因为alarm(3)属于被淘汰的定时器技术。
A: 小四
下面是个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
* Platform : x86/FreeBSD 4.3-RELEASE
* Date : 2001-09-18 15:18
*/
/************************************************************************
* *
* Head File *
* *
************************************************************************/
#include
#include
#include
#include
/************************************************************************
* *
* 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 );
/************************************************************************
* *
* 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 )
{
static int count = 0;
/*
* 演示用,这很危险
*/
fprintf( stderr, "count = %un", 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,
 
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/1102/showart_11627.html |
|