自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入理解Linux內(nèi)核之進(jìn)程睡眠(下)

系統(tǒng) Linux
進(jìn)程睡眠按照應(yīng)用場景可以分為:延遲睡眠和等待某些特定條件而睡眠,實(shí)際上都可以歸于等待某些特定條件而睡眠,因?yàn)檠舆t特定時(shí)間也可以作為特定條件。

[[412976]]

本文轉(zhuǎn)載自微信公眾號「Linux內(nèi)核遠(yuǎn)航者」,作者Linux內(nèi)核遠(yuǎn)航者。轉(zhuǎn)載本文請聯(lián)系Linux內(nèi)核遠(yuǎn)航者公眾號。

4.用戶態(tài)睡眠

以sleep為例來說明任務(wù)在用戶態(tài)是如何睡眠的。

首先我們通過strace工具來看下其調(diào)用的系統(tǒng)調(diào)用:

  1. $ strace sleep 1 
  2.  
  3. ... 
  4. close(3)                                = 0 
  5. clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0}, NULL) = 0 
  6. close(1)                                = 0 
  7. ... 

可以發(fā)現(xiàn)sleep主要調(diào)用clock_nanosleep系統(tǒng)調(diào)用來進(jìn)行睡眠(也就是說用戶態(tài)任務(wù)睡眠需要調(diào)用系統(tǒng)調(diào)用陷入內(nèi)核)。

