2

ラップトップのバッテリ パラメータを分析し、所定の時間間隔ごとにカーネル ログ ファイル (/var/log/kern.log) に書き込む Linux カーネル モジュール (LKM) を開発しています。ログ ファイルのデータを使用して、リアルタイム グラフをプロットします。

なぜ私がこのようなことをしているのかと思うかもしれません。これは私の学問の一部であり、それに対する答えです。また、これはかなり興味深いものだと思います。

このタスクに対する私の全体的な分析は、最近まで単純なものでした。私が従った手順は次のとおりです。

  1. ここでの最良の回答に示されているようにパラメーターを取得でき、さらにいくつかのパラメーターがここで見つかりました。これら 2 つのリンクを使用して、私のタスクは簡単に開始できました。
  2. LKM の機能を定期的に実行するコードは、こちらから取得しました。これは毎秒実行するように変更されました。

これら 2 つの LKM を使用すると、この 2 つの LKM を組み合わせて、必要なパラメーター データをログ ファイルに書き込むだけのタスクしか残されていなかったため、自分のタスクは簡単なものであると誤解されていました。

両方の LKM のコードを組み合わせて 1 つの LKM を作成し、それを Ubuntu 14.04 64 ビット OS のカーネルにプラグインしました。システムが即座にフリーズし、私はショックを受けました。この時点で私は無知でした。

この後、目的の出力のために実行する必要が最も少ないバッテリーアナライザーLKMとタイマーLKMのコードを削減しました。快適に動作する縮小コードを見つけました。

後で、バッテリー パラメータを分析するコードを関数に移行し、それをエクスポートしました (EXPORT_SYMBOL(<function_name>)タイマー メソッドから呼び出されるように を使用しました。両方の LKM がクールに動作していたので、これが確実に実行されると確信していました。

次の LKM コードとビデオを使用して、私のマシンで何が起こったかの実際の画像を提供したいと思います。

bat_stat_analyzer.c - バッテリ パラメータをフェッチし、カーネル ログ ファイルに書き込む LKM。

#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/power_supply.h>

static int result = 0;
static struct power_supply *psy;
static void bat_stat(void);

EXPORT_SYMBOL(bat_stat);

static void bat_stat(void) {
  union power_supply_propval value;
  int charge_status, voltage_now, current_now, capacity;

  psy = power_supply_get_by_name("BAT1");
  result = psy->get_property(psy,POWER_SUPPLY_PROP_STATUS, &value);
  charge_status = (!result) ? value.intval : -1;
  result = psy->get_property(psy,POWER_SUPPLY_PROP_VOLTAGE_NOW, &value);
  voltage_now = (!result) ? value.intval : -1;
  result = psy->get_property(psy,POWER_SUPPLY_PROP_CURRENT_NOW, &value);
  current_now = (!result) ? value.intval : -1;
  result = psy->get_property(psy,POWER_SUPPLY_PROP_CAPACITY, &value);
  capacity = (!result) ? value.intval : -1;

  printk(KERN_INFO "%s:%d,%d,%d,%d\n",
    __func__, charge_status, voltage_now, current_now, capacity);
}

static int __init bat_stat_init(void) /* Constructor */
{
  bat_stat();
  return 0;
}

static void __exit bat_stat_exit(void) /* Destructor */
{
  printk(KERN_INFO "Good bye\n");
}

module_init(bat_stat_init);
module_exit(bat_stat_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sriram Kumar <sriramhearing_at_gmail_dot_com>");
MODULE_DESCRIPTION("First Battery Analyzer");

bat_stat_repeater.c - 関数を繰り返し呼び出す LKM

#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */
#include <linux/init.h>     /* Needed for the macros */
#include <linux/jiffies.h>
#include <linux/time.h>
#include <linux/hrtimer.h>

static unsigned long period_ms;
static unsigned long period_ns;
static ktime_t ktime_period_ns;
static struct hrtimer my_hrtimer;
extern int bat_stat(void);


//~ static void bat_stat_repeat(unsigned long data)
static enum hrtimer_restart bat_stat_repeat(struct hrtimer *timer)
{
  unsigned long tjnow;
  ktime_t kt_now;
  bat_stat();
  printk(KERN_INFO "Repeating...\n");

  tjnow = jiffies;
  kt_now = hrtimer_cb_get_time(&my_hrtimer);
  hrtimer_forward(&my_hrtimer, kt_now, ktime_period_ns);
  return HRTIMER_RESTART;
}

static int __init bat_stat_init(void)
{
  struct timespec tp_hr_res;
  period_ms = 1000;
  hrtimer_get_res(CLOCK_MONOTONIC, &tp_hr_res);

  hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  my_hrtimer.function = &bat_stat_repeat;
  period_ns = period_ms*( (unsigned long)1E6L );
  ktime_period_ns = ktime_set(0,period_ns);
  hrtimer_start(&my_hrtimer, ktime_period_ns, HRTIMER_MODE_REL);

  return 0;
}

static void __exit bat_stat_exit(void)
{
  int ret_cancel = 0;
  while( hrtimer_callback_running(&my_hrtimer) ) {
    ret_cancel++;
  }
  if (ret_cancel != 0) {
    printk(KERN_INFO " testjiffy Waited for hrtimer callback to finish (%d)\n", ret_cancel);
  }
  if (hrtimer_active(&my_hrtimer) != 0) {
    ret_cancel = hrtimer_cancel(&my_hrtimer);
    printk(KERN_INFO " testjiffy active hrtimer cancelled: %d\n", ret_cancel);
  }
  if (hrtimer_is_queued(&my_hrtimer) != 0) {
    ret_cancel = hrtimer_cancel(&my_hrtimer);
    printk(KERN_INFO " testjiffy queued hrtimer cancelled: %d\n", ret_cancel);
  }
  printk(KERN_INFO "Exit testjiffy\n");
}

module_init(bat_stat_init);
module_exit(bat_stat_exit);

MODULE_LICENSE("GPL");

私が使用したMakefileは、両方の LKM をコンパイルするために使用されたため、次のようになりました。

obj-m := <file_name>.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

これら 2 つの LKM を使用して取得した出力は、この youtubeリンク(わずか 2 分のビデオ)で見ることができます。画像で見たい方は以下に添付します。

バッテリー アナライザーの出力のスクリーンショット。 システムが停止しています。

私が知りたいのですが:

  • 私が試みていることを達成するためのより良い方法や簡単な方法はありますか? また
  • CPU を詰まらせる LKM の問題は何ですか?

前もって感謝します。

4

1 に答える 1