acct


Rev.1を表示中。最新版はこちら

プロセス毎のpidネームスペースns->ns->bacctに、acctファイルおよびかかる情報を有していて、プロセス
の起動/終了毎にその情報が設定/出力されます。プロセスのネームスペース毎に独自に管理していて、ネームスペース間で異なるログファイルを設定する事ができます。通常システム起動時、initのネームスペースを継承していて、任意のプロセスで設定したログファイルが、すべてのログファイルとなっていますが。

acctはログファイル名を引数にして、acctシステムコールが呼ばれます。nameが設定されていたらacct_on()でacctの設定を、NULLなら引数をNULLとしてacct_file_reopen()をコールします。引数がNULLならacctファイルのクローズを行っています。
SYSCALL_DEFINE1(acct, const char __user *, name)
{
       int error = 0;

       if (!capable(CAP_SYS_PACCT))
               return -EPERM;

       if (name) {
               char *tmp = getname(name);
               if (IS_ERR(tmp))
                       return (PTR_ERR(tmp));
               error = acct_on(tmp);
               putname(tmp);
       } else {
               struct bsd_acct_struct *acct;

               acct = task_active_pid_ns(current)->bacct;
               if (acct == NULL)
                       return 0;

               spin_lock(&acct_lock);
               acct_file_reopen(acct, NULL, NULL);
               spin_unlock(&acct_lock);
       }

       return error;
}

acct_on()でacctファイルの設定を行います。acctファイルは書きこみ/追加/ラージファイル属性でオープンされます。オープンできなければエラーです。従ってacctonする場合、前もって通常ファイルを作成しておく必要があり、ログファイルの参照はこのファイル権限となります。OKならacct_file_reopen()をコールして、ログファイルの設定を行います。

mnt_pin(mnt)は、real_mount(mnt)->mnt_pinned++とし、これは、ログファイルを作成したファイルシステムがアンマウントした時、acctファイルのクローズするためです。
static int acct_on(char *name)
{
       struct file *file;
       struct vfsmount *mnt;
       struct pid_namespace *ns;
       struct bsd_acct_struct *acct = NULL;

       /* Difference from BSD - they don't do O_APPEND */
       file = filp_open(name, O_WRONLY|O_APPEND|O_LARGEFILE, 0);
       if (IS_ERR(file))
               return PTR_ERR(file);

       if (!S_ISREG(file->f_path.dentry->d_inode->i_mode)) {
               filp_close(file, NULL);
               return -EACCES;
       }

       if (!file->f_op->write) {
               filp_close(file, NULL);
               return -EIO;
       }

       ns = task_active_pid_ns(current);
       if (ns->bacct == NULL) {
               acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL);
               if (acct == NULL) {
                       filp_close(file, NULL);
                       return -ENOMEM;
               }
       }

       spin_lock(&acct_lock);
       if (ns->bacct == NULL) {
               ns->bacct = acct;
               acct = NULL;
       }

       mnt = file->f_path.mnt;
       mnt_pin(mnt);
       acct_file_reopen(ns->bacct, file, ns);
       spin_unlock(&acct_lock);

       mntput(mnt); /* it's pinned, now give up active reference */
       kfree(acct);

       return 0;
}
全ログファイルはacct_listにリストされます。if (acct->file)ならこのプロセスはaccton状態です。これはログファイルを別のログファイルに置き換えようとしています。acct構造体を初期化し、

if (file)なら、新規にログファイルを設定します。acct構造体をそれで初期化した後、acct_listにリストします。

if (old_acct)なら、旧ログファイルをlist_del()でacct_listからアンリストします。先の処理でold_acct = acct->fileとしたものです。mnt_unpin()でreal_mount(mnt)->mnt_pinned--し、現在の状態を書き出してファイルクローズします。
static void acct_file_reopen(struct bsd_acct_struct *acct, struct file *file,
               struct pid_namespace *ns)
{
       struct file *old_acct = NULL;
       struct pid_namespace *old_ns = NULL;

       if (acct->file) {
               old_acct = acct->file;
               old_ns = acct->ns;
               acct->active = 0;
               acct->file = NULL;
               acct->ns = NULL;
               list_del(&acct->list);
       }
       if (file) {
               acct->file = file;
               acct->ns = ns;
               acct->needcheck = jiffies + ACCT_TIMEOUT*HZ;
               acct->active = 1;
               list_add(&acct->list, &acct_list);
       }
       if (old_acct) {
               mnt_unpin(old_acct->f_path.mnt);
               spin_unlock(&acct_lock);
               do_acct_process(acct, old_ns, old_acct);
               filp_close(old_acct, NULL);
               spin_lock(&acct_lock);
       }
}
プロセス終了時、do_exit()がコールされ、ここから、まずacct_collect()でacct構造体を最新の状態にした後、acct_process()でその内容を書き出しします。

