日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux时间子系统之(十二):periodic tick

發(fā)布時(shí)間:2024/1/17 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux时间子系统之(十二):periodic tick 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

專題文檔匯總目錄

?Notes:TickDevice模式,以及clocckevent設(shè)備。TickDevice設(shè)備的初始化,TickDevice是如何加入到系統(tǒng)中的。周期性Tick的產(chǎn)生。

原文地址:Linux時(shí)間子系統(tǒng)之(十二):periodic tick

?

一、tick device概念介紹

1、數(shù)據(jù)結(jié)構(gòu)

在內(nèi)核中,使用struct tick_device來抽象系統(tǒng)中的tick設(shè)備,如下:

struct tick_device {
??? struct clock_event_device *evtdev;
??? enum tick_device_mode mode;
};

從上面的定義就可以看出:所謂tick device其實(shí)就是工作在某種模式下的clock event設(shè)備。工作模式體現(xiàn)在tick device的mode成員,evtdev指向了和該tick device關(guān)聯(lián)的clock event設(shè)備。

Notes:clock_event_device是對(duì)能觸發(fā)clock時(shí)間設(shè)備的屬性、能力的一個(gè)描述。屬性包括features、irq、cpumask、rating等,能力包括設(shè)置next_event、event_handler、broadcast等。兩種模式是周期性和一次觸發(fā)模式。周期性tick只需要設(shè)置一次tick周期;而one shot每次到期后需要再次設(shè)置cycles,主要用戶NOTICK和hrtimer。

tick device的工作模式定義如下:

enum tick_device_mode {
??? TICKDEV_MODE_PERIODIC,
??? TICKDEV_MODE_ONESHOT,
};

tick device可以工作在兩種模式下,一種是周期性tick模式,另外一種是one shot模式。one shot模式主要和tickless系統(tǒng)以及高精度timer有關(guān),會(huì)在另外的文檔中描述,本文主要介紹periodic mode。

2、tick device的分類以及和CPU的關(guān)系

Notes:

local tick deviceDEFINE_PER_CPU(struct tick_device, tick_cpu_device)必須能將中斷送達(dá)綁定CPU
global tick deviceint tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT;

從local tick device中選一個(gè)而作為global tick device

broadcast tick devicestatic struct tick_device tick_broadcast_device.必須具備廣播功能,將中斷送達(dá)每一個(gè)CPU。

?

(1) local tick device。在單核系統(tǒng)中,傳統(tǒng)的unix都是在tick驅(qū)動(dòng)下進(jìn)行任務(wù)調(diào)度、低精度timer觸發(fā)等,在多核架構(gòu)下,系統(tǒng)為每一個(gè)cpu建立了一個(gè)tick device,如下:

DEFINE_PER_CPU(struct tick_device, tick_cpu_device);

local tick device的clock event device應(yīng)該具備下面的特點(diǎn):

(a)該clock event device對(duì)應(yīng)的HW timer必須是和該CPU core是有關(guān)聯(lián)的的(也就是說,該hw timer的中斷是可以送達(dá)到該CPU core的)。struct clock_event_device 有一個(gè)cpumask成員,它可以指示該clock event device為哪一個(gè)或者哪幾個(gè)CPU core工作。如果采用ARM generic timer的硬件,其HW timer總是為一個(gè)CPU core服務(wù)的,我們稱之為per cpu timer。

(b)該clock event device支持one shot模式,并且精度最高(rating最大)

(2)global tick device。具體定義如下:

int tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT;

有些任務(wù)不適合在local tick device中處理,例如更新jiffies,更新系統(tǒng)的wall time,更新系統(tǒng)的平均負(fù)載(不是單一CPU core的負(fù)載),這些都是系統(tǒng)級(jí)別的任務(wù),只需要在local tick device中選擇一個(gè)作為global tick device就OK了。tick_do_timer_cpu指明哪一個(gè)cpu上的local tick作為global tick。

(3)broadcast tick device,定義如下:

static struct tick_device tick_broadcast_device;

