进程记账,这一内核特性会使系统在每个进程结束后记录一条账单信息
打开进程记账功能后,内核会在每个进程终止时将一条记账信息写入系统级的进程记账文件。这条账单记录包含了内核为该进程所维护的多种信息,包括终止状态以及进程消耗的CPU时间。借助于标准工具(sa(8) 对账单文件进行汇总,lastcomm(1)则就先前执行的命令列出相关信息)或是定制应用,可对记账文件进行分析。
内核2.6.10之前,内核会为基于NRTL线程实现所创建的每个线程单独记录一条继承记账信息。自内核 2.6.10 开始,只有当最后一个线程退出时才会为整个进程保存一条账单记录。至于更老的 LinuxThread 线程实现,则会为每个线程单独记录一条进程记账信息。
从历史上看,进程记账主要用于在多用户Unix系统上针对用户所消耗的系统资源进行计费。不过,如果进程的信息并未由其父进程进行监控和报告,那么就可以使用进程记账来获取。
虽然大部分 UNIX 实现都支持进程记账功能,但 SUSv3 并未对其进行规范。账单记录的格式、记账文件的位置也随系统实现的不同而多少存在差别
Linux 系统的进程记账功能属于可选内核组件,可以通过CONFIG_BSD_PROCESS_ ACCT 选项进行配置。
打开和关闭进程记账功能
特权进程可利用系统调用 acct()来打开和关闭进程记账功能。应用程序很少使用这一系统调用。一般会将相应命令置于系统启动脚本中,在系统每次重启时开启进程记账功能
NAME
acct - switch process accounting on or off
SYNOPSIS
#include <unistd.h>
int acct(const char *filename);
为了打开进程账单功能,需要在参数 acctfile 中指定一个现有常规文件的路径名。记账文件通常的路径名是/var/log/pacct 或/usr/account/pacct。若想关闭进程记账功能,则指定 acctfile为 NULL 即可
下面程序使用 acct()来开关进程的记账功能。该程序的作用类似于 shell 命令accton(8)
#include <cstring>
#include <stdio.h>
#include <cstdlib>
#include <zconf.h>
#include <fcntl.h>
#define _BSD_SOURCE
#include <unistd.h>
int
main(int argc, char *argv[])
{
if (argc > 2 || (argc > 1 && strcmp(argv[1], "--help") == 0)){
printf("%s [file]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (acct(argv[1]) == -1){
perror("acct");
exit(EXIT_FAILURE);
}
printf("Process accounting %s\n",
(argv[1] == NULL) ? "disabled" : "enabled");
exit(EXIT_SUCCESS);
}
进程账单记录功能
一旦打开进程记账功能,每当一进程终止时,就会有一条acct记录写入记账文件。acct结构定义于头文件<sys/acct.h>中,具体如下
下面程序显示进程记账文件中的数据 :
#include <cstring>
#include <stdio.h>
#include <cstdlib>
#include <zconf.h>
#include <fcntl.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/acct.h>
#include <limits.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
uid_t /* Return UID corresponding to 'name', or -1 on error */
userIdFromName(const char *name)
{
struct passwd *pwd;
uid_t u;
char *endptr;
if (name == NULL || *name == '\0') /* On NULL or empty string */
return -1; /* return an error */
u = strtol(name, &endptr, 10); /* As a convenience to caller */
if (*endptr == '\0') /* allow a numeric string */
return u;
pwd = getpwnam(name);
if (pwd == NULL)
return -1;
return pwd->pw_uid;
}
char * /* Return name corresponding to 'uid', or NULL on error */
userNameFromId(uid_t uid)
{
struct passwd *pwd;
pwd = getpwuid(uid);
return (pwd == NULL) ? NULL : pwd->pw_name;
}
#define TIME_BUF_SIZE 100
static long long /* Convert comp_t value into long long */
comptToLL(comp_t ct)
{
const int EXP_SIZE = 3; /* 3-bit, base-8 exponent */
const int MANTISSA_SIZE = 13; /* Followed by 13-bit mantissa */
const int MANTISSA_MASK = (1 << MANTISSA_SIZE) - 1;
long long mantissa, exp;
mantissa = ct & MANTISSA_MASK;
exp = (ct >> MANTISSA_SIZE) & ((1 << EXP_SIZE) - 1);
return mantissa << (exp * 3); /* Power of 8 = left shift 3 bits */
}
int main(int argc, char *argv[])
{
int acctFile;
struct acct ac;
ssize_t numRead;
char *s;
char timeBuf[TIME_BUF_SIZE];
struct tm *loc;
time_t t;
if (argc != 2 || strcmp(argv[1], "--help") == 0){
printf("%s file\n", argv[0]);
exit(EXIT_FAILURE);
}
acctFile = open(argv[1], O_RDONLY);
if (acctFile == -1){
perror("open");
exit(EXIT_FAILURE);
}
printf("command flags term. user start time CPU elapsed\n ");
printf(" status "" time time\n");
while ((numRead = read(acctFile, &ac, sizeof(struct acct))) > 0) {
if (numRead != sizeof(struct acct)){
perror("read");
exit(EXIT_FAILURE);
}
printf("%-8.8s ", ac.ac_comm);
printf("%c", (ac.ac_flag & AFORK) ? 'F' : '-') ;
printf("%c", (ac.ac_flag & ASU) ? 'S' : '-') ;
/* Not all implementations support AXSIG and ACORE */
#ifdef AXSIG
printf("%c", (ac.ac_flag & AXSIG) ? 'X' : '-') ;
#else
printf(" ");
#endif
#ifdef ACORE
printf("%c", (ac.ac_flag & ACORE) ? 'C' : '-') ;
#else
printf(" ");
#endif
#ifdef __linux__
printf(" %#6lx ", (unsigned long) ac.ac_exitcode);
#else /* Many other implementations provide ac_stat instead */
/* But the BSDs don't provide this field either */
#if ! defined(__FreeBSD__) && ! defined(__NetBSD__) && \
! defined(__APPLE__)
printf(" %#6lx ", (unsigned long) ac.ac_stat);
#else
printf(" ");
#endif
#endif
s = userNameFromId(ac.ac_uid);
printf("%-8.8s ", (s == NULL) ? "???" : s);
t = ac.ac_btime;
loc = localtime(&t);
if (loc == NULL) {
printf("???Unknown time??? ");
} else {
strftime(timeBuf, TIME_BUF_SIZE, "%Y-%m-%d %T ", loc);
printf("%s ", timeBuf);
}
printf("%5.2f %7.2f ", (double) (comptToLL(ac.ac_utime) +
comptToLL(ac.ac_stime)) / sysconf(_SC_CLK_TCK),
(double) comptToLL(ac.ac_etime) / sysconf(_SC_CLK_TCK));
printf("\n");
}
if (numRead == -1){
perror("read");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}