麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

Linux會話淺析

2019-11-11 05:02:36
字體:
來源:轉載
供稿:網友
說起會話,我們經常登錄到linux系統,執行各種各樣的程序,這都牽涉到會話。但是,一般情況下我們又很少會去關注到會話的存在,很少會去了解它的來龍去脈。本文就對linux會話相關的信息做一些整理,看看隱藏在我們日常使用的背后,都有些什么樣的邏輯。【會話的維系】維系一個會話,最常見的有兩種方式:一是基于某種憑證,比如web網站的登錄會話,在登錄驗證之后,服務器就會返回一個session id作為憑證。用戶之后的請求總是會帶上這個id,而服務器通過這個id也就能知道用戶是誰。直到用戶注銷登錄、或者登錄超時,服務器會清洗掉對應的session id,這個id就失效了,會話也就隨之而結束。第二種方式是基于連接的,當用戶和系統之間的連接啟用時,系統會對用戶進行驗證,驗證通過之后,來自這個連接的操作都是屬于這個用戶的。直到連接斷開,則會話結束。linux系統的會話就是以第二種方式來維系的。會話基于連接,那么連接的安全性就決定了會話的安全性。以最常見的兩種連接為例:1、本地連接,用戶是直接通過鍵盤顯示器來跟系統交互的,鍵盤顯示器直接連接在主機上,連接被篡改基本上是不可能的;2、遠程連接,以ssh為例,其協議會進行加密,從而避免連接被篡改;下面在討論會話的時候就圍繞兩種連接來展開。【連接的啟用】前面說到,會話是基于連接的。會話的源頭,就是用戶與系統之間連接的啟用。對于本地連接,連接是在系統初始化時建立的。linux會初始化若干個tty(/dev/tty?),形成若干個虛擬終端。本地連接的鍵盤和顯示器通過對應的驅動程序,跟其中某個tty綁定上。用戶可以通過ALT+F[1-12]鍵,將鍵盤和顯示器切換到不同的tty上(也就是說,一套鍵盤顯示器可以對應多個本地連接)。在系統啟動的時候,init程序會根據相應的配置(如:/etc/init/tty1.conf),啟動相應的程序來監聽這些tty(如:/sbin/getty -8 38400 tty1。下文說到這個監聽程序時,就以getty為例)。當用戶通過ALT+F?切換到對應的tty?、并有所輸入時,在該tty上監聽的getty就能讀取到輸入信息,然后通過exec啟動login程序來進行登錄驗證。從getty得到輸入的這一刻起,tty?上的這個連接就算是啟用了。對于遠程連接,本文都以ssh為例。系統安裝ssh server之后,在/etc/init.d/下會有它對應的啟動腳本,這會使得sshd程序(ssh服務器)在系統啟動的時候被啟動(當然,root用戶也可以在系統啟動之后,手動啟動sshd)。sshd監聽網絡上的相應端口(如:tcp:22),等待遠程用戶的連接。用戶的連接始于TCP連接,然后sshd和ssh-client會打通ssh隧道。從這時起,連接啟用,sshd會要求用戶進行登錄驗證。【登錄驗證】不管是本地終端登錄,還是遠程登錄,登錄驗證無非就是要讓用戶輸入用戶名和密碼。用戶輸入的信息通過已經啟用的連接到達對端程序(如:login、sshd),然后由其進行校驗。系統中的用戶信息存放在/etc/passwd文件中,里面保存的系統中所有用戶的信息。這些信息主要有:用戶名、密碼、組、home路徑、以及登錄后執行的程序、等。這些信息可以分兩個層面來看:一、用于驗證的。包括用戶名和密碼兩項。即用戶在與系統建立連接后,需要提供用戶名和密碼來進行驗證(注意,密碼一般并不是明文保存在passwd文件中的);二、登錄后用于設置用戶屬性的。除密碼外的所有項。下面會詳細解釋;用戶登錄后會得到一個進程,關于這個進程,它有如下一些特征:1、對于本地連接,它就是原本執行getty的那個進程;而對于ssh連接,它是由sshd fork出來的進程;2、進程的用戶名、組、當前路徑、等都被按/etc/passwd文件中的描述進行設置;3、進程的stdin、stdout、stderr連接到一個對應的終端。對于本地連接,這個終端就是getty監聽的那個tty;對于ssh連接,它是由sshd打開的pty(偽終端,后面會詳細解釋);4、這個進程會執行/etc/passwd中配置的"登錄后執行的程序",這個程序一般就是/bin/sh,即登錄后為用戶提供一個shell控制臺。于是用戶可以使用自己的終端跟這個shell程序交互,干各種事情。這個"登錄后執行的程序"也常被配置為/bin/nologin,表示對應用戶是不允許登錄的,因為nologin程序不會與用戶進行交互,打印錯誤信息后就會退出了,所以登錄只是白費勁。當然,如果你愿意,并且有root權限,也可以將"登錄后執行的程序"配置成其他程序;于是,用戶在登錄完成之后,系統中就存在這樣一個用戶名為該登錄用戶的進程。通常這個進程運行shell程序,并且其輸入輸出連接到用戶的終端,所以用戶可以用鍵盤來操作這個shell,并且用顯示器接收shell的輸出。【關于終端】前面講到,登錄后的shell其輸入輸出是連接到用戶使用的終端的,不管是本地登錄的tty,還是遠程登錄的pty。但是,為什么要有終端呢?shell的輸入直接接到鍵盤、輸出直接接到顯示器,這樣不行么?尤其是遠程的情況,shell的輸入輸出為什么不能直接接到網絡,而非要弄一個pty出來呢?最容易想到的一點,終端能夠使得上層不必關心輸入輸出設備本身的細節,只管對其讀寫就行了。不過這一點似乎并不是終端所特有的,因為vfs已經能夠勝任了。應用程序open設備文件,得到fd,然后同樣只用管對其讀寫就行了,而不用關心這個fd代表的是鍵盤、還是普通文件,具體的細節已經被隱藏在設備驅動程序之中。不過,相比于普通的讀寫,終端還實現了很多可以通過ioctl系統調用進行配置的功能,能夠完成一些針對輸入輸出的處理邏輯。如:1、回車換行的轉換:定義輸入輸出如何映射回車換行符。比如:回車鍵是/r、還是/n、還是/r/n;再如:/n應該如何打印到屏幕上,是回車+換行、還是只換行不回車、等等;2、行編輯:允許讓輸入字符不是立馬送到應用程序,而是在換行以后才能被讀取到。未換行的輸入字符可以通過退格鍵進行編輯(比如在你密碼輸錯的時候,是可以用CTRL+退格來進行編輯的);3、回顯:可以讓輸入字符自動被回顯到終端的輸出上。于是,鍵盤每輸入一個字符都能在顯示器上看到它,而這些字符其實很可能是還沒被應用程序讀取到的(因為有行編輯);4、功能鍵:允許定義功能鍵。比如最常用的Ctrl+C,殺死前臺進程,就是由終端來觸發的。終端檢測到Ctrl+C輸入,會向前臺進程組發送SIGTERM信號。而誰是前臺進程組呢?這是由shell通過ioctl系統調用對終端使用TIOCSPGRP命令來設置的,每當shell啟動一個前臺進程,它都需要這么設置一下;5、輸入輸出流向控制,只有前臺進程組能夠從終端中讀數據、而不管前臺后臺程序都能向終端寫數據。這點也是必須的,跟用戶進程交互的是前臺進程,用戶的輸入當然不能被其他后臺進程搶走。但是一個進程是前臺還是后臺,是它自己是所不知道的,沒法靠進程自己來判斷什么時候可以讀終端、什么時候不能讀。所以需要終端來提供支持,如果后臺進程讀這個終端,終端的驅動程序將向其發送SIGTTIN信號,從而將其掛起。直到shell將其重新置為前臺進程時(通過fg命令),該進程才會繼續執行;6、等等;終端提供的這些功能未必都會被打開它的程序使用到,但是如果要使用,則可以通過統一的ioctl接口來設置,而不需要關心終端具體接的是什么設備,是鍵盤顯示器?還是網絡。大多數應用程序則根本不關心終端,只當它是能夠滿足讀寫需求的文件。而像shell這樣為人機交互而生的程序,則注定會跟終端打交道。對于shell來說,像“行編輯”、“回顯”這樣的功能,其實是可以不需要依賴終端的,shell程序自己可以做這樣的處理,因為要做處理的數據正是shell從終端里面讀到的數據。但是像“功能鍵”、“輸入輸出流向控制”這樣的功能,則又不得不依賴終端。比如“功能鍵”,因為這時在對終端進行讀操作的是shell啟動的前臺程序,而不是shell自己,所以不可能由shell來讀取功能鍵,然后觸發信號。可以說,終端是人機交互時,應用程序與用戶之間的一個中間層。如果應用程序是在跟人交互,使用終端是其不二的選擇;否則則沒有必要使用終端。這一點在sshd上面有很好的體現。當用戶使用ssh登錄到遠程機器remotehost,并執行一些操作時(比如執行cat test.txt操作),可以有兩種方式:1、ssh user:pass@remotehost,遠程登錄后,再在shell中執行cat test.txt命令;2、ssh user:pass@remotehost 'cat test.txt',直接由sshd啟動一個shell、自動執行'cat test.txt'命令;第一種方式,是登錄之后,再通過人機交互輸入命令,這時sshd會為登錄后得到的shell程序準備一個pty,以支持人機交互;而第二種方式,在登錄之后并不需要交互,所以sshd就并不會使用pty,而是直接通過socket將shell的輸入輸出跟自己連接起來(再由sshd將其轉發給ssh-client)。下面再說一下pty。pty分master和slave兩端,跟pipe的兩端很像,寫入到master端的數據可以原樣從slave端讀出,反之亦然。在上面所述的第一種方式下,各個進程的聯系如下:ssh-client <-> [socket] <-> sshd <-> [master] <-> [slave] <-> shellpty的master和slave兩端分別被sshd和shell打開,就像pipe一樣,將它們的輸入輸出連接起來。而pty跟pipe的不同之處,則正是前面所說的終端的功能(pty的slave端對于shell來說就是一個終端)。跟tty不同,tty是系統初始化時生成的,其數目是固定的(比如12個)。而pty是系統啟動后動態創建的。其創建的方法是:fd = open('/dev/ptms'),這樣就得到了一個pty。open返回的fd代表master端,通過ptyname(fd)可以得到對應slave端的文件路徑(比如'/dev/pts/2'),這個設備文件是master端被open之后動態生成的。【權限控制】用戶登錄驗證成功后,getty、sshd這樣的程序就會為用戶啟動他所對應的"登錄后執行的程序",并且在此之前會通過setuid()、setgid()這樣的系統調用,設置好進程的uid和gid(用戶和組)。之后,這個進程對文件的讀、寫、執行,以及對系統調用的使用,等都會受到進程uid和gid的限制。用戶對系統的各種操作都是通過進程來完成的。而用戶的輸入輸出都被定向到他登錄后所得到的那個進程上,于是用戶能夠控制這個進程,來干他想干的事情(如果這個進程接受控制的話,比如進程運行著一個shell程序)。用戶的操作都受限于進程的uid和gid,比如文件訪問、向進程發送信號、使用系統調用、監聽網絡端口、等都需要對其做檢查。除非是root用戶,否則沒有權限使用setuid()、setgid()這樣的系統調用來修改進程的uid和gid。而login時,login、sshd這樣的進程之所以能夠設置登錄后進程的uid和gid,正是因為它們都是root用戶的進程。如果想要改變登錄用戶,則必須利用屬于root用戶的進程。一種辦法是退出登錄,然后走老路,重新跟getty、sshd這樣的進程打交道,而使用其他用戶名登錄。另一種辦法是執行一個setuid/setgid程序(參見《記一個linux內核內存提權問題》中的說明),臨時獲得root權限,再實現用戶的切換(例如su就是這樣一個能實現用戶切換的setuid命令)。為了實現進程權限控制中的各種功能,進程的uid和gid其實并不止一組,主要有如下三組:1、ruid/rgid,代表實際的用戶和組id;2、euid/egid,代表當前生效的用戶和組id;3、suid/sgid,代表保留的用戶和組id;什么意思呢?一般情況下,在用戶登錄得到的進程中,這三組id都是相同的值,與登錄用戶相對應(假設為aaa)。而用戶新創建的進程也會全部繼承這三組id。當用戶執行一個setuid/setgid程序時,執行程序的進程將得到可執行程序owner(假設為bbb)的用戶屬性,euid/egid和suid/sgid會更新為bbb(而ruid/rgid不會被更新)。這些id在進程操作別的對象時(比如寫文件、發信號、等)或被別的進程操作時(比如其他進程向其發信號、等)會被用做校驗。ruid/rgid和euid/egid在進程被操作的時候會使用到,euid/egid在進程發起操作的時候會用到。比如有一個進程,其ruid是aaa、euid是bbb,則euid為aaa或bbb的其他進程都可以向其發送信號。而該進程在進行讀寫文件、創建文件、等操作時,使用的則是用戶bbb的權限:它創建的文件owner是bbb、它在訪問owner是aaa的文件時以other權限進行校驗。那么第三組id,suid/sgid又是干什么用的呢?之前說普通用戶沒有權限使用setuid()這樣的系統調用來改變進程的uid和gid,其實這個說法并不確切。進程其實是有權限將自己的ruid/rgid、euid/egid、suid/sgid的值修改成與這三者之一相等的值。比如ruid/rgid為aaa、suid/sgid為bbb,則用戶可以任意將euid/egid設置為aaa或bbb。因為大多數情況下這三組id是等值的,所以一般說用戶進程不能修改自己的uid和gid(只能從aaa修改為aaa,相當于不能修改)。但是執行setuid的程序后,進程的這三組id就有了兩種不同的取值,ruid/rgid等于aaa、euid/egid和suid/sgid等于bbb,于是進程的uid就有了一定的選擇余地。比如此時進程的euid/egid被更新成了bbb,那就不能再以owner身份去操作aaa的文件了(注意,這個進程原本是以aaa用戶登錄而得到的)。不過沒關系,進程是有權限將euid/egid改回aaa的,因為ruid/rgid的值是aaa。但是改回來之后,ruid/rgid和euid/egid都是aaa了,要再想把euid/egid改為bbb怎么辦呢?suid/sgid就是為此而生的,作為一個備份,它的值是bbb,這使得euid/egid還能夠修改回bbb。當然,進程也有權限將euid/egid和suid/sgid都改回aaa,這將使得它們不再能修改成bbb了。【會話退出】用戶登錄是一個會話的開始。登錄之后,用戶會得到一個跟用戶使用的終端相連的進程,這個進程被稱作是這個會話的leader,會話的id就等于該進程的pid。由該進程fork出來的子進程都是這個會話的成員(進程的sid等于該會話id)。leader進程的退出,將導致它所連接的終端被hangup,這意味著會話結束。反過來,像ssh這樣的遠程連接也可以通過斷開連接的方式來使終端hangup,這將使得leader進程收到SIGHUP信號而退出。如果會話使用的是pty,其本身是隨會話的建立而創建出來的,會話結束,則pty被銷毀;而如果會話使用的是tty,其本身是在系統初始化時創建的,并不依賴于會話的建立,則會話結束時,tty依然存在。init進程檢測到使用該tty的會話已經結束,便會重新啟動一個getty來監聽該tty。不過,會話結束,并不意味著在該會話中創建的所有進程都結束了。所謂的daemon進程,正是在某個會話中創建,但是卻不依賴該會話,而常駐后臺的進程。具體來說,當終端hangup時,內核會有如下兩個動作:1、向對應會話的leader進程發送SIGHUP信號。而一般來說,會話的leader進程很可能是一個shell,它在收到SIGHUP信號后,并不是馬上退出,而是會向它所啟動的子進程都各自發送一個SIGHUP信號,將它們都殺死,然后自己才退出。不過,如果是這個作為leader進程shell自己退出,而導致終端hangup的話,向其子進程發送SIGHUP信號的事情就不會發生了,因為shell退出在先,它再也不會收到SIGHUP信號;2、修改所有打開該終端的文件句柄,改成一個不可讀不可寫的實現;所以,在會話退出之后還常駐后臺的進程肯定是沒法跟終端交互的。而要想讓進程常駐后臺,一般有如下幾種方法:1、避免shell發SIGHUP信號;主要有兩種辦法:1)主動exit,而不是直接斷開終端;2)兩次fork。因為shell只認識它自己fork出來的子進程,并不知道"子又生孫"的事情,也就不會給孫子進程發送SIGHUP信號了;2、忽略SIGHUP信號;終端hangup時進程可能收到shell發送的SIGHUP信號。信號的默認處理動作是退出進程,但是該信號是可以忽略的。忽略信號,就可以使得后臺進程不會隨會話退出而退出。nohup命令就是做這件事情的,而且它做得更完整一些,不僅忽略SIGHUP,還會將進程的標準輸入重定向為/dev/null、輸出重定向到nohup.out文件;3、使用setsid()系統調用,為進程開啟一個新的會話;從一個會話中fork出來的進程,默認都是屬于這個會話的。但是進程可以調用setsid(),使自己脫離原先的會話,而成為一個新會話的leader。于是,原先的會話退出,就不會影響到新建的會話了。setsid命令包裝了setsid()系統調用,可以為進程創建新的會話。不過它并不對新進程的輸入輸出進行重定向,這就意味著新進程的輸入輸出還是連接到原先的那個tty的,這可能跟原先的會話爭搶輸入。所以,對新會話的進程進行輸入輸出的重定向也是一件很重要的事情。一個進程成為daemon進程,可以不隨會話的退出而退出,但是進程的uid/gid并不會因此而改變。對應的用戶還可以在其他會話中,通過發信號等方式,操作那些原來由他所啟動的daemon進程(因為權限控制是以用戶為準的,而并不考慮會話)。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 国产成人高清在线观看 | 巨乳毛片| 欧美精品成人一区二区在线观看 | 国产99久久 | 国产免费高清在线视频 | 亚洲国产在 | 久久久久久久久淑女av国产精品 | 欧美77 | 国产99精品视频 | 精国品产一区二区三区有限公司 | 久久精品高清 | a一级黄色毛片 | 亚洲精品成人在线视频 | 亚洲午夜不卡 | 免费一级欧美在线观看视频 | va毛片 | 国产精品一品二区三区四区18 | 欧美成人高清视频 | 免费99热在线观看 | 黄色特级一级片 | 国产大片全部免费看 | av免播放| 国产精品美女久久久久久网站 | 18欧美性xxxx极品hd | 九色免费视频 | 免费a视频 | 一级黄色毛片免费 | av免费av | 欧美成年性h版影视中文字幕 | 9999在线视频 | 日韩视| 美女啪网站| 久久久久九九九女人毛片 | 日本在线不卡免费 | 国产99视频精品免视看9 | 日韩视频一区二区三区四区 | av电影在线网站 | 日韩电影av在线 | 99视频有精品视频高清 | 中文在线日韩 | h色网站免费观看 |