www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 程序喵大人
[導(dǎo)讀]前言 開(kāi)場(chǎng)小故事 炎炎夏日,張三騎著單車(chē)去面試花了 1 小時(shí),一路上汗流浹背。 結(jié)果面試過(guò)程只花了 5 分鐘就結(jié)束了,面完的時(shí)候,天還是依然是亮的,還得在烈日下奔波 1 小時(shí)回去。 面試五分鐘,騎車(chē)兩小時(shí)。 你看,張三因面試沒(méi)準(zhǔn)備好,吹空調(diào)的時(shí)間只有 5


前言

開(kāi)場(chǎng)小故事

炎炎夏日,張三騎著單車(chē)去面試花了 1 小時(shí),一路上汗流浹背。

結(jié)果面試過(guò)程只花了 5 分鐘就結(jié)束了,面完的時(shí)候,天還是依然是亮的,還得在烈日下奔波 1 小時(shí)回去。

面試五分鐘,騎車(chē)兩小時(shí)。

你看,張三因面試沒(méi)準(zhǔn)備好,吹空調(diào)的時(shí)間只有 5 分鐘,來(lái)回路上花了 2 小時(shí)曬太陽(yáng),你說(shuō)慘不慘?

所以啊,炎炎夏日,為了能延長(zhǎng)吹空調(diào)的時(shí)間,我們應(yīng)該在面試前準(zhǔn)備得更充分些,吹空調(diào)時(shí)間是要自己爭(zhēng)取的。

很明顯,在這一場(chǎng)面試中, 張三在進(jìn)程通信這一塊沒(méi)復(fù)習(xí)好,雖然列出了進(jìn)程間通信的方式,但這只是表面功夫,應(yīng)該需要進(jìn)一步了解每種通信方式的優(yōu)缺點(diǎn)及應(yīng)用場(chǎng)景。

說(shuō)真的,我們這次一起幫張三一起復(fù)習(xí)下,加深他對(duì)進(jìn)程間通信的理解,好讓他下次吹空調(diào)的時(shí)間能長(zhǎng)一點(diǎn)。


正文

每個(gè)進(jìn)程的用戶(hù)地址空間都是獨(dú)立的,一般而言是不能互相訪問(wèn)的,但內(nèi)核空間是每個(gè)進(jìn)程都共享的,所以進(jìn)程之間要通信必須通過(guò)內(nèi)核。

Linux 內(nèi)核提供了不少進(jìn)程間通信的機(jī)制,我們來(lái)一起瞧瞧有哪些?

管道

如果你學(xué)過(guò) Linux 命令,那你肯定很熟悉「|」這個(gè)豎線。

$ ps auxf | grep mysql

上面命令行里的「|」豎線就是一個(gè)管道,它的功能是將前一個(gè)命令(ps auxf)的輸出,作為后一個(gè)命令(grep mysql)的輸入,從這功能描述,可以看出管道傳輸數(shù)據(jù)是單向的,如果想相互通信,我們需要?jiǎng)?chuàng)建兩個(gè)管道才行。

同時(shí),我們得知上面這種管道是沒(méi)有名字,所以「|」表示的管道稱(chēng)為匿名管道,用完了就銷(xiāo)毀。

管道還有另外一個(gè)類(lèi)型是命名管道,也被叫做 FIFO,因?yàn)閿?shù)據(jù)是先進(jìn)先出的傳輸方式。

在使用命名管道前,先需要通過(guò) mkfifo 命令來(lái)創(chuàng)建,并且指定管道名字:

$ mkfifo myPipe

myPipe 就是這個(gè)管道的名稱(chēng),基于 Linux 一切皆文件的理念,所以管道也是以文件的方式存在,我們可以用 ls 看一下,這個(gè)文件的類(lèi)型是 p,也就是 pipe(管道) 的意思:

$ ls -l
prw-r--r--. 1 root    root         0 Jul 17 02:45 myPipe

接下來(lái),我們往 myPipe 這個(gè)管道寫(xiě)入數(shù)據(jù):

echo "hello" > myPipe  // 將數(shù)據(jù)寫(xiě)進(jìn)管道
                         // 停住了 ...

你操作了后,你會(huì)發(fā)現(xiàn)命令執(zhí)行后就停在這了,這是因?yàn)楣艿览锏膬?nèi)容沒(méi)有被讀取,只有當(dāng)管道里的數(shù)據(jù)被讀完后,命令才可以正常退出。

于是,我們執(zhí)行另外一個(gè)命令來(lái)讀取這個(gè)管道里的數(shù)據(jù):

$ cat < myPipe  // 讀取管道里的數(shù)據(jù)
hello

