linux用戶空間和內核exit的語義--linux沒有線程

如果你在程序中調用了exit,那么很顯然你的程序會退出,可是至于為何會退出那就是庫的事情了,我為什么說只是庫的事情而不關linux內核的事情呢?那是因為linux內核根本不管用戶空間的行為策略。庫的策略是什么?很簡單的退出當前進程嗎?如果是多線程的程序呢?多線程的程序它的行為又是什么呢?在我們探究庫的行為以及探究庫為何會有這樣的行為之前首先談談內核對exit的實現sys_exit,在sys_exit中,我絲毫找不到關于退出線程的代碼,按照常規的思考,如果sys_exit負責將本進程的所有線程都退出的話,那么合理的辦法就是在本進程的signal結構體上append上一個SIGKILL信號,然后喚醒所有的線程的task_struct,因為一個進程的所有線程共享信號處理結構,因此每個被喚醒的線程的task_struct都會在信號處理時自己退出,這樣看起來很不錯,也很合理,如果是windows想到了這個主意,那么它肯定會采用的,可是這是linux。

linux中沒有線程的概念,我這么說不是在說linux很落后嗎?現代操作系統中都會有線程的實現。其實不然,在linux中,線程并不是一個操作系統的內秉性概念,在linux中只有進程,然后可以在進程的基礎上輕松實現線程,這個意義上,線程僅僅是一個策略,一個由很多的實體概念組合而成的概念。其實在linux上,完全不能用傳統的操作系統的標準來說明,比如線程,進程,然后又分了什么X程,這樣豈不是越來越亂,每引入一個概念,操作系統的體系就要大動一番,在linux中就有一個執行緒的概念,該執行緒就是task_struct,你把它理解成進程也好,理解成線程也罷,其實它可以表達成任何可以執行的東西,在task_struct的基礎上,我們可以壘砌很多外延的東西,比如進程,線程等等,在linux中,只有task_struct一個概念,進程,線程只是它的外延而已,task_struct和不同的特性組合就可以表示程不同的外延,比如,它和gid,uid等組合就是進程,它和線程組或者TGID組合就是線程。

理解了一行原則,exit的內核行為就十分簡單了,既然內核原始的只有task_struct這一個執行緒概念,那么它就是應該有它的創建和銷毀的內核api,并且在哲學意義上這兩個內核api是對稱的,我們很多人都了解linux的線程創建,其實也是用的fork,cone在內核不也是用fork實現的嗎?創建一個現代操作系統的線程在linux中就是創建一個執行緒,也就是創建一個task_struct,那么do_fork就是干這個的,相反的,對于退出并沒有多少人去關注,既然fork創建了一個task_struct,那么exit就是銷毀一個task_struct,別的并不做什么,fork沒有線程的概念,它只負責按照用戶提供的參數創建一個task_struct,這里線程這個外延是通過參數體現的,既然參數可以賦予一個task_struct以線程的含義,那么fork中也就根據此含義對task_struct的字段進行了設置,以表示這是一個線程,在linux內核中并沒有線程的概念,而僅有線程的外延,既然創建行為fork如此,那么銷毀行為exit也是如此,如此一來就可以理解exit中根本就不可能有什么向本進程的所有線程發送退出信號一說,它只管銷毀這個調用exit的執行緒的task_struct(其實是遞減這個task_stuct的引用計數),而不管什么線程的概念,那么誰會去管線程的概念呢?當然是誰定義誰管了,比如Posix或者用戶的其它庫,內核將線程這個task_struct的外延導出給用戶,那么用戶就可以用這個外延的一系列特性以及行為準則來操作這個線程外延,故而用戶庫可以用內核提供的最小化的正交組合接口配上線程這個外延來組合成一個可以退出所有線程的接口,其實就是對于每一個線程調用其exit。

內核實現畢竟是內核實現,linux還是遵循posix的,因此它提供了一個系統調用sys_exit_group,之所以如此是因為這樣的話,用戶庫就不必再費勁心機切入每個線程并且在每個線程調用exit了,當然這也不是linux內核所希望的。sys_exit_group中會調用zap_other_threads(current)來退出每個線程,不管怎樣,exit_group的提供僅僅是為了遵循posix,而exit才是linux的設計中原汁原味的執行緒操作系統調用,其實在用戶庫里面完全可以用向所有的線程發送SIGKILL信號來實現exit。

舉個例子來說明一切:

#include.h>

#include

#include.h>

#include

void direct_exit() //這個函數直接用系統調用實現了exit,即到了內核直接調用sys_exit

