acct


acctシステムコールは、全プロセス終了時のプロセス属性をファイルに追加更新します。カーネルプロセスも対象となり、カーネルプロセスのexeは、lkmをrmmod以外稀で、従ってシェルからfork新規プロセス作成によるコマンド履歴が主たる目的となっているわけです。

履歴ファイルのnameを、カレントプロセスのpidネームスペースのns->bacct->fileにnameのFILEを設定し、プロセス終了時にdo_exet()で、終了プロセスpidネームスペースのns->bacct->fileに、プロセスの属性をACCTに設定し、追加更新されます。

acctファイルはカレントプロセスのpidネームスペースで管理されるため、acct処理はpidネームスペース下の全プロセスに適用されます。
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ファイルをO_WRONLY|O_APPEND|O_LARGEFILEでオープンします。O_CREAT無い故、既存ファイルでなければなりません。acctファイルの更新はカーネルが直接行うため、acctファイルのファイルID/ファイル属性に影響されず、参照するユーザの参照権限のみ考慮すればOKです。

acct_file_reopen()でns->bacctのacctファイルをクローズし、新規acctファイルをns->bacctに設定します。新規acctファイルがNULLなら、既存acctファイルをクローズしるだけで、acct機能が無効とします。

登録したacctはacct_listにネームスペースに依存せずリストされます。これはumount時、全acctファイルをクローズするため走査参照されます。
static int acct_on(char *name)
{
       struct file *file;
       struct vfsmount *mnt;
       struct pid_namespace *ns;
       struct bsd_acct_struct *acct = NULL;

       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;
}

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()でプロセス属性をcurrent->signal->pacctに設定し、acct_process()がコールされ、プロセスのネームスペースから親に遡って設定されている全ACCTファイルにプロセスのACCT情報が更新されます。
void acct_process(void)
{
       struct pid_namespace *ns;

       for (ns = task_active_pid_ns(current); ns != NULL; ns = ns->parent)
               acct_process_in_ns(ns);
}

static void acct_process_in_ns(struct pid_namespace *ns)
{
       struct file *file = NULL;
       struct bsd_acct_struct *acct;

       acct = ns->bacct;

       if (!acct || !acct->file)
               return;

       spin_lock(&acct_lock);
       file = acct->file;
       if (unlikely(!file)) {
               spin_unlock(&acct_lock);
               return;
       }
       get_file(file);
       spin_unlock(&acct_lock);

       do_acct_process(acct, ns, file);
       fput(file);
}
do_acct_process()でacctファイルを更新します。ACCTファイルはACCT_VERSION=1/2/3のカーネル依存の3つの属性構造体を有しており、ACCT_VERSIONに従ったacct_tに、current->signal->pacctを設定し、acct_tバイナリイメージで更新します。ACCT_VERSIONはac.ac_versionに、インデアン(ACCT_BYTEORDER)を考慮して設定されており、ユーザ空間からacct参照は、ac.ac_versionが1/2/3に応じて、参照する必要があります。
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
       {
               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;
       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
       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ファイル更新は、current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITYで、サイズの制限を無効とし、更新後プロセスの元サイズに戻しています。これはACCTファイルはユーザ空間から更新できるからです。

ACCTファイルはumountされるまでクローズされません。従ってシステムがダウンした場合、それまでの全プロセス情報が更新されている保障はありません。

ACCT_VERSION=3なら、acct_v3で、ACCT_VERSION=1/2ならacctとなり、ACCT_VERSION=1/2の違いは、タイマ計算にかかるクロックタイマHZ=の違いです。
#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

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 */
};


最終更新 2015/08/17 15:13:01 - north
(2013/10/31 13:05:51 作成)


検索

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