可以看到,管道里的內(nèi)容被讀取出來(lái)了,并打印在了終端上,另外一方面,echo 那個(gè)命令也正常退出了。

我們可以看出,管道這種通信方式效率低,不適合進(jìn)程間頻繁地交換數(shù)據(jù)。當(dāng)然,它的好處,自然就是簡(jiǎn)單,同時(shí)也我們很容易得知管道里的數(shù)據(jù)已經(jīng)被另一個(gè)進(jìn)程讀取了。

那管道如何創(chuàng)建呢,背后原理是什么?

匿名管道的創(chuàng)建,需要通過(guò)下面這個(gè)系統(tǒng)調(diào)用:

int pipe(int fd[2])

這里表示創(chuàng)建一個(gè)匿名管道,并返回了兩個(gè)描述符,一個(gè)是管道的讀取端描述符 fd[0],另一個(gè)是管道的寫(xiě)入端描述符 fd[1]。注意,這個(gè)匿名管道是特殊的文件,只存在于內(nèi)存,不存于文件系統(tǒng)中。

其實(shí),所謂的管道,就是內(nèi)核里面的一串緩存。從管道的一段寫(xiě)入的數(shù)據(jù),實(shí)際上是緩存在內(nèi)核中的,另一端讀取,也就是從內(nèi)核中讀取這段數(shù)據(jù)。另外,管道傳輸?shù)臄?shù)據(jù)是無(wú)格式的流且大小受限。

看到這,你可能會(huì)有疑問(wèn)了,這兩個(gè)描述符都是在一個(gè)進(jìn)程里面,并沒(méi)有起到進(jìn)程間通信的作用,怎么樣才能使得管道是跨過(guò)兩個(gè)進(jìn)程的呢?

我們可以使用 fork 創(chuàng)建子進(jìn)程,創(chuàng)建的子進(jìn)程會(huì)復(fù)制父進(jìn)程的文件描述符,這樣就做到了兩個(gè)進(jìn)程各有兩個(gè)「 fd[0]fd[1]」,兩個(gè)進(jìn)程就可以通過(guò)各自的 fd 寫(xiě)入和讀取同一個(gè)管道文件實(shí)現(xiàn)跨進(jìn)程通信了。

管道只能一端寫(xiě)入,另一端讀出,所以上面這種模式容易造成混亂,因?yàn)楦高M(jìn)程和子進(jìn)程都可以同時(shí)寫(xiě)入,也都可以讀出。那么,為了避免這種情況,通常的做法是:

  • 父進(jìn)程關(guān)閉讀取的 fd[0],只保留寫(xiě)入的 fd[1];

  • 子進(jìn)程關(guān)閉寫(xiě)入的 fd[1],只保留讀取的 fd[0];

所以說(shuō)如果需要雙向通信,則應(yīng)該創(chuàng)建兩個(gè)管道。

到這里,我們僅僅解析了使用管道進(jìn)行父進(jìn)程與子進(jìn)程之間的通信,但是在我們 shell 里面并不是這樣的。

在 shell 里面執(zhí)行 A | B命令的時(shí)候,A 進(jìn)程和 B 進(jìn)程都是 shell 創(chuàng)建出來(lái)的子進(jìn)程,A 和 B 之間不存在父子關(guān)系,它倆的父進(jìn)程都是 shell。

所以說(shuō),在 shell 里通過(guò)「|」匿名管道將多個(gè)命令連接在一起,實(shí)際上也就是創(chuàng)建了多個(gè)子進(jìn)程,那么在我們編寫(xiě) shell 腳本時(shí),能使用一個(gè)管道搞定的事情,就不要多用一個(gè)管道,這樣可以減少創(chuàng)建子進(jìn)程的系統(tǒng)開(kāi)銷(xiāo)。

我們可以得知,對(duì)于匿名管道,它的通信范圍是存在父子關(guān)系的進(jìn)程。因?yàn)楣艿罌](méi)有實(shí)體,也就是沒(méi)有管道文件,只能通過(guò) fork 來(lái)復(fù)制父進(jìn)程 fd 文件描述符,來(lái)達(dá)到通信的目的。

另外,對(duì)于命名管道,它可以在不相關(guān)的進(jìn)程間也能相互通信。因?yàn)槊罟艿?,提前?chuàng)建了一個(gè)類(lèi)型為管道的設(shè)備文件,在進(jìn)程里只要使用這個(gè)設(shè)備文件,就可以相互通信。

不管是匿名管道還是命名管道,進(jìn)程寫(xiě)入的數(shù)據(jù)都是緩存在內(nèi)核中,另一個(gè)進(jìn)程讀取數(shù)據(jù)時(shí)候自然也是從內(nèi)核中獲取,同時(shí)通信數(shù)據(jù)都遵循先進(jìn)先出原則,不支持 lseek 之類(lèi)的文件定位操作。


