Unix编程/应用问答中文版
这种技术要求运行者拥有root权限,否则无法有效获取非自己拥有的进程PID。注意
下面的演示
# ps -f -p 223
UID PID PPID C STIME TTY TIME CMD
root 223 1 0 3月 09 ? 0:00 /usr/sbin/vold
# ./getpid /usr/sbin/vold <-- 这个用法无法找到匹配
# ./getpid vold <-- 只能匹配相对路径
[ vold ] is: <223>
当然你可以自己修改、增强程序,使之匹配各种命令行指定,我就不替你做了。上述
程序在32-bit kernel的Solaris 2.6和64-bit kernel的Solaris 7上均测试通过。
D: microcat <rotm@263.net>
在介绍第二种办法之前,先看一下microcat提供的这个程序
--------------------------------------------------------------------------
/*
* gcc -Wall -DSOLARIS=6 -O3 -o listpid listpid.c -lkvm
*
* /opt/SUNWspro/SC5.0/bin/cc -xarch=v9 -DSOLARIS=7 -O -o listpid listpid.c -lkv
m
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <kvm.h>
#include <fcntl.h>
int main ( int argc, char * argv[] )
{
kvm_t * kd;
struct proc * p;
struct pid pid;
if ( ( kd = kvm_open( NULL, NULL, NULL, O_RDONLY, NULL ) ) == NULL )
{
perror( "kvm_open" );
exit( EXIT_FAILURE );
}
while ( ( p = kvm_nextproc( kd ) ) ) /* 遍历P区 */
{
#if SOLARIS == 7
if ( kvm_kread( kd, ( uintptr_t )p->p_pidp, &pid, sizeof( pid ) ) < 0 )
#elif SOLARIS == 6
if ( kvm_kread( kd, ( unsigned long )p->p_pidp, ( char * )&pid, sizeof(
pid ) ) < 0 )
#endif
{
perror( "kvm_kread" );
}
else
{
printf( "PID: %d\n", ( int )pid.pid_id );
}
} /* end of while */
kvm_close( kd );
exit( EXIT_SUCCESS );
} /* end of main */
--------------------------------------------------------------------------
A: Andrew Gierth <andrew@erlenstar.demon.co.uk>
第二种办法是使用kvm_*()函数
--------------------------------------------------------------------------
#define _KMEMUSER /* 必须定义这个宏 */
#include <stdio.h>
#include <stdlib.h>
#include <regexpr.h>
#include <sys/proc.h>
#include <kvm.h>
#include <fcntl.h>
/*
static void argv_free ( char ** argv )
{
size_t i;
for ( i = 0; argv[i] != NULL; i++ )
{
free( argv[i] );
argv[i] = NULL;
}
free( argv );
}
*/
static pid_t getpidbyname ( char * name, pid_t skipit )
{
kvm_t * kd;
int error;
char ** argv = NULL;
char * p_name = NULL;
pid_t pid = -1;
char expbuf[256];
char regexp_str[256];
struct user * cur_user;
struct proc * cur_proc;
struct pid p;
sprintf( regexp_str, "^.*%s$", name );
if ( compile( regexp_str, expbuf, expbuf + 256 ) == NULL ) /* 正则表达式 */
{
perror( "compile" );
return( -1 );
}
if ( ( kd = kvm_open( NULL, NULL, NULL, O_RDONLY, NULL ) ) == NULL )
{
perror( "kvm_open" );
return( -1 );
}
while ( ( cur_proc = kvm_nextproc( kd ) ) ) /* 遍历P区 */
{
#if SOLARIS == 7
if ( kvm_kread( kd, ( uintptr_t )cur_proc->p_pidp, &p, sizeof( p ) ) < 0
)
#elif SOLARIS == 6
if ( kvm_kread( kd, ( unsigned long )cur_proc->p_pidp, ( char * )&p, siz
eof( p ) ) < 0 )
#endif
{
perror( "kvm_kread" );
continue;
}
pid = p.pid_id;
if ( ( cur_user = kvm_getu( kd, cur_proc ) ) != NULL )
{
/* fprintf( stderr, "cur_proc = %p cur_user = %p\n", cur_proc, cur_u
ser ); */
error = kvm_getcmd( kd, cur_proc, cur_user, &argv, NULL );
/*
* fprintf( stderr, "[ %s ] is: <%u>\n", cur_user->u_comm, ( unsigne
d int )pid );
*
* 比如in.telnetd、syslogd、bash、login
*/
if ( error == -1 ) /* 失败,比如argv[]已经被进程自己修改过 */
{
if ( cur_user->u_comm[0] != '\0' )
{
p_name = cur_user->u_comm; /* 从另外一个地方获取信息 */
}
}
else /* 成功 */
{
/*
* fprintf( stderr, "[ %s ] is: <%u>\n", argv[0], ( unsigned int
)pid );
*
* 比如-bash、login、in.telnetd、/usr/sbin/syslogd
*/
p_name = argv[0];
}
}
if ( p_name )
{
if ( ( strcmp( p_name, name ) == 0 ) || step( p_name, expbuf ) )
{
if ( skipit != -1 && pid == skipit ) /* -1做为无效pid对待 */
{
pid = -1;
}
else /* 找到匹配,返回pid */
{
break; /* 跳出while循环 */
}
}
}
if ( argv != NULL )
{
/* argv_free( argv ); */
free( argv );
argv = NULL;
}
p_name = NULL; /* 必须增加这条,否则流程有问题 */
} /* end of while */
if ( argv != NULL )
{
/* argv_free( argv ); */
free( argv );
argv = NULL;
}
kvm_close( kd );
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 */
--------------------------------------------------------------------------
这个程序同样必须以root身份运行,在SPARC/Solaris 2.6/7上测试通过
13.4 Solaris 7/8下ps输出中的问号
Q: 比如ps -el的输出中有很多问号,可我觉得它们应该有一个确定的值
A: Michael Shapiro <mws@poptart.Sun.Com>
有些时候ps(1)输出的单行过于长了,为了输出美观,某些列的值用问号代替,尤
其64-bit内核下ADDR列。可以用-o参数指定要显示的列,比如
# ps -o pid,tty,addr,wchan,fname -p $$
PID TT ADDR WCHAN COMMAND
2602 pts/4 30000a154b8 30000a15578 bash
# ps -e -o pid,tty,addr,wchan,fname
13.7 给定一个PID,如何知道它对应一个运行中的进程
A: Andrew Gierth <andrew@erlenstar.demon.co.uk>
这个回答来自著名的<<Unix Programming FAQ ver 1.37>>,由Andrew Gierth负责维
护,其它细节请参看原文。
kill( pid, 0 ),此时有四种可能的返回值
1) kill()返回0
意味着指定PID的确对应着一个运行中的进程,系统允许你向该进程发送信号。
至于该进程能否是zombie process(僵尸进程),是系统相关的。
2) kill()返回-1,errno == ESRCH
指定PID并不对应一个运行中的进程,或者权限不够无法完成判断。某些系统上,
如果对应进程是僵尸进程时,也如此返回。
3) kill()返回-1,errno == EPERM
系统不允许你kill指定进程,进程存在(可能是zombie),权限不够。
4) kill()返回-1,errno是其它值
你麻烦来了(嘿嘿)
最有用的技术,假设成功表示进程存在,EPERM失败也表示进程存在,其它失败表示
指定PID不对应一个运行中的进程。
此外如果系统支持proc伪文件系统,检查/proc/<pid>是否存在,存在表明指定PID对
应运行中的进程。
13.8 Unix/Linux编程中所谓"僵尸进程"指什么
Q: Unix/Linux编程中所谓"僵尸进程"指什么,什么情况下会产生僵尸进程,如何杀
掉僵尸进程。
A: 在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之
前既没安装SIGCHLD信号处理函数调用waitpid()等待子进程结束,又没有显式忽
略该信号,则子进程成为僵尸进程,无法正常结束,此时即使是root身份kill -9
也不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然
存在),僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵
尸进程。
13.9 x86/FreeBSD 4.3-RELEASE的ptrace(2)手册页
A: scz <scz@nsfocus.com>
下面来看一个简单的ptrace(2)演示,x86/FreeBSD 4.3-RELEASE
--------------------------------------------------------------------------
/*
* gcc -Wall -pipe -O3 -o target target.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
int main ( int argc, char * argv[] )
{
write( STDERR_FILENO, "Hello world\n", 12 );
return( EXIT_SUCCESS );
} /* end of main */
--------------------------------------------------------------------------
--------------------------------------------------------------------------
/*
* gcc -Wall -pipe -O3 -o ptracetest ptracetest.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>
int main ( int argc, char * argv[] )
{
pid_t p;
p = fork();
if ( p < 0 )
{
perror( "fork error" );
exit( EXIT_FAILURE );
}
else if ( p == 0 )
{
/*
* 子进程
*/
errno = 0;
ptrace( PT_TRACE_ME, 0, 0, 0 );
if ( errno != 0 )
{
perror( "child process ptrace error" );
exit( EXIT_FAILURE );
}
else
{
char * name[2];
name[0] = "./target";
name[1] = NULL;
/*
* 切换进程映像时停止执行
*/
execve( name[0], name, NULL );
perror( "child process execve error" );
exit( EXIT_FAILURE );
}
}
else
{
/*
* 父进程
*/
fprintf( stderr, "Having a child process <%d>\n", ( int )p );
/*
* 阻塞式waitpid()
*/
waitpid( p, NULL, 0 );
fprintf( stderr, "Now in parent process, "
"please enter [CR] to continue ... ...\n" );
getchar();
errno = 0;
ptrace( PT_CONTINUE, p, ( caddr_t )1, 0 );
if ( errno != 0 )
{
perror( "parent process ptrace error" );
exit( EXIT_FAILURE );
}
/*
* 作为ptrace(2)演示,这里必须等待子进程先结束,否则由于父进程终止
* 而杀死子进程
*/
fprintf( stderr, "Waiting the child process terminate ... ...\n" );
getchar();
}
return( EXIT_SUCCESS );
} /* end of main */
--------------------------------------------------------------------------
13.10 Solaris下如何知道哪个进程使用了哪个端口
Q: netstat -na -P tcp告诉我哪些端口是打开的,但它没有报告是哪个进程打开的。
lsof可以满足我的需求,可我不想用lsof,它不是缺省安装的
D: FreeBSD 4.3-RELEASE中
netstat -s -p tcp 查香tcp协议的统计量
netstat -na | grep tcp4 才能达到类似Solaris下netstat -na -P tcp的效果
FreeBSD 4.4-RELEASE中
netstat -na -p tcp效果类似于Solaris下netstat -na -P tcp
A: Vitaly Filatov <vitaly@royint.com> & scz <scz@nsfocus.com>
对于Solaris 8,可以使用这个演示脚本,如果不能满足你的需要,请自行修改
--------------------------------------------------------------------------
#! /bin/sh
# find_socket_proc.sh for x86/SPARC Solaris 8
#
# File : find_socket_proc.sh
# Author : Vitaly Filatov <vitaly@royint.com>
# Fix : scz <scz.nsfocus.com>
# Platform : x86/SPARC Solaris 8
# Version : 1.00 aleph
# Usage :
# Date : 2001-10-28 00:32
# Modify :
#
PLATFORM="`uname -p`"
if [ "${PLATFORM}" = "sparc" ] ; then
PREFIX=""
elif [ "${PLATFORM}" = "i386" ] ; then
PREFIX="/usr"
fi
EGREP="${PREFIX}/bin/egrep"
NAWK="${PREFIX}/bin/nawk"
PFILES="/usr/proc/bin/pfiles"
PS="${PREFIX}/bin/ps"
SED="${PREFIX}/bin/sed"
PROCLIST="`${PS} -ef | ${NAWK} 'NR > 1 {print $2}'`"
for PID in ${PROCLIST} ; do
if [ -n "`${PFILES} ${PID} 2>/dev/null | ${EGREP} S_IFSOCK`" ] ; then
LINE_1="`${PS} -o pid,args -p ${PID} | ${NAWK} 'NR > 1 {print $0}'`"
PORTLIST="`${PFILES} ${PID} 2>/dev/null | ${EGREP} 'sockname:' | \
${SED} -e 's/.*port: \(.*\)/\1/g'`"
for PORT in ${PORTLIST} ; do
echo "${LINE_1} port-->${PORT}"
done
fi
done
--------------------------------------------------------------------------
如果你以普通用户身份运行,只能检查自己的进程,如果以root身份运行,可以检查
所有用户的进程。
13.11 x86/FreeBSD如何快速获取指定用户拥有的进程数
Q: 谁能给我一段C代码,快速统计出一个指定用户所拥有的进程数。我想修改Apache
以阻止它超过kern.maxprocperuid限制后继续fork()产生新进程。如果Apache以
sudo方式启动,就可能出现这种情况。我该看ps(1)的源代码吗?
A: Maxim Konovalov <maxim@macomnet.ru>
参看src/usr.bin/killall/killall.c,这里用了sysctl()接口
A: Andrew <andrew@ugh.net.au>
可以试试kvm_getprocs( KERN_PROC_UID )
14.3 只在本地文件系统上查找
Q: 我不想在NFS AUTOFS CACHEFS这些文件系统上查找文件
A: Sun Microsystems 2001-02-12
下面举例演示如何不在NFS文件系统上查找文件
# find / -name su -type f -print -o -fstype nfs -prune
下例表示不在PROC文件系统中查找文件(参看/etc/vfstab文件)
# find / -name su -type f -print -o -fstype proc -prune
15. 32-bit/64-bit相关问题
15.1 Solaris下如何识别当前内核版本
Q: 我编写了一个内核模块,在Solaris 7/8下编译通过,模块中如何识别当前正在运
行内核版本
A: Andrew Gabriel
参看adb使用的宏"utsname",32-bit的位于usr/lib/adb/utsname,此外可以参看
/usr/include/sys/utsname.h文件,但是这些不是DDI/DKI兼容的。
sysdef | more 看看
A: scz <scz@nsfocus.com>
非DDI/DKI兼容意味着丧失良好的可移植性,未来版本的Solaris可能不再提供相应的
支持,但是就Kernel Hacking而言是可以一试的。
adb -P 'sun>' -k /dev/ksyms /dev/mem
sun>$</usr/lib/adb/sparcv9/utsname <-- 使用64-bit宏
utsname:
utsname: sys SunOS
utsname+0x101: node sun27
utsname+0x202: release 5.7
utsname+0x303: version Generic_106541-08
utsname+0x404: machine sun4u
sun>$q
cat /usr/lib/adb/sparcv9/utsname
utsname+0="" <-- 指定当前地址.为utsname
+/"sys"8t257c <-- t表示tab,c表示以字符方式显示1个字节
+/"node"8t257c
+/"release"8t257c <-- +表示当前地址.递增
+/"version"8t257c
+/"machine"8t257c
more /usr/include/sys/utsname.h
#define _SYS_NMLN 257 /*
* 4.0 size of utsname elements
* Must be at least 257 to
* support Internet hostnames.
*/
#define SYS_NMLN _SYS_NMLN
struct utsname
{
char sysname[_SYS_NMLN];
char nodename[_SYS_NMLN];
char release[_SYS_NMLN];
char version[_SYS_NMLN];
char machine[_SYS_NMLN];
};
显然adb下的命令是针对struct utsname结构来的。简单地truss uname -a,可以看
到如下输出
ioctl(1, TCGETA, 0xFFBEE5DC) = 0
sysinfo(SI_ARCHITECTURE, "sparc", 257) = 6
sysinfo(SI_PLATFORM, "SUNW,Ultra-5_10", 257) = 16
暂时没有跟踪这几个系统调用在做什么,想必类似/usr/lib/adb/sparcv9/utsname宏。
15.2 如何启动Solaris 32-bit/64-bit内核
Q: Algos@Unix 水木清华 2001-12-04 18:12
对于UltraSPARC-I/Solaris 8,可以修改/platform/sun4u/boot.conf文件,使操作
系统运行在64位或者32位,但对于其他机型呢,比如E420、E450、E4500之类服务器
怎么改?好像起来后就是64位的,怎么才能改成32位的呢?
Q: 显然有一些32-bit驱动程序以及一些应用软件不能工作在64-bit内核下,比如gcc
编译的32-bit IP Filter。我必须启动到32-bit内核模式下,怎么办?
32-bit : ok boot disk kernel/unix
64-bit : ok boot disk kernel/sparcv9/unix
为了设置成缺省启动内核模式
32-bit : ok setenv boot-file kernel/unix
64-bit : ok setenv boot-file kernel/sparcv9/unix
为了确定你所启动的内核模式
isainfo -b
根据你所启动的内核模式,该命令分别返回32、64
A: Will Wang <willcyw@kimo.com.tw> 2001-06-09 02:17
一个办法就是启动时按Stop-A进入OK模式,输入
ok> setenv boot-file kernel/unix
ok> boot
另一个办法是已经在shell状态下了,执行命令
# eeprom "boot-file=kernel/unix"
系统重启之后将自动加载32-bit内核
15.3 gcc支持64-bit编译吗
Q: gcc -v显示版本2.95.2,isainfo -kv显示64-bit sparcv9 kernel modules,我
企图通过指定"-mcpu=v9 -m64"获得64-bit代码,提示m64未被支持,仅仅指定
mcpu=v9,在汇编阶段报告""v8 can't generate v9 code",我使用的汇编器是
/usr/ccs/bin/as,随Solaris 7/8提供的。
这是什么问题,我需要一个64-bit汇编器吗,从哪里获取呢?
A: Robert Banniza <robert@rootprompt.net>
我并不认为gcc 2.95.2已经开始支持64-bit编译模式,或许你应该考虑采用
Sun WorkShop Compiler SPARC 5.0/6.0。
15.4 Solaris启动时内核文件找不到了
Q: 我的Solaris 7莫名其妙死机了,只好关电源,再开,发现错误
boot with command:boot now
cann't open now
enter filename[now]:
怎么办
启动时按Stop-A进入ok状态,在这里输入
32-bit : ok boot disk kernel/unix
64-bit : ok boot disk kernel/sparcv9/unix
为了设置成缺省启动内核模式
32-bit : ok setenv boot-file kernel/unix
64-bit : ok setenv boot-file kernel/sparcv9/unix
试试这个,启动时按Stop-A进入OK状态
OK> setenv boot-file kernel/unix
OK> reset
15.5 64-bit驱动程序无法在8下关联,但在7下工作正常
Q: 一个64-bit驱动程序在Solaris 7下加载、关联(load & attach)成功,但在8下加
载(load)成功、关联(attach)失败。
A: Sun Microsystems 1998-06-13
从Solaris 8开始,64-bit驱动程序必须位于"sparcv9/"目录中。而在Solaris 7中,
尽管不提倡,但即使64-bit驱动程序不在"sparcv9/"目录中,也可以加载并关联成功。
16. 库相关问题
16.1 在Solaris 7下编写网络程序需要链接哪些库
Q: inet_pton()是什么库里的,为什么man手册里无对应内容
A: scz <scz@nsfocus.com>
这个函数比较新,还有另外几个,比如inet_ntop()。关于它们的详细介绍参看
<<Unix Network Programming>> 3.7 小节。文件/usr/include/arpa/inet.h中定义
有:
extern int inet_pton ( int, const char *, void * );
用/usr/ccs/bin/nm工具观察三个动态链接库libresolv.so、libsocket.so、
libnsl.so提供的全局函数
显然,如果涉及RPC编程,必然需要libnsl.so,而inet_pton()来自libresolv.so。
总结一下,实在不能确定的时候,编译时指定链接开关如下:
-lsocket -lnsl -lresolv
16.2 SUID设置和LD_LIBRARY_PATH环境变量
Q: RedHat Linux 6.1/6.2,C编程,还有一些脚本
execl()以及其他exec...()执行一个SUID程序的时候,出于安全考虑,会清除
LD_LIBRARY_PATH环境变量,仅仅依靠系统全局设置搜索共享库。参看如下URL
http://spdoc.pdc.kth.se/doc_link/C/a_doc_lib/libs/basetrf1/exec.htm
现在有一个程序,需要一个正确的LD_LIBRARY_PATH环境变量设置才能运行,可是
由于某些原因必须做SUID设置,结果最终运行失败。我尝试在程序中putenv()、
setenv(),失败,显然LD_LIBRARY_PATH环境变量需要在程序加载过程中由动态链
接器使用,程序中的putenv()、setenv()为时已晚。
于是我写了一个脚本,在脚本中设置LD_LIBRARY_PATH环境变量,调用C程序,对
脚本做SUID设置。但是脚本的SUID设置并没有传递给子进程(这里就是那个C程序)
A: Paul Sack <paul-sackun@jefe.eyep.net>
到www.google.com用"suid shell scripts race conditions"进行搜索,查看
BugTraq相关讨论。安全的解决办法是用C写一个SUID WRAPPER去exec...()你的C程序,
在SUID WRAPPER中设置LD_LIBRARY_PATH环境变量。
A: Andrew Gierth <andrew@erlenstar.demon.co.uk>
如果一个程序是SUID过的,将导致LD_LIBRARY_PATH环境变量被忽略,但是这不是问
题本质所在,本质原因在于ruid不等于euid(或者rgid不等于egid)。所以wrapper中
仅仅重置环境变量是不够的,必须想办法修改ruid等于euid。最好还是重新编译程序,
使之不依赖于LD_LIBRARY_PATH环境变量。
16.3 链接过程中库的顺序
Q: 有几个库文件A.a、B.a、common.a,前两者用到了定义在后者中的例程,如果把
common.a放在前面,链接器报告存在无法解析的符号名,放在最后则无问题。
A: Floyd Davidson <floyd@ptialaska.net>
链接器按照命令行上指定顺序搜索库文件和目标文件(.a .o),二者之间的区别在
于.o文件被全部链接进来,而只从库文件中析取所需模块,仅当某个模块可以解
析当前尚未成功解析的符号时,该模块被析取后链接进来。如果库文件无法解析
任何当前尚未成功解析的符号,不从中析取也不发生链接。
Unix编程新手的常见问题是数学函数并不在标准C库中,而是在libm.a中
cc -lm foo.c
这里foo.c用到了数学库中的符号,但是链接器无法正确解析。当搜索到libm.a时,
来自foo.c的数学函数符号尚未出现,因此不需要析取libm.a的任何模块。接下来
foo.o链接进来,增加了一批尚未成功解析的符号,但已经没有libm.a可供使用了,
因此数学库必须在foo.o之后被搜索到。
cc foo.c -lm
在你的问题中,如果common.a首先被搜索到,因为不匹配尚未成功解析的符号,
而被丢弃。结果A.a和B.a真正链接进来的时候,已经没有库可以解析符号了。
16.6 /usr/lib/ld.so.1损坏或丢失
Q: 意外地覆盖了ld.so.1,幸运的是有一个原始备份,可我没有一个静态链接版本的
命令去恢复它。
Q: 我在Solaris 2.6中做了"mv /usr/lib /usr/lib1",本意是想使用自己的库,但
是现在所有程序都报告"找不到/usr/lib/ld.so.1",怎么办
A: scz <scz@nsfocus.com>
不要重启动,立即用/usr/sbin/static/mv、/usr/sbin/static/cp命令恢复
# ls /usr/sbin/static
cp* ln* mv* rcp* tar*
#
Q: 那如果此时/usr被改名了,怎么办?
A: faint,谁这么变态。假设/usr改名成了/faint,
1) /faint/sbin/static/cp /faint/sbin/static/mv /tmp/mv
2) /tmp/mv /faint /usr
我不确定
1) /faint/sbin/static/mv /faint /usr
能否成功,你可以自己测试一下效果。或者
ok boot cdrom -s (放入启动安装光盘)
mount /dev/dsk/c0t0d0s0 /mnt (这里指定原根区对应的原始设备名)
mv /mnt/faint /mnt/usr
D: cirrus@SMTH
建议把/usr/sbin/static下的东西拷一份到/sbin下或者其它比较可信的跟/在同一个
fs的目录下。装机器的时候,不管什么OS,/usr都是单独一个fs的。
16.9 Solaris 8下如何配置运行时链接环境
Q: 在Linux下我知道用ldconfig(8)配置运行时链接环境,但是在Solaris 8下呢
A: <cypher@punk.net>
你总是可以利用 LD_LIBRARY_PATH 环境变量,对于Solaris 8,还可以参看crle(1)
手册页。
A: Logan Shaw <logan@cs.utexas.edu>
如果在链接时使用了"-R"和"-L"选项,则相关动态库的路径将保存在ELF文件中,于
是以后的运行中不再需要设置环境变量去定位动态库。比如,有一个
/usr/local/lib/libfoo.so,而你的bar程序需要这个libfoo.so,编译、链接时最好
这样
gcc -Wall -pipe -O3 -o bar -R/usr/local/lib -L/usr/local/lib bar.c -lfoo
17. 文件查看问题
17.1 如何直接查看man文件
A: scz <scz@nsfocus.com>
下面几种方法都可以
/bin/nroff -man <your man doc> | more -s
groff -Tlatin1 -mandoc <your man doc> | less
groff -s -p -t -e -Tascii -mandoc <your man doc> | less
有less的时候建议使用less,而不是more。
在Linux下更简单,不用这样麻烦,比如
ls /usr/man/man5/nologin.5.gz
man /usr/man/man5/nologin.5.gz
man /usr/man/man1/finger6.1
无论什么系统,总是可以利用MANPATH环境变量的
$ mkdir man1
$ cp /usr/man/man1/proc.1 man1
$ man -s 1 -M . proc
17.2 .tex文件怎么读
A: shuoshu.bbs@bbs.whnet.edu.cn
用 latex *.tex 编译生成dvi文件,然后用 xdvi 看
17.3 Solaris下怎么看.ps文件
A: lose@水木清华 Unix
/usr/dt/bin/sdtimage *.ps
18. 补丁相关问题
18.1 如何根据补丁号从Sun主站下载补丁
Q: 已经知道补丁号,可我如何从Sun主站下载这个补丁呢。有些补丁不是安全补丁,
Sun公司并未在主站上提供非安全补丁的下载链接。
A: scz <scz@nsfocus.com>
首先去http://sunsolve.sun.com申请一个帐号(免费),其次假设补丁号是107589,
参看如下链接
http://sunsolve.sun.com/private-cgi/retrieve.pl?type=0&doc=patches/107589
wget -O 107589.tar.Z http://username:password@sunsolve.sun.com/private-cgi
/patchDownload.pl?target=107589&method=h
然后用"file 107589.tar.Z"命令确认这个文件格式,选择不同的解压工具。补丁版
本号在压缩包内有体现。这个方法适用于所有补丁,包括非安全补丁。这样下载获得
的补丁是最新版。
如果你不但知道补丁号,还知道补丁版本号,也可以这样下载
wget http://username:password@sunsolve.sun.com/private-cgi
/patches/107589-06.zip
一般只提供最新版补丁,旧版补丁将被删除。
18.5 已知补丁号,如何最快判断系统中是否已经安装该补丁
A: scz <scz@nsfocus.com>
# showrev -p | grep "^Patch: 105181"
Patch: 105181-05 Obsoletes: 105636-01, 105776-01 Requires: ...
#
18.6 如何安装补丁
A: scz <scz@nsfocus.com>
mkdir -p /tmp/patch
cd /tmp/patch
wget -O 105181.tar.Z http://...
wget -O 106429.tar.Z http://...
zcat 105181.tar.Z | tar xvf -
zcat 106429.tar.Z | tar xvf -
rm 105181.tar.Z
rm 106429.tar.Z
patchadd -M /tmp/patch 106429-02 105181-28
检查补丁安装日志
/var/sadm/patch/106429-02/log
/var/sadm/patch/105181-28/log
cd /
rm -rf /tmp/patch
# showrev -p | grep "^Patch: 105181"
# init 6 (重启系统)
19. 终端相关问题
19.1 如何使Backspace键做删除操作,而不是显示^H
Q: Backspace键并未删除光标左面那个字符,仅仅显示^H,而DEL键完成了删除操作
A: Sun Microsystems 2001-03-08
执行"stty -a"将看到"erase = ^?",表示此时DEL键对应删除操作。
如果正在使用xterm,可以用"tset"命令设置控制字符对应的操作。其他窗口中,假
设目前使用/sbin/sh,尝试
$ stty erase ^H
这里^H的输入是Ctrl-H,某些时候可能需要Ctrl-V、Ctrl-H输入,还可以尝试
$ stty erase "^h"
$ stty erase "^H" (大小写不敏感)
这里输入"^H",就是两个字符,一个^,一个H。
同样,如果想恢复到DEL删除
$ stty erase ^?
这里^?的输入是DEL,某些时候可能需要Ctrl-V、DEL输入,还可以尝试
$ stty erase "^?"
这里输入"^?",就是两个字符,一个^,一个?。
为了永久保留这个设置,在所使用的shell初始化文件中增加设置命令,比如c shell
的".cshrc",其他shell的".login"。
19.3 如何清空stdin的缓冲
stdin->_IO_read_ptr = stdin->_IO_read_end;
不过这个办法实在不怎么样。一是只对glibc有效,不可移植。二是违背流的思想,
老老实实用fgets()好了。
19.4 Linux Console下一按错键就叫,怎么关
A: windtear@bbs.tsinghua.edu.cn Linux版
有个1050110 背一下就可以了
echo -e "\\33[10;50]\\33[11;0]"
10 50 11 0
放到那些登录言启动脚本里
Q: 输完命令后是没声了,可从KDE回来之后又有了,请问能彻底关掉吗
A: TheCool@bbs.tsinghua.edu.cn Linux版
setterm -blength 0 -bfreq 0
20. shell script问题
20.1 如何获取一个字符串的长度
A: Andrei Ivanov <iva@racoon.riga.lv>
expr `echo $string | wc -c` - 1
echo $string | awk '{ print length( $0 ); }'
/usr/ucb/expr length "$string"
expr "$string" : ".*"
echo "$string" | sed 's/./1+/g;s/+/ /;s/$/p/' | dc
假设是bash
$ string='1234567890'
$ echo ${#string}
10
$
20.2 读超时自动使用缺省值
Q: shell script编程,不介入expect、perl、tcl等类似工具。读等待60秒,超时则
自动使用缺省值。可以使用系统缺省外部命令,要求能广泛移植在常用Unix平台
上
A: CERNET 华中地区网络中心 PUE(UNIX环境程序设计)版 lookout
参看comp.unix.shell新闻组,下面以SPARC/Solaris 2.6为例
--------------------------------------------------------------------------
#! /sbin/sh
stty -icanon min 0 time 255
while true
do
/usr/bin/echo "Press a key or press ENTER to exit:\c"
read key
if [ "$key" = "" ] ; then
echo "\nYou press Enter or timeout"
break
else
echo "You press the key $key"
fi
done
stty sane
--------------------------------------------------------------------------
20.4 BASH中如何得到一个字符串的子串
BASH 2.0.3 以上版本
${var:offset:length}
20.8 使用tr命令加密文件
A: 水木清华 TheCool
著名的 rot13 密码, 通过把字母移动13个位置实现对文本的加密
tr "[a-m][n-z][A-M][N-Z]" "[n-z][a-m][N-Z][A-M]" < message > newmessage
然后可以用同样的命令进行解密
tr "[a-m][n-z][A-M][N-Z]" "[n-z][a-m][N-Z][A-M]" < newmessage > message
20.9 有哪些命令用于查找定位
A: 小四
type -a telnet
whereis telnet
which telnet
whatis telnet <=> man -k telnet
20.11 如何将大写文件名转换为小写文件名
A: 小四 <cloudsky@263.net>
如果要处理整个目录树的话,可以这样
find <target_dir> -exec sh -c 'mv -f "$0" `echo "$0" | tr "[A-Z]" "[a-z]"` > /de
v/null 2>&1' {} \;
同理,将小写文件名转换为大写文件名如下
find <target_dir> -exec sh -c 'mv -f "$0" `echo "$0" | tr "[a-z]" "[A-Z]"` > /de
v/null 2>&1' {} \;
这个办法有待修正,处理多层目录名本身带有大写字母的情况,有问题。比如存在如
下目录的时候,./A/B/C/D.txt。
A: Potash@www.linuxforum.net 2002-02-05 18:58
--------------------------------------------------------------------------
#! /bin/sh
# Usage: ./loworup.sh <-l | -u> <target_directory>
#
# 第二形参必须是目录,第一形参指定-l或-u
#
if [ $# -ne 2 ] ; then
echo "Usage: ${0} <-l | -u> <target_directory>"
exit 1
fi
if [ ! -d ${2} -o "${1}" != "-l" -a "${1}" != "-u" ] ; then
echo "Usage: ${0} <-l | -u> <target_directory>"
exit 1
fi
exec 1>/dev/null 2>&1
dir=`dirname "${2}"`
cd ${dir}
if [ "${1}" = "-l" ] ; then
base=`basename "${2}" | tr "[A-Z]" "[a-z]"`
else
base=`basename "${2}" | tr "[a-z]" "[A-Z]"`
fi
mv -f "`basename ${2}`" "${base}"
for entry in `find ${base}`
do
before="."
#
# 这个办法依赖for in语法,用空格做分隔符,所以不能处理那些本身名字带空
# 格的目录项,属于小BUG
#
for after in `echo "${entry}" | sed -e 's,/, ,g'`
do
tmp_entry="${before}/${after}"
if [ "${1}" = "-l" ] ; then
before=`echo "${tmp_entry}" | tr "[A-Z]" "[a-z]"`
else
before=`echo "${tmp_entry}" | tr "[a-z]" "[A-Z]"`
fi
mv -f "${tmp_entry}" "${before}"
done
done
--------------------------------------------------------------------------
21.2 如何将一个512字节的文件写入主引导扇区
A: All of DOS Programmers 2001-10-16 18:05
这个问题如果在90年代初MS-DOS盛行的时候出现,是要被人砍死的,如今时过境迁,
居然能进入这份Unix文档,权当是一种追忆吧。所谓主引导扇区就是硬盘0柱面、0磁
头、1扇区。启动DEBUG,
-f 0200 l 0200 0 <-- 从0200h处开始清零,长512字节
-n mbr <-- 假设我们的要处理的文件名为mbr
-l 0200 <-- 读到0200h处
-d 03be 03ff <-- 检查分区表
XXXX:03B0 00 00 ..
XXXX:03C0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
XXXX:03D0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
XXXX:03E0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
XXXX:03F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 55 AA ..............U.
-
-a 100 <-- 读主引导扇区的汇编代码
XXXX:0100 mov ax, 0201 <-- 读取一个扇区,ah=02(功能码),al=01(扇区总数)
XXXX:0103 mov bx, 0400 <-- 读取后存放在0400h处,长512字节
XXXX:0106 mov cx, 0001 <-- ch=00(柱面号,10bit),cl=01(扇区号,6bit)
XXXX:0109 mov dx, 0080 <-- dh=00(磁头号),dl=80h(驱动器号)
XXXX:010C int 13 <-- int 13h 磁盘I/O BIOS
XXXX:010E int 3 <-- 单步中断,可以换成int 20h
XXXX:010F
-g=100 <-- 从0100h处开始执行
AX=0050 BX=0400 CX=0001 DX=0080 SP=FFEE BP=0000 SI=0000 DI=0000
DS=XXXX ES=XXXX SS=XXXX CS=XXXX IP=010E NV UP EI PL NZ NA PO NC
XXXX:010E CC INT 3
-d 05be 05ff <-- 检查分区表
XXXX:05B0 80 01 ..
XXXX:05C0 01 00 06 FE 3F 7F 3F 00-00 00 41 60 1F 00 00 00 ....?.?...A`....
XXXX:05D0 01 80 0F FE FF FF 80 60-1F 00 22 3C A0 01 00 00 .......`.."<....
XXXX:05E0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
XXXX:05F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 55 AA ..............U.
-
-m 05be l 40 03be <-- 复制分区表
-d 03be l 40 <-- 确认分区表复制成功
XXXX:03B0 80 01 ..
XXXX:03C0 01 00 06 FE 3F 7F 3F 00-00 00 41 60 1F 00 00 00 ....?.?...A`....
XXXX:03D0 01 80 0F FE FF FF 80 60-1F 00 22 3C A0 01 00 00 .......`.."<....
XXXX:03E0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
XXXX:03F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 ..............
-a 100 <-- 写主引导扇区的汇编代码
XXXX:0100 mov ax, 0301 <-- 写一个扇区,ah=03(功能码),al=01(扇区总数)
XXXX:0103 mov bx, 0200 <-- 待写入数据存放在0200h处,长512字节
XXXX:0106 mov cx, 0001 <-- ch=00(柱面号,10bit),cl=01(扇区号,6bit)
XXXX:0109 mov dx, 0080 <-- dh=00(磁头号),dl=80h(驱动器号)
XXXX:010C int 13 <-- int 13h 磁盘I/O BIOS
XXXX:010E int 3 <-- 单步中断,可以换成int 20h
XXXX:010F
-g=100 <-- 从0100h处开始执行
-q <-- 退出DEBUG
A: All of Solaris/FreeBSD/Linux Users
dd if=<path to file> of=/dev/... bs=512 count=1
^^^^^^^^ 对应要处理的物理硬盘设备
21.6 x86/FreeBSD 4.x下不能cp覆盖/kernel
Q: 重新编译内核后用cp命令无法覆盖/kernel
A: deepin <deepin@nsfocus.com>
# ls -lo /kernel
-rwxr-xr-x 1 root wheel schg /kernel*
^^^^注意这里,类似Linux的chattr那些东西
# chflags noschg /kernel
参看CHFLAGS(1)、INSTALL(1)手册页。这样修改后可以cp覆盖/kernel了。最后恢复
chflags设置
# chflags schg /kernel
21.7 x86/FreeBSD下如何设置路由
A: backend <backend@nsfocus.com> 2001-10-25 11:33
/etc/defaults/rc.conf或者/etc/rc.conf中会有这样的设置
--------------------------------------------------------------------------
defaultrouter="NO" # Set to default gateway (or NO).
static_routes="" # Set to static route list (or leave empty).
--------------------------------------------------------------------------
下面分析static_routes的用法,从/etc/rc.network脚本中可以看到这样的处理
--------------------------------------------------------------------------
# Configure routing
#
case ${defaultrouter} in
[Nn][Oo] | '')
;;
*)
static_routes="default ${static_routes}"
route_default="default ${defaultrouter}"
;;
esac
# Set up any static routes. This should be done before router discovery.
#
if [ -n "${static_routes}" ]; then
for i in ${static_routes}; do
eval route_args=\$route_${i}
route add ${route_args}
done
fi
--------------------------------------------------------------------------
注意eval命令导致二次变量替换,对上述脚本分析后可知static_routes用法如下
--------------------------------------------------------------------------
defaultrouter="<IP>"
static_routes="<name1> <name2> ..."
route_<name1>="符合route add命令的语法格式"
route_<name2>="符合route add命令的语法格式"
... ...
--------------------------------------------------------------------------
举例说明
--------------------------------------------------------------------------
defaultrouter="192.168.0.1"
static_routes="entry1 entry2"
route_entry1="-net 10.10.1.0 -netmask 255.255.255.0 -gateway 192.168.254.1"
route_entry2="-net 10.10.2.0 -netmask 255.255.255.0 -gateway 192.168.254.2"
--------------------------------------------------------------------------
当然,你可以不用两个rc.conf文件,而是在/etc/rc.local中直接用route命令增加
路由。
21.9 什么是locale
A: Shen Chuan-Hsing <statue@freebsd.sinica.edu.tw>
locale 指定一组C语言处理自然语言(文字)的方式,也可以简单地说,locale反映了
一组"地区性语言"的配置信息
LC_ALL 代表所有的locale(如下)
LC_CTYPE 字符定义(包含字符分类与转换规则)
LC_MESSAGES 信息显示
LC_TIME 时间格式
LC_NUMERIC 数字格式
LC_MONETARY 货币格式
LC_COLLATE 字母顺序与特殊字符比较顺序
其中与一般使用者息息相关的是是LC_CTYPE与LC_MESSAGES。LC_CTYPE直接关系到某
些字符或內码在目前locale下是否可显示?要如何转换编码?对应到哪一个字?等等。
LC_MESSAGES则关系到软件的信息输出是否符合地域性,例如:我们需要的是中文。
而一个真正完整支持locale系统,是当使用者在shell prompt下,直接设置好环境变
量后就马上切换到那种语言了,例如:
% export LC_CTYPE=zh_TW.Big5
设置locale的字符定义为台湾地区的Big5繁体中文码定义。有了正确的locale定义后,
使得任何地区的的文字,只要在加入适当的locale data之后,C Library就能正确地
处理软件显示信息,而我们使用的[中文]当然也不例外。
21.10 用cvsup安装vim
A: deepin <deepin@nsfocus.com> & scz <scz@nsfocus.com> 2001-11-20 09:42
0) vim主站在http://www.vim.org/
1) # which cvsup
/usr/local/bin/cvsup
如果没有,就用www.google.com去搜一个好了,以"cvsup-bin tgz"做关键字
# wget http://people.freebsd.org/~jdp/s1g/i386-nogui/cvsup-16.1e.tgz
# pkg_add cvsup-16.1e.tgz
2) # cd /usr/share/examples/cvsup
# cp ports-supfile scz
# vi scz
# cvsup -g -L 2 scz
--------------------------------------------------------------------------
#
# cvsup配置文件
#
*default host=cvsup.cn.FreeBSD.org
*default base=/usr
*default prefix=/usr
*default release=cvs tag=.
*default delete use-rel-suffix
*default compress
#ports-all
ports-editors
--------------------------------------------------------------------------
3) # cd /usr/ports/editors/vim
# make -DWITHOUT_X11 install <-- 否则必须在X下使用vim
# whic vim
/usr/local/bin/vim <-- vim直接支持输入中文
21.11 FreeBSD下vi输入中文会显示\x??\x??
A: Shen Chuan-Hsing <statue@freebsd.sinica.edu.tw>
这通常都是设定了LC_CTYPE为zh_TW.Big5(对大陆是zh_CN.EUC)或是没设定LC_CTYPE
才会发生的问题,在~/.cshrc中加上下面的alias即可:
alias vi 'env LC_CTYPE=en_US.ISO_8859-1 vi'
参看PRINTENV(1)手册页了解更多env命令细节。直接改用vim也可以支持中文。
21.15 UDMA ICRC error是什么意思
Q: 在console上出现错误信息"UDMA ICRC error writing... ...",什么意思
A: tt <warning3@nsfocus.com>
通常是使用了40线的IDE硬盘线,然而硬盘被设置成使用DMA模式,这种模式需要80线
硬盘线。也有可能是您的硬盘不支持DMA方式。解决方法有几种
1) 换用一根80线的IDE硬盘线(没干过)
2) 在CMOS BIOS中关闭对UDMA的支持
3) 在FreeBSD中关闭对UDMA的支持
vi /etc/sysctl.conf
hw.atamodes=pio,pio,pio,pio,
这样做,可能会降低硬盘速率。
21.16 Limiting closed port RST response什么意思
Q: console上出现"Limiting closed port RST response",什么意思
A: tt <warning3@nsfocus.com>
某些主机快速访问你的主机上一些没有开放的端口,你的主机正在回复RST报文,这
是正常反应。但FreeBSD内核限制了每秒钟回复RST报文的数量,以防止发生可能的
DoS攻击。例如,如果攻击者通过伪造源IP来向你的未开端口发送大量连接请求,就
可能诱使你的主机向该主机发送RST报文。这可能导致受害主机所在网络的带宽占用。
如果你不想看到上述信息,可以打开黑洞模式来停止响应RST报文。这也可以减缓远
程攻击者对你的主机的扫描速度。
# sysctl -w net.inet.tcp.blackhole=2
# sysctl -w net.inet.udp.blackhole=1
也可以在/etc/sysctl.conf中增加下列选项使黑洞模式每次启动后都生效
net.inet.tcp.blackhole=2
net.inet.udp.blackhole=1
22. Linux Kernel Programming
22.1 直接访问内存[显存]地址
Q: 现在在修改linux内核,希望能访问一段地址(其实是显存)。但发觉不能直接访问
A: Kongming <ymwei@263.net> (Luther <Luther@pku.edu> 整理)
通过/dev/mem设备文件和mmap系统调用,可以将线性地址描述的物理内存映射到进程
的地址空间,然后就可以直接访问这段内存了。
比如,标准VGA 16色模式的实模式地址是A000:0000,而线性地址则是A0000。设定显
存大小为0x10000,则可以如下操作
mem_fd = open( "/dev/mem", O_RDWR );
vga_mem = mmap( 0, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED,
mem_fd, 0xA0000 );
close( mem_fd );
然后直接对vga_mem进行访问,就可以了。当然,如果是操作VGA显卡,还要获得I/O
端口的访问权限,以便进行直接的I/O操作,用来设置模式/调色板/选择位面等等
在工控领域中还有一种常用的方法,用来在内核和应用程序之间高效传递数据:
1) 假定系统有64M物理内存,则可以通过lilo通知内核只使用63M,而保留1M物理内
存作为数据交换使用(使用 mem=63M 标记)。
2) 然后打开/dev/mem设备,并将63M开始的1M地址空间映射到进程的地址空间。












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