真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何理解Linux內(nèi)核信號量

本篇文章給大家分享的是有關(guān)如何理解Linux內(nèi)核信號量,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

在綏寧等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作 網(wǎng)站設(shè)計(jì)制作定制網(wǎng)站制作,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站建設(shè),營銷型網(wǎng)站,成都外貿(mào)網(wǎng)站建設(shè)公司,綏寧網(wǎng)站建設(shè)費(fèi)用合理。

概念

Linux內(nèi)核的信號量在概念和原理上和用戶態(tài)的System V的IPC機(jī)制信號量是相同的,不過他絕不可能在內(nèi)核之外使用,因此他和System  V的IPC機(jī)制信號量毫不相干。

如果有一個(gè)任務(wù)想要獲得已經(jīng)被占用的信號量時(shí),信號量會將其放入一個(gè)等待隊(duì)列(它不是站在外面癡癡地等待而是將自己的名字寫在任務(wù)隊(duì)列中)然后讓其睡眠。

當(dāng)持有信號量的進(jìn)程將信號釋放后,處于等待隊(duì)列中的一個(gè)任務(wù)將被喚醒(因?yàn)殛?duì)列中可能不止一個(gè)任務(wù)),并讓其獲得信號量。這一點(diǎn)與自旋鎖不同,處理器可以去執(zhí)行其它代碼。

應(yīng)用場景

由于爭用信號量的進(jìn)程在等待鎖重新變?yōu)榭捎脮r(shí)會睡眠,所以信號量適用于鎖會被長時(shí)間持有的情況;相反,鎖被短時(shí)間持有時(shí),使用信號量就不太適宜了,因?yàn)樗?、維護(hù)等待隊(duì)列以及喚醒所花費(fèi)的開銷可能比鎖占用的全部時(shí)間表還要長。

舉2個(gè)生活中的例子:

我們坐火車從南京到新疆需要2天的時(shí)間,這個(gè)'任務(wù)'特別的耗時(shí),只能坐在車上等著車到站,但是我們沒有必要一直睜著眼睛等,理想的情況就是我們上車就直接睡覺,醒來就到站(看過《異形》的讀者會深有體會),這樣從人(用戶)的角度來說,體驗(yàn)是最好的,對比于進(jìn)程,程序在等待一個(gè)耗時(shí)事件的時(shí)候,沒有必須要一直占用CPU,可以暫停當(dāng)前任務(wù)使其進(jìn)入休眠狀態(tài),當(dāng)?shù)却氖录l(fā)生之后再由其他任務(wù)喚醒,類似于這種場景采用信號量比較合適。

我們有時(shí)候會等待電梯、洗手間,這種場景需要等待的時(shí)間并不是很多,如果我們還要找個(gè)地方睡一覺,然后等電梯到了或者洗手間可以用了再醒來,那很顯然這也沒有必要,我們只需要排好隊(duì),刷一刷抖音就可以了,對比于計(jì)算機(jī)程序,比如驅(qū)動(dòng)在進(jìn)入中斷例程,在等待某個(gè)寄存器被置位,這種場景需要等待的時(shí)間往往很短暫,系統(tǒng)開銷甚至遠(yuǎn)小于進(jìn)入休眠的開銷,所以這種場景采用自旋鎖比較合適。

關(guān)于信號量和自旋鎖,以及死鎖問題,我們后面會再詳細(xì)討論。

使用方法

一個(gè)任務(wù)要想訪問共享資源,首先必須得到信號量,獲取信號量的操作將把信號量的值減1,若當(dāng)前信號量的值為負(fù)數(shù),表明無法獲得信號量,該任務(wù)必須掛起在  該信號量的等待隊(duì)列等待該信號量可用;若當(dāng)前信號量的值為非負(fù)數(shù),表示能獲得信號量,因而能即時(shí)訪問被該信號量保護(hù)的共享資源。

當(dāng)任務(wù)訪問完被信號量保護(hù)的共享資源后,必須釋放信號量,釋放信號量通過把信號量的值加1實(shí)現(xiàn),如果信號量的值為非正數(shù),表明有任務(wù)等待當(dāng)前信號量,因此他也喚醒所有等待該信號量的任務(wù)。

