① linux 內核堆棧總大小 怎麼決定
Linux內核棧溢出(stack overflow)問題
最近一段時間在設計和開發一個Linux內核模塊,進入到最後的正確性測試與穩定性測試階段。在這個階段發現了一個非常有意思的問題,堆棧溢出(stack overflow)。Linux內核堆棧溢出之後直接導致了系統kernel Panic。由於導致stack overflow的原因是遞歸調用導致的,所以,最後通過調試串口導出的kernel panic信息很快就定位問題所在了,否則這樣的問題還真是很難調試和發現。通過這次bug,我們應該記住的是:Linux內核stack資源是有限的,而遞歸調用將大量消耗stack資源,因此在內核編程中盡量少用遞歸演算法,否則將會導致出乎意料的一些問題。依次類推,為了減少stack資源的消耗,程序的局部變數定義的不要太大,否則也將會消耗大量stack資源,從而導致內核程序的不穩定。
為了解決遞歸調用導致的問題,我將遞歸演算法改寫成了非遞歸演算法,解決了stack overflow的問題。在此介紹一下遞歸演算法改寫成非遞歸演算法的一些思想。在項目實現過程中,需要對IO請求進行按順序排隊,因此採用了效率較高並且實現簡單的快速排序演算法,該演算法是一種分治演算法,即將排序隊列進行切分,分解成一系列的小問題進行求解,針對這種問題,很容易採用遞歸的辦法進行實現,偽代碼描述如下:
/* qs_sort實現從小到大的排序 */
Struct bio qs_sort(struct bio_list *list_head, struct bio *bio_tail) {
Struct bio_list *less_list, *large_list;
Struct bio *middle_bio;
/* 遞歸調用結束點,小問題求解完畢,直接返回最後一個元素 */
If (!list_head) {
Return bio_tail;
}
/* 對隊列進行切分,選擇一個middle_bio,並且按照middle_bio將其切分成less_list隊列和large_list隊列 */
Split_list(list_head, less_list, large_list, &middle_bio);
/* 採用遞歸的方法實現大隊列的排序操作 */
Middle_bio->bi_next = qs_sort(large_list, bio_tail);
/* 採用遞歸的方法實現小隊列的排序操作 */
Return qs_sort(less_list, middle_bio);
}
② linux為什麼需要內核棧,系統調用時直接使用用戶棧不行嗎
內核棧和用戶棧區別:
intel的cpu分為四個運行級別ring0~ring3
內核創建進程,創建進程的同時創建進程式控制制塊,創建進程自己的堆棧
一個進程有兩個堆棧,用戶棧和系統棧
用戶堆棧的空間指向用戶地址空間,內核堆棧的空間指向內核地址空間。
有個CPU堆棧指針寄存器,進程運行的狀態有用戶態和內核態,當進程運行在用戶態時。CPU堆棧指針寄存器指向的是用戶堆棧地址,使用的是用戶堆棧;當進程運行在內核態時,CPU堆棧指針寄存器指向的是內核堆棧地址,使用的是內核堆棧。
堆棧切換
當系統因為系統調用(軟中斷)或硬體中斷,CPU切換到特權工作模式,進程陷入內核態,進程使用的棧也要從用戶棧轉向系統棧。
從用戶態到內核態要兩步驟,首先是將用戶堆棧地址保存到內核堆棧中,然後將CPU堆棧指針寄存器指向內核堆棧。
當由內核態轉向用戶態,步驟首先是將內核堆棧中得用戶堆棧地址恢復到CPU堆棧指針寄存器中。
內核棧和用戶棧區別
1.
棧是系統運行在內核態的時候使用的棧,用戶棧是系統運行在用戶態時候使用的棧。
當進程由於中斷進入內核態時,系統會把一些用戶態的數據信息保存到內核棧中,當返回到用戶態時,取出內核棧中得信息恢復出來,返回到程序原來執行的地方。
用戶棧就是進程在用戶空間時創建的棧,比如一般的函數調用,將會用到用戶棧。
2.
內核棧是屬於操作系統空間的一塊固定區域,可以用於保存中斷現場、保存操作系統子程序間相互調用的參數、返回值等。
用戶棧是屬於用戶進程空間的一塊區域,用戶保存用戶進程子程序間的相互調用的參數、返回值等。
3.
每個Windows 都有4g的進程空間,系統棧使用進程空間的地段部分,用戶棧是高端部分如果用戶要直接訪問系統棧部分,需要有特殊的方式。
為何要設置兩個不同的棧?
共享原因:
內核的代碼和數據是為所有的進程共享的,如果不為每一個進程設置對應的內核棧,那麼就不能實現不同的進程執行不同的代碼。
安全原因:
如果只有一個棧,那麼用戶就可以修改棧內容來突破內核安全保護。
③ linux進程為什麼有用戶棧和內核棧,
linux進程在運行時要調用到系統的功能,這些是靠進程調用系統調用,內核介面實現的,他們具有較高的許可權,所以要分開兩個棧。
④ linux中斷處理程序使用的堆棧是內核的堆棧嗎,在哪裡
當然是,進程生成時,會被分配一個task_struct 結構(常說的進程式控制制塊),2.4內核中版,在task_struct 結構體上面的7KB空間就權是。加上task_struct結構本身(1KB),進程內核棧共8KB(兩個頁面 ),不會動態擴展,所以非常有限(你會見到內核代碼用"大塊"內存都會kmalloc申請的,就是這個原因)。2.6內核的沒注意,不知一樣否。詳見:《Linux內核源代碼情景分析(上)》267頁。
為什麼會在內核的原因是CPU的保護機制,中斷處理需要更高的許可權(可能執行硬體相關的操作),故要在0級,不會在用戶區的。
⑤ 進程內核棧,用戶棧及 Linux 進程棧和線程棧的區別
內核棧、用戶棧
32位Linux系統上,進程的地址空間為4G,包括1G的內核地址空間-----內核棧,和3G的用戶地址空間-----用戶棧。
內核棧,是各個進程在剛開始建立的時候通過內存映射共享的,但是每個進程擁有獨立的4G的虛擬內存空間從這一點看又是獨立的,互不幹擾的(只是剛開始大家都是映射的同一份內存拷貝)
用戶棧就是大家所熟悉的內存四區,包括:代碼區、全局數據區、堆區、棧區
用戶棧中的堆區、棧區即為進程堆、進程棧
進程堆、進程棧與線程棧
1.線程棧的空間開辟在所屬進程的堆區與共享內存區之間,線程與其所屬的進程共享進程的用戶空間,所以線程棧之間可以互訪。線程棧的起始地址和大小存放在pthread_attr_t 中,棧的大小並不是用來判斷棧是否越界,而是用來初始化避免棧溢出的緩沖區的大小(或者說安全間隙的大小)
2.進程初始化的時候,系統會在進程的地址空間中創建一個堆,叫進程默認堆。進程中所有的線程共用這一個堆。當然,可以增加1個或幾個堆,給不同的線程共同使用或單獨使用。----一個進程可以多個堆
3、創建線程的時候,系統會在進程的地址空間中分配1塊內存給線程棧,通常是1MB或4MB或8MB。線程棧是獨立的,但是還是可以互訪,因為線程共享內存空間
4.堆的分配:從操作系統角度來看,進程分配內存有兩種方式,分別由兩個系統調用完成:brk()和mmap(),glibc中malloc封裝了
5.線程棧位置-內存分布測試代碼
[cpp] view plain
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>
#include <sys/syscall.h>
void* func(void* arg)
{
long int tid = (long int)syscall(SYS_gettid);
printf("The ID of this thread is: %ld\n", tid );
static int a=10;
int b=11;
int* c=(int *)malloc(sizeof(int));
printf("in thread id:%u a:%p b:%p c:%p\n",tid,&a,&b,c);
printf("leave thread id:%ld\n",tid);
sleep(20);
free((void *)c);
}
void main()
{
pthread_t th1,th2;
printf("pid=%u\n",(int)getpid());
func(NULL);
int ret=pthread_create(&th1,NULL,func,NULL);
if(ret!=0)
{
printf("thread1[%d]:%s\n",th1,strerror(errno));
}
ret=pthread_create(&th2,NULL,func,NULL);
if(ret!=0)
{
printf("thread2[%d]:%s\n",th2,strerror(errno));
}
pthread_join(th1,NULL);
pthread_join(th2,NULL);
}
輸出:
[le@localhost threadStack]$ ./threadStack_main pid=16433
The ID of this thread is: 16433
in thread id:16433 a:0x60107c b:0x7fffc89ce7ac c:0x1b54010
leave thread id:16433
The ID of this thread is: 16461
The ID of this thread is: 16460
in thread id:16461 a:0x60107c b:0x7f6abb096efc c:0x7f6ab40008c0
leave thread id:16461
in thread id:16460 a:0x60107c b:0x7f6abb897efc c:0x7f6aac0008c0
leave thread id:16460
主線程調用func後
[le@localhost threadStack]$ sudo cat /proc/16433/maps
00400000-00401000 r-xp 00000000 fd:02 11666 /home/le/code/threadStack/threadStack_main
00600000-00601000 r--p 00000000 fd:02 11666 /home/le/code/threadStack/threadStack_main
00601000-00602000 rw-p 00001000 fd:02 11666 /home/le/code/threadStack/threadStack_main
01b54000-01b75000 rw-p 00000000 00:00 0 [heap]
7f6abb899000-7f6abba4f000 r-xp 00000000 fd:00 100678959 /usr/lib64/libc-2.17.so
7f6abba4f000-7f6abbc4f000 ---p 001b6000 fd:00 100678959 /usr/lib64/libc-2.17.so
7f6abbc4f000-7f6abbc53000 r--p 001b6000 fd:00 100678959 /usr/lib64/libc-2.17.so
7f6abbc53000-7f6abbc55000 rw-p 001ba000 fd:00 100678959 /usr/lib64/libc-2.17.so
7f6abbc55000-7f6abbc5a000 rw-p 00000000 00:00 0
7f6abbc5a000-7f6abbc70000 r-xp 00000000 fd:00 105796566 /usr/lib64/libpthread-2.17.so
7f6abbc70000-7f6abbe70000 ---p 00016000 fd:00 105796566 /usr/lib64/libpthread-2.17.so
7f6abbe70000-7f6abbe71000 r--p 00016000 fd:00 105796566 /usr/lib64/libpthread-2.17.so
7f6abbe71000-7f6abbe72000 rw-p 00017000 fd:00 105796566 /usr/lib64/libpthread-2.17.so
7f6abbe72000-7f6abbe76000 rw-p 00000000 00:00 0
7f6abbe76000-7f6abbe97000 r-xp 00000000 fd:00 105796545 /usr/lib64/ld-2.17.so
7f6abc073000-7f6abc076000 rw-p 00000000 00:00 0
7f6abc095000-7f6abc097000 rw-p 00000000 00:00 0
7f6abc097000-7f6abc098000 r--p 00021000 fd:00 105796545 /usr/lib64/ld-2.17.so
7f6abc098000-7f6abc099000 rw-p 00022000 fd:00 105796545 /usr/lib64/ld-2.17.so
7f6abc099000-7f6abc09a000 rw-p 00000000 00:00 0
7fffc89b0000-7fffc89d1000 rw-p 00000000 00:00 0 [stack]
7fffc89fe000-7fffc8a00000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
兩個子線程啟動後
[le@localhost threadStack]$ sudo cat /proc/16433/maps
00400000-00401000 r-xp 00000000 fd:02 11666 /home/le/code/threadStack/threadStack_main
00600000-00601000 r--p 00000000 fd:02 11666 /home/le/code/threadStack/threadStack_main
00601000-00602000 rw-p 00001000 fd:02 11666 /home/le/code/threadStack/threadStack_main
01b54000-01b75000 rw-p 00000000 00:00 0 [heap]
7f6aac000000-7f6aac021000 rw-p 00000000 00:00 0
7f6aac021000-7f6ab0000000 ---p 00000000 00:00 0
7f6ab4000000-7f6ab4021000 rw-p 00000000 00:00 0
7f6ab4021000-7f6ab8000000 ---p 00000000 00:00 0
7f6aba897000-7f6aba898000 ---p 00000000 00:00 0
7f6aba898000-7f6abb098000 rw-p 00000000 00:00 0 [stack:16461]
7f6abb098000-7f6abb099000 ---p 00000000 00:00 0
7f6abb099000-7f6abb899000 rw-p 00000000 00:00 0 [stack:16460]
7f6abb899000-7f6abba4f000 r-xp 00000000 fd:00 100678959 /usr/lib64/libc-2.17.so
7f6abba4f000-7f6abbc4f000 ---p 001b6000 fd:00 100678959 /usr/lib64/libc-2.17.so
7f6abbc4f000-7f6abbc53000 r--p 001b6000 fd:00 100678959 /usr/lib64/libc-2.17.so
7f6abbc53000-7f6abbc55000 rw-p 001ba000 fd:00 100678959 /usr/lib64/libc-2.17.so
7f6abbc55000-7f6abbc5a000 rw-p 00000000 00:00 0
7f6abbc5a000-7f6abbc70000 r-xp 00000000 fd:00 105796566 /usr/lib64/libpthread-2.17.so
7f6abbc70000-7f6abbe70000 ---p 00016000 fd:00 105796566 /usr/lib64/libpthread-2.17.so
7f6abbe70000-7f6abbe71000 r--p 00016000 fd:00 105796566 /usr/lib64/libpthread-2.17.so
7f6abbe71000-7f6abbe72000 rw-p 00017000 fd:00 105796566 /usr/lib64/libpthread-2.17.so
7f6abbe72000-7f6abbe76000 rw-p 00000000 00:00 0
7f6abbe76000-7f6abbe97000 r-xp 00000000 fd:00 105796545 /usr/lib64/ld-2.17.so
7f6abc073000-7f6abc076000 rw-p 00000000 00:00 0
7f6abc095000-7f6abc097000 rw-p 00000000 00:00 0
7f6abc097000-7f6abc098000 r--p 00021000 fd:00 105796545 /usr/lib64/ld-2.17.so
7f6abc098000-7f6abc099000 rw-p 00022000 fd:00 105796545 /usr/lib64/ld-2.17.so
7f6abc099000-7f6abc09a000 rw-p 00000000 00:00 0
7fffc89b0000-7fffc89d1000 rw-p 00000000 00:00 0 [stack]
7fffc89fe000-7fffc8a00000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
⑥ 嵌入式Linux內核和網路協議棧的特點,和代表性產品有哪些
首先,嵌入Linux內核是可定製的內核:
1 Linux內核的配置系統
2 Linux內核的模塊機制
3 Linux內核的源代碼開放
4 經裁減的 Linux內核最小可達到 150KB以下,尤其適合嵌入式領域中資源受限的實際情況。
其次,它的性能優越:Linux 系統內核精簡、高效和穩定,能夠充分發揮硬體的功能,因此它比其他操作系統的運行效率更高。
再者,它有良好的網路支持:
1 支持 TCP/IP 協議棧
2 提供對包括十兆位、百兆位及千兆位的乙太網,還有無線網路、Tokenring(令牌環)和光纖甚至衛星的支持
3 對現在依賴於網路的嵌入式設備來說是很好的選擇。
至於網路協議的話,有很多種,就目前主流的3種網路協議是:NetBEUI、IPX/SPX及其兼容協議、TCP/IP,而其他的像Lwip、ZigBee、Sip等很多。
1 NetBEUI的前身是NetBIOS,這一協議是IBM1983年開發完成的,早期的微軟OS產品中都選擇該協議作為通信協議。它是一套用於實現僅僅在小型區域網上PC見相互通信的標准。該網路最大用戶數不能超過30個,1985年,微軟對其改進,增加了SMB(Server Message Blocks,伺服器消息塊)的組成部分,以降低網路的通信阻塞,形成了現在的NetBEUI通信協議。
特點:體積小、效率高、速度快、佔用內存少。
2 IPX/SPX及其兼容協議:它的全稱是「Internwork Packet Exchange/Sequence Packet Exchange」即網際包交換/順序包交換。
特點:體積較大、能夠連接多種網路、具有強大的路由功能,適合大型網路的使用。windows網路中無法直接使用該協議。
3 TCP/IP是國際互聯網Internet採用的協議標准,早期用於ARPANet網路,後來開放用於民間。
特點:靈活性,支持任意規模的網路,可以連接所有的計算機,具有路由功能,且TCP/IP的地址是分級的,容易確定並找到網上的用戶,提高了網路代換的利用率。
而其他的像Lwip協議棧的特點:
(1)支持多網路介面的IP轉發
(2)支持ICMP協議
(3)包括實驗性擴展的UDP
(4)包括阻塞控制,RTT估算和快速恢復和快速轉發的TCP
(5)提供專門的內部回調介面用於提高應用程序性能
(6)可選擇的Berkeley介面API
(7)在最新的版本中支持ppp
不知道這些是不是你想要的。
⑦ linux 線程間共享內核棧嗎
首先,我們知道所有線程共享主線程的虛擬地址空間(current->mm指向同一個地址),且都有自己的用戶態堆棧(共享父進程的地址空間,再在裡面分配自己的獨立棧,默認2M)。這是毫無疑問的,但還有一點我沒搞明白,內核棧是共享還是獨立的?猜測:獨立的。理由:要不然內核棧對應的thread_info中的tast_struct沒有辦法與每個線程對應起來,因為現在已經有多個task_struct了,但保存內核棧的thread_info(其實是thread_union聯合體)中只能保存一個task_struct。所以理論上分析,雖然可以共享地址空間,但每個線程還是需要一個單獨的內核棧的。看代碼:分析創建線程最終肯定會走到內核函數do_fork()中來的,所以從此函數看起。do_fork()->_process()->p_task_struct()fork.c中p_task_struct()的實現:static struct task_struct *p_task_struct(struct task_struct *orig){struct task_struct *tsk;struct thread_info *ti;unsigned long *stackend;int node = tsk_fork_get_node(orig);int err;tsk = alloc_task_struct_node(node);if (!tsk)return NULL;ti = alloc_thread_info_node(tsk, node);/*就是這里,果然分配內核棧了*/if (!ti)goto free_tsk;err = arch_p_task_struct(tsk, orig);/*這里分配task_struct結構*/if (err)goto free_ti;tsk->stack = ti; ...}