发布时间:2025-12-09 14:51:47 浏览次数:4
启用进程会计选项后,当进程结束后内核会写一个会计记录。会计记录一般包括命令名,使用的CPU时间总量,用户ID,组ID和启动时间等。
root用户可以执行accton命令来启用会计处理。会计记录邪道指定的文件中,linux中该文件是/var/account/pacct。
acct结构定义在中:
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进程的会计记录
如下test1程序fork了4个进程,每个进程处理不同的事情,生成会计记录:
#include #include #include #include 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 #include #include #include #include #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 longcompt2ulong(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);}#endifint 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/bashgcc -o test1 test1.cgcc -o test2 test2.c -DLINUX按照如下步骤进行测试:
(1)进入超级用户,新建会计记录文件,安装acct并启用会计记录
(2)终止超级用户,进入普通用户,运行test1程序,这会追加6个记录到会计文件中(超级用户shell,父进程,4个子进程)
在第二个子进程中,execl并不是创建一个新进程,所以对于第二个子子进程来说只有一个会计记录
(3)进入超级用户,停用会计记录
accton off(4)终止超级用户,进入普通用户,运行test2程序,从会计文件中选出字段并打印
./test2 /var/log/pacct会计文件记录如下:
accton e = 0, chars = 0, Sbash e = 4303, chars = 0, Ssu e = 4737, chars = 0, Sdd e = 0, chars = 0, //子进程2test1 e = 200, chars = 0, //父进程test1 e = 407, chars = 0, D X F //子进程1test1 e = 600, chars = 0, X F //子进程4test1 e = 800, chars = 0, F //子进程3e表示墙上时钟时间值,例如父进程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。