group_dead=1は、対象のプロセスはグループリーダで、他のプロセスが使用した全仮想メモリサイズを取得し、ユーザ空間/カーネル空間の動作時間/ページフォルト数等が設定されます。ac_majfltはメモリマップまたはスワップに起因(VM_FAULT_MAJOR)するもので、ac_minfltはその他のページフォルト数です。
void acct_collect(long exitcode, int group_dead)
{
       struct pacct_struct *pacct = &current->signal->pacct;
       unsigned long vsize = 0;

       if (group_dead && current->mm) {
               struct vm_area_struct *vma;
               down_read(&current->mm->mmap_sem);
               vma = current->mm->mmap;
               while (vma) {
                       vsize += vma->vm_end - vma->vm_start;
                       vma = vma->vm_next;
               }
               up_read(&current->mm->mmap_sem);
       }

       spin_lock_irq(&current->sighand->siglock);
       if (group_dead)
               pacct->ac_mem = vsize / 1024;
       if (thread_group_leader(current)) {
               pacct->ac_exitcode = exitcode;
               if (current->flags & PF_FORKNOEXEC)
                       pacct->ac_flag |= AFORK;
       }
       if (current->flags & PF_SUPERPRIV)
               pacct->ac_flag |= ASU;
       if (current->flags & PF_DUMPCORE)
               pacct->ac_flag |= ACORE;
       if (current->flags & PF_SIGNALED)
               pacct->ac_flag |= AXSIG;
       pacct->ac_utime += current->utime;
       pacct->ac_stime += current->stime;
       pacct->ac_minflt += current->min_flt;
       pacct->ac_majflt += current->maj_flt;
       spin_unlock_irq(&current->sighand->siglock);
}
acct_process()はdo_exit()で、プロセスがグループリーダの時のみコールされます。
static void do_acct_process(struct bsd_acct_struct *acct,
               struct pid_namespace *ns, struct file *file)
{
       struct pacct_struct *pacct = &current->signal->pacct;
       acct_t ac;
       mm_segment_t fs;
       unsigned long flim;
       u64 elapsed;
       u64 run_time;
       struct timespec uptime;
       struct tty_struct *tty;
       const struct cred *orig_cred;

       orig_cred = override_creds(file->f_cred);

       if (!check_free_space(acct, file))
               goto out;

       memset(&ac, 0, sizeof(acct_t));

       ac.ac_version = ACCT_VERSION | ACCT_BYTEORDER;
       strlcpy(ac.ac_comm, current->comm, sizeof(ac.ac_comm));

       do_posix_clock_monotonic_gettime(&uptime);
       run_time = (u64)uptime.tv_sec*NSEC_PER_SEC + uptime.tv_nsec;
       run_time -= (u64)current->group_leader->start_time.tv_sec * NSEC_PER_SEC
                      + current->group_leader->start_time.tv_nsec;

       elapsed = nsec_to_AHZ(run_time);
#if ACCT_VERSION==3
       ac.ac_etime = encode_float(elapsed);
#else
       ac.ac_etime = encode_comp_t(elapsed < (unsigned long) -1l ?
                              (unsigned long) elapsed : (unsigned long) -1l);
#endif
#if ACCT_VERSION==1 || ACCT_VERSION==2
       {
               /* new enlarged etime field */
               comp2_t etime = encode_comp2_t(elapsed);
               ac.ac_etime_hi = etime >> 16;
               ac.ac_etime_lo = (u16) etime;
       }
#endif
       do_div(elapsed, AHZ);
       ac.ac_btime = get_seconds() - elapsed;
       /* we really need to bite the bullet and change layout */
       ac.ac_uid = orig_cred->uid;
       ac.ac_gid = orig_cred->gid;
#if ACCT_VERSION==2
       ac.ac_ahz = AHZ;
#endif
#if ACCT_VERSION==1 || ACCT_VERSION==2
       /* backward-compatible 16 bit fields */
       ac.ac_uid16 = ac.ac_uid;
       ac.ac_gid16 = ac.ac_gid;
#endif
#if ACCT_VERSION==3
       ac.ac_pid = task_tgid_nr_ns(current, ns);
       rcu_read_lock();
       ac.ac_ppid = task_tgid_nr_ns(rcu_dereference(current->real_parent), ns);
       rcu_read_unlock();
#endif

       spin_lock_irq(&current->sighand->siglock);
       tty = current->signal->tty;     /* Safe as we hold the siglock */
       ac.ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0;
       ac.ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime)));
       ac.ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime)));
       ac.ac_flag = pacct->ac_flag;
       ac.ac_mem = encode_comp_t(pacct->ac_mem);
       ac.ac_minflt = encode_comp_t(pacct->ac_minflt);
       ac.ac_majflt = encode_comp_t(pacct->ac_majflt);
       ac.ac_exitcode = pacct->ac_exitcode;
       spin_unlock_irq(&current->sighand->siglock);
       ac.ac_io = encode_comp_t(0 /* current->io_usage */);    /* %% */
       ac.ac_rw = encode_comp_t(ac.ac_io / 1024);
       ac.ac_swaps = encode_comp_t(0);

       fs = get_fs();
       set_fs(KERNEL_DS);

       flim = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
       current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
       file->f_op->write(file, (char *)&ac,
                              sizeof(acct_t), &file->f_pos);
       current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim;
       set_fs(fs);
