導航:首頁 > 操作系統 > linuxhrtimer

linuxhrtimer

發布時間:2023-04-04 07:37:05

A. linux 2.6.23的內核怎麼打equalize的補丁

如果要使2.6.23.11升級到2.6.23.12. 我橋埋應該先把2.6.23.11 回退成2.6.23 然後再打 2.6.23.12的補丁假設我已經在內核的目錄中。補丁放在上層目錄。bzcat ../patch-2.6.23.11.bz2|patch -p1 -R #回退到2.6.23bzcat ../patch-2.6.23.12.bz2|patch -p1 #打到2.6.23.12總的來說就是這樣。正確的輸出應該全都像下面這樣patching file include/net/sock.hpatching file include/net/tcp.hpatching file include/scsi/scsi_device.hpatching file include/xen/interface/vcpu.hpatching file ipc/mqueue.cpatching file kernel/exit.cpatching file kernel/fork.cpatching file kernel/futex.cpatching file kernel/futex_compat.cpatching file kernel/hrtimer.cpatching file kernel/irq/manage.cpatching file kernel/lockdep.cpatching file kernel/params.c。。。如果出現rej文件。說明有的文件打補丁失敗。請查看那個rej文件。看看是為什麼導致失敗。一般來說。可能是你的目錄差消彎樹有問題。如虛悶果還有問題。參考內核/Docmuent目錄下面的補丁教程。資料來源:學網(www.xue5.com),原文地址: http://www.xue5.com/ite/200707/121959.html

B. linux中的hrtimer怎麼使用

1.hrtimers - 為高解析度kernel定時器,可作為超時或納燃周期性定時器使用
1). hrtimer_init初始化定時器工作模式。
hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
vibe_timer.function = timer_func;
/* 設置定時器的回調函數,定時器到時該函數將被調用 */
static enum hrtimer_restart timer_func(struct hrtimer *timer)
註:該回調函數為原子操作不能被中斷
關於Linux命令的介紹,看看《linux就該這洞尺虛么學》,具體關於這一章困李地址3w(dot)linuxprobe/chapter-02(dot)html
2). hrtimer_start的第二個參數用於設置超時參數。
hrtimer_start(&vibe_timer,
ktime_set(value / 1000, (value % 1000) * 1000000),HRTIMER_MODE_REL);
3).int hrtimer_cancel(struct hrtimer *timer);

要取消一個hrtimer,使用hrtimer_cancel:

C. linux中斷的下半部機制有哪些

一、中斷處理為什麼要下半部?Linux在中斷處理中間中斷處理分了上半部和下半部,目的就是提高系統的響應能力和並發能力。通俗一點來講:當一個中斷產生,調用該中斷對應的處理程序(上半部)然後告訴系統,對應的後半部可以執行了。然後中斷處理程序就返回,下半部會在合適的時機有系統調用。這樣一來就大大的減少了中斷處理所需要的時間。

二、那些工作應該放在上半部,那些應該放在下半部?
沒有嚴格的規則,只有一些提示:
1、對時間非常敏感,放在上半部。
2、與硬體相關的,放在上半部。
3、不能被其他中斷打斷的工作,放在上半部。
以上三點之外的,考慮放在下半部。

三、下半部機制在Linux中是怎麼實現的?
下半部在Linux中有以下實現機制:
1、BH(在2.5中刪除)
2、任務隊列(task queue,在2.5刪除)
3、軟中斷(softirq,2.3開始。本文重點)
4、tasklet(2.3開始)
5、工作隊列(work queue,2.5開始)

四、軟中斷是怎麼實現的(以下代碼出自2.6.32)?
軟中斷不會搶占另外一個軟中斷,唯一可以搶占軟中斷的是中斷處理程序。
軟中斷可以在不同CPU上並發執行(哪怕是同一個軟中斷)

1、軟中斷是編譯期間靜態分配的,定義如下:
struct softirq_action { void (*action)(struct softirq_action *); };

