[8 进程控制]使用accton进行进程会计处理

1 进程会计介绍

启用进程会计选项后,当进程结束后内核会写一个会计记录。会计记录一般包括命令名,使用的CPU时间总量,用户ID,组ID和启动时间等。

root用户可以执行accton命令来启用会计处理。会计记录邪道指定的文件中,linux中该文件是/var/account/pacct。

acct结构定义在<sys/acct.h>中:

typedef u_short comp_t;
struct acct
{
    char ac_flag;    //flag
    char ac_stat;    //termination staus,Solaris only
    uid_t ac_uid;    //real user ID
    gid_t ac_gid;    //real group ID
    comp_t ac_utime;    //user CPU time
    comp_t ac_stime;    //system CPU time
    comp_t ac_etime;    //elapsed time
    comp_t ac_mem;    //average memory usage
    char ac_comm[8];    //command name
    ...
}

ac_flag成员记录了进程执行期间的某些事件。linux支持的ac_flag如下:

ac_flag
说明
AFORK(F)
进程由fork产生,但未调用exec
ASU(S)
进程使用超级用户特权
ACORE(D)
进程转储core
AXSIC(X)
进程由一个信号杀死

会计记录由内核保存在进程表中。在进程新创建时初始化,在进程结束时写入会计记录。由此得出结论:
(1)对于没终止的进程,我们无法获取进程记录
(2)会计文件中记录的顺序对应进程终止的顺序,不是进程启动的顺序
(3)会计记录对应的是进程而不是程序。如果一个进程顺序执行了3个程序(A exec B, B exec C),则只会写C进程的会计记录

2 使用accton进行进程会计处理

如下test1程序fork了4个进程,每个进程处理不同的事情,生成会计记录:

#include    <sys/wait.h>
#include    <unistd.h>
#include    <stdlib.h>
#include    <stdio.h>

int main(void)
{
    pid_t    pid;

    if ((pid = fork()) < 0)
        printf("fork error");
    else if (pid != 0) {        /* parent */
        sleep(2);
        exit(2);                /* terminate with exit status 2 */
    }

    if ((pid = fork()) < 0)
        printf("fork error");
    else if (pid != 0) {        /* first child */
        sleep(4);
        abort();                /* terminate with core dump */
    }

    if ((pid = fork()) < 0)
        printf("fork error");
    else if (pid != 0) {        /* second child */
        execl("/bin/dd", "dd", "if=/etc/passwd", "of=/dev/null", NULL);
        exit(7);                /* shouldn't get here */
    }

    if ((pid = fork()) < 0)
        printf("fork error");
    else if (pid != 0) {        /* third child */
        sleep(8);
        exit(0);                /* normal exit */
    }

    sleep(6);                   /* fourth child */
    kill(getpid(), SIGKILL);    /* terminate w/signal, no core dump */
    exit(6);                    /* shouldn't get here */
}

如下test2程序从会计记录中选择一些字段打印:

#include    <sys/acct.h>
#include    <sys/wait.h>
#include    <unistd.h>
#include    <stdlib.h>
#include    <stdio.h>

#if defined(BSD)    
#define acct acctv2
#define ac_flag ac_trailer.ac_flag
#define FMT "%-*.*s  e = %.0f, chars = %.0f, %c %c %c %c\n"
#elif defined(HAS_AC_STAT)
#define FMT "%-*.*s  e = %6ld, chars = %7ld, stat = %3u: %c %c %c %c\n"
#else
#define FMT "%-*.*s  e = %6ld, chars = %7ld, %c %c %c %c\n"
#endif

#if defined(LINUX)
#define acct acct_v3    /* different structure in Linux */
#endif

#if !defined(HAS_ACORE)
#define ACORE 0
#endif
#if !defined(HAS_AXSIG)
#define AXSIG 0
#endif

#if !defined(BSD)
static unsigned long
compt2ulong(comp_t comptime)    /* convert comp_t to unsigned long */
{
    unsigned long    val;
    int                exp;


    val = comptime & 0x1fff;    /* 13-bit fraction */
    exp = (comptime >> 13) & 7;    /* 3-bit exponent (0-7) */
    while (exp-- > 0)
        val *= 8;
    return(val);
}
#endif

int main(int argc, char *argv[])
{
    struct acct        acdata;
    FILE            *fp;


    if (argc != 2) {
        printf("usage: pracct filename \n");
                exit(127);
        }
    if ((fp = fopen(argv[1], "r")) == NULL) {
        printf("can't open %s \n", argv[1]);
                exit(127);
        }
    while (fread(&acdata, sizeof(acdata), 1, fp) == 1) {
        printf(FMT, (int)sizeof(acdata.ac_comm),
            (int)sizeof(acdata.ac_comm), acdata.ac_comm,
#if defined(BSD)
            acdata.ac_etime, acdata.ac_io,
#else
            compt2ulong(acdata.ac_etime), compt2ulong(acdata.ac_io),
#endif
#if defined(HAS_AC_STAT)
            (unsigned char) acdata.ac_stat,
#endif
            acdata.ac_flag & ACORE ? 'D' : ' ',
            acdata.ac_flag & AXSIG ? 'X' : ' ',
            acdata.ac_flag & AFORK ? 'F' : ' ',
            acdata.ac_flag & ASU   ? 'S' : ' ');
    }
    if (ferror(fp))
        printf("read error");
    exit(0);
}

编译脚本如下:

#!/bin/bash

gcc -o test1 test1.c
gcc -o test2 test2.c -DLINUX

按照如下步骤进行测试:
(1)进入超级用户,新建会计记录文件,安装acct并启用会计记录

touch /var/log/pacct
apt install acct
accton -h
accton /var/log/pacct

(2)终止超级用户,进入普通用户,运行test1程序,这会追加6个记录到会计文件中(超级用户shell,父进程,4个子进程)
在第二个子进程中,execl并不是创建一个新进程,所以对于第二个子子进程来说只有一个会计记录

./test1
ls -lh /var/log/pacct

(3)进入超级用户,停用会计记录

accton off

(4)终止超级用户,进入普通用户,运行test2程序,从会计文件中选出字段并打印

./test2 /var/log/pacct

会计文件记录如下:

accton            e =      0, chars =       0,       S
bash              e =   4303, chars =       0,       S
su                e =   4737, chars =       0,       S
dd                e =      0, chars =       0,                   //子进程2
test1             e =    200, chars =       0,                   //父进程
test1             e =    407, chars =       0,   D X F           //子进程1
test1             e =    600, chars =       0,     X F           //子进程4
test1             e =    800, chars =       0,       F           //子进程3

e表示墙上时钟时间值,例如父进程sleep(2)对应墙上时钟200个时钟滴答。
accton是由超级用户(ASU)关闭的。S。
除了第2个子进程(fork且exec),其他子进程都设置了F标志(AFORK)。F。
子进程1调用abort,abort产生信号(AXSIG)SIGABRT,产生core转储(ACORE)。D X F。
子进程4调用kill,kill产生信号SIGKILL,但不产生core转储。X F。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值