linux用戶棧
㈠ linux用戶態棧空間的大小有沒有限制
當然有限制,你可以使用命令 ulimit -s 查看大小。
因為物理空間存在著大小限制,因此棧空間肯定也是有大小限制的,不然程序還不崩了。
甚至,你可以測試一下,分配一個超大的棧空間(方法是,在函數中,定義一個超大空間的數組),然後看是否可以運行,當達到一個限制的時候,程序是無法正常運行的
㈡ linux進程為什麼有用戶棧和內核棧,
linux下的cpu有兩個狀態:內核態和用戶態,內核態的cpu的許可權高於用戶態下的cpu。
linux下的內存分為用戶態內存和內核態內存,一般4個G內存,3個G的分給用戶態,1個G分給內核態。
linux進程有時需要調用內核資源時,如讀寫文件,io讀寫等,這時候是通過系統調用實現對內核資源的訪問的,在訪問內核資源前是用戶棧,經過系統調用進入到內核態時,cpu的狀態也由用戶態變為內核態,訪問的內存就是內核態下管理的內存了-內核棧,對內核里的資源訪問完返回,內存又回到了用戶棧,cpu也回到用戶態。
㈢ 進程內核棧,用戶棧及 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內核——用戶堆棧和內核堆棧
每個進程都有用戶堆棧和內核堆棧兩個堆棧。進程在用戶態時使用用戶堆棧,陷入到內核態時便使用內核堆棧。
㈥ linux 管道原理
Linux原理的學習,我打算由淺入深,從上之下,也就是先了解個大概再逐個深入。先了解一下Linux的進程先。
一、Linux進程上下文
Linux進程上下文,我理解就是進程組成元素的集合。包括進程描述符tast_struct,正文段,數據段,棧,寄存器內容,頁表等。
1)tast_struct
它是一種數據結構,存儲著進程的描述信息,例如pid,uid,狀態,信號項,打開文件表等。是進程管理和調度的重要依據。
2)用戶棧和核心棧
顧名思義,用戶棧是進程運行在用戶態使用的棧,含有用戶態執行時候函數調用的參數,局部變數等;核心棧是該進程運行在核心態下用的棧,保存調用系統函數所用的參數和調用序列。這兩個棧的指針都保存在tast_struct結構中。
3)寄存器
保存程序計數器,狀態字,通用寄存器,棧指針。
4)頁表
線性地址到物理地址的映射
5)正文段,數據段。
二、Linux進程的狀態
Linux中進程共有5個狀態:就緒,可中斷睡眠,不可中斷睡眠,暫停,僵死。也就是說,linux不區分就緒和運行,它們統一叫做就緒態。進程所處的狀態記錄在tast_struct中。
三、進程的控制
1)進程樹的形成
計算機啟動後,BIOS從磁碟引導扇區載入系統引導程序,它將Linux系統裝入內存,並跳到內核處執行,Linux內核就執行初始化工作:初始化硬體、初始化內部數據結構、建立進程0。進程0創建進程1,進程1是以後所有創建的進程的祖先,它負責初始化所有的用戶進程。進程1創建shell進程,shell進程顯示提示符,等待命令的輸入。
2)進程的創建
任何一個用戶進程的創建都是由現有的一個進程完成的,進程的創建要經過fork和exec兩個過程。Fork是為新進程分配相應的數據結構,並將父進程的相應上下文信息復制過來。Exec是將可執行文件的正文和數據轉入內存覆蓋它原來的(從父進程復制過來的),並開始執行正文段。
3)進程的終止
系統調用exit()就可自我終結,exit釋放除了tast_struct以外的所有上下文,父進程收到子進程終結的消息後,釋放子進程的tast_struct。
4)進程的調度
進程的調度是由schele()完成的,一種情況是,當處理機從核心態向用戶態轉換之前,它會檢查調度標志是否為1,如果是1,則運行schele(),執行進程的調度。另一種情況是進程自動放棄處理機,時候進行進程調度。
進程的調度過程分為兩步,首先利用相關策略選擇要執行的進程,然後進行上下文的切換。
四、進程的通信
進程的通信策略主要有,消息,管道,消息隊列,共享存儲區和信號量。
1)信息
消息機制主要是用來傳遞進程間的軟中斷信號,通知對方發生了非同步事件。發送進程將信號(約定好的符號)發送到目標進程的tast_struct中的信號項,接收進程看到有消息後就調用相應的處理程序,注意,處理程序必須到進程執行時候才能執行,不能立即響應。
2)管道
我理解就是兩個進程使用告訴緩沖區中的一個隊列(每兩個進程一個),發送進程將數據發送到管道入口,接收進程從管道出口讀數據。
3) 消息隊列
消息隊列是操作系統維護的一個個消息鏈表,發送進程根據消息標識符將消息添加到制定隊列中,接收進程從中讀取消息。
4)共享存儲區
在內存中開辟一個區域,是個進程共享的,也就是說進程可以把它附加到自己的地址空間中,對此區域中的數據進行操作。
5)信號量
控制進程的同步。
㈦ Linux內核中用戶空間棧和內核棧的區別
您好,很高興為您解答。
1.進程的堆棧
內核在創建進程的時候,在創建task_struct的同事,會為進程創建相應的堆棧。每個進程會有兩個棧,一個用戶棧,存在於用戶空間,一個內核棧,存在於內核空間。當進程在用戶空間運行時,cpu堆棧指針寄存器裡面的內容是用戶堆棧地址,使用用戶棧;當進程在內核空間時,cpu堆棧指針寄存器裡面的內容是內核棧空間地址,使用內核棧。
2.進程用戶棧和內核棧的切換
當進程因為中斷或者系統調用而陷入內核態之行時,進程所使用的堆棧也要從用戶棧轉到內核棧。
進程陷入內核態後,先把用戶態堆棧的地址保存在內核棧之中,然後設置堆棧指針寄存器的內容為內核棧的地址,這樣就完成了用戶棧向內核棧的轉換;當進程從內核態恢復到用戶態之行時,在內核態之行的最後將保存在內核棧裡面的用戶棧的地址恢復到堆棧指針寄存器即可。這樣就實現了內核棧和用戶棧的互轉。
那麼,我們知道從內核轉到用戶態時用戶棧的地址是在陷入內核的時候保存在內核棧裡面的,但是在陷入內核的時候,我們是如何知道內核棧的地址的呢?
關鍵在進程從用戶態轉到內核態的時候,進程的內核棧總是空的。這是因為,當進程在用戶態運行時,使用的是用戶棧,當進程陷入到內核態時,內核棧保存進程在內核態運行的相關信心,但是一旦進程返回到用戶態後,內核棧中保存的信息無效,會全部恢復,因此每次進程從用戶態陷入內核的時候得到的內核棧都是空的。所以在進程陷入內核的時候,直接把內核棧的棧頂地址給堆棧指針寄存器就可以了。
3.內核棧的實現
內核棧在kernel-2.4和kernel-2.6裡面的實現方式是不一樣的。
在kernel-2.4內核裡面,內核棧的實現是:
union task_union {
struct task_struct task;
unsigned long stack[init_stack_size/sizeof(long)];
}; 其中,init_stack_size的大小隻能是8k。
內核為每個進程分配task_struct結構體的時候,實際上分配兩個連續的物理頁面,底部用作task_struct結構體,結構上面的用作堆棧。使用current()宏能夠訪問當前正在運行的進程描述符。
注意:這個時候task_struct結構是在內核棧裡面的,內核棧的實際能用大小大概有7k。
內核棧在kernel-2.6裡面的實現是(kernel-2.6.32):
union thread_union {
struct thread_info thread_info;
unsigned long stack[thread_size/sizeof(long)];
}; 其中thread_size的大小可以是4k,也可以是8k,thread_info佔52bytes。
當內核棧為8k時,thread_info在這塊內存的起始地址,內核棧從堆棧末端向下增長。所以此時,kernel-2.6中的current宏是需要更改的。要通過thread_info結構體中的task_struct域來獲得於thread_info相關聯的task。更詳細的參考相應的current宏的實現。
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
… ..
}; 注意:此時的task_struct結構體已經不在內核棧空間裡面了。
如若滿意,請點擊右側【採納答案】,如若還有問題,請點擊【追問】
希望我的回答對您有所幫助,望採納!
~
o(∩_∩)o~