/*
* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
* frequency threaded job scheling. For almost all the purposes
* tasklets are more than enough. F.e. all serial device BHs et
* al. should be converted to tasklets, not to softirqs.
*/
enum {
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS

D. 怎麼控制GPIO引腳輸出5M方波(linux 2.6.28+S3C2440)

如果你讓繫念鎮統產生一個5MHz的中斷,那內核肯定會掛死孝簡。用PWM模塊輸出一個5MHz的波形就可以了,不需要中斷。
再看看別人怎麼說的。巧高褲

E. 內核計時器hrtimer怎麼獲取計時時間

Linux 提供了一個爛知簡單的 API 來構造和管理計時器。它包含一些函數(和助手函數),用於創建、取消和管理計時器。

計時器通過 timer_list 結構定義,該結構包括實現一個計時器所需的所有數據(其中包括列表指針和在編譯時配置的可陪飢選計時器統計數據)。從用戶角度看,timer_list 包含一個過期時間,一個回調函數(當/如果計時器過期),以及一個用戶提供的上下文。用戶必須初始化計時器,可以採取幾種方法,最簡單的方法是調用 setup_timer,該函數初始化計時器並設置用戶提供的回調函數和上下文。或者,用戶可以設置計時器中的這些值(函數和數據)並簡單地調用 init_timer。注意,init_timer 由 setup_timer 內部調用。

void init_timer( struct timer_list *timer );
void setup_timer( struct timer_list *timer, void (*function)(unsigned long), unsigned long data );

擁有一個經過初始化的計時器之後,用戶現在需要設置過期時間,這通過調用 mod_timer 來完成。由於用戶通常提供一個未來的過期時間,他們通常在這里添加 jiffies 來從當前時間偏移。用戶也可以通過調用 del_timer 來刪除一個計時器(如果它還沒有過期):

int mod_timer( struct timer_list *timer, unsigned long expires );
void del_timer( struct timer_list *timer );

最後,用戶可以通過調用 timer_pending(如果正在等待,將返回 1)來發現計時器是否正在等待(還沒有發出):

int timer_pending( const struct timer_list *timer );

計時器示例

我們來檢查一飢亂消下這些 API 函數的實際運行情況。清單 1 提供了一個簡單的內核模塊,用於展示簡單計時器 API 的核心特點。在 init_mole 中,您使用 setup_timer 初始化了一個計時器,然後調用 mod_timer 來啟動它。當計時器過期時,將調用回調函數 my_timer_callback。最後,當您刪除模塊時,計時器刪除(通過 del_timer)發生。(注意來自 del_timer 的返回檢查,它確定計時器是否還在使用。)

清單 1. 探索簡單計時器 API

#include <linux/kernel.h>
#include <linux/mole.h>
#include <linux/timer.h>

MODULE_LICENSE("GPL");

static struct timer_list my_timer;

void my_timer_callback( unsigned long data )
{
printk( "my_timer_callback called (%ld).\n", jiffies );
}

int init_mole( void )
{
int ret;

printk("Timer mole installing\n");

// my_timer.function, my_timer.data
setup_timer( &my_timer, my_timer_callback, 0 );

printk( "Starting timer to fire in 200ms (%ld)\n", jiffies );
ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(200) );
if (ret) printk("Error in mod_timer\n");

return 0;
}

void cleanup_mole( void )
{
int ret;

ret = del_timer( &my_timer );
if (ret) printk("The timer is still in use...\n");

printk("Timer mole uninstalling\n");

return;
}

F. Linux 之mutex 源碼分析

 mutex相關的函數並不是linux kernel實現的,而是glibc實現的,源碼位於nptl目錄下。

http://ftp.gnu.org/pub/gnu/glibc/glibc-2.3.5.tar.gz

首先說數據結構:

typedef union

{

  struct

  {

    int __lock;

    unsigned int __count;

    int __owner;

    unsigned int __nusers;

    /* KIND must stay at this position in the structure to maintain

       binary compatibility.  */

    int __kind;

    int __spins;

  } __data;

  char __size[__SIZEOF_PTHREAD_MUTEX_T];

  long int __align;

} pthread_mutex_t;

 int __lock;  資源競爭引用計數

 int __kind; 鎖類型,init 函數中mutexattr 參數傳遞,該參數可以為NULL,一般為 PTHREAD_MUTEX_NORMAL

結構體其他元素暫時不了解,以後更新。

/*nptl/pthread_mutex_init.c*/

int

__pthread_mutex_init (mutex, mutexattr)

     pthread_mutex_t *mutex;

     const pthread_mutexattr_t *mutexattr;

{

  const struct pthread_mutexattr *imutexattr;

  assert (sizeof (pthread_mutex_t) <= __SIZEOF_PTHREAD_MUTEX_T);

  imutexattr = (const struct pthread_mutexattr *) mutexattr ?: &default_attr;

  /* Clear the whole variable.  */

  memset (mutex, '\0', __SIZEOF_PTHREAD_MUTEX_T);

  /* Copy the values from the attribute.  */

  mutex->__data.__kind = imutexattr->mutexkind & ~0x80000000;

  /* Default values: mutex not used yet.  */

  // mutex->__count = 0;        already done by memset

  // mutex->__owner = 0;        already done by memset

  // mutex->__nusers = 0;        already done by memset

  // mutex->__spins = 0;        already done by memset

  return 0;

}

init函數就比較簡單了,將mutex結構體清零,設置結構體中__kind屬性。

/*nptl/pthread_mutex_lock.c*/

int

__pthread_mutex_lock (mutex)

     pthread_mutex_t *mutex;