我們會(huì)單獨(dú)一份文檔描述它,這里就不再描述了。

二、初始化tick device

1、注冊(cè)一個(gè)新的clock event device的時(shí)候,tick device layer要做什么?

在clock event device的文章中,我們知道:底層的timer硬件驅(qū)動(dòng)在初始化的時(shí)候會(huì)注冊(cè)clock event device,在注冊(cè)過程中就會(huì)調(diào)用tick_check_new_device函數(shù)來看看是否需要進(jìn)行tick device的初始化,如果已經(jīng)已經(jīng)初始化OK的tick device是否有更換更高精度clock event device的需求。代碼如下:

void tick_check_new_device(struct clock_event_device *newdev)
{
??? struct clock_event_device *curdev;
??? struct tick_device *td;
??? int cpu;

??? cpu = smp_processor_id();---------------------------(1)
??? if (!cpumask_test_cpu(cpu, newdev->cpumask))??????? goto out_bc;

??? td = &per_cpu(tick_cpu_device, cpu);---獲取當(dāng)前cpu的tick device
??? curdev = td->evtdev; ---目前tick device正在使用的clock event device

??? if (!tick_check_percpu(curdev, newdev, cpu))-------------------(2)
??????? goto out_bc;

??? if (!tick_check_preferred(curdev, newdev))--------------------(3)
??????? goto out_bc;

??? if (!try_module_get(newdev->owner)) -----增加新設(shè)備的reference count
??????? return;

??? if (tick_is_broadcast_device(curdev)) { ----------------------(4)
??????? clockevents_shutdown(curdev);
??????? curdev = NULL;
??? }
??? clockevents_exchange_device(curdev, newdev); ---通知clockevent layer
??? tick_setup_device(td, newdev, cpu, cpumask_of(cpu)); ---------------(5)
??? if (newdev->features & CLOCK_EVT_FEAT_ONESHOT) ---其他文檔中描述
??????? tick_oneshot_notify();
??? return;

out_bc:
??? tick_install_broadcast_device(newdev); ----其他文檔中描述
}

Notes:從一大堆goto out_bc可知,對(duì)于替換當(dāng)前tick device還是很謹(jǐn)慎的。不滿足條件的clock event device作為broadcast device備選。交給tick_install_broadcast_device裁決,tick_check_broadcast_device裁決通過后,替換當(dāng)前的tick_broadcast_device.evtdev。

(1)是否是為本CPU服務(wù)的clock event device?如果不是,那么不需要考慮per cpu tick device的初始化或者更換該cpu tick device的clock event device。當(dāng)然,這是還是可以考慮用在broadcast tick device的。

(2)第二個(gè)關(guān)卡是per cpu的檢查。如果檢查不通過,那么說明這個(gè)新注冊(cè)的clock event device和該CPU不來電,不能用于該cpu的local tick。如果注冊(cè)的hw timer都是cpu local的(僅僅屬于一個(gè)cpu,這時(shí)候該clock event device的cpumask只有一個(gè)bit被set),那么事情會(huì)比較簡(jiǎn)單。然而,事情往往沒有那么簡(jiǎn)單,一個(gè)hw timer可以服務(wù)多個(gè)cpu。我們這里說HW timer服務(wù)于某個(gè)cpu其實(shí)最重要的是irq是否可以分發(fā)到指定的cpu上。我們可以看看tick_check_percpu的實(shí)現(xiàn):

static bool tick_check_percpu(struct clock_event_device *curdev,
????????????????? struct clock_event_device *newdev, int cpu)
{
??? if (!cpumask_test_cpu(cpu, newdev->cpumask))-------------------(a)
??????? return false;
??? if (cpumask_equal(newdev->cpumask, cpumask_of(cpu)))---------------(b)
??????? return true;
??? if (newdev->irq >= 0 && !irq_can_set_affinity(newdev->irq))--------------(c)
??????? return false;
??? if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu)))----------(d)
??????? return false;
??? return true;
}

