成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

無棧協程:用戶態的Linux進程調度

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

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

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

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

協程,也被稱為用戶態的進程。

協程的調度,跟Linux內核對進程的調度是類似的。

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

函數是它們要運行的代碼,上下文是它們的運行狀態。

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

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

這個函數,就是線程要運行的函數。

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

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

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

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

協程也跟進程、線程類似,也有一個要運行的函數。

另外,無論進程、線程、協程都有一個運行的狀態上下文:

這個上下文里最重要的數據,就是棧!?

Linux內核的進程的內存布局

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

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

不管是線程、進程、協程,都是這樣。

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

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

在Linux內核里,它們都用上圖的數據結構描述:

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

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

這8k內存的高地址,是進程在內核里運行時(例如執行系統調用時)的(內核)棧。

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

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

char buf[4096];

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

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

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

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

2,協程的實現

多個協程要想在用戶態交替運行,也必須為每個協程配備不同的棧。

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

所以,為每個協程配備棧的時候,每個棧的內存范圍必須在進程棧的范圍內。

有棧協程的內存布局

如上圖:

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

每個協程的棧又要預留多大?

預留小了,協程函數的局部變量把協程的描述結構覆蓋了的事,也會發生的。

預留大了,同一個進程所能支持的總協程數就會減少。

而且,程序員的用戶態代碼一般都比內核代碼更粗放。

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

沒有哪個程序員愿意,寫個用戶代碼還像寫內核驅動一樣戰戰兢兢的。

所以,有棧協程的劣勢非常明顯!

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

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

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

有這么個限制,在創建協程的時候就要每次都檢查是否成功。

代碼就是這樣的:

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

而不是這樣的:

coroutine_create();

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

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

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

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

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

然后,就有了無棧協程。

3,無棧協程

無棧協程的實現也很簡單,只要在切換協程之前,把當前協程的棧數據保存到堆上就可以了。

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

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

無棧協程的內存布局

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

1)先把進程棧上的數據復制到協程0的上下文里。

這時進程棧上的數據,全是協程0的棧數據。

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

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

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

2)當協程的棧不再受到限制之后,可以創建的協程數量也只受制于進程的堆的大小。

只有整個進程的堆內存被耗盡之后,協程的創建和運行才會沒法進行。

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

它在scf/coroutine目錄。

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

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

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

2024-12-03 15:15:22

2020-11-29 17:03:08

進程線程協程

2022-04-19 20:39:03

協程多進程

2021-09-16 09:59:13

PythonJavaScript代碼

2023-10-12 09:46:00

并發模型線程

2024-09-25 08:28:45

2023-10-26 11:39:54

Linux系統CPU

2023-11-17 11:36:59

協程纖程操作系統

2020-04-07 11:10:30

Python數據線程

2020-08-04 10:56:09

進程線程協程

2022-03-25 12:31:49

Linux根文件內核

2009-09-16 08:40:53

linux進程調度linuxlinux操作系統

2023-04-26 01:12:53

進程線程語言

2024-02-05 09:06:25

Python協程Asyncio庫

2021-08-31 07:54:24

TCPIP協議

2024-06-27 07:56:49

2023-11-01 11:27:10

Linux協程

2021-06-17 07:55:34

線程進程COW

2023-03-29 08:18:16

Go調試工具

2023-10-24 19:37:34

協程Java
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: www.久| 成人精品免费视频 | 国产精品高清在线 | 欧洲视频一区二区 | 91av视频 | 国产一区二区三区 | 成人在线欧美 | 国产99久久精品一区二区300 | 国产91丝袜在线熟 | 精品久久九九 | 黄色片免费看视频 | 国产精彩视频在线观看 | 色狠狠一区 | 午夜av电影| 亚洲天堂av网 | 日韩在线播放第一页 | 精品国产一区二区三区免费 | 一区二区高清 | 亚洲欧美中文日韩在线v日本 | 久久久久国色av免费观看性色 | 99精品视频一区二区三区 | 黄色片在线| 久久成人18免费网站 | 狠狠做深爱婷婷综合一区 | 午夜爱爱毛片xxxx视频免费看 | 国外成人免费视频 | www成人免费视频 | 中文字幕亚洲欧美 | 成年人网站在线观看视频 | 看片网站在线 | 久久综合久久综合久久综合 | 韩日一区二区 | 欧美日韩国产一区二区 | 亚洲成人精品 | 国产精品一区二区在线 | 久久激情视频 | 看真人视频一级毛片 | 超碰免费在线观看 | 国产视频精品区 | 久久精品国产清自在天天线 | 污书屋|