消息隊(duì)列

前面說(shuō)到管道的通信方式是效率低的,因此管道不適合進(jìn)程間頻繁地交換數(shù)據(jù)。

對(duì)于這個(gè)問(wèn)題,消息隊(duì)列的通信模式就可以解決。比如,A 進(jìn)程要給 B 進(jìn)程發(fā)送消息,A 進(jìn)程把數(shù)據(jù)放在對(duì)應(yīng)的消息隊(duì)列后就可以正常返回了,B 進(jìn)程需要的時(shí)候再去讀取數(shù)據(jù)就可以了。同理,B 進(jìn)程要給 A 進(jìn)程發(fā)送消息也是如此。

再來(lái),消息隊(duì)列是保存在內(nèi)核中的消息鏈表,在發(fā)送數(shù)據(jù)時(shí),會(huì)分成一個(gè)一個(gè)獨(dú)立的數(shù)據(jù)單元,也就是消息體(數(shù)據(jù)塊),消息體是用戶(hù)自定義的數(shù)據(jù)類(lèi)型,消息的發(fā)送方和接收方要約定好消息體的數(shù)據(jù)類(lèi)型,所以每個(gè)消息體都是固定大小的存儲(chǔ)塊,不像管道是無(wú)格式的字節(jié)流數(shù)據(jù)。如果進(jìn)程從消息隊(duì)列中讀取了消息體,內(nèi)核就會(huì)把這個(gè)消息體刪除。

消息隊(duì)列生命周期隨內(nèi)核,如果沒(méi)有釋放消息隊(duì)列或者沒(méi)有關(guān)閉操作系統(tǒng),消息隊(duì)列會(huì)一直存在,而前面提到的匿名管道的生命周期,是隨進(jìn)程的創(chuàng)建而建立,隨進(jìn)程的結(jié)束而銷(xiāo)毀。

消息這種模型,兩個(gè)進(jìn)程之間的通信就像平時(shí)發(fā)郵件一樣,你來(lái)一封,我回一封,可以頻繁溝通了。

但郵件的通信方式存在不足的地方有兩點(diǎn),一是通信不及時(shí),二是附件也有大小限制,這同樣也是消息隊(duì)列通信不足的點(diǎn)。

消息隊(duì)列不適合比較大數(shù)據(jù)的傳輸,因?yàn)樵趦?nèi)核中每個(gè)消息體都有一個(gè)最大長(zhǎng)度的限制,同時(shí)所有隊(duì)列所包含的全部消息體的總長(zhǎng)度也是有上限。在 Linux 內(nèi)核中,會(huì)有兩個(gè)宏定義 MSGMAXMSGMNB,它們以字節(jié)為單位,分別定義了一條消息的最大長(zhǎng)度和一個(gè)隊(duì)列的最大長(zhǎng)度。

消息隊(duì)列通信過(guò)程中,存在用戶(hù)態(tài)與內(nèi)核態(tài)之間的數(shù)據(jù)拷貝開(kāi)銷(xiāo),因?yàn)檫M(jìn)程寫(xiě)入數(shù)據(jù)到內(nèi)核中的消息隊(duì)列時(shí),會(huì)發(fā)生從用戶(hù)態(tài)拷貝數(shù)據(jù)到內(nèi)核態(tài)的過(guò)程,同理另一進(jìn)程讀取內(nèi)核中的消息數(shù)據(jù)時(shí),會(huì)發(fā)生從內(nèi)核態(tài)拷貝數(shù)據(jù)到用戶(hù)態(tài)的過(guò)程。


共享內(nèi)存

消息隊(duì)列的讀取和寫(xiě)入的過(guò)程,都會(huì)有發(fā)生用戶(hù)態(tài)與內(nèi)核態(tài)之間的消息拷貝過(guò)程。那共享內(nèi)存的方式,就很好的解決了這一問(wèn)題。

現(xiàn)代操作系統(tǒng),對(duì)于內(nèi)存管理,采用的是虛擬內(nèi)存技術(shù),也就是每個(gè)進(jìn)程都有自己獨(dú)立的虛擬內(nèi)存空間,不同進(jìn)程的虛擬內(nèi)存映射到不同的物理內(nèi)存中。所以,即使進(jìn)程 A 和 進(jìn)程 B 的虛擬地址是一樣的,其實(shí)訪問(wèn)的是不同的物理內(nèi)存地址,對(duì)于數(shù)據(jù)的增刪查改互不影響。