內(nèi)核信號量的構(gòu)成

內(nèi)核信號量類似于自旋鎖,因?yàn)楫?dāng)鎖關(guān)閉著時(shí),它不允許內(nèi)核控制路徑繼續(xù)進(jìn)行。然而,當(dāng)內(nèi)核控制路徑試圖獲取內(nèi)核信號量鎖保護(hù)的忙資源時(shí),相應(yīng)的進(jìn)程就被掛起。只有在資源被釋放時(shí),進(jìn)程才再次變?yōu)榭蛇\(yùn)行。

只有可以睡眠的函數(shù)才能獲取內(nèi)核信號量;中斷處理程序和可延遲函數(shù)都不能使用內(nèi)核信號量。

內(nèi)核信號量是struct semaphore類型的對象,在內(nèi)核源碼中位于include\linux\semaphore.h文件

struct semaphore{     raw_spinlock_t        lock;     unsigned int        count;     struct list_head    wait_list; }
成員描述
lock在2.6.33之后的版本,內(nèi)核加入了raw_spin_lock系列,使用方法和spin_lock系列一模一樣,只是參數(shù)spinlock_t變?yōu)榱藃aw_spinlock_t
count相當(dāng)于信號量的值,大于0,資源空閑;等于0,資源忙,但沒有進(jìn)程等待這個(gè)保護(hù)的資源;小于0,資源不可用,并至少有一個(gè)進(jìn)程等待資源
wait_list內(nèi)核鏈表,當(dāng)前獲得信號量的任務(wù)會與該成員一起注冊到等待的鏈表中

信號量的API

初始化

DECLARE_MUTEX(name)

該宏聲明一個(gè)信號量name并初始化他的值為1,即聲明一個(gè)互斥鎖。

DECLARE_MUTEX_LOCKED(name)

該宏聲明一個(gè)互斥鎖name,但把他的初始值設(shè)置為0,即鎖在創(chuàng)建時(shí)就處在已鎖狀態(tài)。因此對于這種鎖,一般是先釋放后獲得。

void sema_init (struct semaphore *sem, int val);

該函用于數(shù)初始化設(shè)置信號量的初值,他設(shè)置信號量sem的值為val。

注意:

val設(shè)置為1說明只有一個(gè)持有者,這種信號量叫二值信號量或者叫互斥信號量。

我們還允許信號量可以有多個(gè)持有者,這種信號量叫計(jì)數(shù)信號量,在初始化時(shí)要說明最多允許有多少個(gè)持有者也可以把信號量中的val初始化為任意的正數(shù)值n,在這種情況下,最多有n個(gè)進(jìn)程可以并發(fā)地訪問這個(gè)資源。

void init_MUTEX (struct semaphore *sem);

該函數(shù)用于初始化一個(gè)互斥鎖,即他把信號量sem的值設(shè)置為1。

void init_MUTEX_LOCKED (struct semaphore *sem);

該函數(shù)也用于初始化一個(gè)互斥鎖,但他把信號量sem的值設(shè)置為0,即一開始就處在已鎖狀態(tài)。

PV操作

獲取信號量(P)

void down(struct semaphore * sem);

該函數(shù)用于獲得信號量sem,他會導(dǎo)致調(diào)用該函數(shù)的進(jìn)程睡眠,因此不能在中斷上下文(包括IRQ上下文和softirq上下文)使用該函數(shù)。該函數(shù)將把sem的值減1,如果信號量sem的值非負(fù),就直接返回,否則調(diào)用者將被掛起,直到別的任務(wù)釋放該信號量才能繼續(xù)運(yùn)行。

int down_interruptible(struct semaphore * sem);

該函數(shù)功能和down類似,不同之處為,down不會被信號(signal)打斷,但down_interruptible能被信號打斷,因此該函數(shù)有返回值來區(qū)分是正常返回還是被信號中斷,如果返回0,表示獲得信號量正常返回,如果被信號打斷,返回-EINTR。

int down_trylock(struct semaphore * sem);

該函數(shù)試著獲得信號量sem,如果能夠即時(shí)獲得,他就獲得該信號量并返回0,否則,表示不能獲得信號量sem,返回值為非0值。因此,他不會導(dǎo)致調(diào)用者睡眠,能在中斷上下文使用。

