網絡基礎4(TCP三次握手,四次握手,TCP流量控制,TCP狀態轉換 , TCP異常斷開,設置TCP屬性,端口復用)

TCP協議

TCP通信時序
下圖是一次TCP通訊的時序圖。TCP連接建立斷開。包含大家熟知的三次握手和四次握手。
在這里插入圖片描述TCP通訊時序

在這個例子中,首先客戶端主動發起連接、發送請求,然后服務器端響應請求,然后客戶端主動關閉連接。
兩條豎線表示通訊的兩端,從上到下表示時間的先后順序,
注意,數據從一端傳到網絡的另一端也需要時間,所以圖中的箭頭都是斜的。
雙方發送的段按時間順序編號為1-10,各段中的主要信息在箭頭上標出,例如段2的箭頭上標著SYN, 8000(0), ACK1001, ,表示該段中的SYN位置1,32位序號是8000,該段不攜帶有效載荷(數據字節數為0),ACK位置1,32位確認序號是1001,帶有一個mss(Maximum Segment Size,最大報文長度)選項值為1024。

建立連接(三次握手)的過程:

1. 客戶端發送一個帶SYN標志的TCP報文到服務器。這是三次握手過程中的段1。

客戶端發出段1,SYN位表示連接請求。序號是1000,這個序號在網絡通訊中用作臨時的地址,每發一個數據字節,這個序號要加1,這樣在接收端可以根據序號排出數據包的正確順序,也可以發現丟包的情況,另外,規定SYN位和FIN位也要占一個序號,這次雖然沒發數據,但是由于發了SYN位,因此下次再發送應該用序號1001。mss表示最大段尺寸,如果一個段太大,封裝成幀后超過了鏈路層的最大幀長度,就必須在IP層分片,為了避免這種情況,客戶端聲明自己的最大段尺寸,建議服務器端發來的段不要超過這個長度。

2 . 服務器端回應客戶端,是三次握手中的第2個報文段,同時帶ACK標志和SYN標志。它表示對剛才客戶端SYN的回應;同時又發送SYN給客戶端,詢問客戶端是否準備好進行數據通訊。

服務器發出段2,也帶有SYN位,同時置ACK位表示確認,確認序號是1001,表示“我接收到序號1000及其以前所有的段,請你下次發送序號為1001的段”,也就是應答了客戶端的連接請求,同時也給客戶端發出一個連接請求,同時聲明最大尺寸為1024。

  1. 客戶必須再次回應服務器端一個ACK報文,這是報文段3。
    .
    客戶端發出段3,對服務器的連接請求進行應答,確認序號是8001。在這個過程中,客戶端和服務器分別給對方發了連接請求,也應答了對方的連接請求,其中服務器的請求和應答在一個段中發出,因此一共有三個段用于建立連接,稱為“三方握手(three-way-handshake)”。在建立連接的同時,雙方協商了一些信息,例如雙方發送序號的初始值、最大段尺寸等。

在TCP通訊中,如果一方收到另一方發來的段,讀出其中的目的端口號,發現本機并沒有任何進程使用這個端口,就會應答一個包含RST位的段給另一方。例如,服務器并沒有任何進程使用8080端口,我們卻用telnet客戶端去連接它,服務器收到客戶端發來的SYN段就會應答一個RST段,客戶端的telnet程序收到RST段后報告錯誤Connection refused:
$ telnet 192.168.0.200 8080
Trying 192.168.0.200…
telnet: Unable to connect to remote host: Connection refused

數據傳輸的過程:

  1. 客戶端發出段4,包含從序號1001開始的20個字節數據。

  2. 服務器發出段5,確認序號為1021,對序號為1001-1020的數據表示確認收到,同時請求發送序號1021開始的數據,
    服務器在應答的同時也向客戶端發送從序號8001開始的10個字節數據,這稱為piggyback。

  3. 客戶端發出段6,對服務器發來的序號為8001-8010的數據表示確認收到,請求發送序號8011開始的數據。
    在數據傳輸過程中,ACK和確認序號是非常重要的,應用程序交給TCP協議發送的數據會暫存在TCP層的發送緩沖區中,
    發出數據包給對方之后,只有收到對方應答的ACK段才知道該數據包確實發到了對方,可以從發送緩沖區中釋放掉了,
    如果因為網絡故障丟失了數據包或者丟失了對方發回的ACK段,經過等待超時后TCP協議自動將發送緩沖區中的數據包重發。