共享內(nèi)存的機(jī)制,就是拿出一塊虛擬地址空間來(lái),映射到相同的物理內(nèi)存中。這樣這個(gè)進(jìn)程寫(xiě)入的東西,另外一個(gè)進(jìn)程馬上就能看到了,都不需要拷貝來(lái)拷貝去,傳來(lái)傳去,大大提高了進(jìn)程間通信的速度。


信號(hào)量

用了共享內(nèi)存通信方式,帶來(lái)新的問(wèn)題,那就是如果多個(gè)進(jìn)程同時(shí)修改同一個(gè)共享內(nèi)存,很有可能就沖突了。例如兩個(gè)進(jìn)程都同時(shí)寫(xiě)一個(gè)地址,那先寫(xiě)的那個(gè)進(jìn)程會(huì)發(fā)現(xiàn)內(nèi)容被別人覆蓋了。

為了防止多進(jìn)程競(jìng)爭(zhēng)共享資源,而造成的數(shù)據(jù)錯(cuò)亂,所以需要保護(hù)機(jī)制,使得共享的資源,在任意時(shí)刻只能被一個(gè)進(jìn)程訪問(wèn)。正好,信號(hào)量就實(shí)現(xiàn)了這一保護(hù)機(jī)制。

信號(hào)量其實(shí)是一個(gè)整型的計(jì)數(shù)器,主要用于實(shí)現(xiàn)進(jìn)程間的互斥與同步,而不是用于緩存進(jìn)程間通信的數(shù)據(jù)

信號(hào)量表示資源的數(shù)量,控制信號(hào)量的方式有兩種原子操作:

  • 一個(gè)是 P 操作,這個(gè)操作會(huì)把信號(hào)量減去 -1,相減后如果信號(hào)量 < 0,則表明資源已被占用,進(jìn)程需阻塞等待;相減后如果信號(hào)量 >= 0,則表明還有資源可使用,進(jìn)程可正常繼續(xù)執(zhí)行。

  • 另一個(gè)是 V 操作,這個(gè)操作會(huì)把信號(hào)量加上 1,相加后如果信號(hào)量 <= 0,則表明當(dāng)前有阻塞中的進(jìn)程,于是會(huì)將該進(jìn)程喚醒運(yùn)行;相加后如果信號(hào)量 > 0,則表明當(dāng)前沒(méi)有阻塞中的進(jìn)程;

P 操作是用在進(jìn)入共享資源之前,V 操作是用在離開(kāi)共享資源之后,這兩個(gè)操作是必須成對(duì)出現(xiàn)的。

接下來(lái),舉個(gè)例子,如果要使得兩個(gè)進(jìn)程互斥訪問(wèn)共享內(nèi)存,我們可以初始化信號(hào)量為 1。

具體的過(guò)程如下:

  • 進(jìn)程 A 在訪問(wèn)共享內(nèi)存前,先執(zhí)行了 P 操作,由于信號(hào)量的初始值為 1,故在進(jìn)程 A 執(zhí)行 P 操作后信號(hào)量變?yōu)?0,表示共享資源可用,于是進(jìn)程 A 就可以訪問(wèn)共享內(nèi)存。

  • 若此時(shí),進(jìn)程 B 也想訪問(wèn)共享內(nèi)存,執(zhí)行了 P 操作,結(jié)果信號(hào)量變?yōu)榱?-1,這就意味著臨界資源已被占用,因此進(jìn)程 B 被阻塞。

  • 直到進(jìn)程 A 訪問(wèn)完共享內(nèi)存,才會(huì)執(zhí)行 V 操作,使得信號(hào)量恢復(fù)為 0,接著就會(huì)喚醒阻塞中的線程 B,使得進(jìn)程 B 可以訪問(wèn)共享內(nèi)存,最后完成共享內(nèi)存的訪問(wèn)后,執(zhí)行 V 操作,使信號(hào)量恢復(fù)到初始值 1。

可以發(fā)現(xiàn),信號(hào)初始化為 1,就代表著是互斥信號(hào)量,它可以保證共享內(nèi)存在任何時(shí)刻只有一個(gè)進(jìn)程在訪問(wèn),這就很好的保護(hù)了共享內(nèi)存。

另外,在多進(jìn)程里,每個(gè)進(jìn)程并不一定是順序執(zhí)行的,它們基本是以各自獨(dú)立的、不可預(yù)知的速度向前推進(jìn),但有時(shí)候我們又希望多個(gè)進(jìn)程能密切合作,以實(shí)現(xiàn)一個(gè)共同的任務(wù)。

例如,進(jìn)程 A 是負(fù)責(zé)生產(chǎn)數(shù)據(jù),而進(jìn)程 B 是負(fù)責(zé)讀取數(shù)據(jù),這兩個(gè)進(jìn)程是相互合作、相互依賴(lài)的,進(jìn)程 A 必須先生產(chǎn)了數(shù)據(jù),進(jìn)程 B 才能讀取到數(shù)據(jù),所以執(zhí)行是有前后順序的。

