Unix程式設計/應用問答中文版 ---12.日誌相關問題 13.程式相關問題(轉)

post0發表於2007-08-11
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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章