[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 編程技術 > C/C++ > 正文
Unix編程/應用問答中文版 ---12.日志相關問題 13.進程相關問題
本文出自:http://www.nsfocus.com 維護:小四 (2002-11-01 06:02:00)
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 <andrew@erlenstar.demon.co.uk> 

第一種辦法是讀取/proc接口提供的信息 

-------------------------------------------------------------------------- 
/* gcc -Wall -O3 -o getpid getpid.c */ 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/procfs.h> 
#include <unistd.h> 
#include <stropts.h> 
#include <dirent.h> 
#include <fcntl.h> 

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

*/ 
#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 ) 
(http://www.fanqiang.com)
    進入【UNIX論壇

相關文章
Unix編程/應用問答中文版 ---11. package相關問題 (2002-10-31 06:02:00)
Unix編程/應用問答中文版 ---10.網卡相關問題 (2002-10-30 06:02:01)
Unix編程/應用問答中文版 ---9.圖形界面相關問題 (2002-10-29 06:02:00)
Unix編程/應用問答中文版 ---8.Solaris內核編程相關問題 (2002-10-28 06:02:00)
Unix編程/應用問答中文版 ---7.DNS相關問題 (2002-10-25 06:02:00)
Unix編程/應用問答中文版 ---6./etc/system可調資源限制 (2002-10-24 06:02:00)
Unix編程/應用問答中文版 ---5.塊設備相關問題 (2002-10-23 06:02:00)
Unix編程/應用問答中文版 ---4.系統資源相關問題 (2002-10-22 06:02:00)
Unix編程/應用問答中文版 ---3.-lelf、-lkvm、-lkstat相關問題 (2002-10-21 06:02:01)
Unix編程/應用問答中文版 ---2.堆棧相關問題 (2002-10-18 06:02:00)

===更多相關===
 

★  樊強制作 歡迎分享  ★