那么這時(shí)候,就可以用信號(hào)量來(lái)實(shí)現(xiàn)多進(jìn)程同步的方式,我們可以初始化信號(hào)量為 0。

具體過(guò)程:

  • 如果進(jìn)程 B 比進(jìn)程 A 先執(zhí)行了,那么執(zhí)行到 P 操作時(shí),由于信號(hào)量初始值為 0,故信號(hào)量會(huì)變?yōu)?-1,表示進(jìn)程 A 還沒(méi)生產(chǎn)數(shù)據(jù),于是進(jìn)程 B 就阻塞等待;

  • 接著,當(dāng)進(jìn)程 A 生產(chǎn)完數(shù)據(jù)后,執(zhí)行了 V 操作,就會(huì)使得信號(hào)量變?yōu)?0,于是就會(huì)喚醒阻塞在 P 操作的進(jìn)程 B;

  • 最后,進(jìn)程 B 被喚醒后,意味著進(jìn)程 A 已經(jīng)生產(chǎn)了數(shù)據(jù),于是進(jìn)程 B 就可以正常讀取數(shù)據(jù)了。

可以發(fā)現(xiàn),信號(hào)初始化為 0,就代表著是同步信號(hào)量,它可以保證進(jìn)程 A 應(yīng)在進(jìn)程 B 之前執(zhí)行。


信號(hào)

上面說(shuō)的進(jìn)程間通信,都是常規(guī)狀態(tài)下的工作模式。對(duì)于異常情況下的工作模式,就需要用「信號(hào)」的方式來(lái)通知進(jìn)程。

信號(hào)跟信號(hào)量雖然名字相似度 66.66%,但兩者用途完全不一樣,就好像 Java 和 JavaScript 的區(qū)別。

在 Linux 操作系統(tǒng)中, 為了響應(yīng)各種各樣的事件,提供了幾十種信號(hào),分別代表不同的意義。我們可以通過(guò) kill -l 命令,查看所有的信號(hào):

$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

運(yùn)行在 shell 終端的進(jìn)程,我們可以通過(guò)鍵盤(pán)輸入某些組合鍵的時(shí)候,給進(jìn)程發(fā)送信號(hào)。例如

  • Ctrl+C 產(chǎn)生 SIGINT 信號(hào),表示終止該進(jìn)程;

  • Ctrl+Z 產(chǎn)生 SIGTSTP 信號(hào),表示停止該進(jìn)程,但還未結(jié)束;

如果進(jìn)程在后臺(tái)運(yùn)行,可以通過(guò) kill 命令的方式給進(jìn)程發(fā)送信號(hào),但前提需要知道運(yùn)行中的進(jìn)程 PID 號(hào),例如:

  • kill -9 1050 ,表示給 PID 為 1050 的進(jìn)程發(fā)送 SIGKILL 信號(hào),用來(lái)立即結(jié)束該進(jìn)程;

所以,信號(hào)事件的來(lái)源主要有硬件來(lái)源(如鍵盤(pán) Cltr+C )和軟件來(lái)源(如 kill 命令)。

信號(hào)是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,因?yàn)榭梢栽谌魏螘r(shí)候發(fā)送信號(hào)給某一進(jìn)程,一旦有信號(hào)產(chǎn)生,我們就有下面這幾種,用戶(hù)進(jìn)程對(duì)信號(hào)的處理方式。

1.執(zhí)行默認(rèn)操作。Linux 對(duì)每種信號(hào)都規(guī)定了默認(rèn)操作,例如,上面列表中的 SIGTERM 信號(hào),就是終止進(jìn)程的意思。Core 的意思是 Core Dump,也即終止進(jìn)程后,通過(guò) Core Dump 將當(dāng)前進(jìn)程的運(yùn)行狀態(tài)保存在文件里面,方便程序員事后進(jìn)行分析問(wèn)題在哪里。

2.捕捉信號(hào)。我們可以為信號(hào)定義一個(gè)信號(hào)處理函數(shù)。當(dāng)信號(hào)發(fā)生時(shí),我們就執(zhí)行相應(yīng)的信號(hào)處理函數(shù)。

3.忽略信號(hào)。當(dāng)我們不希望處理某些信號(hào)的時(shí)候,就可以忽略該信號(hào),不做任何處理。有兩個(gè)信號(hào)是應(yīng)用進(jìn)程無(wú)法捕捉和忽略的,即 SIGKILLSEGSTOP,它們用于在任何時(shí)候中斷或結(jié)束某一進(jìn)程。