out:
       revert_creds(orig_cred);
}
acct_update_integrals()は、CPU時間等、動的に変化する内容を更新するために、プログラムスタート時の
do_execve()および、tick毎に定期的にコールされています。

tsk->acct_rss_mem1/tsk->acct_vm_mem1は、delta(前回の処理した時間からの経過時間)で重みをつけることで、メモリサイズというのでなくメモリの占有度合いというところです。

acct_rss_mem1は実際に使用したメモリで、acct_vm_memは仮想メモリとなります。
void acct_update_integrals(struct task_struct *tsk)
{
       if (likely(tsk->mm)) {
               cputime_t time, dtime;
               struct timeval value;
               unsigned long flags;
               u64 delta;

               local_irq_save(flags);
               time = tsk->stime + tsk->utime;
               dtime = time - tsk->acct_timexpd;
               jiffies_to_timeval(cputime_to_jiffies(dtime), &value);
               delta = value.tv_sec;
               delta = delta * USEC_PER_SEC + value.tv_usec;

               if (delta == 0)
                       goto out;
               tsk->acct_timexpd = time;
               tsk->acct_rss_mem1 += delta * get_mm_rss(tsk->mm);
               tsk->acct_vm_mem1 += delta * tsk->mm->total_vm;
       out:
               local_irq_restore(flags);
       }
}
だらだらとなってしまいましたが、要は以下のフォーマットを追加更新されたファイルにすぎないということです。acctのログファイルには、コンパイルオプションでVERSION1,2,3とあり、struct acct_v3ないしstruct acctのフォーマットがあります。なお、動作してるacctのバージョンは、acct_v3/act->ac_versionで表示されます。
#ifdef CONFIG_BSD_PROCESS_ACCT_V3
#define ACCT_VERSION    3
#define AHZ             100
typedef struct acct_v3 acct_t;
#else
#ifdef CONFIG_M68K
#define ACCT_VERSION    1
#else
#define ACCT_VERSION    2
#endif
#define AHZ             (USER_HZ)
typedef struct acct acct_t;
#endif

#else
#define ACCT_VERSION    2
#define AHZ             (HZ)
#endif  /* __KERNEL */


struct acct
{
       char            ac_flag;                /* Flags */
       char            ac_version;             /* Always set to ACCT_VERSION */
       /* for binary compatibility back until 2.0 */
       __u16           ac_uid16;               /* LSB of Real User ID */
       __u16           ac_gid16;               /* LSB of Real Group ID */
       __u16           ac_tty;                 /* Control Terminal */
       __u32           ac_btime;               /* Process Creation Time */
       comp_t          ac_utime;               /* User Time */
       comp_t          ac_stime;               /* System Time */
       comp_t          ac_etime;               /* Elapsed Time */
       comp_t          ac_mem;                 /* Average Memory Usage */
       comp_t          ac_io;                  /* Chars Transferred */
       comp_t          ac_rw;                  /* Blocks Read or Written */
       comp_t          ac_minflt;              /* Minor Pagefaults */
       comp_t          ac_majflt;              /* Major Pagefaults */
       comp_t          ac_swaps;               /* Number of Swaps */
/* m68k had no padding here. */
#if !defined(CONFIG_M68K) || !defined(__KERNEL__)
       __u16           ac_ahz;                 /* AHZ */
#endif
       __u32           ac_exitcode;            /* Exitcode */
       char            ac_comm[ACCT_COMM + 1]; /* Command Name */
       __u8            ac_etime_hi;            /* Elapsed Time MSB */
       __u16           ac_etime_lo;            /* Elapsed Time LSB */
       __u32           ac_uid;                 /* Real User ID */
       __u32           ac_gid;                 /* Real Group ID */
};

struct acct_v3
{
       char            ac_flag;                /* Flags */
       char            ac_version;             /* Always set to ACCT_VERSION */
       __u16           ac_tty;                 /* Control Terminal */
       __u32           ac_exitcode;            /* Exitcode */
       __u32           ac_uid;                 /* Real User ID */
       __u32           ac_gid;                 /* Real Group ID */
       __u32           ac_pid;                 /* Process ID */
       __u32           ac_ppid;                /* Parent Process ID */
       __u32           ac_btime;               /* Process Creation Time */
#ifdef __KERNEL__
       __u32           ac_etime;               /* Elapsed Time */
#else
       float           ac_etime;               /* Elapsed Time */
#endif
       comp_t          ac_utime;               /* User Time */
       comp_t          ac_stime;               /* System Time */
       comp_t          ac_mem;                 /* Average Memory Usage */
       comp_t          ac_io;                  /* Chars Transferred */
       comp_t          ac_rw;                  /* Blocks Read or Written */
       comp_t          ac_minflt;              /* Minor Pagefaults */
       comp_t          ac_majflt;              /* Major Pagefaults */
       comp_t          ac_swaps;               /* Number of Swaps */
       char            ac_comm[ACCT_COMM];     /* Command Name */
};
プロセスアカウンティングでコマンド履歴を残す - いますぐ実践! Linux ...&#8206;

最終更新 2013/10/31 13:05:51 - north
(2013/10/31 13:05:51 作成)


検索

アクセス数
3586069
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。