int down_killable(struct semaphore *sem); int down_timeout(struct semaphore *sem, long jiffies); int down_timeout_interruptible(struct semaphore *sem, long jiffies);

釋放內(nèi)核信號量(V)

void up(struct semaphore * sem);

該函數(shù)釋放信號量sem,即把sem的值加1,如果sem的值為非正數(shù),表明有任務(wù)等待該信號量,因此喚醒這些等待者。

補(bǔ)充

int down_interruptible(struct semaphore *sem)

這個(gè)函數(shù)的功能就是獲得信號量,如果得不到信號量就睡眠,此時(shí)沒有信號打斷,那么進(jìn)入睡眠。但是在睡眠過程中可能被信號打斷,打斷之后返回-EINTR,主要用來進(jìn)程間的互斥同步。

下面是該函數(shù)的注釋:

/** * down_interruptible - acquire the semaphore unless interrupted * @sem: the semaphore to be acquired * * Attempts to acquire the semaphore. If no more tasks are allowed to * acquire the semaphore, calling this function will put the task to sleep. * If the sleep is interrupted by a signal, this function will return -EINTR. * If the semaphore is successfully acquired, this function returns 0. */

一個(gè)進(jìn)程在調(diào)用down_interruptible()之后,如果sem<0,那么就進(jìn)入到可中斷的睡眠狀態(tài)并調(diào)度其它進(jìn)程運(yùn)行,  但是一旦該進(jìn)程收到信號,那么就會從down_interruptible函數(shù)中返回。并標(biāo)記錯(cuò)誤號為:-EINTR。

一個(gè)形象的比喻:傳入的信號量為1好比天亮,如果當(dāng)前信號量為0,進(jìn)程睡眠,直到(信號量為1)天亮才醒,但是可能中途有個(gè)鬧鈴(信號)把你鬧醒。

又如:小強(qiáng)下午放學(xué)回家,回家了就要開始吃飯嘛,這時(shí)就會有兩種情況:情況一:飯做好了,可以開始吃;情況二:當(dāng)他到廚房去的時(shí)候發(fā)現(xiàn)媽媽還在做,  媽媽就對他說:“你先去睡會,待會做好了叫你?!?小強(qiáng)就答應(yīng)去睡會,不過又說了一句:“睡的這段時(shí)間要是小紅來找我玩,你可以叫醒我?!? 小強(qiáng)就是down_interruptible,想吃飯就是獲取信號量,睡覺對應(yīng)這里的休眠,而小紅來找我玩就是中斷休眠。

使用可被中斷的信號量版本的意思是,萬一出現(xiàn)了semaphore的死鎖,還有機(jī)會用ctrl+c發(fā)出軟中斷,讓等待這個(gè)內(nèi)核驅(qū)動(dòng)返回的用戶態(tài)進(jìn)程退出。而不是把整個(gè)系統(tǒng)都鎖住了。在休眠時(shí),能被中斷信號終止,這個(gè)進(jìn)程是可以接受中斷信號的!

比如你在命令行中輸入# sleep 10000,按下ctrl +  c,就給上面的進(jìn)程發(fā)送了進(jìn)程終止信號。信號發(fā)送給用戶空間,然后通過系統(tǒng)調(diào)用,會把這個(gè)信號傳給遞給驅(qū)動(dòng)。信號只能發(fā)送給用戶空間,無權(quán)直接發(fā)送給內(nèi)核的,那1G的內(nèi)核空間,我們是無法直接去操作的。

內(nèi)核信號量的使用例程

場景1

在驅(qū)動(dòng)程序中,當(dāng)多個(gè)線程同時(shí)訪問相同的資源時(shí)(驅(qū)動(dòng)中的全局變量時(shí)一種典型的共享資源),可能會引發(fā)“競態(tài)“,因此我們必須對共享資源進(jìn)行并發(fā)控制。Linux內(nèi)核中解決并發(fā)控制的最常用方法是自旋鎖與信號量(絕大多數(shù)時(shí)候作為互斥鎖使用)。

如何理解Linux內(nèi)核信號量

在這里插入圖片描述

場景2