(a)判斷這個(gè)新注冊(cè)的clock event device是否可以服務(wù)該CPU,如果它根本不鳥這個(gè)cpu那么不用浪費(fèi)時(shí)間了。

(b)判斷這個(gè)新注冊(cè)的clock event device是否只服務(wù)該CPU。如果這個(gè)clock event device就是服務(wù)該cpu的,那么別想三想四了,這個(gè)clock event device就是你這個(gè)CPU的人了。

(c)如果能走到這里,說明該clock event device可以服務(wù)多個(gè)CPU,指定的cpu(作為參數(shù)傳遞進(jìn)來)只是其中之一而已,這時(shí)候,可以通過設(shè)定irq affinity將該clock event device的irq定向到該cpu。當(dāng)前,前提是可以進(jìn)行irq affinity的設(shè)定,這里就是進(jìn)行這樣的檢查。

(d)走到這里,說明該新注冊(cè)的clock event device是可以進(jìn)行irq affinity設(shè)定的。我們可以通過修改irq affinity讓該hw timer服務(wù)于這個(gè)指定的CPU。恩,聽起來有些麻煩,的確如此,如果當(dāng)前CPU的tick device正在使用的clock event device就是special for當(dāng)前CPU的(根本不鳥其他CPU),有如此專情的clock event device,夫復(fù)何求,果斷拒絕新注冊(cè)的設(shè)備。

(3)程序來到這里,說明tick_check_percpu返回true,CPU和該clock event device之間的已經(jīng)是眉目傳情了,不過是否可以入主,就看該cpu的原配是否有足夠強(qiáng)大的能力(精度和特性)。tick_check_preferred代碼如下:

static bool tick_check_preferred(struct clock_event_device *curdev,
???????????????? struct clock_event_device *newdev)
{
?? if (!(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) {--------------(a)
??????? if (curdev && (curdev->features & CLOCK_EVT_FEAT_ONESHOT))
??????????? return false;
??????? if (tick_oneshot_mode_active())
??????????? return false;
??? }

??? return !curdev ||
??????? newdev->rating > curdev->rating ||
?????????? !cpumask_equal(curdev->cpumask, newdev->cpumask);-------------(b)
}

(a)首先進(jìn)行one shot能力比拼。如果新的clock event device沒有one shot能力而原配有,新歡失敗。如果都沒有one shot的能力,那么要看看當(dāng)前系統(tǒng)是否啟用了高精度timer或者tickless。本質(zhì)上,如果clock event device沒有oneshot功能,那么高精度timer或者tickless都是處于委曲求全的狀態(tài),如果這樣,還是維持原配的委曲求全的狀態(tài),新歡失敗

(b)如果current是NULL的話,事情變得非常簡(jiǎn)單,當(dāng)然是新來的這個(gè)clock event device勝出了(這時(shí)候,后面的比較都沒有意義了)。如果原配存在的話,那么可以看rating,如果新來的精度高,那也選擇新來的clock event device。是否精度低就一定不選新的呢?也不是,新設(shè)備還是有機(jī)會(huì)力挽狂瀾的:如果新來的是local timer,而原配是非local timer,這時(shí)候,也可以考慮選擇新的,畢竟新來的clock event device是local timer,精度低一些也沒有關(guān)系。

當(dāng)tick_check_percpu返回true的時(shí)候有兩種情況:一種是不管current是什么狀態(tài),新設(shè)備是CPU的local timer(只為這個(gè)cpu服務(wù))。另外一種情況是新設(shè)備不是CPU的local timer,當(dāng)然原配也沒有那么專一。

我們先看看第一種情況:如果cpumask_equal返回true,那么說明原配也是local timer,那么沒有辦法了,誰的rating高就選誰。如果cpumask_equal返回false,那么說明原配不是local timer,那么即便新來的rating低一些也還是優(yōu)先選擇local timer。

我們?cè)倏纯吹诙N情況:這里我絕對(duì)邏輯有問題,不知道是代碼的問題還是我還沒有考慮清楚,先TODO吧。