{

  assert (sizeof (mutex->__size) >= sizeof (mutex->__data));

  pid_t id = THREAD_GETMEM (THREAD_SELF, tid);

  switch (__builtin_expect (mutex->__data.__kind, PTHREAD_MUTEX_TIMED_NP))

    {

     …

    default:

      /* Correct code cannot set any other type.  */

    case PTHREAD_MUTEX_TIMED_NP:

    simple:

      /* Normal mutex.  */

      LLL_MUTEX_LOCK (mutex->__data.__lock);

      break;

  …

  }

  /* Record the ownership.  */

  assert (mutex->__data.__owner == 0);

  mutex->__data.__owner = id;

#ifndef NO_INCR

  ++mutex->__data.__nusers;

#endif

  return 0;

}

該函數主要是調用LLL_MUTEX_LOCK, 省略部分為根據mutex結構體__kind屬性不同值做些處理。

宏定義函數LLL_MUTEX_LOCK最終調用,將結構體mutex的__lock屬性作為參數傳遞進來

#define __lll_mutex_lock(futex)                                                \

  ((void) ({                                                                \

    int *__futex = (futex);                                                \

    if (atomic_compare_and_exchange_bool_acq (__futex, 1, 0) != 0)        \

      __lll_lock_wait (__futex);                                        \

  }))

atomic_compare_and_exchange_bool_acq (__futex, 1, 0)宏定義為:

#define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \

  ({ __typeof (mem) __gmemp = (mem);                                      \

     __typeof (*mem) __gnewval = (newval);                              \

      \

     *__gmemp == (oldval) ? (*__gmemp = __gnewval, 0) : 1; })

這個宏實現的功能是:

如果mem的值等於oldval,則把newval賦值給mem,放回0,否則不做任何處理,返回1.

由此可以看出,當mutex鎖限制的資源沒有競爭時,__lock 屬性被置為1,並返回0,不會調用__lll_lock_wait (__futex); 當存在競爭時,再次調用lock函數,該宏不做任何處理,返回1,調用__lll_lock_wait (__futex);

void

__lll_lock_wait (int *futex)

{

  do

    {

      int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);

      if (oldval != 0)

lll_futex_wait (futex, 2);

    }

  while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);

}

atomic_compare_and_exchange_val_acq (futex, 2, 1); 宏定義:

/* The only basic operation needed is compare and exchange.  */

#define atomic_compare_and_exchange_val_acq(mem, newval, oldval) \

  ({ __typeof (mem) __gmemp = (mem);                                      \

     __typeof (*mem) __gret = *__gmemp;                                      \

     __typeof (*mem) __gnewval = (newval);                              \

      \

     if (__gret == (oldval))                                              \

       *__gmemp = __gnewval;                                              \

     __gret; })

這個宏實現的功能是,當mem等於oldval時,將mem置為newval,始終返回mem原始值。

此時,futex等於1,futex將被置為2,並且返回1. 進而調用

lll_futex_wait (futex, 2);

#define lll_futex_timed_wait(ftx, val, timespec)                        \

({                                                                        \

   DO_INLINE_SYSCALL(futex, 4, (long) (ftx), FUTEX_WAIT, (int) (val),        \

     (long) (timespec));                                \

   _r10 == -1 ? -_retval : _retval;                                        \

})

該宏對於不同的平台架構會用不同的實現,採用匯編語言實現系統調用。不過確定的是調用了Linux kernel的futex系統調用。

futex在linux kernel的實現位於:kernel/futex.c

SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,

struct timespec __user *, utime, u32 __user *, uaddr2,

u32, val3)

{

struct timespec ts;

ktime_t t, *tp = NULL;

u32 val2 = 0;

int cmd = op & FUTEX_CMD_MASK;

if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||

      cmd == FUTEX_WAIT_BITSET ||

      cmd == FUTEX_WAIT_REQUEUE_PI)) {

if (_from_user(&ts, utime, sizeof(ts)) != 0)

return -EFAULT;

if (!timespec_valid(&ts))

return -EINVAL;

t = timespec_to_ktime(ts);

if (cmd == FUTEX_WAIT)

t = ktime_add_safe(ktime_get(), t);

tp = &t;

}

/*

 * requeue parameter in 'utime' if cmd == FUTEX_*_REQUEUE_*.

 * number of waiters to wake in 'utime' if cmd == FUTEX_WAKE_OP.

 */

if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||

    cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)

val2 = (u32) (unsigned long) utime;

return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);

}

futex具有六個形參,pthread_mutex_lock最終只關注了前四個。futex函數對參數進行判斷和轉化之後,直接調用do_futex。

long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,

u32 __user *uaddr2, u32 val2, u32 val3)

{

int clockrt, ret = -ENOSYS;

int cmd = op & FUTEX_CMD_MASK;

int fshared = 0;

if (!(op & FUTEX_PRIVATE_FLAG))

fshared = 1;

clockrt = op & FUTEX_CLOCK_REALTIME;

if (clockrt && cmd != FUTEX_WAIT_BITSET && cmd != FUTEX_WAIT_REQUEUE_PI)

return -ENOSYS;

switch (cmd) {

case FUTEX_WAIT:

val3 = FUTEX_BITSET_MATCH_ANY;

case FUTEX_WAIT_BITSET:

ret = futex_wait(uaddr, fshared, val, timeout, val3, clockrt);

break;

         …

default:

ret = -ENOSYS;

}

return ret;

}

省略部分為對其他cmd的處理,pthread_mutex_lock函數最終傳入的cmd參數為FUTEX_WAIT,所以在此只關注此分之,分析futex_wait函數的實現。

static int futex_wait(u32 __user *uaddr, int fshared,

      u32 val, ktime_t *abs_time, u32 bitset, int clockrt)

{

struct hrtimer_sleeper timeout, *to = NULL;

struct restart_block *restart;

struct futex_hash_bucket *hb;

struct futex_q q;

int ret;

           … … //delete parameters check and convertion

retry:

/* Prepare to wait on uaddr. */

ret = futex_wait_setup(uaddr, val, fshared, &q, &hb);

if (ret)

goto out;

/* queue_me and wait for wakeup, timeout, or a signal. */

futex_wait_queue_me(hb, &q, to);

… … //other handlers

return ret;

}

futex_wait_setup 將線程放進休眠隊列中,

futex_wait_queue_me(hb, &q, to);將本線程休眠,等待喚醒。

喚醒後,__lll_lock_wait函數中的while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0); 語句將被執行,由於此時futex在pthread_mutex_unlock中置為0,所以atomic_compare_and_exchange_bool_acq (futex, 2, 0)語句將futex置為2,返回0. 退出循環,訪問用戶控制項的臨界資源。

/*nptl/pthread_mutex_unlock.c*/

int

internal_function attribute_hidden

__pthread_mutex_unlock_usercnt (mutex, decr)

     pthread_mutex_t *mutex;

     int decr;

{

  switch (__builtin_expect (mutex->__data.__kind, PTHREAD_MUTEX_TIMED_NP))

    {

   … …

    default:

      /* Correct code cannot set any other type.  */

    case PTHREAD_MUTEX_TIMED_NP:

    case PTHREAD_MUTEX_ADAPTIVE_NP:

      /* Normal mutex.  Nothing special to do.  */

      break;

    }

  /* Always reset the owner field.  */

  mutex->__data.__owner = 0;

  if (decr)

    /* One less user.  */

    --mutex->__data.__nusers;

  /* Unlock.  */

  lll_mutex_unlock (mutex->__data.__lock);

  return 0;

}

省略部分是針對不同的__kind屬性值做的一些處理,最終調用 lll_mutex_unlock。

該宏函數最終的定義為:

#define __lll_mutex_unlock(futex)                        \

  ((void) ({                                                \

    int *__futex = (futex);                                \

    int __val = atomic_exchange_rel (__futex, 0);        \

\

    if (__builtin_expect (__val > 1, 0))                \

      lll_futex_wake (__futex, 1);                        \

  }))

atomic_exchange_rel (__futex, 0);宏為:

#define atomic_exchange_rel(mem, value) \

  (__sync_synchronize (), __sync_lock_test_and_set (mem, value))

實現功能為:將mem設置為value,返回原始mem值。

__builtin_expect (__val > 1, 0) 是編譯器優化語句,告訴編譯器期望值,也就是大多數情況下__val > 1 ?是0,其邏輯判斷依然為if(__val > 1)為真的話執行 lll_futex_wake。

現在分析,在資源沒有被競爭的情況下,__futex 為1,那麼返回值__val則為1,那麼 lll_futex_wake (__futex, 1);        不會被執行,不產生系統調用。 當資源產生競爭的情況時,根據對pthread_mutex_lock 函數的分析,__futex為2, __val則為2,執行 lll_futex_wake (__futex, 1); 從而喚醒等在臨界資源的線程。

lll_futex_wake (__futex, 1); 最終會調動同一個系統調用,即futex, 只是傳遞的cmd參數為FUTEX_WAKE。

在linux kernel的futex實現中,調用

static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)

{

struct futex_hash_bucket *hb;

struct futex_q *this, *next;

struct plist_head *head;

union futex_key key = FUTEX_KEY_INIT;

int ret;

if (!bitset)

return -EINVAL;

ret = get_futex_key(uaddr, fshared, &key);

if (unlikely(ret != 0))

goto out;

hb = hash_futex(&key);

spin_lock(&hb->lock);

head = &hb->chain;

plist_for_each_entry_safe(this, next, head, list) {

if (match_futex (&this->key, &key)) {

if (this->pi_state || this->rt_waiter) {

ret = -EINVAL;

break;

}

/* Check if one of the bits is set in both bitsets */

if (!(this->bitset & bitset))

continue;

wake_futex(this);

if (++ret >= nr_wake)

break;

}

}

spin_unlock(&hb->lock);

put_futex_key(fshared, &key);

out:

return ret;

}

該函數遍歷在該mutex上休眠的所有線程,調用wake_futex進行喚醒,

static void wake_futex(struct futex_q *q)

{

struct task_struct *p = q->task;

/*

 * We set q->lock_ptr = NULL _before_ we wake up the task. If

 * a non futex wake up happens on another CPU then the task

 * might exit and p would dereference a non existing task

 * struct. Prevent this by holding a reference on p across the

 * wake up.

 */

get_task_struct(p);

plist_del(&q->list, &q->list.plist);

/*

 * The waiting task can free the futex_q as soon as

 * q->lock_ptr = NULL is written, without taking any locks. A

 * memory barrier is required here to prevent the following

 * store to lock_ptr from getting ahead of the plist_del.

 */

smp_wmb();

q->lock_ptr = NULL;

wake_up_state(p, TASK_NORMAL);

put_task_struct(p);

}

wake_up_state(p, TASK_NORMAL);  的實現位於kernel/sched.c中,屬於linux進程調度的技術。

G. linux中的hrtimer怎麼關閉

1、相關數塵手爛據結構 include/linux/notifier.h struct notifier_block { int (*notifier_call)(struct notifier_block *, unsigned long, void *); struct notifier_block *next; int priority; }; 通知鏈中的元素,記錄了當發出通知薯閉時,應該派漏執行的。

H. 5.5 Linux softirq

在普通的驅動中一般是不會用到softirq,但是由於驅動經常使用的tasklet是基於softirq的,因此,了解softirq機制有助於撰寫更優雅的driver。softirq不能動態分配,都是靜態定義的。內核已經定義了若干種softirq number,例如網路數據的收發、block設備的數據訪問(數據量大,通信帶寬高),timer的deferable task(時間方面要求高)。

1、softirq number
和IRQ number一樣,對於軟中斷,linux kernel也是用一個softirq number唯一標識一個softirq,具體定義如下

HI_SOFTIRQ用於高優先順序的tasklet,TASKLET_SOFTIRQ用於普通的tasklet。TIMER_SOFTIRQ是for software timer的(伍態判所謂software timer就是說該timer是基於系統tick的)。NET_TX_SOFTIRQ和NET_RX_SOFTIRQ是用於網卡數據收發的。BLOCK_SOFTIRQ和BLOCK_IOPOLL_SOFTIRQ是用於block device的。SCHED_SOFTIRQ用於多CPU之間的負載均衡的。HRTIMER_SOFTIRQ用於高精度timer的。RCU_SOFTIRQ是處理RCU的。

2、softirq描述符
softirq是靜態定義的,也就是說系統中有一個定義softirq描述符的數組,而softirq number就是這個數組的index。

1、注冊softirq
通過調用open_softirq介面函數可以注冊softirq的action callback函數

2、觸發softirq
軟中斷的觸發時機

1)、irq_exit:在硬中斷退出時,會檢查local_softirq_pending和preemt_count,如果都符合條件,則執行軟中斷。

if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
2)、local_bh_enable:使用此函數開啟軟中斷時,會檢查local_softirq_pending,如果都符合條件,則執行軟中斷。調用鏈為local_bh_enable()->__local_bh_enable()->do_softirq()。

3)、raise_softirq:主動喚起一個軟中斷,會首先設置__softirq_pending對應的軟中斷位為掛起,然後檢查in_interrupt,如果不在中斷中,則喚起ksoftirq線程執行軟中斷(ksoftirq是softirq的一種執行機制,在軟中的運行流程中會提到)。

3、執行softirq
在中斷處理程序中觸發軟中斷是最常見的形式,一個硬體中斷處理完成之後。下面的函數在處理完硬體中斷之後退出中斷處理函數,在irq_exit中會觸發軟體中斷腔改的處理,最後會調用__do_softirq執行軟中閉友斷。

1、注冊

2、喚醒
timer interrupt handler->
timer_tick->
update_process_times->
run_local_timers->
hrtimer_run_queues()和raise_softirq(TIMER_SOFTIRQ)->
raise_softirq_irqoff->
__raise_softirq_irqoff { or_softirq_pending(1UL << (nr)); }

3、執行
對於TIMER_SOFTIRQ來說,每次system clock產生中斷時,即一個tick 到來時,在system clock的中斷處理函數中會調用run_local_timers來設置TIMER_SOFTIRQ觸發條件;也就是當前CPU對應的irq_cpustat_t結構體中的__softirq_pending成員的第TIMER_SOFTIRQ個BIT被置為1。 而當這個條件滿足時,ksoftirqd線程(入口函數run_ksoftirqd,cpu_callback:kthread_create(run_ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);)會被喚醒,然後按照下面的流程調用TIMER_SOFTIRQ在數組softirq_vec中注冊的action,即run_timer_softirq。
run_ksoftirqd--->do_softirq--->__do_softirq--->softirq_vec[TIMER_SOFTIRQ].action

