linux單進程網路不能滿負荷運行的原因主要有以下幾點迅扒:
1、Linux內核限制了單個進程擁有的最大文件描述符數量,導致單進程無法處理過多的連接;
2、Linux網路子系統使用了單槽接收灶巧隊列,這意味著每個CPU核心只能處理一個網路中斷,當網路中斷數量較多時,單CPU核心就無法滿足處理能力;
3、Linux網路子系統會受到內核版本、硬體設備驅動程序和系統參數的影響,當這些參數不合理時,會影響網路性能,從畝辯昌而降低單進程的網路負載能力。
『貳』 linux 消息隊列接收端如何知道有消息過來了
好像沒抄有判斷消息隊列是否有消襲息可讀的機制(不像網路的select可通過結果得到可讀可寫的socket狀態),你只要用msgrcv()循環去讀取就好了 如果消息有類型 可以根據特定的類型去讀,不設置類型去讀的話就會讀走隊列的第一條,隊列的消息讀取之後就從隊列裡面移出了。
『叄』 Linux程序設計:關於消息隊列 題目:編寫兩程序,程序1從消息隊列接收消息,程序2則發送消息
我的作業,你湊合著用吧
//msgq_send.c
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#define MAXSIZE 256
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("Error args\n");
return -1;
}
int msgid;
msgid = msgget((key_t)2000, IPC_CREAT | 0644);
if (msgid == -1)
{
printf("msgget error\n");
return -1;
}
if (msgsnd(msgid, (void *)argv[1], MAXSIZE, 0) == -1)
{
printf("msgsnd error\n");
return -1;
}
return 0;
}
//msgq_recv.c
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#define MAXSIZE 256
int main(void)
{
int msgid;
int msgsize;
char buff[MAXSIZE];
msgid = msgget((key_t)2000, IPC_CREAT | 0644);
if (msgid == -1)
{
printf("msgget error\n");
return -1;
}
msgsize = msgrcv(msgid, (void *)&buff, MAXSIZE, 0, 0);
if (msgsize == -1)
{
printf("msgrcv error\n");
return -1;
}
printf("%s\n", buff);
return 0;
}
//Makefile
TARGET := msgq_send msgq_recv
CC := gcc
CFLAGS := -Wall -g
all: msgq_send msgq_recv
msgq_send: msgq_send.o
$(CC) $(CFLAGS) $^ -o $@
msgq_recv: msgq_recv.o
$(CC) $(CFLAGS) $^ -o $@
clean:
rm -fr *.o $(TARGET)
.PHONY :clean
『肆』 關於 Linux 網路,你必須知道這些
我們一起學習了文件系統和磁碟 I/O 的工作原理,以及相應的性能分析和優化方法。接下來,我們將進入下一個重要模塊—— Linux 的網路子系統。
由於網路處理的流程最復雜,跟我們前面講到的進程調度、中斷處理、內存管理以及 I/O 等都密不可分,所以,我把網路模塊作為最後一個資源模塊來講解。
同 CPU、內存以及 I/O 一樣,網路也是 Linux 系統最核心的功能。網路是一種把不同計算機或網路設備連接到一起的技術,它本質上是一種進程間通信方式,特別是跨系統的進程間通信,必須要通過網路才能進行。隨著高並發、分布式、雲計算、微服務等技術的普及,網路的性能也變得越來越重要。
說到網路,我想你肯定經常提起七層負載均衡、四層負載均衡,或者三層設備、二層設備等等。那麼,這里說的二層、三層、四層、七層又都是什麼意思呢?
實際上,這些層都來自國際標准化組織制定的開放式系統互聯通信參考模型(Open System Interconnection Reference Model),簡稱為 OSI 網路模型。
但是 OSI 模型還是太復雜了,也沒能提供一個可實現的方法。所以,在 Linux 中,我們實際上使用的是另一個更實用的四層模型,即 TCP/IP 網路模型。
TCP/IP 模型,把網路互聯的框架分為應用層、傳輸層、網路層、網路介面層等四層,其中,
為了幫你更形象理解 TCP/IP 與 OSI 模型的關系,我畫了一張圖,如下所示:
當然了,雖說 Linux 實際按照 TCP/IP 模型,實現了網路協議棧,但在平時的學習交流中,我們習慣上還是用 OSI 七層模型來描述。比如,說到七層和四層負載均衡,對應的分別是 OSI 模型中的應用層和傳輸層(而它們對應到 TCP/IP 模型中,實際上是四層和三層)。
OSI引入了服務、介面、協議、分層的概念,TCP/IP借鑒了OSI的這些概念建立TCP/IP模型。
OSI先有模型,後有協議,先有標准,後進行實踐;而TCP/IP則相反,先有協議和應用再提出了模型,且是參照的OSI模型。
OSI是一種理論下的模型,而TCP/IP已被廣泛使用,成為網路互聯事實上的標准。
有了 TCP/IP 模型後,在進行網路傳輸時,數據包就會按照協議棧,對上一層發來的數據進行逐層處理;然後封裝上該層的協議頭,再發送給下一層。
當然,網路包在每一層的處理邏輯,都取決於各層採用的網路協議。比如在應用層,一個提供 REST API 的應用,可以使用 HTTP 協議,把它需要傳輸的 JSON 數據封裝到 HTTP 協議中,然後向下傳遞給 TCP 層。
而封裝做的事情就很簡單了,只是在原來的負載前後,增加固定格式的元數據,原始的負載數據並不會被修改。
比如,以通過 TCP 協議通信的網路包為例,通過下面這張圖,我們可以看到,應用程序數據在每個層的封裝格式。
這些新增的頭部和尾部,增加了網路包的大小,但我們都知道,物理鏈路中並不能傳輸任意大小的數據包。網路介面配置的最大傳輸單元(MTU),就規定了最大的 IP 包大小。在我們最常用的乙太網中,MTU 默認值是 1500(這也是 Linux 的默認值)。
一旦網路包超過 MTU 的大小,就會在網路層分片,以保證分片後的 IP 包不大於 MTU 值。顯然,MTU 越大,需要的分包也就越少,自然,網路吞吐能力就越好。
理解了 TCP/IP 網路模型和網路包的封裝原理後,你很容易能想到,Linux 內核中的網路棧,其實也類似於 TCP/IP 的四層結構。如下圖所示,就是 Linux 通用 IP 網路棧的示意圖:
我們從上到下來看這個網路棧,你可以發現,
這里我簡單說一下網卡。網卡是發送和接收網路包的基本設備。在系統啟動過程中,網卡通過內核中的網卡驅動程序注冊到系統中。而在網路收發過程中,內核通過中斷跟網卡進行交互。
再結合前面提到的 Linux 網路棧,可以看出,網路包的處理非常復雜。所以,網卡硬中斷只處理最核心的網卡數據讀取或發送,而協議棧中的大部分邏輯,都會放到軟中斷中處理。
我們先來看網路包的接收流程。
當一個網路幀到達網卡後,網卡會通過 DMA 方式,把這個網路包放到收包隊列中;然後通過硬中斷,告訴中斷處理程序已經收到了網路包。
接著,網卡中斷處理程序會為網路幀分配內核數據結構(sk_buff),並將其拷貝到 sk_buff 緩沖區中;然後再通過軟中斷,通知內核收到了新的網路幀。
接下來,內核協議棧從緩沖區中取出網路幀,並通過網路協議棧,從下到上逐層處理這個網路幀。比如,
最後,應用程序就可以使用 Socket 介面,讀取到新接收到的數據了。
為了更清晰表示這個流程,我畫了一張圖,這張圖的左半部分表示接收流程,而圖中的粉色箭頭則表示網路包的處理路徑。
了解網路包的接收流程後,就很容易理解網路包的發送流程。網路包的發送流程就是上圖的右半部分,很容易發現,網路包的發送方向,正好跟接收方向相反。
首先,應用程序調用 Socket API(比如 sendmsg)發送網路包。
由於這是一個系統調用,所以會陷入到內核態的套接字層中。套接字層會把數據包放到 Socket 發送緩沖區中。
接下來,網路協議棧從 Socket 發送緩沖區中,取出數據包;再按照 TCP/IP 棧,從上到下逐層處理。比如,傳輸層和網路層,分別為其增加 TCP 頭和 IP 頭,執行路由查找確認下一跳的 IP,並按照 MTU 大小進行分片。
分片後的網路包,再送到網路介面層,進行物理地址定址,以找到下一跳的 MAC 地址。然後添加幀頭和幀尾,放到發包隊列中。這一切完成後,會有軟中斷通知驅動程序:發包隊列中有新的網路幀需要發送。
最後,驅動程序通過 DMA ,從發包隊列中讀出網路幀,並通過物理網卡把它發送出去。
多台伺服器通過網卡、交換機、路由器等網路設備連接到一起,構成了相互連接的網路。由於網路設備的異構性和網路協議的復雜性,國際標准化組織定義了一個七層的 OSI 網路模型,但是這個模型過於復雜,實際工作中的事實標准,是更為實用的 TCP/IP 模型。
TCP/IP 模型,把網路互聯的框架,分為應用層、傳輸層、網路層、網路介面層等四層,這也是 Linux 網路棧最核心的構成部分。
我結合網路上查閱的資料和文章中的內容,總結了下網卡收發報文的過程,不知道是否正確:
當發送數據包時,與上述相反。鏈路層將數據包封裝完畢後,放入網卡的DMA緩沖區,並調用系統硬中斷,通知網卡從緩沖區讀取並發送數據。
了解 Linux 網路的基本原理和收發流程後,你肯定迫不及待想知道,如何去觀察網路的性能情況。具體而言,哪些指標可以用來衡量 Linux 的網路性能呢?
實際上,我們通常用帶寬、吞吐量、延時、PPS(Packet Per Second)等指標衡量網路的性能。
除了這些指標,網路的可用性(網路能否正常通信)、並發連接數(TCP 連接數量)、丟包率(丟包百分比)、重傳率(重新傳輸的網路包比例)等也是常用的性能指標。
分析網路問題的第一步,通常是查看網路介面的配置和狀態。你可以使用 ifconfig 或者 ip 命令,來查看網路的配置。我個人更推薦使用 ip 工具,因為它提供了更豐富的功能和更易用的介面。
以網路介面 eth0 為例,你可以運行下面的兩個命令,查看它的配置和狀態:
你可以看到,ifconfig 和 ip 命令輸出的指標基本相同,只是顯示格式略微不同。比如,它們都包括了網路介面的狀態標志、MTU 大小、IP、子網、MAC 地址以及網路包收發的統計信息。
第一,網路介面的狀態標志。ifconfig 輸出中的 RUNNING ,或 ip 輸出中的 LOWER_UP ,都表示物理網路是連通的,即網卡已經連接到了交換機或者路由器中。如果你看不到它們,通常表示網線被拔掉了。
第二,MTU 的大小。MTU 默認大小是 1500,根據網路架構的不同(比如是否使用了 VXLAN 等疊加網路),你可能需要調大或者調小 MTU 的數值。
第三,網路介面的 IP 地址、子網以及 MAC 地址。這些都是保障網路功能正常工作所必需的,你需要確保配置正確。
第四,網路收發的位元組數、包數、錯誤數以及丟包情況,特別是 TX 和 RX 部分的 errors、dropped、overruns、carrier 以及 collisions 等指標不為 0 時,通常表示出現了網路 I/O 問題。其中:
ifconfig 和 ip 只顯示了網路介面收發數據包的統計信息,但在實際的性能問題中,網路協議棧中的統計信息,我們也必須關注。你可以用 netstat 或者 ss ,來查看套接字、網路棧、網路介面以及路由表的信息。
我個人更推薦,使用 ss 來查詢網路的連接信息,因為它比 netstat 提供了更好的性能(速度更快)。
比如,你可以執行下面的命令,查詢套接字信息:
netstat 和 ss 的輸出也是類似的,都展示了套接字的狀態、接收隊列、發送隊列、本地地址、遠端地址、進程 PID 和進程名稱等。
其中,接收隊列(Recv-Q)和發送隊列(Send-Q)需要你特別關注,它們通常應該是 0。當你發現它們不是 0 時,說明有網路包的堆積發生。當然還要注意,在不同套接字狀態下,它們的含義不同。
當套接字處於連接狀態(Established)時,
當套接字處於監聽狀態(Listening)時,
所謂全連接,是指伺服器收到了客戶端的 ACK,完成了 TCP 三次握手,然後就會把這個連接挪到全連接隊列中。這些全連接中的套接字,還需要被 accept() 系統調用取走,伺服器才可以開始真正處理客戶端的請求。
與全連接隊列相對應的,還有一個半連接隊列。所謂半連接是指還沒有完成 TCP 三次握手的連接,連接只進行了一半。伺服器收到了客戶端的 SYN 包後,就會把這個連接放到半連接隊列中,然後再向客戶端發送 SYN+ACK 包。
類似的,使用 netstat 或 ss ,也可以查看協議棧的信息:
這些協議棧的統計信息都很直觀。ss 只顯示已經連接、關閉、孤兒套接字等簡要統計,而 netstat 則提供的是更詳細的網路協議棧信息。
比如,上面 netstat 的輸出示例,就展示了 TCP 協議的主動連接、被動連接、失敗重試、發送和接收的分段數量等各種信息。
接下來,我們再來看看,如何查看系統當前的網路吞吐量和 PPS。在這里,我推薦使用我們的老朋友 sar,在前面的 CPU、內存和 I/O 模塊中,我們已經多次用到它。
給 sar 增加 -n 參數就可以查看網路的統計信息,比如網路介面(DEV)、網路介面錯誤(EDEV)、TCP、UDP、ICMP 等等。執行下面的命令,你就可以得到網路介面統計信息:
這兒輸出的指標比較多,我來簡單解釋下它們的含義。
其中,Bandwidth 可以用 ethtool 來查詢,它的單位通常是 Gb/s 或者 Mb/s,不過注意這里小寫字母 b ,表示比特而不是位元組。我們通常提到的千兆網卡、萬兆網卡等,單位也都是比特。如下你可以看到,我的 eth0 網卡就是一個千兆網卡:
其中,Bandwidth 可以用 ethtool 來查詢,它的單位通常是 Gb/s 或者 Mb/s,不過注意這里小寫字母 b ,表示比特而不是位元組。我們通常提到的千兆網卡、萬兆網卡等,單位也都是比特。如下你可以看到,我的 eth0 網卡就是一個千兆網卡:
我們通常使用帶寬、吞吐量、延時等指標,來衡量網路的性能;相應的,你可以用 ifconfig、netstat、ss、sar、ping 等工具,來查看這些網路的性能指標。
小狗同學問到: 老師,您好 ss —lntp 這個 當session處於listening中 rec-q 確定是 syn的backlog嗎?
A: Recv-Q為全連接隊列當前使用了多少。 中文資料里這個問題講得最明白的文章: https://mp.weixin.qq.com/s/yH3PzGEFopbpA-jw4MythQ
看了源碼發現,這個地方講的有問題.關於ss輸出中listen狀態套接字的Recv-Q表示全連接隊列當前使用了多少,也就是全連接隊列的當前長度,而Send-Q表示全連接隊列的最大長度
『伍』 Linux網路 - 數據包在內核中接收和發送的過程(轉)
本文將介紹在Linux系統中, 數據包是如何一步一步從網卡傳到進程手中的 以及 數據包是如何一步一步從應用程序到網卡並最終發送出去的 。
如果英文沒有問題,強烈建議閱讀後面參考里的文章,裡面介紹的更詳細。
本文只討論乙太網的物理網卡,不涉及虛擬設備,並且以一個UDP包的接收過程作為示例.
網卡需要有驅動才能工作,驅動是載入到內核中的模塊,負責銜接網卡和內核的網路模塊,驅動在載入的時候將自己注冊進網路模塊,當相應的網卡收到數據包時,網路模塊會調用相應的驅動程序處理數據。
下圖展示了數據包(packet)如何進入內存,並被內核的網路模塊開始處理:
軟中斷會觸發內核網路模塊中的軟中斷處理函數,後續流程如下
由於是UDP包,所以第一步會進入IP層,然後一級一級的函數往下調:
應用層一般有兩種方式接收數據,一種是recvfrom函數阻塞在那裡等著數據來,這種情況下當socket收到通知後,recvfrom就會被喚醒,然後讀取接收隊列的數據;另一種是通過epoll或者select監聽相應的socket,當收到通知後,再調用recvfrom函數去讀取接收隊列的數據。兩種情況都能正常的接收到相應的數據包。
了解數據包的接收流程有助於幫助我們搞清楚我們可以在哪些地方監控和修改數據包,哪些情況下數據包可能被丟棄,為我們處理網路問題提供了一些參考,同時了解netfilter中相應鉤子的位置,對於了解iptables的用法有一定的幫助,同時也會幫助我們後續更好的理解Linux下的網路虛擬設備。
ndo_start_xmit會綁定到具體網卡驅動的相應函數,到這步之後,就歸網卡驅動管了,不同的網卡驅動有不同的處理方式,這里不做詳細介紹,其大概流程如下:
在網卡驅動發送數據包過程中,會有一些地方需要和netdevice子系統打交道,比如網卡的隊列滿了,需要告訴上層不要再發了,等隊列有空閑的時候,再通知上層接著發數據。
『陸』 Linux 消息隊列長度處理
問題:
在Linux 系統中通過消息隊列進行進程間的通訊時,只要定義的BufSize小於1024,隊列就能正常讀寫,當Size定義大於1024時,隊列就無法成功。
處理步驟:
SystemV的消息隊列
/etc/sysctl.conf
修改
kernel.msgmni=1000
kernel.msgmax=81920
kernel.msgmnb=163840
msgmni為MSGMNI,即系統的消息隊列數目。平台每個DTA需要使用3個消息隊列,即最大DTA數為1000/3。該參數應該比平台最大隊列個數參數配置大。
msgmax為MSGMAX,即一個消息的位元組大小。目前擴展值為8k,平台一個交易消息為4個位元組,不會超過限制。
msgmnb為MSGMNB,即隊列存放消息的總位元組數。
POSIX消息隊列
修改
fs.mqueue.msg_max=1000 <-消息個數
fs. mqueue. msgsize_max=8192 <-消息長度
另外操作系統對文件大小的限制ulimit -q你可以看到POSIX消息隊列的最大容量
cat /proc/sys/kernel/msgmax
cat /proc/sys/kernel/msgmni
cat /proc/sys/kernel/msgmnb
『柒』 linux 消息隊列進程通信問題,能發送消息,但接收時接收不到,停在那裡等,請指導下
可定義一個大的char buff[2048] 大於sizeof(msg2)就行,試試
(msgrcv(iMsgid2, buff, iType, 100, 0) == -1
『捌』 請教一個關於linux消息隊列的問題
一般使用步驟:
1. 用ftok產生一個key。
2. 調用msgget(使用key作為參數)產生一個隊列
3. 進程可以用msgsnd發送消息到這個隊列,相應的別的進程用msgrcv讀取。
這里需要注意msgsnd可能會失敗的兩個情況:
a) 可能被中斷打斷(包括msgsnd和msgrcv). 尤其是大流量應用中更容易出現. 比較安全的用法是判斷操作是否被中斷打斷,如果被打斷, 則需要繼續嘗試。
b) 消息隊列滿。產生這個錯誤,則需要考慮提高系統消息隊列規格,或者查看消息接收處是否有問題
4. msgctl函數可以用來刪除消息隊列
消息隊列產生之後,除非明確的刪除(可以用),產生的隊列會一直保留在系統中。linux下消息隊列的個數是有限的,注意不要泄露。如果 使用已經達到上限,msgget調用會失敗,產生的錯誤碼對應的提示信息為no space left on device.
注意點:
1.消息的類型 mtype 不需為非0值。如果使用0,則msgsnd會失敗,並得到」Invalid argument「錯誤。
2.msgflg為0表示阻塞等待,如果msgflg為IPC_NOWAIT表示非阻塞。
3.最好使用root許可權執行消息隊列,否則msgrcv 提示 "Permission denied"。
『玖』 Linux系統編程—消息隊列
消息隊列本質上是位於內核空間的鏈表,鏈表的每個節點都是一條消息。每一條消息都有自己的消息類型,消息類型用整數來表示,而且必須大於 0。每種類型的消息都被對應的鏈表所維護:
其中數字 1 表示類型為 1 的消息,數字2、3、4 類似。彩色塊表示消息數據,它們被掛在對應類型的鏈表上。
值得注意的是,剛剛說過沒有消息類型為 0 的消息,實際上,消息類型為 0 的鏈表記錄了所有消息加入隊列的順序,其中紅色箭頭表示消息加入的順序。
無論你是發送還是接收消息,消息的格式都必須按照規范來。簡單的說,它一般長成下面這個樣子:
所以,只要你保證首4位元組(32 位 linux 下的 long)是一個整數就行了。
舉個例子:
從上面可以看出,正文部分是什麼數據類型都沒關系,因為消息隊列傳遞的是 2 進制數據,不一定非得是文本。
msgsnd 函數用於將數據發送到消息隊列。如果該函數被信號打斷,會設置 errno 為 EINTR。
參數 msqid:ipc 內核對象 id
參數 msgp:消息數據地址
參數 msgsz:消息正文部分的大小(不包含消息類型)
參數 msgflg:可選項
該值為 0:如果消息隊列空間不夠,msgsnd 會阻塞。
IPC_NOWAIT:直接返回,如果空間不夠,會設置 errno 為 EAGIN.
返回值:0 表示成功,-1 失敗並設置 errno。
msgrcv 函數從消息隊列取出消息後,並將其從消息隊列里刪除。
參數 msqid:ipc 內核對象 id
參數 msgp:用來接收消息數據地址
參數 msgsz:消息正文部分的大小(不包含消息類型)
參數 msgtyp:指定獲取哪種類型的消息
msgtyp = 0:獲取消息隊列中的第一條消息
msgtyp > 0:獲取類型為 msgtyp 的第一條消息,除非指定了 msgflg 為MSG_EXCEPT,這表示獲取除了 msgtyp 類型以外的第一條消息。
msgtyp < 0:獲取類型 ≤|msgtyp|≤|msgtyp| 的第一條消息。
參數 msgflg:可選項。
如果為 0 表示沒有消息就阻塞。
IPC_NOWAIT:如果指定類型的消息不存在就立即返回,同時設置 errno 為 ENOMSG
MSG_EXCEPT:僅用於 msgtyp > 0 的情況。表示獲取類型不為 msgtyp 的消息
MSG_NOERROR:如果消息數據正文內容大於 msgsz,就將消息數據截斷為 msgsz
程序 msg_send 和 msg_recv 分別用於向消息隊列發送數據和接收數據。
msg_send 程序定義了一個結構體 Msg,消息正文部分是結構體 Person。該程序向消息隊列發送了 10 條消息。
msg_send.c
程序 msg_send 第一次運行完後,內核中的消息隊列大概像下面這樣:
msg_recv 程序接收一個參數,表示接收哪種類型的消息。比如./msg_recv 4 表示接收類型為 4 的消息,並列印在屏幕。
先運行 msg_send,再運行 msg_recv。
接收所有消息
接收類型為 4 的消息
獲取和設置消息隊列的屬性
msqid:消息隊列標識符
cmd:控制指令
IPC_STAT:獲得msgid的消息隊列頭數據到buf中
IPC_SET:設置消息隊列的屬性,要設置的屬性需先存儲在buf中,可設置的屬性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes
buf:消息隊列管理結構體。
返回值:
成功:0
出錯:-1,錯誤原因存於error中
EACCESS:參數cmd為IPC_STAT,確無許可權讀取該消息隊列
EFAULT:參數buf指向無效的內存地址
EIDRM:標識符為msqid的消息隊列已被刪除
EINVAL:無效的參數cmd或msqid
EPERM:參數cmd為IPC_SET或IPC_RMID,卻無足夠的許可權執行