關閉連接(四次握手)的過程:

由于TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務后就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味著這一方向上沒有數據流動,一個TCP連接在收到一個FIN后仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

  1. 客戶端發出段7,FIN位表示關閉連接的請求。
  2. 服務器發出段8,應答客戶端的關閉連接請求。
  3. 服務器發出段9,其中也包含FIN位,向客戶端發送關閉連接請求。
  4. 客戶端發出段10,應答服務器的關閉連接請求。
    建立連接的過程是三方握手,而關閉連接通常需要4個段,**服務器的應答和關閉連接請求通常不合并在一個段中,因為有連接半關閉的情況,**這種情況下客戶端關閉連接之后就不能再發送數據給服務器了,但是服務器還可以發送數據給客戶端,直到服務器也關閉連接為止。

滑動窗口 (TCP流量控制)

介紹UDP時我們描述了這樣的問題:如果發送端發送的速度較快,接收端接收到數據后處理的速度較慢,而接收緩沖區的大小是固定的,就會丟失數據。TCP協議通過“滑動窗口(Sliding Window)”機制解決這一問題。看下圖的通訊過程:
在這里插入圖片描述滑動窗口

  1. 發送端發起連接,聲明最大段尺寸是1460,初始序號是0,窗口大小是4K,表示“我的接收緩沖區還有4K字節空閑,你發的數據不要超過4K”。接收端應答連接請求,聲明最大段尺寸是1024,初始序號是8000,窗口大小是6K。發送端應答,三方握手結束。
  2. 發送端發出段4-9,每個段帶1K的數據,發送端根據窗口大小知道接收端的緩沖區滿了,因此停止發送數據。
  3. 接收端的應用程序提走2K數據,接收緩沖區又有了2K空閑,接收端發出段10,在應答已收到6K數據的同時聲明窗口大小為2K。
  4. 接收端的應用程序又提走2K數據,接收緩沖區有4K空閑,接收端發出段11,重新聲明窗口大小為4K。
  5. 發送端發出段12-13,每個段帶2K數據,段13同時還包含FIN位。
  6. 接收端應答接收到的2K數據(6145-8192),再加上FIN位占一個序號8193,因此應答序號是8194,連接處于半關閉狀態,接收端同時聲明窗口大小為2K。
  7. 接收端的應用程序提走2K數據,接收端重新聲明窗口大小為4K。
  8. 接收端的應用程序提走剩下的2K數據,接收緩沖區全空,接收端重新聲明窗口大小為6K。
  9. 接收端的應用程序在提走全部數據后,決定關閉連接,發出段17包含FIN位,發送端應答,連接完全關閉。

上圖在接收端用小方塊表示1K數據,實心的小方塊表示已接收到的數據,虛線框表示接收緩沖區,因此套在虛線框中的空心小方塊表示窗口大小,從圖中可以看出,隨著應用程序提走數據,虛線框是向右滑動的,因此稱為滑動窗口。

從這個例子還可以看出,發送端是**一K一K地發送數據,而接收端的應用程序可以兩K兩K地提走數據,當然也有可能一次提走3K或6K數據,或者一次只提走幾個字節的數據。**也就是說,應用程序所看到的數據是一個整體,或說是一個流(stream),在底層通訊中這些數據可能被拆成很多數據包來發送,但是一個數據包有多少字節對應用程序是不可見的,因此TCP協議是面向流的協議。而UDP是面向消息的協議,每個UDP段都是一條消息,應用程序必須以消息為單位提取數據,不能一次提取任意字節的數據,這一點和TCP是很不同的。

TCP狀態轉換

這個圖N多人都知道,它排除和定位網絡或系統故障時大有幫助,但是怎樣牢牢地將這張圖刻在腦中呢?那么你就一定要對這張圖的每一個狀態,及轉換的過程有深刻的認識,不能只停留在一知半解之中。下面對這張圖的11種狀態詳細解析一下,以便加強記憶!不過在這之前,先回顧一下TCP建立連接的三次握手過程,以及 關閉連接的四次握手過程。
在這里插入圖片描述TCP狀態轉換圖

CLOSED:表示初始狀態。

LISTEN:該狀態表示服務器端的某個SOCKET處于監聽狀態,可以接受連接。