(4)OK,經(jīng)過復(fù)雜的檢查,我們終于決定要用這個(gè)新注冊(cè)的clock event device來替代current了(當(dāng)然,也有可能current根本不存在)。在進(jìn)行替換之前,我們還有檢查一下current是否是broadcast tick device,如果是的話,還不能將其退回clockevents layer,僅僅是設(shè)定其狀態(tài)為shutdown。curdev = NULL這一句很重要,在clockevents_exchange_device函數(shù)中,如果curdev == NULL的話,old device將不會(huì)從全局鏈表中摘下,掛入clockevents_released鏈表。

(5)setup tick device,參考下一節(jié)描述。

2、如何Setup 一個(gè) tick device?

所謂setup一個(gè)tick device就是對(duì)tick device心儀的clock event設(shè)備進(jìn)行設(shè)置,并將該tick device的evtdev指向新注冊(cè)的這個(gè)clock event device,具體代碼如下:

static void tick_setup_device(struct tick_device *td,
????????????????? struct clock_event_device *newdev, int cpu,
????????????????? const struct cpumask *cpumask)
{
??? ktime_t next_event;
??? void (*handler)(struct clock_event_device *) = NULL;

??? if (!td->evtdev) {
??????? if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {--------------(1)
??????????? ……
??????? }

??????? td->mode = TICKDEV_MODE_PERIODIC;------------------(2)
??? } else {
??????? handler = td->evtdev->event_handler;
??????? next_event = td->evtdev->next_event;
??????? td->evtdev->event_handler = clockevents_handle_noop; ------------(3)
??? }

??? td->evtdev = newdev; -----終于修成正果了,呵呵

??? if (!cpumask_equal(newdev->cpumask, cpumask)) ---------------(4)
??????? irq_set_affinity(newdev->irq, cpumask);

??? if (tick_device_uses_broadcast(newdev, cpu)) -------留給broadcast tick文檔吧
??????? return;

??? if (td->mode == TICKDEV_MODE_PERIODIC)
??????? tick_setup_periodic(newdev, 0); ----------------------(5)
??? else
??????? tick_setup_oneshot(newdev, handler, next_event); -----其他文檔描述
}

(1)在multi core的環(huán)境下,每一個(gè)CPU core都自己的tick device(可以稱之local tick device),這些tick device中有一個(gè)被選擇做global tick device,負(fù)責(zé)維護(hù)整個(gè)系統(tǒng)的jiffies。如果該tick device的是第一次設(shè)定,并且目前系統(tǒng)中沒有g(shù)lobal tick設(shè)備,那么可以考慮選擇該tick設(shè)備作為global設(shè)備,進(jìn)行系統(tǒng)時(shí)間和jiffies的更新。更細(xì)節(jié)的內(nèi)容請(qǐng)參考timekeeping文檔。

(2)在最初設(shè)定tick device的時(shí)候,缺省被設(shè)定為周期性的tick。當(dāng)然,這僅僅是初始設(shè)定,實(shí)際上在滿足一定的條件下,在適當(dāng)?shù)臅r(shí)間,tick device是可以切換到其他模式的,下面會(huì)具體描述。

(3)舊的clockevent設(shè)備就要退居二線了,將其handler修改為clockevents_handle_noop。

(4)如果不是local timer,那么還需要調(diào)用irq_set_affinity函數(shù),將該clockevent的中斷,定向到本CPU。

(5)tick_setup_periodic的代碼如下(注:下面的代碼分析中暫不考慮broadcast tick的情況):

void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
{
??? tick_set_periodic_handler(dev, broadcast); ----設(shè)定event handler為tick_handle_periodic

??? if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) && !tick_broadcast_oneshot_active()) {
??????? clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);---------(a)
??? } else {
??????? unsigned long seq;
??????? ktime_t next;

??????? do {
??????????? seq = read_seqbegin(&jiffies_lock);
??????????? next = tick_next_period; -----獲取下一個(gè)周期性tick觸發(fā)的時(shí)間
??????? } while (read_seqretry(&jiffies_lock, seq));

??????? clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); ---模式設(shè)定

