免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2534 | 回复: 0
打印 上一主题 下一主题

经典问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-02-04 15:52 |只看该作者 |倒序浏览
.      系统管理配置问题  
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,  
&nbsp

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/1102/showart_11627.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP