2015-04-23 32 views
2

我正在開發一個Linux內核模塊(LKM),它分析我的筆記本電腦的電池參數並將其寫入內核日誌文件(/var/log/kern.log)給定的時間間隔。使用來自日誌文件的數據,我將繪製一個實時圖。當運行兩個不同的LKM來每秒獲取電池統計信息時系統掛起

你可能會認爲我爲什麼這麼做。這是我的學者的一部分是它的答案。另外,我覺得這很有趣。

直到最近,我對此任務的整個分析都很簡單。我遵循的步驟是:作爲最佳答案here給出,發現here一些參數

  1. 的參數可以是牽強。使用這兩個鏈接,我的任務有一個簡單的開始。
  2. here獲取定期執行LKM功能的代碼。這被修改爲每秒運行一次。

有了這兩個LKM,我誤解了我的任務是小菜一碟,因爲剩下的唯一任務就是組合這兩個LKM並將所需的參數數據寫入日誌文件。

我將兩個LKM的代碼分成兩部分,然後製作一個LKM,並將其插入到我的Ubuntu 14.04 64位操作系統的內核中。系統瞬間凍結讓我震驚。我在這一點上無能爲力。

在此之後,我減少了電池分析儀LKM和定時器LKM的代碼,這些代碼對於我所需的輸出而言是最不需要的。我發現運行舒適的簡化代碼。

後來我將代碼遷移到一個函數中分析電池參數並導出(使用EXPORT_SYMBOL(<function_name>),從timer方法中調用)。我確信這肯定會運行,因爲兩個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 

,我獲得使用這兩種的LKM可以在這個YouTube link(僅2分鐘視頻)被視爲輸出。如果你想看到它作爲一個圖像,它是連接如下:

Screenshot of battery analyzer output. System getting halted.

我想知道:

  • 是否有更好的和/或更簡單的方式來實現我是什麼試?或
  • 在我的LKM中有什麼麻煩讓我的CPU被堵塞?

在此先感謝。

+0

另外,您可以在虛擬機中進行初始的高風險測試嗎?如果你讓一個虛擬機「分裂它不會造成你的實際系統的危害。 –

+0

最初嘗試了VM,但虛擬機操作系統無法通過VM Manager(虛擬框)獲得電池信息。 – SriramK89

+0

安裝VB guest添加時電池信息不可用? –

回答

2

由於使用hrtimers,您遇到了問題。該機制專爲高精度設計,在hardirq上下文中調用回調(禁用IRQ),因此您的回調函數必須是原子的。然而,你從回調中調用的函數不是原子的,可能會睡眠(因爲互斥體)。正常的定時器有類似的問題,所以爲了解決這個問題,你應該使用其他方式或重複任務,例如工作隊列。

代碼中的其他一些小問題:兩個模塊

  • batt_stat函數聲明不同
  • 你不第一個模塊中檢查power_supply_get_by_name()的輸出

此外,我真的沒有看到任何理由將其分解爲兩個內核模塊,您應該只使用一個。

+0

感謝Krzysztof Adamski的這些觀點。我會嘗試使用替代方案。 - 'bat_stat'功能幾乎沒有修改,我錯過了改變它在另一個文件。感謝那。 - 我已經分析了'power_supply_get_by_name()'的輸出每次都是安全的。我會檢查是否可以使用任何條件。 由於我沒有使用單個模塊,我拆分了它們並開始用作不同的模塊。 – SriramK89

+0

我刪除了定時器LKM,並使用'kthread'重複了電池統計分析器,並在'msleep'的幫助下應用了無限循環來施加延遲執行。我還將一個小邏輯應用於(無限)循環,以便在模塊從內核中移除時線程將終止。感謝提示Krzysztof Adamski! – SriramK89

+0

@ SriramK89:那麼接受答案如何? –