有時(shí)候我們希望設(shè)備只能被一個(gè)進(jìn)程打開,當(dāng)設(shè)備被占用的時(shí)候,其他設(shè)備必須進(jìn)入休眠。

信號處理示意圖

如何理解Linux內(nèi)核信號量

在這里插入圖片描述

如上圖:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 進(jìn)程A首先通過open()打開設(shè)備文件,調(diào)用到內(nèi)核的hello_open(),并調(diào)用down_interruptible(),因?yàn)榇藭r(shí)信號量沒有被占用,所以進(jìn)程A可以獲得信號量;

  3. 進(jìn)程A獲得信號量之后繼續(xù)處理原有任務(wù),此時(shí)進(jìn)程B也要通過open()打開設(shè)備文件,同樣調(diào)用內(nèi)核函數(shù)hello_open(),但此時(shí)信號量獲取不到,于是進(jìn)程B被阻塞;

  4. 進(jìn)程A任務(wù)執(zhí)行完畢,關(guān)閉設(shè)備文件,并通過up()釋放信號量,于是進(jìn)程B被喚醒,并得以繼續(xù)執(zhí)行剩下的任務(wù),

  5. 進(jìn)程B執(zhí)行完任務(wù),釋放設(shè)備文件,通過up()釋放信號量

代碼如下:

#include  #include  #include  #include  #include  #include  #include   static int major = 250; static int minor = 0; static dev_t devno; static struct cdev cdev;   static struct class *cls; static struct device *test_device;  static struct semaphore sem; static int hello_open (struct inode *inode, struct file *filep) {          if(down_interruptible(&sem))//p     {         return -ERESTARTSYS;     }       return 0; } static int hello_release (struct inode *inode, struct file *filep) {     up(&sem);//v     return 0; } static struct file_operations hello_ops = {     .open = hello_open,     .release = hello_release, }; static int hello_init(void) {     int result;     int error;         printk("hello_init \n");     result = register_chrdev( major, "hello", &hello_ops);     if(result < 0)     {         printk("register_chrdev fail \n");         return result;     }     devno = MKDEV(major,minor);     cls = class_create(THIS_MODULE,"helloclass");     if(IS_ERR(cls))     {         unregister_chrdev(major,"hello");         return result;     }     test_device = device_create(cls,NULL,devno,NULL,"test");     if(IS_ERR(test_device ))     {         class_destroy(cls);         unregister_chrdev(major,"hello");         return result;     }     sem_init(&sem,1);     return 0; } static void hello_exit(void) {     printk("hello_exit \n");     device_destroy(cls,devno);         class_destroy(cls);     unregister_chrdev(major,"hello");     return; } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("daniel.peng");

測試程序 test.c

#include  #include  #include  #include  main() {     int fd;          printf("before open\n ");         fd = open("/dev/test",O_RDWR);  //原子變量  0     if(fd<0)     {         perror("open fail \n");         return;     }     printf("open ok ,sleep......\n ");         sleep(20);     printf("wake up from sleep!\n ");             close(fd);   //加為1 }

編譯步驟

1 make 生成 hello.ko

2 gcc test.c -o a

3 gcc test.c -o b

測試步驟

1.安裝驅(qū)動(dòng)

insmod hello.ko

2.先運(yùn)行進(jìn)程A,在運(yùn)行進(jìn)程B

如何理解Linux內(nèi)核信號量

可見進(jìn)程A成功打開設(shè)備,在進(jìn)程A  sleep期間會一直占有該字符設(shè)備,進(jìn)程B由于無法獲得信號量,進(jìn)入休閑,結(jié)合代碼可知,進(jìn)程B被阻塞在函數(shù)open()中。

3.進(jìn)程A 結(jié)束了sleep,并釋放字符設(shè)備以及信號量,進(jìn)程B被喚醒獲得信號量,并成功打開了字符設(shè)備。

如何理解Linux內(nèi)核信號量

進(jìn)程B執(zhí)行完sleep函數(shù)后退出,并釋放字符設(shè)備和信號量。

如何理解Linux內(nèi)核信號量

讀-寫信號量

跟自旋鎖一樣,信號量也有區(qū)分讀-寫信號量之分。