下面我們來研究下clock_nanosleep的實(shí)現(xiàn)(這里集中到睡眠的實(shí)現(xiàn),先忽略掉定時(shí)器等諸多的技術(shù)細(xì)節(jié)):

  1. kernel/time/posix-timers.c 
  2.  
  3. SYSCALL_DEFINE4(clock_nanosleep 
  4. ->const struct k_clock *kc = clockid_to_kclock(which_clock);  //根據(jù)時(shí)鐘類型得到內(nèi)核時(shí)鐘結(jié)構(gòu) 
  5.     return kc->nsleep(which_clock, flags, &t); //調(diào)用內(nèi)核時(shí)鐘結(jié)構(gòu)的nsleep回調(diào) 

我們傳遞過來的時(shí)鐘類型為CLOCK_REALTIME,則調(diào)用鏈為:

  1. kc->nsleep(CLOCK_REALTIME, flags, &t) 
  2. ->clock_realtime.nsleep 
  3.     ->common_nsleep 
  4.         ->hrtimer_nanosleep  //kernel/time/hrtimer.c 
  5.             ->hrtimer_init_sleeper_on_stack 
  6.                     ->__hrtimer_init_sleeper 
  7.                         ->__hrtimer_init(&sl->timer, clock_id, mode); //初始化高精度定時(shí)器 
  8.                             sl->timer.function = hrtimer_wakeup;  //設(shè)置超時(shí)回調(diào)函數(shù) 
  9.                             sl->task = current;.//設(shè)置超時(shí)時(shí)要喚醒的任務(wù) 
  10.                      ->do_nanosleep  //睡眠操作 

可以看到,睡眠函數(shù)最終調(diào)用到hrtimer_nanosleep,它調(diào)用了兩個(gè)主要函數(shù):__hrtimer_init_sleeper和do_nanosleep,前者主要設(shè)置高精度定時(shí)器,后者就是真正的睡眠,主要來看下 do_nanosleep:

  1. kernel/time/hrtimer.c 
  2.  do_nanosleep 
  3.  -> 
  4.          do { 
  5.                  set_current_state(TASK_INTERRUPTIBLE);  //設(shè)置可中斷的睡眠狀態(tài) 
  6.                  hrtimer_sleeper_start_expires(t, mode); //開啟高精度定時(shí)器 
  7.  
  8.                  if (likely(t->task)) 
  9.                          freezable_schedule(); //主動(dòng)調(diào)度 
  10.                     
  11.  
  12.                  hrtimer_cancel(&t->timer); 
  13.                  mode = HRTIMER_MODE_ABS; 
  14.  
  15.          } while (t->task && !signal_pending(current));  //是否記錄的有任務(wù)且沒有掛起的信號 
  16.  
  17.          __set_current_state(TASK_RUNNING);  //設(shè)置為可運(yùn)行狀態(tài) 

do_nanosleep函數(shù)是睡眠的核心實(shí)現(xiàn):首先設(shè)置任務(wù)的狀態(tài)為可中斷的睡眠狀態(tài),然后開啟了之前設(shè)置的高精度定時(shí)器,隨即調(diào)用freezable_schedule進(jìn)行真正的睡眠。

來看下freezable_schedule:

  1. //include/linux/freezer.h 
  2. freezable_schedule 
  3. ->schedule() 
  4.     ->__schedule(false);  
  5.   

可以看到最終調(diào)用主調(diào)度器__schedule進(jìn)行主動(dòng)調(diào)度。

當(dāng)任務(wù)睡眠完成,定時(shí)器超時(shí),會(huì)調(diào)用之前在__hrtimer_init_sleeper設(shè)置的超時(shí)回調(diào)函數(shù)hrtimer_wakeup將睡眠的任務(wù)喚醒(關(guān)于進(jìn)程喚醒在這里就不在贅述,在后面的進(jìn)程喚醒專題文章在進(jìn)行詳細(xì)解讀),然后就可以再次獲得處理器的使用權(quán)了。

總結(jié):處于用戶態(tài)的任務(wù),如果想要睡眠一段時(shí)間必須向內(nèi)核請求服務(wù)(如調(diào)用clock_nanosleep系統(tǒng)調(diào)用),內(nèi)核中會(huì)設(shè)置一個(gè)高精度定時(shí)器,來記錄要睡眠的任務(wù),然后設(shè)置任務(wù)狀態(tài)為可中斷的睡眠狀態(tài),緊接著發(fā)生主動(dòng)調(diào)度,這樣任務(wù)就發(fā)生睡眠了。

5.內(nèi)核態(tài)睡眠

當(dāng)任務(wù)處于內(nèi)核態(tài)時(shí),有時(shí)候也需要睡眠一段時(shí)間,不像任務(wù)處于用戶態(tài)需要發(fā)生系統(tǒng)調(diào)用來請求內(nèi)核進(jìn)行睡眠,在內(nèi)核態(tài)可以直接調(diào)用睡眠函數(shù)。當(dāng)然,內(nèi)核態(tài)中,睡眠有兩種場景:一種是睡眠特定的時(shí)間的延遲操作(喚醒條件為超時(shí)),一種是等待特定條件滿足(如IO讀寫完成,可睡眠的鎖被釋放等)。

下面分別以msleep和mutex鎖為例講解內(nèi)核態(tài)睡眠:

5.1 msleep

msleep做ms級別的睡眠延遲。

  1. //kernel/time/timer.c 
  2. void msleep(unsigned int msecs) 
  3.         unsigned long timeout = msecs_to_jiffies(msecs) + 1;  //ms時(shí)間轉(zhuǎn)換為jiffies 
  4.  
  5.         while (timeout) 
  6.                 timeout = schedule_timeout_uninterruptible(timeout);  //不可中斷睡眠 

下面看下schedule_timeout_uninterruptible:

這里涉及到一個(gè)重要數(shù)據(jù)結(jié)構(gòu)process_timer

  1. struct process_timer { 
  2.         struct timer_list timer;  //定時(shí)器結(jié)構(gòu) 
  3.         struct task_struct *task; //定時(shí)器到期要喚醒的任務(wù) 
  4. }; 
  1. schedule_timeout_uninterruptible 
  2. ->  __set_current_state(TASK_UNINTERRUPTIBLE);  //設(shè)置任務(wù)狀態(tài)為不可中斷睡眠 
  3.   return schedule_timeout(timeout);  
  4.     ->expire = timeout + jiffies;   //計(jì)算到期時(shí)的jiffies值 
  5.         timer.task = current; //記錄定時(shí)器到期要喚醒的任務(wù) 為當(dāng)前任務(wù) 
  6.         timer_setup_on_stack(&timer.timer, process_timeout, 0);  //初始化定時(shí)器   超時(shí)回調(diào)為process_timeout 
  7.         __mod_timer(&timer.timer, expire, MOD_TIMER_NOTPENDING); //添加定時(shí)器 
  8.         schedule();  //主動(dòng)調(diào)度 

再看下超時(shí)回調(diào)為process_timeout:

  1. process_timeout 
  2.  ->struct process_timer *timeout = from_timer(timeout, t, timer); //通過定時(shí)器結(jié)構(gòu)獲得process_timer 
  3.     wake_up_process(timeout->task); //喚醒其管理的任務(wù) 

可以看到,msleep實(shí)現(xiàn)睡眠也是通過定時(shí)器,首先設(shè)置當(dāng)前任務(wù)狀態(tài)為不可中斷睡眠,然后設(shè)置定時(shí)器超時(shí)時(shí)間為傳遞的ms級延遲轉(zhuǎn)換的jiffies,超時(shí)回調(diào)為process_timeout,然后將定時(shí)器添加到系統(tǒng)中,最后調(diào)用schedule發(fā)起主動(dòng)調(diào)度,當(dāng)定時(shí)器超時(shí)的時(shí)候調(diào)用process_timeout來喚醒睡眠的任務(wù)。

5.2 mutex鎖

mutex鎖是可睡眠鎖的一種,當(dāng)申請mutex鎖時(shí)發(fā)現(xiàn)其他內(nèi)核路徑已經(jīng)持有這把鎖,當(dāng)前任務(wù)就會(huì)睡眠等待在這把鎖上。

下面我們來看他的實(shí)現(xiàn),主要看睡眠的部分:

  1. kernel/locking/mutex.c 
  2.  
  3. mutex_lock 
  4. ->__mutex_lock_slowpath 
  5.     ->__mutex_lock(lock, TASK_UNINTERRUPTIBLE, 0, NULL, _RET_IP_)  //睡眠的狀態(tài)為不可中斷睡眠 
  6.         ->__mutex_lock_common 
  7.             -> 
  8.             ... 
  9.             waiter.task = current;  //記錄需要喚醒的任務(wù)為當(dāng)前任務(wù) 
  10.             set_current_state(state);  //設(shè)置睡眠狀態(tài) 
  11.             for (;;) { 
  12.                  
  13.                      if (__mutex_trylock(lock))  //嘗試獲得鎖 
  14.                          goto acquired; 
  15.  
  16.                     schedule_preempt_disabled();  
  17.                         ->schedule();  //主動(dòng)調(diào)度 
  18.  
  19.             } 
  20.        acquired: 
  21.             __set_current_state(TASK_RUNNING);//設(shè)置狀態(tài)為可運(yùn)行狀態(tài) 

可以看到mutex鎖實(shí)現(xiàn)睡眠套路和之前是一樣的:申請mutex鎖的時(shí)候,如果其他內(nèi)核路徑已經(jīng)持有這把鎖,首先通過mutex鎖的相關(guān)結(jié)構(gòu)來記錄下當(dāng)前任務(wù),然后設(shè)置任務(wù)狀態(tài)為不可中斷睡眠,接著在一個(gè)for循環(huán)中調(diào)用schedule_preempt_disabled發(fā)生主動(dòng)調(diào)度,于是當(dāng)前任務(wù)就睡眠在這把鎖上。當(dāng)其他內(nèi)核路徑釋放了這把鎖,就會(huì)喚醒等待在這把鎖上的任務(wù),當(dāng)前任務(wù)就獲得了這把鎖,然后進(jìn)入鎖的臨界區(qū),喚醒操作就完成了(關(guān)于喚醒的技術(shù)細(xì)節(jié),后面的喚醒專題會(huì)詳細(xì)講解)。

6.總結(jié)

進(jìn)程睡眠按照應(yīng)用場景可以分為:延遲睡眠和等待某些特定條件而睡眠,實(shí)際上都可以歸于等待某些特定條件而睡眠,因?yàn)檠舆t特定時(shí)間也可以作為特定條件。進(jìn)程睡眠按照進(jìn)程所處的特權(quán)級別可以分為:用戶態(tài)進(jìn)程睡眠和內(nèi)核態(tài)進(jìn)程睡眠,用戶態(tài)進(jìn)程睡眠需要進(jìn)程通過系統(tǒng)調(diào)用陷入內(nèi)核來發(fā)起睡眠請求。對于進(jìn)程睡眠,內(nèi)核主要需要做三大步操作:1.設(shè)置任務(wù)狀態(tài)為睡眠狀態(tài) 2.記錄睡眠的任務(wù) 3.發(fā)起主動(dòng)調(diào)度。這三大步操作都是非常有必要,第一步設(shè)置睡眠狀態(tài)為后面調(diào)用主調(diào)度器做必要的標(biāo)識準(zhǔn)備;第二步記錄下睡眠的任務(wù)是為了以后喚醒任務(wù)來準(zhǔn)備的;第三步是睡眠的主體部分,這里會(huì)將睡眠的任務(wù)從運(yùn)行隊(duì)列中踢出,選擇下一個(gè)任務(wù)運(yùn)行。

 