??????? for (;;) {
??????????? if (!clockevents_program_event(dev, next, false)) ----program next clock event
??????????????? return;
??????????? next = ktime_add(next, tick_period); ------計(jì)算下一個(gè)周期性tick觸發(fā)的時(shí)間
??????? }
??? }
}

(a)如果底層的clock event device支持periodic模式,那么直接調(diào)用clockevents_set_mode設(shè)定模式就OK了

(b)如果底層的clock event device不支持periodic模式,而tick device目前是周期性tick mode,那么要稍微復(fù)雜一些,需要用clock event device的one shot模式來實(shí)現(xiàn)周期性tick。

三、周期性tick的運(yùn)作

1、從中斷到clock event handler

一般而言,底層的clock event chip driver會(huì)注冊(cè)中斷,我們用ARM generic timer驅(qū)動(dòng)為例,注冊(cè)的代碼如下:

err = request_percpu_irq(ppi, arch_timer_handler_phys, "arch_timer", arch_timer_evt);

……

具體的timer的中斷handler如下:

static irqreturn_t arch_timer_handler_phys_mem(int irq, void *dev_id)
{

????? ……
??????? evt->event_handler(evt);
?? ……

}

也就是說,在timer interrupt handler中會(huì)調(diào)用clock event device的event handler,而在周期性tick的場(chǎng)景下,這個(gè)event handler被設(shè)定為tick_handle_periodic。

2、周期性tick的clock event handler的執(zhí)行分析

由于每個(gè)cpu都有自己的tick device,因此,在每個(gè)cpu上,每個(gè)tick到了的時(shí)候,都會(huì)調(diào)用tick_handle_periodic函數(shù)進(jìn)行周期性tick中要處理的task,具體如下:

void tick_handle_periodic(struct clock_event_device *dev)
{
??? int cpu = smp_processor_id();
??? ktime_t next;

??? tick_periodic(cpu); ----周期性tick中要處理的內(nèi)容,參考下節(jié)描述

??? if (dev->mode != CLOCK_EVT_MODE_ONESHOT)
??????? return;
??? next = ktime_add(dev->next_event, tick_period);----計(jì)算下一個(gè)周期性tick觸發(fā)的時(shí)間
??? for (;;) {
??????? if (!clockevents_program_event(dev, next, false))---設(shè)定下一個(gè)clock event觸發(fā)的時(shí)間
??????????? return;
??????? if (timekeeping_valid_for_hres())------在其他文檔中描述
??????????? tick_periodic(cpu);
??????? next = ktime_add(next, tick_period);
??? }
}

如果該tick device所屬的clock event device工作在one shot mode,那么還需要為產(chǎn)生周期性tick而進(jìn)行一些額外處理。

2、周期性tick中要處理的內(nèi)容

代碼如下:

static void tick_periodic(int cpu)
{
??? if (tick_do_timer_cpu == cpu) {----global tick需要進(jìn)行一些額外處理
??????? write_seqlock(&jiffies_lock);
??????? tick_next_period = ktime_add(tick_next_period, tick_period);

??????? do_timer(1);-------------更新jiffies,計(jì)算平均負(fù)載
??????? write_sequnlock(&jiffies_lock);
??????? update_wall_time();----------更新wall time
??? }

??? update_process_times(user_mode(get_irq_regs()));----更新和當(dāng)前進(jìn)程相關(guān)的內(nèi)容
??? profile_tick(CPU_PROFILING);------和性能剖析相關(guān),不詳述了
}

原創(chuàng)文章,轉(zhuǎn)發(fā)請(qǐng)注明出處。蝸窩科技

http://www.wowotech.net/timer_subsystem/periodic-tick.html

轉(zhuǎn)載于:https://www.cnblogs.com/arnoldlu/p/7078199.html

總結(jié)

以上是生活随笔為你收集整理的Linux时间子系统之(十二):periodic tick的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。