如果一個(gè)讀寫信號量當(dāng)前沒有被寫者擁有并且也沒有寫者等待讀者釋放信號量,那么任何讀者都可以成功獲得該讀寫信號量;否則,讀者必須被掛起直到寫者釋放該信號量。如果一個(gè)讀寫信號量當(dāng)前沒有被讀者或?qū)懻邠碛胁⑶乙矝]有寫者等待該信號量,那么一個(gè)寫者可以成功獲得該讀寫信號量,否則寫者將被掛起,直到?jīng)]有任何訪問者。因此,寫者是排他性的,獨(dú)占性的。

讀寫信號量有兩種實(shí)現(xiàn),一種是通用的,不依賴于硬件架構(gòu),因此,增加新的架構(gòu)不需要重新實(shí)現(xiàn)它,但缺點(diǎn)是性能低,獲得和釋放讀寫信號量的開銷大;另一種是架構(gòu)相關(guān)的,因此性能高,獲取和釋放讀寫信號量的開銷小,但增加新的架構(gòu)需要重新實(shí)現(xiàn)。在內(nèi)核配置時(shí),可以通過選項(xiàng)去控制使用哪一種實(shí)現(xiàn)。

讀寫信號量的相關(guān)API:

DECLARE_RWSEM(name)

該宏聲明一個(gè)讀寫信號量name并對其進(jìn)行初始化。

void init_rwsem(struct rw_semaphore *sem);

該函數(shù)對讀寫信號量sem進(jìn)行初始化。

void down_read(struct rw_semaphore *sem);

讀者調(diào)用該函數(shù)來得到讀寫信號量sem。該函數(shù)會導(dǎo)致調(diào)用者睡眠,因此只能在進(jìn)程上下文使用。

int down_read_trylock(struct rw_semaphore *sem);

該函數(shù)類似于down_read,只是它不會導(dǎo)致調(diào)用者睡眠。它盡力得到讀寫信號量sem,如果能夠立即得到,它就得到該讀寫信號量,并且返回1,否則表示不能立刻得到該信號量,返回0。因此,它也可以在中斷上下文使用。

void down_write(struct rw_semaphore *sem);

寫者使用該函數(shù)來得到讀寫信號量sem,它也會導(dǎo)致調(diào)用者睡眠,因此只能在進(jìn)程上下文使用。

int down_write_trylock(struct rw_semaphore *sem);

該函數(shù)類似于down_write,只是它不會導(dǎo)致調(diào)用者睡眠。該函數(shù)盡力得到讀寫信號量,如果能夠立刻獲得,就獲得該讀寫信號量并且返回1,否則表示無法立刻獲得,返回0。它可以在中斷上下文使用。

void up_read(struct rw_semaphore *sem);

讀者使用該函數(shù)釋放讀寫信號量sem。它與down_read或down_read_trylock配對使用。

如果down_read_trylock返回0,不需要調(diào)用up_read來釋放讀寫信號量,因?yàn)楦揪蜎]有獲得信號量。

void up_write(struct rw_semaphore *sem);

寫者調(diào)用該函數(shù)釋放信號量sem。它與down_write或down_write_trylock配對使用。如果down_write_trylock返回0,不需要調(diào)用up_write,因?yàn)榉祷?表示沒有獲得該讀寫信號量。

void downgrade_write(struct rw_semaphore *sem);

該函數(shù)用于把寫者降級為讀者,這有時(shí)是必要的。因?yàn)閷懻呤桥潘缘?,因此在寫者保持讀寫信號量期間,任何讀者或?qū)懻叨紝o法訪問該讀寫信號量保護(hù)的共享資源,對于那些當(dāng)前條件下不需要寫訪問的寫者,降級為讀者將,使得等待訪問的讀者能夠立刻訪問,從而增加了并發(fā)性,提高了效率。

讀寫信號量適于在讀多寫少的情況下使用,在linux內(nèi)核中對進(jìn)程的內(nèi)存映像描述結(jié)構(gòu)的訪問就使用了讀寫信號量進(jìn)行保護(hù)。

以上就是如何理解Linux內(nèi)核信號量,小編相信有部分知識點(diǎn)可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


文章標(biāo)題:如何理解Linux內(nèi)核信號量
文章起源:http://weahome.cn/article/jhhoco.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部