Socket

前面提到的管道、消息隊(duì)列、共享內(nèi)存、信號(hào)量和信號(hào)都是在同一臺(tái)主機(jī)上進(jìn)行進(jìn)程間通信,那要想跨網(wǎng)絡(luò)與不同主機(jī)上的進(jìn)程之間通信,就需要 Socket 通信了。

實(shí)際上,Socket 通信不僅可以跨網(wǎng)絡(luò)與不同主機(jī)的進(jìn)程間通信,還可以在同主機(jī)上進(jìn)程間通信。

我們來(lái)看看創(chuàng)建 socket 的系統(tǒng)調(diào)用:

int socket(int domain, int type, int protocal)

三個(gè)參數(shù)分別代表:

  • domain 參數(shù)用來(lái)指定協(xié)議族,比如 AF_INET 用于 IPV4、AF_INET6 用于 IPV6、AF_LOCAL/AF_UNIX 用于本機(jī);

  • type 參數(shù)用來(lái)指定通信特性,比如 SOCK_STREAM 表示的是字節(jié)流,對(duì)應(yīng) TCP、SOCK_DGRAM  表示的是數(shù)據(jù)報(bào),對(duì)應(yīng) UDP、SOCK_RAW 表示的是原始套接字;

  • protocal 參數(shù)原本是用來(lái)指定通信協(xié)議的,但現(xiàn)在基本廢棄。因?yàn)閰f(xié)議已經(jīng)通過(guò)前面兩個(gè)參數(shù)指定完成,protocol 目前一般寫(xiě)成 0 即可;

根據(jù)創(chuàng)建 socket 類(lèi)型的不同,通信的方式也就不同:

  • 實(shí)現(xiàn) TCP 字節(jié)流通信:socket 類(lèi)型是 AF_INET 和 SOCK_STREAM;

  • 實(shí)現(xiàn) UDP 數(shù)據(jù)報(bào)通信:socket 類(lèi)型是 AF_INET 和 SOCK_DGRAM;

  • 實(shí)現(xiàn)本地進(jìn)程間通信:「本地字節(jié)流 socket 」類(lèi)型是 AF_LOCAL 和 SOCK_STREAM,「本地?cái)?shù)據(jù)報(bào) socket 」類(lèi)型是 AF_LOCAL 和 SOCK_DGRAM。另外,AF_UNIX 和 AF_LOCAL 是等價(jià)的,所以 AF_UNIX 也屬于本地 socket;

接下來(lái),簡(jiǎn)單說(shuō)一下這三種通信的編程模式。

針對(duì) TCP 協(xié)議通信的 socket 編程模型

  • 服務(wù)端和客戶(hù)端初始化 socket,得到文件描述符;

  • 服務(wù)端調(diào)用 bind,將綁定在 IP 地址和端口;

  • 服務(wù)端調(diào)用 listen,進(jìn)行監(jiān)聽(tīng);

  • 服務(wù)端調(diào)用 accept,等待客戶(hù)端連接;

  • 客戶(hù)端調(diào)用 connect,向服務(wù)器端的地址和端口發(fā)起連接請(qǐng)求;

  • 服務(wù)端 accept 返回用于傳輸?shù)?socket 的文件描述符;

  • 客戶(hù)端調(diào)用 write 寫(xiě)入數(shù)據(jù);服務(wù)端調(diào)用 read 讀取數(shù)據(jù);

  • 客戶(hù)端斷開(kāi)連接時(shí),會(huì)調(diào)用 close,那么服務(wù)端 read 讀取數(shù)據(jù)的時(shí)候,就會(huì)讀取到了 EOF,待處理完數(shù)據(jù)后,服務(wù)端調(diào)用 close,表示連接關(guān)閉。

這里需要注意的是,服務(wù)端調(diào)用 accept 時(shí),連接成功了會(huì)返回一個(gè)已完成連接的 socket,后續(xù)用來(lái)傳輸數(shù)據(jù)。

所以,監(jiān)聽(tīng)的 socket 和真正用來(lái)傳送數(shù)據(jù)的 socket,是「兩個(gè)」 socket,一個(gè)叫作監(jiān)聽(tīng) socket,一個(gè)叫作已完成連接 socket。

成功連接建立之后,雙方開(kāi)始通過(guò) read 和 write 函數(shù)來(lái)讀寫(xiě)數(shù)據(jù),就像往一個(gè)文件流里面寫(xiě)東西一樣。

針對(duì) UDP 協(xié)議通信的 socket 編程模型

UDP 是沒(méi)有連接的,所以不需要三次握手,也就不需要像 TCP 調(diào)用 listen 和 connect,但是 UDP 的交互仍然需要 IP 地址和端口號(hào),因此也需要 bind。