{

int a = 1,b = 0;

asm("movl %0,%%eax/n/t" /

"movl %1,%%ebx/n/t" /

"int $0x80/n/t" /

::"r" (a),"r" (b));

}

int handler( void *p)

{

while(1)

{

sleep(1);

printf("Sub thread is running/n");

}

}

int main(int argc, char* argv[])

{

int i = 0;

clone(handler, &i-1024, CLONE_SIGHAND|CLONE_VM|CLONE_THREAD, NULL);

while(1)

{

sleep(1);

printf("Main thread is running/n");

if(i++>15)

{

direct_exit();//直接退出,和線程沒有關系,子線程handler繼續運行,不受影響

//exit(); //調用庫里面的exit,當然要遵循posix的線程語義,所有線程退出

}

}

return 0;

}

作為最后,為了使得本文的副標題不是空設,稍微談一下linux的進程uid等特征,前面的文章說過,linux靠uid,gid實現了多用戶,這個多用戶是進程意義上的,如果按照本文前面說的概念和外延的觀點來看的話,多用戶只是為了實現多用戶而必須的一個執行緒的參數而已,其實每個執行緒即task_struct都有一個uid和gid等信息而并不一定僅僅指進程,如下的例子可以證明:

int handler( void *p)

{

open("/root/b",O_CREATE);

perror("open b");

setuid(500);

seteuid(500);

open("/root/c",O_CREATE);

perror("open c");

}

int main(int argc, char* argv[])

{

int i = 0;

clone(handler, &i-1024, CLONE_SIGHAND|CLONE_VM|CLONE_THREAD, NULL);

if(getchar()=="w")

{

open("/root/a",O_CREATE);

perror("open a");

}

return 0;

}

在以上的例子中,以root用戶運行這個代碼,c的打開將失敗,而a,b將成功,這里可以說明在不同的線程里面可以有不同的uid和gid,其實這里的執行緒已經不再是線程了,這個例子再次說明,在linux內核中沒有線程的明確定義,再抽象一點其實也沒有進程,而僅僅有執行緒而已,這個執行緒到底是什么,就看用戶提供什么策略使他成為什么外延了。


?本文轉自 dog250 51CTO博客,原文鏈接:http://blog.51cto.com/dog250/1273411


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/393169.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/393169.shtml
英文地址,請注明出處:http://en.pswp.cn/news/393169.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode1328. 破壞回文串

給你一個回文字符串 palindrome ,請你將其中 一個 字符用任意小寫英文字母替換,使得結果字符串的字典序最小,且 不是 回文串。 請你返回結果字符串。如果無法做到,則返回一個空串。 示例 1: 輸入:palindro…

php補充 擴展,PHP安裝擴展補充說明

上一篇文章中用到了,php的sodium擴展,那么如何安裝PHP擴展呢?基于我之前踩過的一些坑,大致整理了幾種安裝php擴展的方法。已安裝sodium為例1、先做點準備工作,安裝sodium依賴rpm -ivh http://mirrors.whsir.com/centos…

Java調用存儲過程出現Bug,sql語法錯誤

因為SQL Server運行沒有正常,檢查了傳入參數的值,發現問題,然后傳入默認參數,解決了問題.轉載于:https://www.cnblogs.com/JimmySeraph/p/11043490.html

leetcode1438. 絕對差不超過限制的最長連續子數組

給你一個整數數組 nums ,和一個表示限制的整數 limit,請你返回最長連續子數組的長度,該子數組中的任意兩個元素之間的絕對差必須小于或者等于 limit 。 如果不存在滿足條件的子數組,則返回 0 。 示例 1: 輸入&#…

gitlab 2.7版本升級到2.8

第一步 關閉服務 /etc/init.d/gitlab stop第二部 更新代碼cd /home/gitlab/gitlab# Get latest codesudo -u gitlab git pull origin stable# Install libssudo -u gitlab bundle install --without development test# update dbsudo -u gitlab bundle exec rake db:migrate RA…

arkit技術介紹_面向移動AR的觸覺技術:如何以“觸摸”感增強ARKit應用

arkit技術介紹by Neil Mathew通過尼爾馬修(Neil Mathew) 面向移動AR的觸覺技術:如何以“觸摸”感增強ARKit應用 (Haptics for mobile AR: how to enhance ARKit apps with a sense of “touch”) I’m really excited about the future of haptics for AR and VR. …

Unity3D的坑系列:動態加載dll

Unity3D的坑系列:動態加載dll 我現在參與的項目是做MMO手游,目標平臺是Android和iOS,iOS平臺不能動態加載dll(什么原因找喬布斯去),可以直接忽略,而在Android平臺是可以動態加載dll的&#xff0…

