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

無棧協(xié)程:用戶態(tài)的Linux進程調度

系統(tǒng) Linux
pthread庫對線程函數(shù)的定義是void* (*run)(void*),它是一個參數(shù)和返回值都是void*的函數(shù)指針:這么定義的線程函數(shù),可以給它傳遞任何類型的參數(shù),也可以從它獲取任何類型的返回值。

?協(xié)程(coroutine),是為了把epoll異步事件變成同步的一種編程模式。

它的出現(xiàn)也就近幾年的事,是隨著go語言而提出的一種編程模式。

因為異步事件編程的可讀性比較差,然后就有了協(xié)程。

協(xié)程,也被稱為用戶態(tài)的進程。

協(xié)程的調度,跟Linux內(nèi)核對進程的調度是類似的。

1,不管是協(xié)程、進程、線程,它們都有一個要運行的函數(shù),以及相關的上下文。

函數(shù)是它們要運行的代碼,上下文是它們的運行狀態(tài)。

pthread庫對線程函數(shù)的定義是void* (*run)(void*),它是一個參數(shù)和返回值都是void*的函數(shù)指針:

這么定義的線程函數(shù),可以給它傳遞任何類型的參數(shù),也可以從它獲取任何類型的返回值。

這個函數(shù),就是線程要運行的函數(shù)。

如果是進程的話,main()函數(shù)就是它要運行的進程函數(shù)。

任何不使用fork()系統(tǒng)調用的進程,都是從main()函數(shù)開始運行的。

fork()系統(tǒng)調用之后的(父)子進程,會運行fork()返回之后的代碼,例如:

pid_t cpid = fork();
if (-1 == cpid) printf("fork error\n");
else if (0 == cpid) { // 子進程的代碼 }
else { // 父進程接下來的代碼}

協(xié)程也跟進程、線程類似,也有一個要運行的函數(shù)。

另外,無論進程、線程、協(xié)程都有一個運行的狀態(tài)上下文:

這個上下文里最重要的數(shù)據(jù),就是棧!?

Linux內(nèi)核的進程的內(nèi)存布局

函數(shù)的局部變量是分配在棧上的,函數(shù)調用的返回地址也是在棧上的,各種寄存器也是保存在棧上的。

對于一個正在運行的函數(shù)來說,棧必須是獨立的,不能與其他函數(shù)共享:因為運行著的函數(shù)會隨時修改棧上的數(shù)據(jù)。

不管是線程、進程、協(xié)程,都是這樣。

同一個進程內(nèi)的不同線程之間雖然會共享全局變量和堆內(nèi)存,但棧是不能共享的。

在Linux上,線程和進程除了共享全局變量和堆之外,基本上是一回事。

在Linux內(nèi)核里,它們都用上圖的數(shù)據(jù)結構描述:

1)最早是4096字節(jié)(1個內(nèi)存頁),后來擴展到8k字節(jié)(2個頁)。

2)這8k內(nèi)存的低地址是進程的描述結構,也就是main()函數(shù)運行時需要的信息。

這8k內(nèi)存的高地址,是進程在內(nèi)核里運行時(例如執(zhí)行系統(tǒng)調用時)的(內(nèi)核)棧。

這兩部分加起來,就是進程的上下文。

所以,在給Linux內(nèi)核寫模塊時,代碼里不能使用很大的局部變量,以免把進程的描述結構給覆蓋了!

char buf[4096];

這樣的代碼是不能寫在內(nèi)核里的,因為局部變量的內(nèi)存是分配在棧上的,而內(nèi)核給每個進程配備的棧都很?。?k)。

這一個buf數(shù)組就占了4k,那函數(shù)調用稍微復雜一點,就可能把低地址的進程結構給覆蓋了。

Linux內(nèi)核在調度進程的時候,就是不斷地切換上圖的數(shù)據(jù)結構,從而讓多個進程可以交替運行。

因為調度間隔遠小于人眼能察覺的時間間隔,所以即使在單核CPU上,在人看來也是多進程同時運行的。

2,協(xié)程的實現(xiàn)

多個協(xié)程要想在用戶態(tài)交替運行,也必須為每個協(xié)程配備不同的棧。

多個協(xié)程都隸屬于同一個進程,而進程棧的位置是被操作系統(tǒng)提前分配好了的。

所以,為每個協(xié)程配備棧的時候,每個棧的內(nèi)存范圍必須在進程棧的范圍內(nèi)。

有棧協(xié)程的內(nèi)存布局

如上圖:

你說要在“進程”的棧上給協(xié)程提前開多大的空間?

每個協(xié)程的棧又要預留多大?

預留小了,協(xié)程函數(shù)的局部變量把協(xié)程的描述結構覆蓋了的事,也會發(fā)生的。