對(duì)于 UDP 來(lái)說(shuō),不需要要維護(hù)連接,那么也就沒(méi)有所謂的發(fā)送方和接收方,甚至都不存在客戶(hù)端和服務(wù)端的概念,只要有一個(gè) socket 多臺(tái)機(jī)器就可以任意通信,因此每一個(gè) UDP 的 socket 都需要 bind。

另外,每次通信時(shí),調(diào)用 sendto 和 recvfrom,都要傳入目標(biāo)主機(jī)的 IP 地址和端口。

針對(duì)本地進(jìn)程間通信的 socket 編程模型

本地 socket  被用于在同一臺(tái)主機(jī)上進(jìn)程間通信的場(chǎng)景:

  • 本地 socket 的編程接口和 IPv4 、IPv6 套接字編程接口是一致的,可以支持「字節(jié)流」和「數(shù)據(jù)報(bào)」兩種協(xié)議;

  • 本地 socket 的實(shí)現(xiàn)效率大大高于 IPv4 和 IPv6 的字節(jié)流、數(shù)據(jù)報(bào) socket 實(shí)現(xiàn);

對(duì)于本地字節(jié)流 socket,其 socket 類(lèi)型是 AF_LOCAL 和 SOCK_STREAM。

對(duì)于本地?cái)?shù)據(jù)報(bào) socket,其 socket 類(lèi)型是 AF_LOCAL 和 SOCK_DGRAM。

本地字節(jié)流 socket 和 本地?cái)?shù)據(jù)報(bào) socket 在 bind 的時(shí)候,不像 TCP 和 UDP 要綁定 IP 地址和端口,而是綁定一個(gè)本地文件,這也就是它們之間的最大區(qū)別。


總結(jié)

由于每個(gè)進(jìn)程的用戶(hù)空間都是獨(dú)立的,不能相互訪問(wèn),這時(shí)就需要借助內(nèi)核空間來(lái)實(shí)現(xiàn)進(jìn)程間通信,原因很簡(jiǎn)單,每個(gè)進(jìn)程都是共享一個(gè)內(nèi)核空間。

Linux 內(nèi)核提供了不少進(jìn)程間通信的方式,其中最簡(jiǎn)單的方式就是管道,管道分為「匿名管道」和「命名管道」。

匿名管道顧名思義,它沒(méi)有名字標(biāo)識(shí),匿名管道是特殊文件只存在于內(nèi)存,沒(méi)有存在于文件系統(tǒng)中,shell 命令中的「|」豎線就是匿名管道,通信的數(shù)據(jù)是無(wú)格式的流并且大小受限,通信的方式是單向的,數(shù)據(jù)只能在一個(gè)方向上流動(dòng),如果要雙向通信,需要?jiǎng)?chuàng)建兩個(gè)管道,再來(lái)匿名管道是只能用于存在父子關(guān)系的進(jìn)程間通信,匿名管道的生命周期隨著進(jìn)程創(chuàng)建而建立,隨著進(jìn)程終止而消失。

命名管道突破了匿名管道只能在親緣關(guān)系進(jìn)程間的通信限制,因?yàn)槭褂妹艿赖那疤?,需要在文件系統(tǒng)創(chuàng)建一個(gè)類(lèi)型為 p 的設(shè)備文件,那么毫無(wú)關(guān)系的進(jìn)程就可以通過(guò)這個(gè)設(shè)備文件進(jìn)行通信。另外,不管是匿名管道還是命名管道,進(jìn)程寫(xiě)入的數(shù)據(jù)都是緩存在內(nèi)核中,另一個(gè)進(jìn)程讀取數(shù)據(jù)時(shí)候自然也是從內(nèi)核中獲取,同時(shí)通信數(shù)據(jù)都遵循先進(jìn)先出原則,不支持 lseek 之類(lèi)的文件定位操作。

消息隊(duì)列克服了管道通信的數(shù)據(jù)是無(wú)格式的字節(jié)流的問(wèn)題,消息隊(duì)列實(shí)際上是保存在內(nèi)核的「消息鏈表」,消息隊(duì)列的消息體是可以用戶(hù)自定義的數(shù)據(jù)類(lèi)型,發(fā)送數(shù)據(jù)時(shí),會(huì)被分成一個(gè)一個(gè)獨(dú)立的消息體,當(dāng)然接收數(shù)據(jù)時(shí),也要與發(fā)送方發(fā)送的消息體的數(shù)據(jù)類(lèi)型保持一致,這樣才能保證讀取的數(shù)據(jù)是正確的。消息隊(duì)列通信的速度不是最及時(shí)的,畢竟每次數(shù)據(jù)的寫(xiě)入和讀取都需要經(jīng)過(guò)用戶(hù)態(tài)與內(nèi)核態(tài)之間的拷貝過(guò)程。