微信小程序 php配置,微信小程序的配置

我們使用app.json文件來對微信小程序進行全局配置,決定頁面文件的路徑、窗口表現、設置網絡超時時間、設置多 tab 等。以下是一個包含了所有配置選項的簡單配置app.json{"pages": ["pages/index/index","pages/logs/index"],"wi…

leetcode332. 重新安排行程(dfs)

給定一個機票的字符串二維數組 [from, to],子數組中的兩個成員分別表示飛機出發和降落的機場地點,對該行程進行重新規劃排序。所有這些機票都屬于一個從 JFK(肯尼迪國際機場)出發的先生,所以該行程必須從 JFK 開始。 …

PWA - service worker - Workbox(未完)

Get Started(開始) 只有get請求才能cache緩存嗎?Create and Register a Service Worker File(創建和注冊 Service Worker) Before we can use Workbox, we need to create a service worker file and register it to o…

draft.js_如何使用快捷方式在Draft.js中創建有序列表和無序列表

draft.jsby Andrey Semin通過安德烈塞米(Andrey Semin) 如何使用快捷方式在Draft.js中創建有序列表和無序列表 (How to create ordered and unordered lists in Draft.js with a shortcut) We at Propeller have encountered many differences between Draft.js and popular t…

當javaScript從入門到提高前需要注意的細節:變量部分

到了HTML5的時代,對javaScript的要求不是降低了,而是更提高了。javaScript語言的入門非常簡單,如果你有java、C#等C風格的結構化語言的基礎,那javaScript你最多半天就可以寫點什么了。但是javaScript是一種動態語言,這…

PAT乙級 1003. 我要通過!

題目: “答案正確”是自動判題系統給出的最令人歡喜的回復。本題屬于PAT的“答案正確”大派送 —— 只要讀入的字符串滿足下列條件,系統就輸出“答案正確”,否則輸出“答案錯誤”。 得到“答案正確”的條件是: 1. 字符串中必須僅有…

電臺復活節_如何通過在控制臺中隱藏復活節彩蛋使您的應用程序用戶驚訝

電臺復活節by Ethan Ryan由伊桑瑞安(Ethan Ryan) 如何通過在控制臺中隱藏復活節彩蛋使您的應用程序用戶驚訝 (How to surprise your app’s users by hiding Easter eggs in the console) I love console.logging(“stuff”).我喜歡console.logging(“ stuff”)。 I do it th…

leetcode1267. 統計參與通信的服務器(dfs)

這里有一幅服務器分布圖,服務器的位置標識在 m * n 的整數矩陣網格 grid 中,1 表示單元格上有服務器,0 表示沒有。 如果兩臺服務器位于同一行或者同一列,我們就認為它們之間可以進行通信。 請你統計并返回能夠與至少一臺其他服務…

System類入門學習

第三階段 JAVA常見對象的學習 System類 System類包含一些有用的字段和方法,他不能被實例化 //用于垃圾回收 public static void gc()//終止正在運行的java虛擬機。參數用作狀態碼,根據慣例,非0表示異常終止 public static void exit(int stat…

gulpfile php,Laravel利用gulp如何構建前端資源詳解

什么是gulp?gulp是新一代的前端項目構建工具,你可以使用gulp及其插件對你的項目代碼(less,sass)進行編譯,還可以壓縮你的js和css代碼,甚至壓縮你的圖片,gulp僅有少量的API,所以非常容易學習。 gulp 使用 st…

ios jenkins_如何使用Jenkins和Fastlane制作iOS點播構建系統

ios jenkinsby Agam Mahajan通過Agam Mahajan 如何使用Jenkins和Fastlane制作iOS點播構建系統 (How to make an iOS on-demand build system with Jenkins and Fastlane) This article is about creating iOS builds through Jenkins BOT, remotely, without the need of a de…

菜鳥也學hadoop(1)_搭建單節點的hadoop

其實跟官方的教程一樣 只是 我想寫下來 避免自己搞忘記了,,,,好記性不如爛筆頭 首先確認自己是否安裝了 java, ssh 以及 rsync 沒有裝的直接就 apt-get install 了嘛,,,java的不一定…

SP703 SERVICE - Mobile Service[DP]

題意翻譯 Description   一個公司有三個移動服務員。如果某個地方有一個請求,某個員工必須趕到那個地方去(那個地方沒有其他員工),某一時刻只有一個員工能移動。只有被請求后,他才能移動,不允許在同樣的位…