linux核心的syslets補丁

2021-05-21 19:33:54 字數 4624 閱讀 4748

linux的系統呼叫及其豐富,但是卻都是同步的,雖然2.6核心新增加了非同步io,但是對於套接字等卻以及非直接讀寫io卻不可用,於是開發者就有了一 個想法,既然一些任務可以由核心的xxlet或者核心執行緒(工作佇列)來非同步執行,比如tasklet,那麼能否建立一套非同步執行系統呼叫的機制呢?這就 是syslets的由來。

syslets按照常規思想來理解就是建立乙個新的執行緒來執行被阻塞的系統呼叫,然後被阻塞的執行緒直接返回使用者空間,等到新執行緒完成系統呼叫後用事件或者訊號來通知使用者空間或者由使用者空間來輪詢。果真是這樣嗎?如果是的話,那麼改動就大了去了,再說這也不是linux的風格,linux旨在提供一種將系統 呼叫設定成非同步的機制,並沒有說要拋棄同步呼叫,另外linux的風格就是一點一點的模組正交積累,互不影響。於是可以肯定核心補丁肯定不是按照以上方式實現syslets的,那麼怎麼實現的呢?我分析完這個補丁真相就大白了(保留+,省略了-)。

syslets補丁用到了乙個叫做sys_indirect的系統呼叫,它實際上就是對所有系統呼叫的包裝。在使用者空間需要乙個系統呼叫的時候不再直接呼叫之了,而是間接呼叫indirect,且看:

asmlinkage long sys_indirect(struct indirect_registers __user *userregs,...

if (copy_from_user(®s, userregs, sizeof(regs)))

return -efault;

if (paramslen > sizeof(union indirect_params))

return -einval;

if (copy_from_user(¤t->indirect_params, userparams, paramslen)) ;

資料結構主要就是以上的這個,至於syslets本身的,看看字面就可以理解,就不多說了,首先看看syslet_pre_indirect:

int syslet_pre_indirect(void)

struct task_struct *cur = current;

struct syslet_ring __user *ring;

u32 elements;

int ret;

if (!syslet_frame_valid(&cur->indirect_params.syslet.frame)) else

ret = 0;

cur->syslet_ready = 1;

out:

return ret;

call_indirect 實際上就是呼叫了原始的系統呼叫,比如sys_read,但是在sys_read的過程當中可能會發生阻塞,然後怎麼辦呢?我們的初衷是想讓程式非同步返 回,而不是被阻塞,然而如果一被阻塞就返回,那麼**的改動太大,只要有阻塞的地方都要判斷是否在非同步系統呼叫中,如果是那麼直接返回乙個錯誤碼,代表系統呼叫沒有完成,返回使用者空間之前還要建立或者喚醒乙個工作佇列之類的執行緒幫忙完成系統呼叫,這個工程十分龐大,根本就不是乙個補丁所能完成,可能還要徹 底顛覆linux

的設計思想。於是我們可以換一種思路,就是阻塞的系統呼叫不返回,繼續阻塞,返回的是新建立的執行緒,新執行緒返回前從原始執行緒接手一切,然後返回,這就相當於原始執行緒返回,不同的是執行緒號變了,但是無關緊要,原始執行緒阻塞前交待了「後事」,然後就長眠了,新執行緒返回後沿著原始執行緒沒有走完的路繼續前進,等到原始執行緒完成阻塞的時候,將會通知新執行緒,然後原始執行緒也返回,這裡就應該注意了,原始執行緒已經將執行任務完全交給新執行緒了,那麼原始執行緒會返回到** 呢?實際上

在非同步系統呼叫開始的時候要初始化乙個函式指標,就是原始執行緒返回的地點,一般在這個函式裡面直接exit即可,因為任務已經轉手,留著它,多餘了。

現在明白了syslet的機理,那麼就看看**吧:

asmlinkage void __sched schedule(void)

prev = current;

if (unlikely(prev->syslet_ready)) {

if (prev->state && !(preempt_count() & preempt_active) && //prev->state為真代表被阻塞而不是事件片原因的排程(cfs已經沒有事件片了)

(!(prev->state & task_interruptible) ||

!signal_pending(prev)))

syslet_schedule(prev);//排程current的syslet,實際上就是要返回使用者空間

void syslet_schedule(struct task_struct *cur)

struct task_struct *child = null;

spin_lock(&cur->syslet_lock);

child = first_syslet_task(cur);//cur的所有的syslet連線成乙個鍊錶,這裡取出第乙個,每乙個代表乙個task_struct。

if (child) {

move_user_context(child, cur); //這個函式具體結接了使用者空間的工作,見下面。

set_user_frame(cur, &cur->indirect_params.syslet.frame);//設定原始執行緒,即被阻塞的執行緒的返回位址

cur->syslet_ready = 0;

child->syslet_return = 1;//告知新執行緒返回使用者空間以代替原始執行緒繼續革命。

spin_unlock(&cur->syslet_lock);

if (child)

wake_up_process(child);

void move_user_context(struct task_struct *dest, struct task_struct *src)

struct pt_regs *old_regs = task_pt_regs(src);

struct pt_regs *new_regs = task_pt_regs(dest);

union i387_union *tmp;

*new_regs = *old_regs;

當初在create_new_syslet_task中建立的那個執行緒就是syslet即新執行緒,該執行緒的執行緒函式是:

static long syslet_thread(void *data)

struct syslet_task_args args;

struct task_struct *cur = current;

struct syslet_task_entry entry = {

.task = cur,

.item = list_head_init(entry.item),

args = *(struct syslet_task_args *)data;

spin_lock(&args.parent->syslet_lock);

list_add_tail(&entry.item, &args.parent->syslet_tasks);

spin_unlock(&args.parent->syslet_lock);

complete(args.comp);

for (;;) { 

set_task_state(cur, task_interruptible);

//cur->syslet_return 在syslet_schedule中被設定為1,這裡檢測到就退出了for迴圈,如果原始系統呼叫從來都沒有被阻塞,那麼建立的syslet實際上是多餘 的,在exit的時候因為要殺死它的所有的syslet,因此還會延遲exit。

if (cur->syslet_return || cur->syslet_exit || signal_pending(cur))

break;

schedule();

set_task_state(cur, task_running);

spin_lock(&args.parent->syslet_lock);

list_del(&entry.item);

if (list_empty(&args.parent->syslet_tasks))

wake_up_process(args.parent);

spin_unlock(&args.parent->syslet_lock);

if (!cur->syslet_return)

do_exit(0);

return -esysletpending;  //返回使用者空間,告知使用者空間系統呼叫將在將來完成。

如 果原始執行緒阻塞了,那麼它將等待,等到系統呼叫完成了,原始執行緒要呼叫syslet_post_indirect,該函式有兩個作用,一是通知使用者空間, 也就是它的**,實際上此時它的**已經全權接管了它的工作,通知系統呼叫完成了,二是返回使用者空間,返回的位置以及堆疊的位置由庫函式確定,這種實現確實有些ugly,但是也很靈活。

以上就是syslets的大致框架,可以看出,該實現的都實現了,但是總覺得不是那麼規範,比如執行緒轉交這件事我就咋看咋彆扭,還有就是原始執行緒返回使用者空間以後到底做些什麼事,這是核心所無法確定的,核心一向要對使用者空間的大政方針負責,這裡實現好像違背了這個原則。總之,linux是靈活的,小顆粒的物件組成了穩定的系統核心,這個特點使得它無論實現什麼特性都不是很難,因為它是拼裝而成的,沒有牽一髮而動全身的「優點」,雖然這個補丁不是那麼優秀, 但是我對syslets的前景還是抱有很大希望的,希望它能趨於完善。

linux核心的syslets補丁

linux的系統呼叫及其豐富,但是卻都是同步的,雖然2.6核心新增加了非同步io,但是對於套接字等卻以及非直接讀寫io卻不可用,於是開發者就有了一 個想法,既然一些任務可以由核心的xxlet或者核心執行緒 工作佇列 來非同步執行,比如tasklet,那麼能否建立一套非同步執行系統呼叫的機制呢?這就 ...

linux核心測試 Linux核心測試的生命週期

linux核心測試 在針對linux核心的持續整合測試中 我寫了關於 持續核心整合 cki 專案及其更改核心開發人員和維護人員工作方式的使命。本文深入 了該項目的更多技術方面以及所有部分如何組合在一起。核心中每一項令人興奮的功能,改進和錯誤都始於開發人員提出的更改。這些更改將出現在不同核心儲存庫的大...

Linux核心 了解Linux核心搶占

目錄 無強制搶占 可搶占核心 自願核心搶占 完全實時搶占 在配置linux核心時,我們可以設定一些影響系統行為的引數。您可以使用不同的優先順序,排程類和搶占模型。了解並選擇正確的引數非常重要。在這篇文章中,我將介紹不同的搶占模型,以及每種模型如何影響使用者和核心行為 如果配置核心 使用make me...