參考:
http://www.wowotech.net/irq_subsystem/soft-irq.html
https://blog.csdn.net/yhb1047818384/article/details/63687126
https://www.cnblogs.com/lidabo/p/5312856.html

I. 如何linux內核報告問題

Linux Kernel BUG:soft lockup CPU#1 stuck分析
1.線上內核bug日誌
kernel: Deltaway too big! 18428729675200069867 ts=18446743954022816244 write stamp =18014278822746377
kernel:------------[ cut here ]------------
kernel:WARNING: at kernel/trace/ring_buffer.c:1988 rb_reserve_next_event+0x2ce/0x370()(Not tainted)
kernel:Hardware name: ProLiant DL360 G7
kernel:Moles linked in: fuse ipv6 power_meter bnx2 sg microcode serio_raw iTCO_wdtiTCO_vendor_support hpilo hpwdt i7core_edac edac_core shpchp ext4 mbcache jbd2sd_mod crc_t10dif hpsa radeon ttm drm_kms_helper drm i2c_algo_bit i2c_coredm_mirror dm_region_hash dm_log dm_mod [last unloaded: scsi_wait_scan]
kernel: Pid:5483, comm: master Not tainted 2.6.32-220.el6.x86_64 #1
kernel: CallTrace:
kernel:[<ffffffff81069b77>] ? warn_slowpath_common+0x87/0xc0
kernel:[<ffffffff81069bca>] ? warn_slowpath_null+0x1a/0x20
kernel:[<ffffffff810ea8ae>] ? rb_reserve_next_event+0x2ce/0x370
kernel:[<ffffffff810eab02>] ? ring_buffer_lock_reserve+0xa2/0x160
kernel:[<ffffffff810ec97c>] ? trace_buffer_lock_reserve+0x2c/0x70
kernel:[<ffffffff810ecb16>] ? trace_current_buffer_lock_reserve+0x16/0x20
kernel:[<ffffffff8107ae1e>] ? ftrace_raw_event_hrtimer_cancel+0x4e/0xb0
kernel:[<ffffffff81095e7a>] ? hrtimer_try_to_cancel+0xba/0xd0
kernel:[<ffffffff8106f634>] ? do_setitimer+0xd4/0x220
kernel:[<ffffffff8106f88a>] ? alarm_setitimer+0x3a/0x60
kernel:[<ffffffff8107c27e>] ? sys_alarm+0xe/0x20
kernel:[<ffffffff8100b308>] ? tracesys+0xd9/0xde
kernel: ---[end trace 4d0a1ef2e62cb1a2 ]---
abrt-mp-oops: Reported 1 kernel oopses to Abrt
kernel: BUG: softlockup - CPU#11 stuck for 4278190091s! [qmgr:5492]
kernel:Moles linked in: fuse ipv6 power_meter bnx2 sg microcode serio_raw iTCO_wdtiTCO_vendor_support hpilo hpwdt i7core_edac edac_core shpchp ext4 mbcache jbd2sd_mod crc_t10dif hpsa radeon ttm drm_kms_helper drm i2c_algo_bit i2c_coredm_mirror dm_region_hash dm_log dm_mod [last unloaded: scsi_wait_scan]
kernel: CPU 11
kernel:Moles linked in: fuse ipv6 power_meter bnx2 sg microcode serio_raw iTCO_wdtiTCO_vendor_support hpilo hpwdt i7core_edac edac_core shpchp ext4 mbcache jbd2sd_mod crc_t10dif hpsa radeon ttm drm_kms_helper drm i2c_algo_bit i2c_coredm_mirror dm_region_hash dm_log dm_mod [last unloaded: scsi_wait_scan]
kernel:
kernel: Pid:5492, comm: qmgr Tainted: G W ---------------- 2.6.32-220.el6.x86_64 #1 HPProLiant DL360 G7
kernel: RIP:0010:[<ffffffff8106f730>] [<ffffffff8106f730>]do_setitimer+0x1d0/0x220
kernel: RSP:0018:ffff88080a661ef8 EFLAGS: 00000286
kernel: RAX:ffff88080b175a08 RBX: ffff88080a661f18 RCX: 0000000000000000
kernel: RDX:0000000000000000 RSI: 0000000000000082 RDI: ffff88080c8c4c40
kernel: RBP:ffffffff8100bc0e R08: 0000000000000000 R09: 0099d7270e01c3f1
kernel: R10:0000000000000000 R11: 0000000000000246 R12: ffffffff810ef9a3
kernel: R13:ffff88080a661e88 R14: 0000000000000000 R15: ffff88080a65a544
kernel: FS:00007f10b245f7c0(0000) GS:ffff88083c4a0000(0000) knlGS:0000000000000000
kernel: CS:0010 DS: 0000 ES: 0000 CR0: 000000008005003b
kernel: CR2:00007ff955977380 CR3: 000000100a80b000 CR4: 00000000000006e0
kernel: DR0:0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
kernel: DR3:0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
kernel:Process qmgr (pid: 5492, threadinfo ffff88080a660000, task ffff880809577500)
kernel: Stack:
kernel:00007f10b323def0 00007f10b248ead0 00007f10b26d0f78 00007f10b248ede0
kernel:<0> ffff88080a661f68 ffffffff8106f88a 0000000000000000 0000000000000000
kernel:<0> 000000000000014c 00000000000f423d 0000000000000000 0000000000000000
kernel: CallTrace:
kernel:[<ffffffff8106f88a>] ? alarm_setitimer+0x3a/0x60
kernel:[<ffffffff8107c27e>] ? sys_alarm+0xe/0x20
kernel:[<ffffffff8100b308>] ? tracesys+0xd9/0xde
kernel: Code:89 ef e8 74 66 02 00 83 3d 15 69 b5 00 00 75 37 49 8b 84 24 70 07 00 00 48 0508 08 00 00 66 ff 00 66 66 90 fb 66 0f 1f 44 00 00 <31> c0 e9 64 fe ff ff49 8b 84 24 68 07 00 00 48 c7 80 d0 00 00
kernel: CallTrace:
kernel:[<ffffffff8106f769>] ? do_setitimer+0x209/0x220
kernel:[<ffffffff8106f88a>] ? alarm_setitimer+0x3a/0x60
kernel:[<ffffffff8107c27e>] ? sys_alarm+0xe/0x20
kernel:[<ffffffff8100b308>] ? tracesys+0xd9/0xde
abrt-mp-oops: Reported 1 kernel oopses to Abrt