責(zé)任編輯:武曉燕 來源: Linux內(nèi)核遠(yuǎn)航者
相關(guān)推薦

2021-07-20 08:02:41

Linux進(jìn)程睡眠

2021-12-09 08:09:31

Linux內(nèi)核臟頁

2021-05-19 07:56:26

Linux內(nèi)核搶占

2022-11-09 08:12:07

2021-07-05 06:51:45

Linux內(nèi)核調(diào)度器

2020-09-28 08:44:17

Linux內(nèi)核

2017-01-12 19:34:58

2021-02-17 11:25:33

前端JavaScriptthis

2021-07-02 06:54:44

Linux內(nèi)核主調(diào)度器

2023-02-10 08:11:43

Linux系統(tǒng)調(diào)用

2018-12-27 12:34:42

HadoopHDFS分布式系統(tǒng)

2014-12-04 14:01:54

openstacknetworkneutron

2019-03-18 09:50:44

Nginx架構(gòu)服務(wù)器

2022-09-05 22:22:00

Stream操作對象

2015-09-17 10:51:35

修改hostnameLinux

2021-08-31 10:32:11

LinuxPage Cache命令

2013-06-20 10:25:56

2010-06-01 15:25:27

JavaCLASSPATH

2016-12-08 15:36:59

HashMap數(shù)據(jù)結(jié)構(gòu)hash函數(shù)

2020-07-21 08:26:08

SpringSecurity過濾器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號