歡迎您光臨本站 註冊首頁
本文出自:http://www.nsfocus.com 維護:小四


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, "%sn", dirEntry->d_name ); */
if ( ( fd = open( dirEntry->d_name, O_RDONLY ) ) != -1 )
{
if ( ioctl( fd, PIOCPSINFO, &prp ) != -1 )
{
/* fprintf( stderr, "%sn", 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 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許可權,否則無法有效獲取非自己擁有的進程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: %dn", ( 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 = %pn", 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] != '' )
{
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 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

有些時候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 worldn", 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 <%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 & 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${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 )



[火星人 ] Unix編程/應用問答中文版 ---12.日誌相關問題 13.進程相關問題已經有636次圍觀

http://coctec.com/docs/linux/show-post-57618.html