Unix编程/应用问答中文版 ---12.日志相关问题 13.进程相关问题(转)

Unix编程/应用问答中文版 ---12.日志相关问题 13.进程相关问题(转)[@more@]

12. 日志相关问题

12.1

12.2

12.3 如何关闭cron的日志

12.4

--------------------------------------------------------------------------

13. 进程相关问题

13.1 如何根据进程名获得PID

13.2

13.3

13.4 Solaris 7/8下ps输出中的问号

13.5

13.6

13.7 给定一个PID,如何知道它对应一个运行中的进程

13.8 Unix/Linux编程中所谓"僵尸进程"指什么

13.9 x86/FreeBSD 4.3-RELEASE的ptrace(2)手册页

13.10 Solaris下如何知道哪个进程使用了哪个端口

13.11 x86/FreeBSD如何快速获取指定用户拥有的进程数

--------------------------------------------------------------------------

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

第一种办法是读取/proc接口提供的信息

--------------------------------------------------------------------------

/* gcc -Wall -O3 -o getpid getpid.c */

#include

#include

#include

#include

#include

#include

#include

#include

#include

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 ", dirEntry->d_name ); */

if ( ( fd = open( dirEntry->d_name, O_RDONLY ) ) != -1 )

{

if ( ioctl( fd, PIOCPSINFO, &prp ) != -1 )

{

/* fprintf( stderr, "%s ", 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 ", 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: ", argv[1], ( unsigned int )pid );

exit( EXIT_SUCCESS );

}

exit( EXIT_FAILURE );

} /* end of main */

--------------------------------------------------------------------------

这种技术要求运行者拥有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

在介绍第二种办法之前,先看一下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

#include

#include

#include

#include

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 ", ( int )pid.pid_id );

}

} /* end of while */

kvm_close( kd );

exit( EXIT_SUCCESS );

} /* end of main */

--------------------------------------------------------------------------

A: Andrew Gierth

第二种办法是使用kvm_*()函数

--------------------------------------------------------------------------

#define _KMEMUSER /* 必须定义这个宏 */

#include

#include

#include

#include

#include

#include

/*

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 ", cur_proc, cur_u

ser ); */

error = kvm_getcmd( kd, cur_proc, cur_user, &argv, NULL );

/*

* fprintf( stderr, "[ %s ] is: ", cur_user->u_comm, ( unsigne

d int )pid );

*

* 比如in.telnetd、syslogd、bash、login

*/

if ( error == -1 ) /* 失败,比如argv[]已经被进程自己修改过 */

{

if ( cur_user->u_comm[0] != '' )

{

p_name = cur_user->u_comm; /* 从另外一个地方获取信息 */

}

}

else /* 成功 */

{

/*

* fprintf( stderr, "[ %s ] is: ", 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 ", 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: ", 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

有些时候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 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对

应运行中的进程。

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

下面来看一个简单的ptrace(2)演示,x86/FreeBSD 4.3-RELEASE

--------------------------------------------------------------------------

/*

* gcc -Wall -pipe -O3 -o target target.c

*/

#include

#include

#include

#include

#include

int main ( int argc, char * argv[] )

{

write( STDERR_FILENO, "Hello world ", 12 );

return( EXIT_SUCCESS );

} /* end of main */

--------------------------------------------------------------------------

--------------------------------------------------------------------------

/*

* gcc -Wall -pipe -O3 -o ptracetest ptracetest.c

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

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 ", ( int )p );

/*

* 阻塞式waitpid()

*/

waitpid( p, NULL, 0 );

fprintf( stderr, "Now in parent process, "

"please enter [CR] to continue ... ... " );

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 ... ... " );

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

对于Solaris 8,可以使用这个演示脚本,如果不能满足你的需要,请自行修改

--------------------------------------------------------------------------

#! /bin/sh

# find_socket_proc.sh for x86/SPARC Solaris 8

#

# File : find_socket_proc.sh

# Author : Vitaly Filatov

# Fix : scz

# 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--&gt${PORT}"

done

fi

done

--------------------------------------------------------------------------

如果你以普通用户身份运行,只能检查自己的进程,如果以root身份运行,可以检查

所有用户的进程。

13.11 x86/FreeBSD如何快速获取指定用户拥有的进程数

Q: 谁能给我一段C代码,快速统计出一个指定用户所拥有的进程数。我想修改Apache

以阻止它超过kern.maxprocperuid限制后继续fork()产生新进程。如果Apache以

sudo方式启动,就可能出现这种情况。我该看ps(1)的源代码吗?

A: Maxim Konovalov

参看src/usr.bin/killall/killall.c,这里用了sysctl()接口

A: Andrew

可以试试kvm_getprocs( KERN_PROC_UID

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/8225414/viewspace-944702/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/8225414/viewspace-944702/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值