SYN_SENT:這個狀態與SYN_RCVD遙相呼應,當客戶端SOCKET執行CONNECT連接時,它首先發送SYN報文,隨即進入到了SYN_SENT狀態,并等待服務端的發送三次握手中的第2個報文。SYN_SENT狀態表示客戶端已發送SYN報文。

SYN_RCVD: 該狀態表示接收到SYN報文,在正常情況下,這個狀態是服務器端的SOCKET在建立TCP連接時的三次握手會話過程中的一個中間狀態,很短暫。此種狀態時,當收到客戶端的ACK報文后,會進入到ESTABLISHED狀態。
ESTABLISHED:表示連接已經建立。

FIN_WAIT_1: FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。區別是:

FIN_WAIT_1狀態是當socket在ESTABLISHED狀態時,想主動關閉連接,向對方發送了FIN報文,
此時該socket進入到FIN_WAIT_1狀態。FIN_WAIT_2狀態是當對方回應ACK后,該socket進入到FIN_WAIT_2狀態,正常情況下,對方應馬上回應ACK報文,
所以FIN_WAIT_1狀態一般較難見到,而FIN_WAIT_2狀態可用netstat看到。FIN_WAIT_2:主動關閉鏈接的一方,發出FIN收到ACK以后進入該狀態。
稱之為半連接或半關閉狀態。該狀態下的socket只能接收數據,不能發。

TIME_WAIT: 表示收到了對方的FIN報文,并發送出了ACK報文,等2MSL后即可回到CLOSED可用狀態。如果FIN_WAIT_1狀態下,收到對方同時帶 FIN標志和ACK標志的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。

CLOSING: 這種狀態較特殊,屬于一種較罕見的狀態。正常情況下,當你發送FIN報文后,按理來說是應該先收到(或同時收到)對方的 ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你發送FIN報文后,并沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什么情況下會出現此種情況呢?如果雙方幾乎在同時close一個SOCKET的話,那么就出現了雙方同時發送FIN報文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連接。

CLOSE_WAIT: 此種狀態表示在等待關閉。當對方關閉一個SOCKET后發送FIN報文給自己,系統會回應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,察看是否還有數據發送給對方,如果沒有可以 close這個SOCKET,發送FIN報文給對方,即關閉連接。所以在CLOSE_WAIT狀態下,需要關閉連接。

LAST_ACK: 該狀態是被動關閉一方在發送FIN報文后,最后等待對方的ACK報文。當收到ACK報文后,即可以進入到CLOSED可用狀態。

半關閉

當TCP鏈接中A發送FIN請求關閉,B端回應ACK后(A端進入FIN_WAIT_2狀態),B沒有立即發送FIN給A時,A方處在半鏈接狀態,此時A可以接收B發送的數據,但是A已不能再向B發送數據。
從程序的角度,可以使用API來控制實現半連接狀態。

#include <sys/socket.h>
int shutdown(int sockfd, int how);
sockfd: 需要關閉的socket的描述符
how:	允許為shutdown操作選擇以下幾種方式:SHUT_RD(0):	關閉sockfd上的讀功能,此選項將不允許sockfd進行讀操作。該套接字不再接受數據,任何當前在套接字接受緩沖區的數據將被無聲的丟棄掉。SHUT_WR(1):		關閉sockfd的寫功能,此選項將不允許sockfd進行寫操作。進程不能在對此套接字發出寫操作。SHUT_RDWR(2):	關閉sockfd的讀寫功能。相當于調用shutdown兩次:首先是以SHUT_RD,然后以SHUT_WR。

