• 招生咨詢熱線:4008-569-579 
  • 手機(jī)版
    用手機(jī)掃描二維碼直達(dá)商品手機(jī)版
招生咨詢熱線
4008-569-579
機(jī)構(gòu)主頁(yè) > 培訓(xùn)資料 > linux培訓(xùn)機(jī)構(gòu)講解linux運(yùn)行的阻塞機(jī)制
機(jī)構(gòu)主頁(yè) > 培訓(xùn)資料>linux培訓(xùn)機(jī)構(gòu)講解linux運(yùn)行的阻塞機(jī)制

linux培訓(xùn)機(jī)構(gòu)講解linux運(yùn)行的阻塞機(jī)制

來(lái)源:廣州達(dá)內(nèi)教育        時(shí)間:2023-05-30        熱度:37℃        返回列表

       在想要學(xué)習(xí)linux技術(shù)的學(xué)員面前,難學(xué)習(xí)的時(shí)候就是要學(xué)習(xí)linux知識(shí)里面的那些運(yùn)行機(jī)制吧。不管是linux運(yùn)行的阻塞機(jī)制還是運(yùn)行的非阻塞機(jī)制都是學(xué)員比較難懂的。那么linux運(yùn)行的阻塞機(jī)制是怎樣的呢?下面達(dá)內(nèi)科技的講師就給大家講解下:

  一、阻塞和非阻塞


  阻塞操作是指在執(zhí)行設(shè)備操作時(shí)若不能獲得資源則掛起進(jìn)程,直到滿足可操作的條件后再進(jìn)行操作。被掛起的進(jìn)程進(jìn)入休眠狀態(tài),被從調(diào)度器的運(yùn)行隊(duì)列移走,直到等待的條件被滿足。而非阻塞操作的進(jìn)程在不能進(jìn)行設(shè)備操作時(shí)并不掛起,它或者放棄,或者不停地查詢,直至可以進(jìn)行操作為止。


  二、等待隊(duì)列


  在 Linux 驅(qū)動(dòng)程序中,可以使用等待隊(duì)列(wait queue)來(lái)實(shí)現(xiàn)阻塞進(jìn)程的喚醒。wait queue 很早就作為一個(gè)基本的功能單位出現(xiàn)在 Linux 內(nèi)核里了,它以隊(duì)列為基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),與進(jìn)程調(diào)度機(jī)制緊密結(jié)合,能夠用于實(shí)現(xiàn)內(nèi)核中的異步事件通知機(jī)制。等待隊(duì)列可以用來(lái)同步對(duì)系統(tǒng)資源的訪問(wèn),信號(hào)量在內(nèi)核中也依賴等待隊(duì)列來(lái)實(shí)現(xiàn)。


  希望等待特定事件的進(jìn)程把自己放進(jìn)合適的等待隊(duì)列,并放棄控制權(quán)。因此,等待隊(duì)列是一組睡眠的進(jìn)程,當(dāng)某一條件變?yōu)檎鏁r(shí),由內(nèi)核喚醒他們。


  1,等待隊(duì)列頭


  每個(gè)等待隊(duì)列都有一個(gè)等待隊(duì)列頭,驅(qū)動(dòng)注意操作等待隊(duì)列頭來(lái)實(shí)現(xiàn)阻塞的功能,二等待隊(duì)列項(xiàng)的內(nèi)容不需要關(guān)心,因?yàn)榈却?duì)列是由中斷處理程序和主要內(nèi)核函數(shù)修改的,其雙向鏈表必須進(jìn)行保護(hù),防止多進(jìn)程同時(shí)進(jìn)行訪問(wèn)修改,造成不可預(yù)知的后果,所以定義了lock來(lái)鎖住鏈表操作的區(qū)域


  等待隊(duì)列頭結(jié)構(gòu)體的定義:內(nèi)核使用等待隊(duì)列頭來(lái)掛起一個(gè)進(jìn)程,也使用等待隊(duì)列頭來(lái)喚醒進(jìn)程


  struct __wait_queue_head {


  spinlock_t lock; //自旋鎖變量,用于在對(duì)等待隊(duì)列頭


  struct list_head task_list; // 指向等待隊(duì)列的list_head


  };


  typedef struct __wait_queue_head wait_queue_head_t;


  操作函數(shù)


  #include <linux/sched.h>


  #include <linux/wait.h>


  1).定義“等待隊(duì)列頭”


  wait _ queue _ head _ t my _ queue;


  2) .初始化“等待隊(duì)列頭”。


  void init_waitqueue_head(wait_queue_head_t *);


  而下面的 DECLARE_WAIT_QUEUE_HEAD()宏可以作為定義并初始化等待隊(duì)列頭的“快捷方式”。


  DECLARE_WAIT_QUEUE_HEAD(name);


  3).條件等待/休眠函數(shù) 一邊休眠等待條件


  //當(dāng)cond條件是false(0)則休眠(不可中斷版,不推薦使用)


  void wait_event(wait_queue_head_t wq, int cond);


  上面程序的執(zhí)行過(guò)程:


  1.用當(dāng)前的進(jìn)程描述塊(PCB)初始化一個(gè)wait_queue描述的等待任務(wù)。


  2.在等待隊(duì)列鎖資源的保護(hù)下,將等待任務(wù)加入等待隊(duì)列。


  3.判斷等待條件是否滿足,如果滿足,那么將等待任務(wù)從隊(duì)列中移出,退出函數(shù)。


  4.如果條件不滿足,那么任務(wù)調(diào)度,將CPU資源交與其它任務(wù)。


  5.當(dāng)睡眠任務(wù)被喚醒之后,需要重復(fù)(2)、(3)步驟,如果確認(rèn)條件滿足,退出等待事件函數(shù)。


  使用舉例: flag可以是一個(gè)條件表達(dá)式


  static wait_queue_head_t wq;


  init_waitqueue_head(&wq);//初始化等待隊(duì)列頭


  //if(!flag){


  while(!flag){ //條件不滿足


  if(wait_event_interruptible(wq,flag)) //如果是被其他信號(hào)喚醒則返回錯(cuò)誤


  return -ERESTARTSYS;


  }


  使用 while 而不使用if的原因是:wait_event_interruptible


  可以被中斷及信號(hào)打斷,使用while(1),可以避免被打斷的情況。


  注:其實(shí)可以不用加while,查看內(nèi)核源碼的用法,如果被中斷或者信號(hào)打斷,直接返回錯(cuò)誤。


  flag = 1; //先設(shè)置條件,再喚醒


  wake_up(&wq); //條件滿足時(shí)喚醒等待隊(duì)列頭上所有的進(jìn)程


  //當(dāng)cond條件是false(0)則休眠(超時(shí)版,timeout是超時(shí)值,單位是計(jì)數(shù)值)


  //超時(shí)返回值為0 ,被喚醒大于0 需判斷返回值


  int wait_event_timeout(wait_queue_head_t wq, int condition, unsigend long timeout);


  //當(dāng)cond條件是false則休眠(可中斷版)


  //返回值:打斷:負(fù)數(shù),值是錯(cuò)誤碼,成功0 返回值需要做判斷


  int wait_event_interruptible(wait_queue_head_t wq, int condition);


  //當(dāng)cond條件是false則休眠(可超時(shí)中斷版)


  //打斷:負(fù)數(shù),值是錯(cuò)誤碼; 超時(shí):0; 條件滿足:>0


  int wait_event_interruptible_timeout(wait_queue_head_t wq, int condition, unsigend long timeout);


  4).喚醒函數(shù) 另一邊條件成熟時(shí)喚醒


  void wake_up(wait_queue_head_t *) //能喚醒所以狀態(tài)的進(jìn)程


  void wake_up_interruptible(wait_queue_head_t *) //只適用于interruptible,配對(duì)使用


  注意:?jiǎn)拘押瘮?shù)當(dāng)條件滿足時(shí),一定要先設(shè)置條件condition,再喚醒調(diào)用喚醒函數(shù)。因?yàn)榈却吆瘮?shù)返回后會(huì)首先檢查condition是否滿足,若不滿足會(huì)繼續(xù)睡


  如: counter = count;


  wake_up_interruptible(&wq);


  2,等待隊(duì)列項(xiàng)


  定義等待對(duì)列:


  struct __wait_queue {


  unsigned int flags; //prepare_to_wait()里有對(duì)flags的操作,查看以得出其含義


  #define WQ_FLAG_EXCLUSIVE 0x01 //一個(gè)常數(shù),在prepare_to_wait()用于修改flags的值


  void * private //通常指向當(dāng)前任務(wù)控制塊


  wait_queue_func_t func; //喚醒阻塞任務(wù)的函數(shù) ,決定了喚醒的方式


  struct list_head task_list; // 阻塞任務(wù)鏈表


  };


  typedef struct __wait_queue wait_queue_t;


  1) 定義一個(gè)等待隊(duì)列


  wait_queue_t wait;


  2) 初始化等待隊(duì)列


  內(nèi)核中定義的接口如下:


  static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)


  {


  q->flags = 0;


  q->private = p; //私有數(shù)據(jù)指針,一般指向當(dāng)前任務(wù)控制塊


  q->func = default_wake_function; //使用默認(rèn)的喚醒函數(shù)


  }


  使用范例:


  init_waitqueue_entry(&wait, current);


  3) 添加/ 等待隊(duì)列。


  void fastcall add _ wait _ queue(wait _ queue _ head _ t *q, wait _ queue _ t *wait);


  add_wait_queue()用于將等待隊(duì)列 wait 添加到等待隊(duì)列頭 q 指向的等待隊(duì)列鏈表


  4)移除等待隊(duì)列。


  void fastcall remove _ wait _ queue(wait _ queue _ head _ t *q, wait _ queue _ t *wait);


  remove_wait_queue()用于將等待隊(duì)列 wait 從附屬的等待隊(duì)列頭 q 指向的等待隊(duì)列鏈表中移除。


  5)判斷等待隊(duì)列是否為空。


  static inline int waitqueue_active(wait_queue_head_t *q)


  {


  return ! list_empty(&q->task_list);


  }


  判斷等待對(duì)列頭是否為空,當(dāng)一個(gè)進(jìn)程訪問(wèn)設(shè)備而得不到資源時(shí)就會(huì)被放入等待隊(duì)列頭指向的等待隊(duì)列中。


  三、函數(shù) sleep_on的實(shí)現(xiàn)


  Sleep()函數(shù)相信大家早已耳熟能詳了,可是內(nèi)部究竟是怎么實(shí)現(xiàn)的呢?讓我們一起來(lái)揭開(kāi)它的面紗


  void sleep_on(wait_queue_head_t *wq)


  {


  wait_queue_t wait; //定義等待隊(duì)列


  init_waitqueue_entry(&wait, current);


  //初始化等待隊(duì)列


  current->state = TASK_UNINTERRUPTIBALE; //設(shè)置進(jìn)程狀態(tài)


  add_wait_queue(wq,&wait);


  //加入等待隊(duì)列


  schedule();


  //調(diào)度,當(dāng)前進(jìn)程進(jìn)入睡眠


  remove_wait_queue(wq,&wait);


  //醒后從等待隊(duì)列中移除


  }


  可以發(fā)現(xiàn),程序之所以能睡眠,是因?yàn)樗淖兞俗约旱臓顟B(tài),并執(zhí)行調(diào)度,放棄了占用CPU。但是我們要喚醒進(jìn)程,必須要找到它,怎么找到它呢,關(guān)鍵就在于進(jìn)程在睡眠前我們把它加入了等待對(duì)應(yīng),只要找到等待隊(duì)列我們就能找到掛起的進(jìn)程并喚醒它。


  等待隊(duì)列是一個(gè)具有頭節(jié)點(diǎn)的雙向循環(huán)鏈表,把所有睡眠的進(jìn)程連接起來(lái),每個(gè)節(jié)點(diǎn)元素都有進(jìn)程相關(guān)的信息


  以上就是達(dá)內(nèi)科技的小編給大家整理的關(guān)于linux培訓(xùn)機(jī)構(gòu)講解linux運(yùn)行的阻塞機(jī)制的內(nèi)容,如果說(shuō)你是有基礎(chǔ)的學(xué)員的話肯定是可以看得懂小編說(shuō)的內(nèi)容,但是如果說(shuō)你是零基礎(chǔ)的學(xué)員的話估計(jì)就很難看的懂了。如果說(shuō)你想要學(xué)習(xí)linux技術(shù)的話,那么達(dá)內(nèi)科技的小編建議大家可以到我們達(dá)內(nèi)科技的linux培訓(xùn)班進(jìn)行實(shí)地考察下,來(lái)和我們的講師進(jìn)行面對(duì)面的交流和互動(dòng)。也可以點(diǎn)擊我們文章下面的獲取試聽(tīng)資格按鈕來(lái)獲取我們的linux培訓(xùn)的免費(fèi)課程試聽(tīng)資格,來(lái)免費(fèi)體驗(yàn)我們的linux課程并深入的了解我們達(dá)內(nèi)科技。

電話咨詢

電話咨詢

咨詢電話:
4008-569-579
回到頂部

回到頂部