2.內核軟死鎖(soft lockup)bug原因分析
Soft lockup名稱解釋:所謂,soft lockup就是說,這個bug沒有讓系統徹底死機,但是若干個進程(或者kernel thread)被鎖死在了某個狀態(一般在內核區域),很多情況下這個是由於內核鎖的使用的問題。
Linux內核對於每一個cpu都有一個監控進程,在技術界這個叫做watchdog(看門狗)。通過ps –ef | grep watchdog能夠看見,進程名稱大概是watchdog/X(數字:cpu邏輯編號1/2/3/4之類的)。這個進程或者線程每一秒鍾運行一次,否則會睡眠和待機。這個進程運行會收集每一個cpu運行時使用數據的時間並且存放到屬於每個cpu自己的內核數據結構。在內核中有很多特定的中斷函數。這些中斷函數會調用soft lockup計數,他會使用當前的時間戳與特定(對應的)cpu的內核數據結構中保存的時間對比,如果發現當前的時間戳比對應cpu保存的時間大於設定的閥值,他就假設監測進程或看門狗線程在一個相當可觀的時間還沒有執。Cpu軟鎖為什麼會產生,是怎麼產生的?如果linux內核是經過精心設計安排的CPU調度訪問,那麼怎麼會產生cpu軟死鎖?那麼只能說由於用戶開發的或者第三方軟體引入,看我們伺服器內核panic的原因就是qmgr進程引起。因為每一個無限的循環都會一直有一個cpu的執行流程(qmgr進程示一個後台郵件的消息隊列服務進程),並且擁有一定的優先順序。Cpu調度器調度一個驅動程序來運行,如果這個驅動程序有問題並且沒有被檢測到,那麼這個驅動程序將會暫用cpu的很長時間。根據前面的描述,看門狗進程會抓住(catch)這一點並且拋出一個軟死鎖(soft lockup)錯誤。軟死鎖會掛起cpu使你的系統不可用。
如果是用戶空間的進程或線程引起的問題backtrace是不會有內容的,如果內核線程那麼在soft lockup消息中會顯示出backtrace信息。
3.根據linux內核源碼分析錯誤
根據我們第一部分內核拋出的錯誤信息和call trace(linux內核的跟蹤子系統)來分析產生的具體原因。
首先根據我們的centos版本安裝相應的linux內核源碼,具體步驟如下:
(1)下載源碼的rpm包kernel-2.6.32-220.17.1.el6.src.rpm
(2)安裝相應的依賴庫,命令:yuminstall rpm-build redhat-rpm-config asciidoc newt-devel
(3)安裝源碼包:rpm -ikernel-2.6.32-220.17.1.el6.src.rpm
(4)進入建立源碼的目錄:cd~/rpmbuild/SPECS
(5)建立生成源碼目錄:rpmbuild-bp --target=`uname -m` kernel.spec