共享內(nèi)存可以解決消息隊(duì)列通信中用戶(hù)態(tài)與內(nèi)核態(tài)之間數(shù)據(jù)拷貝過(guò)程帶來(lái)的開(kāi)銷(xiāo),它直接分配一個(gè)共享空間,每個(gè)進(jìn)程都可以直接訪問(wèn),就像訪問(wèn)進(jìn)程自己的空間一樣快捷方便,不需要陷入內(nèi)核態(tài)或者系統(tǒng)調(diào)用,大大提高了通信的速度,享有最快的進(jìn)程間通信方式之名。但是便捷高效的共享內(nèi)存通信,帶來(lái)新的問(wèn)題,多進(jìn)程競(jìng)爭(zhēng)同個(gè)共享資源會(huì)造成數(shù)據(jù)的錯(cuò)亂。

那么,就需要信號(hào)量來(lái)保護(hù)共享資源,以確保任何時(shí)刻只能有一個(gè)進(jìn)程訪問(wèn)共享資源,這種方式就是互斥訪問(wèn)。信號(hào)量不僅可以實(shí)現(xiàn)訪問(wèn)的互斥性,還可以實(shí)現(xiàn)進(jìn)程間的同步,信號(hào)量其實(shí)是一個(gè)計(jì)數(shù)器,表示的是資源個(gè)數(shù),其值可以通過(guò)兩個(gè)原子操作來(lái)控制,分別是 P 操作和 V 操作。

與信號(hào)量名字很相似的叫信號(hào),它倆名字雖然相似,但功能一點(diǎn)兒都不一樣。信號(hào)是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制,信號(hào)可以在應(yīng)用進(jìn)程和內(nèi)核之間直接交互,內(nèi)核也可以利用信號(hào)來(lái)通知用戶(hù)空間的進(jìn)程發(fā)生了哪些系統(tǒng)事件,信號(hào)事件的來(lái)源主要有硬件來(lái)源(如鍵盤(pán) Cltr+C )和軟件來(lái)源(如 kill 命令),一旦有信號(hào)發(fā)生,進(jìn)程有三種方式響應(yīng)信號(hào) 1. 執(zhí)行默認(rèn)操作、2. 捕捉信號(hào)、3. 忽略信號(hào)。有兩個(gè)信號(hào)是應(yīng)用進(jìn)程無(wú)法捕捉和忽略的,即 SIGKILLSEGSTOP,這是為了方便我們能在任何時(shí)候結(jié)束或停止某個(gè)進(jìn)程。

前面說(shuō)到的通信機(jī)制,都是工作于同一臺(tái)主機(jī),如果要與不同主機(jī)的進(jìn)程間通信,那么就需要 Socket 通信了。Socket 實(shí)際上不僅用于不同的主機(jī)進(jìn)程間通信,還可以用于本地主機(jī)進(jìn)程間通信,可根據(jù)創(chuàng)建 Socket 的類(lèi)型不同,分為三種常見(jiàn)的通信方式,一個(gè)是基于 TCP 協(xié)議的通信方式,一個(gè)是基于 UDP 協(xié)議的通信方式,一個(gè)是本地進(jìn)程間通信方式。

以上,就是進(jìn)程間通信的主要機(jī)制了。你可能會(huì)問(wèn)了,那線程通信間的方式呢?

同個(gè)進(jìn)程下的線程之間都是共享進(jìn)程的資源,只要是共享變量都可以做到線程間通信,比如全局變量,所以對(duì)于線程間關(guān)注的不是通信方式,而是關(guān)注多線程競(jìng)爭(zhēng)共享資源的問(wèn)題,信號(hào)量也同樣可以在線程間實(shí)現(xiàn)互斥與同步:

  • 互斥的方式,可保證任意時(shí)刻只有一個(gè)線程訪問(wèn)共享資源;

  • 同步的方式,可保證線程 A 應(yīng)在線程 B 之前執(zhí)行;

好了,今日幫張三同學(xué)復(fù)習(xí)就到這了,希望張三同學(xué)早日收到心意的 offer,給夏天劃上充滿(mǎn)汗水的句號(hào)。


好文推薦

進(jìn)程和線程」基礎(chǔ)知識(shí)全家桶,30 張圖一套帶走

20 張圖揭開(kāi)「內(nèi)存管理」的迷霧,瞬間豁然開(kāi)朗

30 張圖帶你走進(jìn)操作系統(tǒng)的「互斥與同步」


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶(hù)希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話(huà)語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