使用close中止一個連接,但它只是減少描述符的引用計數,并不直接關閉連接,只有當描述符的引用計數為0時才關閉連接。
shutdown不考慮描述符的引用計數,直接關閉描述符。也可選擇中止一個方向的連接,只中止讀或只中止寫。
注意:

  1. 如果有多個進程共享一個套接字,close每被調用一次,計數減1,直到計數為0時,也就是所用進程都調用了close,套接字將被釋放。

  2. 在多進程中如果一個進程調用了shutdown(sfd, SHUT_RDWR)后,其它的進程將無法進行通信。但,如果一個進程close(sfd)將不會影響到其它進程。
    2MSL
    2MSL (Maximum Segment Lifetime) TIME_WAIT狀態的存在有兩個理由:

    (1)讓4次握手關閉流程更加可靠;4次握手的最后一個ACK是是由主動關閉方發送出去的,若這個ACK丟失,被動關閉方會再次發一個FIN過來。若主動關閉方能夠保持一個2MSL的TIME_WAIT狀態,則有更大的機會讓丟失的ACK被再次發送出去。
    (2)防止lost duplicate對后續新建正常鏈接的傳輸造成破壞。lost uplicate在實際的網絡中非常常見,經常是由于路由器產生故障,路徑無法收斂,導致一個packet在路由器A,B,C之間做類似死循環的跳轉。IP頭部有個TTL,限制了一個包在網絡中的最大跳數,因此這個包有兩種命運,要么最后TTL變為0,在網絡中消失;要么TTL在變為0之前路由器路徑收斂,它憑借剩余的TTL跳數終于到達目的地。但非常可惜的是TCP通過超時重傳機制在早些時候發送了一個跟它一模一樣的包,并先于它達到了目的地,因此它的命運也就注定被TCP協議棧拋棄。

另外一個概念叫做incarnation connection,指跟上次的socket pair一摸一樣的新連接,叫做incarnation of previous connection。lost uplicate加上incarnation connection,則會對我們的傳輸造成致命的錯誤。
TCP是流式的,所有包到達的順序是不一致的,依靠序列號由TCP協議棧做順序的拼接;假設一個incarnation connection這時收到的seq=1000, 來了一個lost duplicate為seq=1000,len=1000, 則TCP認為這個lost duplicate合法,并存放入了receive buffer,導致傳輸出現錯誤。通過一個2MSL TIME_WAIT狀態,確保所有的lost duplicate都會消失掉,避免對新連接造成錯誤。
該狀態為什么設計在主動關閉這一方:

(1)發最后ACK的是主動關閉一方。
(2)只要有一方保持TIME_WAIT狀態,就能起到避免incarnation connection在2MSL內的重新建立,不需要兩方都有。

如何正確對待2MSL TIME_WAIT?
RFC要求socket pair在處于TIME_WAIT時,不能再起一個incarnation connection。但絕大部分TCP實現,強加了更為嚴格的限制。在2MSL等待期間,socket中使用的本地端口在默認情況下不能再被使用。
若A 10.234.5.5 : 1234和B 10.55.55.60 : 6666建立了連接,A主動關閉,那么在A端只要port為1234,無論對方的port和ip是什么,都不允許再起服務。這甚至比RFC限制更為嚴格,RFC僅僅是要求socket pair不一致,而實現當中只要這個port處于TIME_WAIT,就不允許起連接。這個限制對主動打開方來說是無所謂的,因為一般用的是臨時端口;但對于被動打開方,一般是server,就悲劇了,因為server一般是熟知端口。比如http,一般端口是80,不可能允許這個服務在2MSL內不能起來。
解決方案是給服務器的socket設置SO_REUSEADDR選項,這樣的話就算熟知端口處于TIME_WAIT狀態,在這個端口上依舊可以將服務啟動。當然,雖然有了SO_REUSEADDR選項,但sockt pair這個限制依舊存在。比如上面的例子,A通過SO_REUSEADDR選項依舊在1234端口上起了監聽,但這時我們若是從B通過6666端口去連它,TCP協議會告訴我們連接失敗,原因為Address already in use.
RFC 793中規定MSL為2分鐘,實際應用中常用的是30秒,1分鐘和2分鐘等。
RFC (Request For Comments),是一系列以編號排定的文件。收集了有關因特網相關資訊,以及UNIX和因特網社群的軟件文件。

程序設計中的問題

做一個測試,首先啟動server,然后啟動client,用Ctrl-C終止server,馬上再運行server,運行結果:
itcast$ ./server
bind error: Address already in use
這是因為,雖然server的應用程序終止了,但TCP協議層的連接并沒有完全斷開,因此不能再次監聽同樣的server端口。我們用netstat命令查看一下:

itcast$ netstat -apn |grep 6666
tcp        1      0 192.168.1.11:38103      192.168.1.11:6666       CLOSE_WAIT  3525/client     
tcp        0      0 192.168.1.11:6666       192.168.1.11:38103      FIN_WAIT2   -           

server終止時,socket描述符會自動關閉并發FIN段給client,client收到FIN后處于CLOSE_WAIT狀態,但是client并沒有終止,也沒有關閉socket描述符,因此不會發FIN給server,因此server的TCP連接處于FIN_WAIT2狀態。
現在用Ctrl-C把client也終止掉,再觀察現象:

itcast$ netstat -apn |grep 6666
tcp        0      0 192.168.1.11:6666       192.168.1.11:38104      TIME_WAIT   -
itcast$ ./server
bind error: Address already in use

client終止時自動關閉socket描述符,server的TCP連接收到client發的FIN段后處于TIME_WAIT狀態。TCP協議規定,主動關閉連接的一方要處于TIME_WAIT狀態,等待兩個MSL(maximum segment lifetime)的時間后才能回到CLOSED狀態,因為我們先Ctrl-C終止了server,所以server是主動關閉連接的一方,在TIME_WAIT期間仍然不能再次監聽同樣的server端口。

MSL在RFC 1122中規定為兩分鐘,但是各操作系統的實現不同,在Linux上一般經過半分鐘后就可以再次啟動server了。為什么要規定TIME_WAIT的時間,

端口復用

在server的TCP連接沒有完全斷開之前不允許重新監聽是不合理的。因為,TCP連接沒有完全斷開指的是connfd(127.0.0.1:6666)沒有完全斷開,而我們重新監聽的是lis-tenfd(0.0.0.0:6666),雖然是占用同一個端口,但IP地址不同,connfd對應的是與某個客戶端通訊的一個具體的IP地址,而listenfd對應的是wildcard address。解決這個問題的方法是使用setsockopt()設置socket描述符的選項SO_REUSEADDR為1,表示允許創建端口號相同但IP地址不同的多個socket描述符。
在server代碼的socket()和bind()調用之間插入如下代碼:

	int opt = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

TCP異常斷開

心跳檢測機制

在TCP網絡通信中,經常會出現客戶端和服務器之間的非正常斷開,需要實時檢測查詢鏈接狀態。常用的解決方法就是在程序中加入心跳機制。
Heart-Beat線程
這個是最常用的簡單方法。在接收和發送數據時個人設計一個守護進程(線程),定時發送Heart-Beat包,客戶端/服務器收到該小包后,立刻返回相應的包即可檢測對方是否實時在線。
該方法的好處是通用,但缺點就是會改變現有的通訊協議!大家一般都是使用業務層心跳來處理,主要是靈活可控。
UNIX網絡編程不推薦使用SO_KEEPALIVE來做心跳檢測,還是在業務層以心跳包做檢測比較好,也方便控制。

設置TCP屬性

SO_KEEPALIVE 保持連接檢測對方主機是否崩潰,避免(服務器)永遠阻塞于TCP連接的輸入。設置該選項后,如果2小時內在此套接口的任一方向都沒有數據交換,TCP就自動給對方發一個保持存活探測分節(keepalive probe)。這是一個對方必須響應的TCP分節.它會導致以下三種情況:對方接收一切正常:以期望的ACK響應。

2小時后,TCP將發出另一個探測分節。對方已崩潰且已重新啟動:以RST響應。
套接口的待處理錯誤被置為ECONNRESET,套接 口本身則被關閉。
對方無任何響應:源自berkeley的TCP發送另外8個探測分節,相隔75秒一個,試圖得到一個響應。
在發出第一個探測分節11分鐘 15秒后若仍無響應就放棄。
套接口的待處理錯誤被置為ETIMEOUT,套接口本身則被關閉。
如ICMP錯誤是“host unreachable(主機不可達)”,說明對方主機并沒有崩潰,但是不可達,這種情況下待處理錯誤被置為EHOSTUNREACH。

根據上面的介紹我們可以知道對端以一種非優雅的方式斷開連接的時候,我們可以設置SO_KEEPALIVE屬性使得我們在2小時以后發現對方的TCP連接是否依然存在。

keepAlive = 1;
setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));