下面開始真正的根據內核bug日誌分析源碼:
(1)第一階段內核錯誤日誌分析(時間在Dec 4 14:03:34這個階段的日誌輸出代碼分析,其實這部分代碼不會導致cpu軟死鎖,主要是第二階段錯誤日誌顯示導致cpu軟死鎖)
我們首先通過日誌定位到相關源代碼:看下面日誌:Dec 4 14:03:34 BP-YZH-1-xxxx kernel: WARNING: atkernel/trace/ring_buffer.c:1988 rb_reserve_next_event+0x2ce/0x370() (Not tainted)
根據日誌內容我們可以很容易的定位到kernel/trace/ring_buffer.c這個文件的1988行代碼如下:WARN_ON(1)。
先簡單解釋一下WARN_ON的作用:WARN_ON只是列印出當前棧信息,不會panic。所以會看到後面有一大堆的棧信息。這個宏定義如下:
#ifndef WARN_ON
#defineWARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN(); \
unlikely(__ret_warn_on); \
})
#endif
這個宏很簡單保證傳遞進來的條件值為0或者1(兩次邏輯非操作的結果),然後使用分支預測技術(保證執行概率大的分支緊鄰上面的指令)判斷是否需要調用__WARN()宏定義。如果滿足條件執行了__WARN()宏定義也接著執行一條空指令;。上面調用WARN_ON宏是傳遞的1,所以會執行__WARN()。下面繼續看一下__WARN()宏定義如下:
#define __WARN() warn_slowpath_null(__FILE__,__LINE__)
從接下來的call trace信息中我們也確實發現調用了warn_slowpath_null這個函數。通過在linux內核源代碼中搜索這個函數的實現,發現在panic.c(內核恐慌時的相關功能實現)中實現如下:
voidwarn_slowpath_null(const char *file, int line)
{
warn_slowpath_common(file, line,__builtin_return_address(0),
TAINT_WARN, NULL);
}
EXPORT_SYMBOL(warn_slowpath_null);//都出這個符號,讓其他模塊可以使用這個函數
同樣的我們看到了warn_slowpath_common這個函數,而在call trace當中這個函數在warn_slowpath_null函數之前列印出來,再次印證了這個流程是正確的。同樣在panic.c這個文件中我發現了warn_slowpath_common這個函數的實現如下:
static voidwarn_slowpath_common(const char *file, int line, void *caller,
unsigned taint, struct slowpath_args *args)
{
const char *board;

printk(KERN_WARNING "------------[ cut here]------------\n");
printk(KERN_WARNING "WARNING: at %s:%d %pS()(%s)\n",
file, line, caller, print_tainted());
board = dmi_get_system_info(DMI_PRODUCT_NAME);//得到dmi系統信息
if (board)
printk(KERN_WARNING "Hardware name:%s\n", board);//通過我們的日誌信息可以發現我們硬體名稱是ProLiant DL360 G7

if (args)
vprintk(args->fmt, args->args);

print_moles();//列印系統模塊信息
mp_stack();//mp信息輸出(call trace開始)
print_oops_end_marker();//列印oops結束
add_taint(taint);
}
分析這個函數的實現不難發現我們的很多日誌信息從這里開始輸出,包括列印一些系統信息,就不繼續深入分析了(請看代碼注釋,裡面調用相關函數列印對應信息,通過我分析這些函數的實現和我們的日誌信息完全能夠對應,其中mp_stack是與cpu體系結構相關的,我們的伺服器應該是屬於x86體系)。這里在繼續分析一下mp_stack函數的實現,因為這個是與cpu體系結構相關的,而且這個函數直接反應出導致內核panic的相關進程。這個函數實現如下:
/*
* The architecture-independent mp_stackgenerator
*/
void mp_stack(void)
{
unsigned long stack;

printk("Pid: %d, comm: %.20s %s %s %.*s\n",
current->pid, current->comm,print_tainted(),
init_utsname()->release,
(int

閱讀全文

與linuxhrtimer相關的資料

熱點內容
把文件夾設鎖 瀏覽:568
命令行語句 瀏覽:218
企友3e財務如何連接伺服器 瀏覽:984
華為手機如何刪除卸載app殘留數據 瀏覽:543
rpm的命令作用 瀏覽:365
如何查看網站的伺服器時間 瀏覽:850
編譯局和人民出版社 瀏覽:652
java泛型extends 瀏覽:326
頭條程序員教學 瀏覽:772
安卓合並什麼意思 瀏覽:530
linux在光碟引導 瀏覽:537
imap伺服器地址怎麼查 瀏覽:654
作曲教程pdf 瀏覽:506
pr怎麼壓縮文件大小 瀏覽:863
查看oracle字元集命令 瀏覽:179
鋰電池增加密度 瀏覽:661
linux用戶密碼忘記 瀏覽:242
gb壓縮天然氣 瀏覽:635
圖片拼接不壓縮app 瀏覽:670
我的世界如何編程 瀏覽:86