預留大了,同一個進程所能支持的總協(xié)程數(shù)就會減少。

而且,程序員的用戶態(tài)代碼一般都比內(nèi)核代碼更粗放。

寫個用戶態(tài)代碼,還不讓我這么開緩沖區(qū) char buf[1024*1024],能行嗎??

沒有哪個程序員愿意,寫個用戶代碼還像寫內(nèi)核驅動一樣戰(zhàn)戰(zhàn)兢兢的。

所以,有棧協(xié)程的劣勢非常明顯!

1)首先,每個進程支持的協(xié)程個數(shù)是有限的,而不是無限的。

大多數(shù)情況下,雖然用戶代碼要開的協(xié)程個數(shù)也不至于突破上限,但畢竟它是個有限集,不是個可數(shù)集。

這對用戶代碼的限制還是比較大的。

有這么個限制,在創(chuàng)建協(xié)程的時候就要每次都檢查是否成功。

代碼就是這樣的:

int ret = coroutine_create();
if (ret < 0) {
printf("error\n");
return -1;
}

而不是這樣的:

coroutine_create();

否則代碼就不完善,因為沒有處理異常情況。

2)萬一協(xié)程函數(shù)里有復雜的遞歸,協(xié)程的棧溢出了,那么就可能覆蓋多個協(xié)程的數(shù)據(jù),導致程序掛了。

可以預見,這種掛的位置幾乎肯定不是第一現(xiàn)場!

這種BUG查起來,還是非常麻煩的。

不掛在第一現(xiàn)場的內(nèi)存BUG,都是C語言里很難查的BUG,它很大可能是隨機的?

然后,就有了無棧協(xié)程。

3,無棧協(xié)程

無棧協(xié)程的實現(xiàn)也很簡單,只要在切換協(xié)程之前,把當前協(xié)程的棧數(shù)據(jù)保存到堆上就可以了。

每個協(xié)程的上下文都是用malloc()申請的堆內(nèi)存,在上下文里預留一個空間,在切換協(xié)程時把(當前協(xié)程的)棧數(shù)據(jù)保存到這個預留空間里。

當協(xié)程再次被調度運行時,把上次的棧數(shù)據(jù)從(協(xié)程的)上下文里復制到進程棧上,協(xié)程就可以再次運行了。

無棧協(xié)程的內(nèi)存布局

如上圖,協(xié)程0掛起,協(xié)程1被調度運行:

1)先把進程棧上的數(shù)據(jù)復制到協(xié)程0的上下文里。

這時進程棧上的數(shù)據(jù),全是協(xié)程0的棧數(shù)據(jù)。

協(xié)程的上下文是malloc()申請的堆內(nèi)存,如果棧數(shù)據(jù)太大的話,是可以用realloc()再次分配更大的內(nèi)存的。

這就打破了協(xié)程棧的大小固定的缺陷。

每個協(xié)程可以使用的棧大小,只受制于進程的棧的大小。

2)當協(xié)程的棧不再受到限制之后,可以創(chuàng)建的協(xié)程數(shù)量也只受制于進程的堆的大小。

只有整個進程的堆內(nèi)存被耗盡之后,協(xié)程的創(chuàng)建和運行才會沒法進行。

我在scf編譯器框架里附帶的那個協(xié)程的實現(xiàn),就是無棧協(xié)程?

它在scf/coroutine目錄。

2021年的5月份我就想到了這些問題,并且給了解決的代碼,在github和gitee的scf代碼都有。

2022年以來,我沒往github上更新代碼,目前gitee上的scf是最新的。

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2024-12-03 15:15:22

2020-11-29 17:03:08

進程線程協(xié)程

2022-04-19 20:39:03

協(xié)程多進程

2021-09-16 09:59:13

PythonJavaScript代碼

2024-09-25 08:28:45

2023-10-12 09:46:00

并發(fā)模型線程

2023-10-26 11:39:54

Linux系統(tǒng)CPU

2023-11-17 11:36:59

協(xié)程纖程操作系統(tǒng)

2020-04-07 11:10:30

Python數(shù)據(jù)線程

2020-08-04 10:56:09

進程線程協(xié)程

2009-09-16 08:40:53

linux進程調度linuxlinux操作系統(tǒng)

2022-03-25 12:31:49

Linux根文件內(nèi)核

2023-04-26 01:12:53

進程線程語言

2023-11-01 11:27:10

Linux協(xié)程

2024-06-27 07:56:49

2021-08-31 07:54:24

TCPIP協(xié)議

2024-02-05 09:06:25

Python協(xié)程Asyncio庫

2023-03-29 08:18:16

Go調試工具

2025-02-08 09:13:40

2023-11-24 12:05:47

ucontextLinux
點贊
收藏

51CTO技術棧公眾號