如果我們不能接受如此之長的等待時間,從TCP-Keepalive-HOWTO上可以知道一共有兩種方式可以設置,一種是修改內核關于網絡方面的 配置參數,另外一種就是SOL_TCP字段的TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT三個選項。

  1. The tcp_keepidle parameter specifies the interval of inactivity that causes TCP to generate a KEEPALIVE transmission for an application that requests them. tcp_keepidle defaults to 14400 (two hours).
    /*開始首次KeepAlive探測前的TCP空閉時間 */

  2. The tcp_keepintvl parameter specifies the interval between the nine retriesthat are attempted if a KEEPALIVE transmission is not acknowledged. tcp_keep ntvldefaults to 150 (75 seconds).
    /* 兩次KeepAlive探測間的時間間隔 */

  3. The tcp_keepcnt option specifies the maximum number of keepalive probes tobe sent. The value of TCP_KEEPCNT is an integer value between 1 and n, where n s the value of the systemwide tcp_keepcnt parameter.
    /* 判定斷開前的KeepAlive探測次數*/
    int keepIdle = 1000;
    int keepInterval = 10;
    int keepCount = 10;

    Setsockopt(listenfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));
    Setsockopt(listenfd, SOL_TCP,TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
    Setsockopt(listenfd,SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

SO_KEEPALIVE設置空閑2小時才發送一個“保持存活探測分節”,不能保證實時檢測。對于判斷網絡斷開時間太長,對于需要及時響應的程序不太適應。

當然也可以修改時間間隔參數,但是會影響到所有打開此選項的套接口!關聯了完成端口的socket可能會忽略掉該套接字選項。

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

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

相關文章

linux編程手冊讀書筆記第一章(20140329)

&#xff08;2&#xff09;管道、FIFO、套接字、設備&#xff08;比如終端、偽終端&#xff09;都支持非阻塞模式。&#xff08;因為無法通過open&#xff08;&#xff09;來獲取管道和套接字的文件描述符。所以要啟用非阻塞標志&#xff0c;就必須使用fcntl&#xff08;&#…

排序(基本概念及分類,直接插入排序和希爾排序)

排序的概念 排序&#xff1a;所謂排序&#xff0c;就是使一串記錄&#xff0c;按照其中的某個或某些關鍵字的大小&#xff0c;遞增或遞減的排列起來的操作。 穩定性&#xff1a;假定在待排序的記錄序列中&#xff0c;存在多個具有相同的關鍵字的記錄&#xff0c;若經過排序&a…

Linux編程手冊讀書筆記第二章(20140330)

內核&#xff1a;管理和分配計算機資源&#xff08;即CPU、RAM和設備&#xff09;的核心軟件層Linux內核可執行文件采用&#xff0f;boot&#xff0f;vmlinuz或類似的路徑名&#xff0c;“z”表明內核是經過壓縮的可執行文件。內核主要任務&#xff1a; &#xff08;1&#xff…

直接交換排序

直接交換排序 缺點&#xff1a;進行一些重復性比較&#xff0c;解決放法&#xff1a;堆排序 選擇排序優化 //如果當前的數大于假定最大的數 //改變下標 //如果當前的數小于假定最小的數 //改變下標 //遍歷數組跳到下一個元素 //如果最大的數沒有在它的位置上 //交換 //交換…

Linux編程手冊讀書筆記第三章(20140407)

外殼函數執行一條中斷機器指令&#xff08;int 0x80&#xff09;&#xff0c;引發處理器從用戶態切換到核心態&#xff0c;并執行系統中斷0x80的中斷矢量所指向的代碼。&#xff08;在2.6內核及glib 2.3.2之后的版本都支持sysenter指令&#xff0c;進入內核的速度更快&#xff…

Linux編程手冊讀書筆記第四章(20140407)

標準文件描述符定義在<unistd.h>中&#xff0c;STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO打開一個文件&#xff1a;open&#xff08;&#xff09; &#xff03;include<sys/stat.h> #include<fcntl.h> int open(const char *pathname, int flags, …/* …

快速排序概念及實現

快速排序 快速排序是Hoare于1962年提出的一種二叉樹結構的交換排序方法&#xff0c; 其基本思想為&#xff1a; 任取待排序元素序列中的某元素作為基準值&#xff0c;按照該排序碼將待排序集合分割成兩子序列&#xff0c;左子序列中所有元素均小于基準值&#xff0c;右子序列…

Linux編程手冊讀書筆記第五章(20140408)

改變已打開文件性質&#xff1a;fcntl&#xff08;&#xff09; #include<fcntl.h> int fcntl(int fd, int cmd, …); (1) 調用失敗返回&#xff0d;1 &#xff08;2&#xff09;fcntl函數有5種功能&#xff1a; a. 復制一個現有的描述符&#xff08;cmd&#xff1d;F_D…

歸并排序概念及其實現

基本思想&#xff1a; 歸并排序&#xff08;MERGE-SORT&#xff09;是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一個非常典型的應用。將已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每個…

##連接符和#符的使用

C語言中如何使用宏C&#xff08;和C&#xff09;中的宏&#xff08;Macro&#xff09;屬于編譯器預處理的范疇&#xff0c;屬于編譯期概念&#xff08;而非運行期概念&#xff09;。下面對常遇到的宏的使用問題做了簡單總結。 關于#和## 在C語言的宏中&#xff0c;#的功能是將其…

計數排序和基數排序

適用于數據集中在某個范圍中&#xff0c; //統計每個數據出現的次數 計數排序&#xff1a;鴿巢原理 1找范圍 2給空間 3記次數 4回收 for(int i 0;i<size; i) {temp[array[i]]; }for(int i0;i<range;i&#xff09;{while(temp[i])array[index]i;}代碼實現 時間復雜度&…

信號量sem_wait()的使用

閑來無事&#xff0c;我給大家講下UNIX/Linux下信號量函數的使用。首先你得知道什么叫信號量&#xff0c;什么時候要用信號量。這個嘛&#xff0c;主要就是用來保護共享資源的&#xff0c;也就是說如果你想限制某個&#xff08;些&#xff09;資源在同一時刻只能有一&#xff0…

C++起始(關鍵字,命名空間,缺省參數,函數重載(c語言為什么不支持函數重載))

1. C關鍵字(C98) 2. 命名空間 在C/C中&#xff0c;變量、函數和后面要學到的類都是大量存在的&#xff0c;這些變量、函數和類的名稱將都存在于全局作用 域中&#xff0c;可能會導致很多沖突。使用命名空間的目的是對標識符的名稱進行本地化&#xff0c;以避免命名沖突或名字污…

va_list和vsnprintf、getopt

原理解釋&#xff1a; VA_LIST 是在C語言中解決變參問題的一組宏&#xff0c;在<stdarg.h>頭文件下。 VA_LIST的用法&#xff1a; &#xff08;1&#xff09;首先在函數里定義一具VA_LIST型的變量&#xff0c;這個變量是指向參數的指針 &#xff08;2&a…

GitHub相關

git是一個版本控制工具. 主要解決三個問題 代碼被喵星人吃掉了.產品經理反復修改需求, 需要同時維護多個版本代碼.多人協同開發. 安裝 git for windows 這個是一個git的windows系統的命令行版本 https://git-scm.com/downloads 下載會很慢很慢 使用 Github 創建項目 注冊…

linux中bin與sbin目錄的作用及區別介紹

在linux系統中&#xff0c;有兩個重要的目錄&#xff1a;bin與sbin&#xff0c;分別包括/bin、/usr/bin/與/sbin、/usr/sbin/。 bin: bin為binary的簡寫&#xff0c;主要放置系統的必備執行文件&#xff0c;例如: cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、m…

c++起始(名詞修飾,extern “C” ,引用)

名字修飾(name Mangling) 在C/C中&#xff0c;一個程序要運行起來&#xff0c;需要經歷以下幾個階段&#xff1a;預處理、編譯、匯編、鏈接。 Name Mangling是一種在編譯過程中&#xff0c;將函數、變量的名稱重新改編的機制&#xff0c;簡單來說就是編譯器為了區分各 個函數…

Linux進程間通信方式--本地socket

先上一個代碼 服務端&#xff1a; [cpp] view plaincopy //s_unix.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #define UNIX_DOMAIN "/tmp/UNIX.domain" int main(void) { so…

extern和static的區別

c語言中的 static&#xff1a; 修飾局部變量&#xff1a;存放在靜態數據區&#xff0c;生命周期位整個程序結束&#xff0c;但作用于仍為函數局部。 修飾全局變量&#xff1a;無法被同一工程其他源文件訪問。 修飾函數&#xff1a;與全局變量類似。 extern&#xff1a; 可被…

RT5350原廠SDK及AP移植步驟詳解

最近想搞一下rt5350&#xff0c;所以找了個原廠的SDK包進行了編譯&#xff0c;很快路由器就可以用了&#xff0c;把我的編譯操作步驟寫了下分享給更多的愛好者&#xff0c;供大家參靠&#xff0c;下一步準備移植攝像頭玩玩。有興趣的可以一起交流。 RT5350移植Toolchain工具的安…