當前位置:首頁 » 操作系統 » linux通信方式

linux通信方式

發布時間: 2023-07-14 15:09:55

linux下如何用tcp,c/s模式實現兩台電腦之間通信

1.
建議lz使用socket套接字。這個方式可以很好的實現client/server模式,tcp和udp協議都可以選擇。使用socket來實現兩台電腦的進程間通信,要先理解一些函數,如socket,binder,listen,connect,recv,send等等。。。
2.
lz可以上網搜索關鍵字「linux
socket編程」,或追問我。

Ⅱ Linux 進程間通信方式有哪些

進程間通信(IPC,Interprocess
communication)是一組編程介面,讓程序員能夠協調不同的進程,使之能在一個操作系統里同時運行,並相互傳遞、交換信息。這使得一個程序能夠在同一時間里處理許多用戶的要求。因為即使只有一個用戶發出要求,也可能導致一個操作系統中多個進程的運行,進程之間必須互相通話。IPC介面就提供了這種可能性。每個IPC方法均有它自己的優點和局限性,一般,對於單個程序而言使用所有的IPC方法是不常見的。
1、無名管道通信
無名管道(pipe):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關系的進程間使用,進程的親緣關系通常是指父子進程關系。
2、高級管道通信
高級管道(popen):將另一個程序當做一個新的進程在當前程序進程中啟動,則它算是當前程序的子進程,這種方式我們稱為高級管道方式。
3、有名管道通信
有名管道(named pipe):有名管道也是半雙工的通信方式,但是它允許無親緣關系進程間的通信。
4、消息隊列通信
消息隊列(message
queue):消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識,消息隊列克服了信號傳遞信息少、管道只能承載無格式位元組流以及緩沖區大小受限等缺點。
5、信號量通信
信號量(semophore):信號量是一個計數器,可以用來控制多個進程對共享資源的訪問,它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
6、信號
信號(sinal):信號是一種比較復雜的通信方式,用於通知接收進程某個事件已經發生。
7、共享內存通信
共享內存(shared
memory):共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的IPC方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號量,配合使用,來實現進程間的同步和通信。
8、套接字通信
套接字(socket):套接字也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同機器間的進程通信。

Ⅲ LINUX設備驅動程序如何與硬體通信

LINUX設備驅動程序是怎麼樣和硬體通信的?下面將由我帶大家來解答這個疑問吧,希望對大家有所收獲!

LINUX設備驅動程序與硬體設備之間的通信

設備驅動程序是軟體概念和硬體電路之間的一個抽象層,因此兩方面都要討論。到目前為止,我們已經討論詳細討論了軟體概念上的一些細節,現在討論另一方面,介紹驅動程序在Linux上如何在保持可移植性的前提下訪問I/O埠和I/O內存。

我們在需要示例的場合會使用簡單的數字I/O埠來講解I/O指令,並使用普通的幀緩沖區顯存來講解內存映射I/O。

I/O埠和I/O內存

計算機對每種外設都是通過讀寫它的寄存器進行控制的。大部分外設都有幾個寄存器,不管是在內存地址空間還是在I/O地址空間,這些寄存器的訪問地址都是連續的。

I/O埠就是I/O埠,設備會把寄存器映射到I/O埠,不管處理器是否具有獨立的I/O埠地址空間。即使沒有在訪問外設時也要模擬成讀寫I/O埠。

I/O內存是設備把寄存器映射到某個內存地址區段(如PCI設備)。這種I/O內存通常是首先方案,它不需要特殊的處理器指令,而且CPU核心訪問內存更有效率。

I/O寄存器和常規內存

盡管硬體寄存器和內存非常相似,但程序員在訪問I/O寄存器的時候必須注意避免由於CPU或編譯器不恰當的優化而改變預期的I/O動作。

I/O寄存器和RAM最主要的區別就是I/O操作具有邊際效應,而內存操作則沒有:由於內存沒有邊際效應,所以可以用多種 方法 進行優化,如使用高速緩存保存數值、重新排序讀/寫指令等。

編譯器能夠將數值緩存在CPU寄存器中而不寫入內存,即使儲存數據,讀寫操作也都能在高速緩存中進行而不用訪問物理RAM。無論是在編譯器一級或是硬體一級,指令的重新排序都有可能發生:一個指令序列如果以不同於程序文本中的次序運行常常能執行得更快。

在對常規內存進行這些優化的時候,優化過程是透明的,而且效果良好,但是對I/O操作來說這些優化很可能造成致命的錯誤,這是因為受到邊際效應的干擾,而這卻是驅動程序訪問I/O寄存器的主要目的。處理器無法預料某些 其它 進程(在另一個處理器上運行,或在在某個I/O控制器中發生的操作)是否會依賴於內存訪問的順序。編譯器或CPU可能會自作聰明地重新排序所要求的操作,結果會發生奇怪的錯誤,並且很難調度。因此,驅動程序必須確保不使用高速緩沖,並且在訪問寄存器時不發生讀或寫指令的重新排序。

由硬體自身引起的問題很解決:只要把底層硬體配置成(可以是自動的或是由Linux初始化代碼完成)在訪問I/O區域(不管是內存還是埠)時禁止硬體緩存即可。

由編譯器優化和硬體重新排序引起的問題的解決辦法是:對硬體(或其他處理器)必須以特定順序的操作之間設置內存屏障(memory barrier)。Linux提供了4個宏來解決所有可能的排序問題:

#include <linux/kernel.h>

void barrier(void)

這個函數通知編譯器插入一個內存屏障,但對硬體沒有影響。編譯後的代碼會把當前CPU寄存器中的所有修改過的數值保存到內存中,需要這些數據的時候再重新讀出來。對barrier的調用可避免在屏障前後的編譯器優化,但硬體完成自己的重新排序。

#include <asm/system.h>

void rmb(void);

void read_barrier_depends(void);

void wmb(void);

void mb(void);

這些函數在已編譯的指令流中插入硬體內存屏障;具體實現方法是平台相關的。rmb(讀內存屏障)保證了屏障之前的讀操作一定會在後來的讀操作之前完成。wmb保證寫操作不會亂序,mb指令保證了兩者都不會。這些函數都是barrier的超集。

void smp_rmb(void);

void smp_read_barrier_depends(void);

void smp_wmb(void);

void smp_mb(void);

上述屏障宏版本也插入硬體屏障,但僅僅在內核針對SMP系統編譯時有效;在單處理器系統上,它們均會被擴展為上面那些簡單的屏障調用。

設備驅動程序中使用內存屏障的典型形式如下:

writel(dev->registers.addr, io_destination_address);

writel(dev->registers.size, io_size);

writel(dev->registers.operation, DEV_READ);

wmb();

writel(dev->registers.control, DEV_GO);

在這個例子中,最重要的是要確保控制某種特定操作的所有設備寄存器一定要在操作開始之前已被正確設置。其中的內存屏障會強制寫操作以要求的順序完成。

因為內存屏障會影響系統性能,所以應該只用於真正需要的地方。不同類型的內存屏障對性能的影響也不盡相同,所以最好盡可能使用最符合需要的特定類型。

值得注意的是,大多數處理同步的內核原語,如自旋鎖和atomic_t操作,也能作為內存屏障使用。同時還需要注意,某些外設匯流排(比如PCI匯流排)存在自身的高速緩存問題,我們將在後面的章節中討論相關問題。

在某些體系架構上,允許把賦值語句和內存屏障進行合並以提高效率。內核提供了幾個執行這種合並的宏,在默認情況下,這些宏的定義如下:

#define set_mb(var, value) do {var = value; mb();} while 0

#define set_wmb(var, value) do {var = value; wmb();} while 0

#define set_rmb(var, value) do {var = value; rmb();} while 0

在適當的地方,<asm/system.h>中定義的這些宏可以利用體系架構特有的指令更快的完成任務。注意只有小部分體系架構定義了set_rmb宏。

使用I/O埠

I/O埠是驅動程序與許多設備之間的通信方式——至少在部分時間是這樣。本節講解了使用I/O埠的不同函數,另外也涉及到一些可移植性問題。

I/O埠分配

下面我們提供了一個注冊的介面,它允允許驅動程序聲明自己需要操作的埠:

#include <linux/ioport.h>

struct resource *request_region(unsigned long first, unsigned long n, const char *name);

它告訴內核,我們要使用起始於first的n個埠。name是設備的名稱。如果分配成功返回非NULL,如果失敗返回NULL。

所有分配的埠可從/proc/ioports中找到。如果我們無法分配到我們要的埠集合,則可以查看這個文件哪個驅動程序已經分配了這些埠。

如果不再使用這些埠,則用下面函數返回這些埠給系統:

void release_region(unsigned long start, unsigned long n);

下面函數允許驅動程序檢查給定的I/O埠是否可用:

int check_region(unsigned long first, unsigned long n);//不可用返回負的錯誤代碼

我們不贊成用這個函數,因為它返回成功並不能確保分配能夠成功,因為檢查和其後的分配並不是原子操作。我們應該始終使用request_region,因為這個函數執行了必要的鎖定,以確保分配過程以安全原子的方式完成。

操作I/O埠

當驅動程序請求了需要使用的I/O埠范圍後,必須讀取和/或寫入這些埠。為此,大多數硬體都會把8位、16位、32位區分開來。它們不能像訪問系統內存那樣混淆使用。

因此,C語言程序必須調用不同的函數訪問大小不同的埠。那些只支持映射的I/O寄存器的計算機體系架構通過把I/O埠地址重新映射到內存地址來偽裝埠I/O,並且為了易於移植,內核對驅動程序隱藏了這些細節。Linux內核頭文件中(在與體系架構相關的頭文件<asm/io.h>中)定義了如下一些訪問I/O埠的內聯函數:

unsigned inb(unsigned port);

void outb(unsigned char byte, unsigned port);

位元組讀寫埠。

unsigned inw(unsigned port);

void outw(unsigned short word, unsigned port);

訪問16位埠

unsigned inl(unsigned port);

void outl(unsigned longword, unsigned port);

訪問32位埠

在用戶空間訪問I/O埠

上面這些函數主要是提供給設備驅動程序使用的,但它們也可以用戶空間使用,至少在PC類計算機上可以使用。GNU的C庫在<sys/io.h>中定義了這些函數。如果要要用戶空間使用inb及相關函數,則必須滿足正下面這些條件:

編譯程序時必須帶有-O選項來強制內聯函數的展開。

必須用ioperm(獲取單個埠的許可權)或iopl(獲取整個I/O空間)系統調用來獲取對埠進行I/O操作的許可權。這兩個函數都是x86平台特有的。

必須以root身份運行該程序才能調用ioperm或iopl。或者進程的祖先進程之一已經以root身份獲取對埠的訪問。

如果宿主平台沒有以上兩個系統調用,則用戶空間程序仍然可以使用/dev/port設備文件訪問I/O埠。不過要注意,該設備文件的含義與平台密切相關,並且除PC平台以處,它幾乎沒有什麼用處。

串操作

以上的I/O操作都是一次傳輸一個數據,作為補充,有些處理器實現了一次傳輸一個數據序列的特殊指令,序列中的數據單位可以是位元組、字、雙字。這些指令稱為串操作指令,它們執行這些任務時比一個C語言編寫的循環語句快得多。下面列出的宏實現了串I/O:

void insb(unsigned port, void *addr, unsigned long count);

void outsb(unsigned port, void *addr, unsigned long count);從內存addr開始連續讀/寫count數目的位元組。只對單一埠port讀取或寫入數據

void insw(unsigned port, void *addr, unsigned long count);

void outsw(unsigned port, void *addr, unsigned long count);對一個16位埠讀寫16位數據

void insl(unsigned port, void *addr, unsigned long count);

void outsl(unsigned port, void *addr, unsigned long count);對一個32位埠讀寫32位數據

在使用串I/O操作函數時,需要銘記的是:它們直接將位元組流從埠中讀取或寫入。因此,當埠和主機系統具有不同的位元組序時,將導致不可預期的結果。使用inw讀取埠將在必要時交換位元組,以便確保讀入的值匹配於主機的位元組序。然而,串函數不會完成這種交換。

暫停式I/O

在處理器試圖從匯流排上快速傳輸數據時,某些平台(特別是i386)就會出現問題。當處理器時鍾比外設時鍾(如ISA)快時就會出現問題,並且在設備板上特別慢時表現出來。為了防止出現丟失數據的情況,可以使用暫停式的I/O函數來取代通常的I/O函數,這些暫停式的I/O函數很像前面介紹的那些I/O函數,不同之處是它們的名字用_p結尾,如inb_p、outb_p等等。在linux支持的大多數平台上都定義了這些函數,不過它們常常擴展為非暫停式I/O同樣的代碼,因為如果不使用過時的外設匯流排就不需要額外的暫停。

平台相關性

I/O指令是與處理器密切相關的。因為它們的工作涉及到處理器移入移出數據的細節,所以隱藏平台間的差異非常困難。因此,大部分與I/O埠相關的源代碼都與平台相關。

回顧前面函數列表可以看到有一處不兼容的地方,即數據類型。函數的參數根據各平台體系架構上的不同要相應地使用不同的數據類型。例如,port參數在x86平台上(處理器只支持64KB的I/O空間)上定義為unsigned short,但在其他平台上定義為unsigned long,在這些平台上,埠是與內存在同一地址空間內的一些特定區域。

感興趣的讀者可以從io.h文件獲得更多信息,除了本章介紹的函數,一些與體系架構相關的函數有時也由該文件定義。

值得注意的是,x86家族之外的處理器都不為埠提供獨立的地址空間。

I/O操作在各個平台上執行的細節在對應平台的編程手冊中有詳細的敘述;也可以從web上下載這些手冊的PDF文件。

I/O埠示例

演示設備驅動程序的埠I/O的示例代碼運行於通用的數字I/O埠上,這種埠在大多數計算機平台上都能找到。

數字I/O埠最常見的一種形式是一個位元組寬度的I/O區域,它或者映射到內存,或者映射到埠。當把數字寫入到輸出區域時,輸出引腳上的電平信號隨著寫入的各位而發生相應變化。從輸入區域讀取到的數據則是輸入引腳各位當前的邏輯電平值。

這類I/O埠的具體實現和軟體介面是因系統而異的。大多數情況下,I/O引腳由兩個I/O區域控制的:一個區域中可以選擇用於輸入和輸出的引腳,另一個區域中可以讀寫實際的邏輯電平。不過有時情況簡單些,每個位不是輸入就是輸出(不過這種情況下就不能稱為“通用I/O"了);在所有個人計算機上都能找到的並口就是這樣的非通用的I/O埠。

並口簡介

並口的最小配置由3個8位埠組成。第一個埠是一個雙向的數據寄存器,它直接連接到物理連接器的2~9號引腳上。第二個埠是一個只讀的狀態寄存器;當並口連接列印機時,該寄存器 報告 列印機狀態,如是否是線、缺紙、正忙等等。第三個埠是一個只用於輸出的控制寄存器,它的作用之一是控制是否啟用中斷。

如下所示:並口的引腳

示例驅動程序

while(count--) {

outb(*(ptr++), port);

wmb();

}

使用I/O內存

除了x86上普遍使的I/O埠之外,和設備通信的另一種主要機制是通過使用映射到內存的寄存器或設備內存,這兩種都稱為I/O內存,因為寄存器和內存的差別對軟體是透明的。

I/O內存僅僅是類似RAM的一個區域,在那裡處理器可以通過匯流排訪問設備。這種內存有很多用途,比如存放視頻數據或乙太網數據包,也可以用來實現類似I/O埠的設備寄存器(也就是說,對它們的讀寫也存在邊際效應)。

根據計算機平台和所使用匯流排的不同,i/o內存可能是,也可能不是通過頁表訪問的。如果訪問是經由頁表進行的,內核必須首先安排物理地址使其對設備驅動程序可見(這通常意味著在進行任何I/O之前必須先調用ioremap)。如果訪問無需頁表,那麼I/O內存區域就非常類似於I/O埠,可以使用適當形式的函數讀取它們。

不管訪問I/O內存是否需要調用ioremap,都不鼓勵直接使用指向I/O內存的指針。相反使用包裝函數訪問I/O內存,這一方面在所有平台上都是安全的,另一方面,在可以直接對指針指向的內存區域執行操作的時候,這些函數是經過優化的。並且直接使用指針會影響程序的可移植性。

I/O內存分配和映射

在使用之前,必須首先分配I/O區域。分配內存區域的介面如下(在<linux/ioport.h>中定義):

struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);

該函數從start開始分配len位元組長的內存區域。如果成功返回非NULL,否則返回NULL值。所有的I/O內存分配情況可從/proc/iomem得到。

不再使用已分配的內存區域時,使用如下介面釋放:

void release_mem_region(unsigned long start, unsigned long len);

下面函數用來檢查給定的I/O內存區域是否可用的老函數:

int check_mem_region(unsigned long start, unsigned long len);//這個函數和check_region一樣不安全,應避免使用

分配內存之後我們還必須確保該I/O內存對內存而言是可訪問的。獲取I/O內存並不意味著可引用對應的指針;在許多系統上,I/O內存根本不能通過這種方式直接訪問。因此,我們必須由ioremap函數建立映射,ioremap專用於為I/O內存區域分配虛擬地址。

我們根據以下定義來調用ioremap函數:

#include <asm/io.h>

void *ioremap(unsigned long phys_addr, unsigned long size);

void *ioremap_nocache(unsigned long phys_addr, unsigned long size);在大多數計算機平台上,該函數和ioremap相同:當所有I/O內存已屬於非緩存地址時,就沒有必要實現ioremap的獨立的,非緩沖版本。

void iounmap(void *addr);

記住,由ioremap返回的地址不應該直接引用,而應該使用內核提供的accessor函數。

訪問I/O內存

在某些平台上我們可以將ioremap的返回值直接當作指針使用。但是,這種使用不具有可移植性,訪問I/O內存的正確方法是通過一組專用於些目的的函數(在<asm/io.h>中定義)。

從I/O內存中讀取,可使用以下函數之一:

unsigned int ioread8(void *addr);

unsigned int ioread16(void *addr);

unsigned int ioread32(void *addr);

其中,addr是從ioremap獲得的地址(可能包含一個整數偏移量);返回值是從給定I/O內存讀取到的值。

寫入I/O內存的函數如下:

void iowrite8(u8 value, void *addr);

void iowrite16(u16 value, void *addr);

void iowrite32(u32 value, void *addr);

如果必須在給定的I/O內存地址處讀/寫一系列值,則可使用上述函數的重復版本:

void ioread8_rep(void *addr, void *buf, unsigned long count);

void ioread16_rep(void *addr, void *buf, unsigned long count);

void ioread32_rep(void *addr, void *buf, unsigned long count);

void iowrite8_rep(void *addr, const void *buf, unsigned long count);

void iowrite16_rep(void *addr, const void *buf, unsigned long count);

void iowrite32_rep(void *addr, const void *buf, unsigned long count);

上述函數從給定的buf向給定的addr讀取或寫入count個值。count以被寫入數據的大小為單位。

上面函數均在給定的addr處執行所有的I/O操作,如果我們要在一塊I/O內存上執行操作,則可以使用下面的函數:

void memset_io(void *addr, u8 value, unsigned int count);

void memcpy_fromio(void *dest, void *source, unsigned int count);

void memcpy_toio(void *dest, void *source, unsigned int count);

上述函數和C函數庫的對應函數功能一致。

像I/O內存一樣使用I/O埠

某些硬體具有一種有趣的特性:某些版本使用I/O埠,而其他版本則使用I/O內存。導出給處理器的寄存器在兩種情況下都是一樣的,但訪問方法卻不同。為了讓處理這類硬體的驅動程序更加易於編寫,也為了最小化I/O埠和I/O內存訪問這間的表面區別,2.6內核引入了ioport_map函數:

void *ioport_map(unsigned long port, unsigned int count);

該函數重新映射count個I/O埠,使其看起來像I/O內存。此後,驅動程序可在該函數返回的地址上使用ioread8及其相關函數,這樣就不必理會I/O埠和I/O內存之間的區別了。

當不需要這種映射時使用下面函數一撤消:

void ioport_unmap(void *addr);

這些函數使得I/O埠看起來像內存。但需要注意的是,在重新映射之前,我們必須通過request_region來分配這些I/O埠。

為I/O內存重用short

前面介紹的short示例模塊訪問的是I/O埠,它也可以訪問I/O內存。為此必須在載入時通知它使用I/O內存,另外還要修改base地址以使其指向I/O區域。

下例是在MIPS開發板上點亮調試用的LED:

mips.root# ./short_load use_mem=1 base = 0xb7ffffc0

mips.root# echo -n 7 > /dev/short0

下面代碼是short寫入內存區域時使用的循環:

while(count--) {

iowrite8(*ptr++, address);

wmb();

}

1MB地址空間之下的ISA內存

最廣為人知的I/O內存區之一就是個人計算機上的ISA內存段。它的內存范圍在64KB(0xA0000)到1MB(0x100000)之間,因此它正好出現在常規系統RAM的中間。這種地址看上去有點奇怪,因為這個設計決策是20世紀80年代早期作出的,在當時看來沒有人會用到640KB以上的內存。

Ⅳ Linux進程間通信

linux下進程間通信的幾種主要手段簡介:

一般文件的I/O函數都可以用於管道,如close、read、write等等。

實例1:用於shell

管道可用於輸入輸出重定向,它將一個命令的輸出直接定向到另一個命令的輸入。比如,當在某個shell程序(Bourne shell或C shell等)鍵入who│wc -l後,相應shell程序將創建who以及wc兩個進程和這兩個進程間的管道。

實例二:用於具有親緣關系的進程間通信

管道的主要局限性正體現在它的特點上:

有名管道的創建

小結:

管道常用於兩個方面:(1)在shell中時常會用到管道(作為輸入輸入的重定向),在這種應用方式下,管道的創建對於用戶來說是透明的;(2)用於具有親緣關系的進程間通信,用戶自己創建管道,並完成讀寫操作。

FIFO可以說是管道的推廣,克服了管道無名字的限制,使得無親緣關系的進程同樣可以採用先進先出的通信機制進行通信。

管道和FIFO的數據是位元組流,應用程序之間必須事先確定特定的傳輸"協議",採用傳播具有特定意義的消息。

要靈活應用管道及FIFO,理解它們的讀寫規則是關鍵。

信號生命周期

信號是進程間通信機制中唯一的非同步通信機制,可以看作是非同步通知,通知接收信號的進程有哪些事情發生了。信號機制經過POSIX實時擴展後,功能更加強大,除了基本通知功能外,還可以傳遞附加信息。

可以從兩個不同的分類角度對信號進行分類:(1)可靠性方面:可靠信號與不可靠信號;(2)與時間的關繫上:實時信號與非實時信號。

(1) 可靠信號與不可靠信號

不可靠信號 :Linux下的不可靠信號問題主要指的是信號可能丟失。

可靠信號 :信號值位於SIGRTMIN和SIGRTMAX之間的信號都是可靠信號,可靠信號克服了信號可能丟失的問題。Linux在支持新版本的信號安裝函數sigation()以及信號發送函數sigqueue()的同時,仍然支持早期的signal()信號安裝函數,支持信號發送函數kill()。

對於目前linux的兩個信號安裝函數:signal()及sigaction()來說,它們都不能把SIGRTMIN以前的信號變成可靠信號(都不支持排隊,仍有可能丟失,仍然是不可靠信號),而且對SIGRTMIN以後的信號都支持排隊。這兩個函數的最大區別在於,經過sigaction安裝的信號都能傳遞信息給信號處理函數(對所有信號這一點都成立),而經過signal安裝的信號卻不能向信號處理函數傳遞信息。對於信號發送函數來說也是一樣的。

(2) 實時信號與非實時信號

前32種信號已經有了預定義值,每個信號有了確定的用途及含義,並且每種信號都有各自的預設動作。如按鍵盤的CTRL ^C時,會產生SIGINT信號,對該信號的默認反應就是進程終止。後32個信號表示實時信號,等同於前面闡述的可靠信號。這保證了發送的多個實時信號都被接收。實時信號是POSIX標準的一部分,可用於應用進程。非實時信號都不支持排隊,都是不可靠信號;實時信號都支持排隊,都是可靠信號。

發送信號的主要函數有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()。

調用成功返回 0;否則,返回 -1。

sigqueue()是比較新的發送信號系統調用,主要是針對實時信號提出的(當然也支持前32種),支持信號帶有參數,與函數sigaction()配合使用。

sigqueue的第一個參數是指定接收信號的進程ID,第二個參數確定即將發送的信號,第三個參數是一個聯合數據結構union sigval,指定了信號傳遞的參數,即通常所說的4位元組值。

sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號。sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號。

inux主要有兩個函數實現信號的安裝: signal() sigaction() 。其中signal()在可靠信號系統調用的基礎上實現, 是庫函數。它只有兩個參數,不支持信號傳遞信息,主要是用於前32種非實時信號的安裝;而sigaction()是較新的函數(由兩個系統調用實現:sys_signal以及sys_rt_sigaction),有三個參數,支持信號傳遞信息,主要用來與 sigqueue() 系統調用配合使用,當然,sigaction()同樣支持非實時信號的安裝。sigaction()優於signal()主要體現在支持信號帶有參數。

消息隊列就是一個消息的鏈表。可以把消息看作一個記錄,具有特定的格式以及特定的優先順序。對消息隊列有寫許可權的進程可以向中按照一定的規則添加新消息;對消息隊列有讀許可權的進程則可以從消息隊列中讀走消息。消息隊列是隨內核持續的

消息隊列的內核持續性要求每個消息隊列都在系統范圍內對應唯一的鍵值,所以,要獲得一個消息隊列的描述字,只需提供該消息隊列的鍵值即可;

消息隊列與管道以及有名管道相比,具有更大的靈活性,首先,它提供有格式位元組流,有利於減少開發人員的工作量;其次,消息具有類型,在實際應用中,可作為優先順序使用。這兩點是管道以及有名管道所不能比的。同樣,消息隊列可以在幾個進程間復用,而不管這幾個進程是否具有親緣關系,這一點與有名管道很相似;但消息隊列是隨內核持續的,與有名管道(隨進程持續)相比,生命力更強,應用空間更大。

信號燈與其他進程間通信方式不大相同,它主要提供對進程間共享資源訪問控制機制。相當於內存中的標志,進程可以根據它判定是否能夠訪問某些共享資源,同時,進程也可以修改該標志。除了用於訪問控制外,還可用於進程同步。信號燈有以下兩種類型:

int semop(int semid, struct sembuf *sops, unsigned nsops); semid是信號燈集ID,sops指向數組的每一個sembuf結構都刻畫一個在特定信號燈上的操作。

int semctl(int semid,int semnum,int cmd,union semun arg)
該系統調用實現對信號燈的各種控制操作,參數semid指定信號燈集,參數cmd指定具體的操作類型;參數semnum指定對哪個信號燈操作,只對幾個特殊的cmd操作有意義;arg用於設置或返回信號燈信息。

進程間需要共享的數據被放在一個叫做IPC共享內存區域的地方,所有需要訪問該共享區域的進程都要把該共享區域映射到本進程的地址空間中去。系統V共享內存通過shmget獲得或創建一個IPC共享內存區域,並返回相應的標識符。內核在保證shmget獲得或創建一個共享內存區,初始化該共享內存區相應的shmid_kernel結構注同時,還將在特殊文件系統shm中,創建並打開一個同名文件,並在內存中建立起該文件的相應dentry及inode結構,新打開的文件不屬於任何一個進程(任何進程都可以訪問該共享內存區)。所有這一切都是系統調用shmget完成的。

shmget()用來獲得共享內存區域的ID,如果不存在指定的共享區域就創建相應的區域。shmat()把共享內存區域映射到調用進程的地址空間中去,這樣,進程就可以方便地對共享區域進行訪問操作。shmdt()調用用來解除進程對共享內存區域的映射。shmctl實現對共享內存區域的控制操作。這里我們不對這些系統調用作具體的介紹,讀者可參考相應的手冊頁面,後面的範例中將給出它們的調用方法。

註:shmget的內部實現包含了許多重要的系統V共享內存機制;shmat在把共享內存區域映射到進程空間時,並不真正改變進程的頁表。當進程第一次訪問內存映射區域訪問時,會因為沒有物理頁表的分配而導致一個缺頁異常,然後內核再根據相應的存儲管理機制為共享內存映射區域分配相應的頁表。

Ⅳ Linux進程間通信(互斥鎖、條件變數、讀寫鎖、文件鎖、信號燈)

為了能夠有效的控制多個進程之間的溝通過程,保證溝通過程的有序和和諧,OS必須提供一定的同步機制保證進程之間不會自說自話而是有效的協同工作。比如在 共享內存的通信方式中,兩個或者多個進程都要對共享的內存進行數據寫入,那麼怎麼才能保證一個進程在寫入的過程中不被其它的進程打斷,保證數據的完整性 呢?又怎麼保證讀取進程在讀取數據的過程中數據不會變動,保證讀取出的數據是完整有效的呢?

常用的同步方式有: 互斥鎖、條件變數、讀寫鎖、記錄鎖(文件鎖)和信號燈.

互斥鎖:

顧名思義,鎖是用來鎖住某種東西的,鎖住之後只有有鑰匙的人才能對鎖住的東西擁有控制權(把鎖砸了,把東西偷走的小偷不在我們的討論范圍了)。所謂互斥, 從字面上理解就是互相排斥。因此互斥鎖從字面上理解就是一點進程擁有了這個鎖,它將排斥其它所有的進程訪問被鎖住的東西,其它的進程如果需要鎖就只能等待,等待擁有鎖的進程把鎖打開後才能繼續運行。 在實現中,鎖並不是與某個具體的變數進行關聯,它本身是一個獨立的對象。進(線)程在有需要的時候獲得此對象,用完不需要時就釋放掉。

互斥鎖的主要特點是互斥鎖的釋放必須由上鎖的進(線)程釋放,如果擁有鎖的進(線)程不釋放,那麼其它的進(線)程永遠也沒有機會獲得所需要的互斥鎖。

互斥鎖主要用於線程之間的同步。

條件變數:

上文中提到,對於互斥鎖而言,如果擁有鎖的進(線)程不釋放鎖,其它進(線)程永遠沒機會獲得鎖,也就永遠沒有機會繼續執行後續的邏輯。在實際環境下,一 個線程A需要改變一個共享變數X的值,為了保證在修改的過程中X不會被其它的線程修改,線程A必須首先獲得對X的鎖。現在假如A已經獲得鎖了,由於業務邏 輯的需要,只有當X的值小於0時,線程A才能執行後續的邏輯,於是線程A必須把互斥鎖釋放掉,然後繼續「忙等」。如下面的偽代碼所示:

1.// get x lock

2.while(x

Ⅵ linux進程間通訊的幾種方式

進程間通信(IPC,Interprocess communication)是一組編程介面,讓程序員能夠協調不同的進程,使之能在一個操作系統里同時運行,並相互傳遞、交換信息。這使得一個程序能夠在同一時間里處理許多用戶的要求。因為即使只有一個用戶發出要求,也可能導致一個操作系統中多個進程的運行,進程之間必須互相通話。IPC介面就提供了這種可能性。每個IPC方法均有它自己的優點和局限性,一般,對於單個程序而言使用所有的IPC方法是不常見的。
1、無名管道通信
無名管道(pipe):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關系的進程間使用,進程的親緣關系通常是指父子進程關系。
2、高級管道通信
高級管道(popen):將另一個程序當做一個新的進程在當前程序進程中啟動,則它算是當前程序的子進程,這種方式我們稱為高級管道方式。
3、有名管道通信
有名管道(named pipe):有名管道也是半雙工的通信方式,但是它允許無親緣關系進程間的通信。
4、消息隊列通信
消息隊列(message
queue):消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識,消息隊列克服了信號傳遞信息少、管道只能承載無格式位元組流以及緩沖區大小受限等缺點。
5、信號量通信
信號量(semophore):信號量是一個計數器,可以用來控制多個進程對共享資源的訪問,它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
6、信號
信號(sinal):信號是一種比較復雜的通信方式,用於通知接收進程某個事件已經發生。
7、共享內存通信
共享內存(shared
memory):共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的IPC方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號量,配合使用,來實現進程間的同步和通信。
8、套接字通信
套接字(socket):套接字也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同機器間的進程通信。

Ⅶ 通信的方式有多種,假設需要在Linux系

進程間的通信方式:
1.管道(pipe)及有名管道(named pipe):
管道可用於具有親緣關系進程間的通信,有名管道除了具有管道所具有的功能外,它還允許無親緣關系進程間的通信。
2.信號(signal):
信號是在軟體層次上對中斷機制的一種模擬,它是比較復雜的通信方式,用於通知進程有某事件發生,一個進程收到一個信號與處理器收到一個中斷請求效果上可以說是一致得。

3.消息隊列(message queue):
消息隊列是消息的鏈接表,它克服了上兩種通信方式中信號量有限的缺點,具有寫許可權得進程可以按照一定得規則向消息隊列中添加新信息;對消息隊列有讀許可權得進程則可以從消息隊列中讀取信息。
消息緩沖通信技術是由Hansen首先提出的,其基本思想是:根據」生產者-消費者」原理,利用內存中公用消息緩沖區實現進程之間的信息交換.

內存中開辟了若干消息緩沖區,用以存放消息.每當一個進程向另一個進程發送消息時,便申請一個消息緩沖區,並把已准備好的消息送到緩沖區,然後把該消息緩沖區插入到接收進程的消息隊列中,最後通知接收進程.接收進程收到發送里程發來的通知後,從本進程的消息隊列中摘下一消息緩沖區,取出所需的信息,然後把消息緩沖區不定期給系統.系統負責管理公用消息緩沖區以及消息的傳遞.

一個進程可以給若干個進程發送消息,反之,一個進程可以接收不同進程發來的消息.顯然,進程中關於消息隊列的操作是臨界區.當發送進程正往接收進程的消息隊列中添加一條消息時,接收進程不能同時從該消息隊列中到出消息:反之也一樣.

消息緩沖區通信機制包含以下列內容:

(1) 消息緩沖區,這是一個由以下幾項組成的數據結構:
1、 消息長度
2、 消息正文
3、 發送者
4、 消息隊列指針

(2)消息隊列首指針m-q,一般保存在PCB中。
(1) 互斥信號量m-mutex,初值為1,用於互斥訪問消息隊列,在PCB中設置。
(2) 同步信號量m-syn,初值為0,用於消息計數,在PCB中設置。
(3) 發送消息原語send
(4) 接收消息原語receive(a)

4.共享內存(shared memory):
可以說這是最有用的進程間通信方式。它使得多個進程可以訪問同一塊內存空間,不同進程可以及時看到對方進程中對共享內存中數據得更新。這種方式需要依靠某種同步操作,如互斥鎖和信號量等。
這種通信模式需要解決兩個問題:第一個問題是怎樣提供共享內存;第二個是公共內存的互斥關系則是程序開發人員的責任。
5.信號量(semaphore):
主要作為進程之間及同一種進程的不同線程之間得同步和互斥手段。

6.套接字(socket);
這是一種更為一般得進程間通信機制,它可用於網路中不同機器之間的進程間通信,應用非常廣泛。

http://blog.csdn.net/eroswang/archive/2007/09/04/1772350.aspx
linux下的進程間通信-詳解
詳細的講述進程間通信在這里絕對是不可能的事情,而且筆者很難有信心說自己對這一部分內容的認識達到了什麼樣的地步,所以在這一節的開頭首先向大家推薦著 名作者Richard Stevens的著名作品:《Advanced Programming in the UNIX Environment》,它的中文譯本《UNIX環境高級編程》已有機械工業出版社出版,原文精彩,譯文同樣地道,如果你的確對在Linux下編程有濃 厚的興趣,那麼趕緊將這本書擺到你的書桌上或計算機旁邊來。說這么多實在是難抑心中的景仰之情,言歸正傳,在這一節里,我們將介紹進程間通信最最初步和最 最簡單的一些知識和概念。
首先,進程間通信至少可以通過傳送打開文件來實現,不同的進程通過一個或多個文件來傳遞信息,事實上,在很多應用系統里,都使用了這種方法。但一般說來, 進程間通信(IPC:InterProcess Communication)不包括這種似乎比較低級的通信方法。Unix系統中實現進程間通信的方法很多,而且不幸的是,極少方法能在所有的Unix系 統中進行移植(唯一一種是半雙工的管道,這也是最原始的一種通信方式)。而Linux作為一種新興的操作系統,幾乎支持所有的Unix下常用的進程間通信 方法:管道、消息隊列、共享內存、信號量、套介面等等。下面我們將逐一介紹。

2.3.1 管道
管道是進程間通信中最古老的方式,它包括無名管道和有名管道兩種,前者用於父進程和子進程間的通信,後者用於運行於同一台機器上的任意兩個進程間的通信。
無名管道由pipe()函數創建:
#include <unistd.h>
int pipe(int filedis[2]);
參數filedis返回兩個文件描述符:filedes[0]為讀而打開,filedes[1]為寫而打開。filedes[1]的輸出是filedes[0]的輸入。下面的例子示範了如何在父進程和子進程間實現通信。

#define INPUT 0
#define OUTPUT 1

void main() {
int file_descriptors[2];
/*定義子進程號 */
pid_t pid;
char buf[256];
int returned_count;
/*創建無名管道*/
pipe(file_descriptors);
/*創建子進程*/
if((pid = fork()) == -1) {
printf("Error in fork\n");
exit(1);
}
/*執行子進程*/
if(pid == 0) {
printf("in the spawned (child) process...\n");
/*子進程向父進程寫數據,關閉管道的讀端*/
close(file_descriptors[INPUT]);
write(file_descriptors[OUTPUT], "test data", strlen("test data"));
exit(0);
} else {
/*執行父進程*/
printf("in the spawning (parent) process...\n");
/*父進程從管道讀取子進程寫的數據,關閉管道的寫端*/
close(file_descriptors[OUTPUT]);
returned_count = read(file_descriptors[INPUT], buf, sizeof(buf));
printf("%d bytes of data received from spawned process: %s\n",
returned_count, buf);
}
}
在Linux系統下,有名管道可由兩種方式創建:命令行方式mknod系統調用和函數mkfifo。下面的兩種途徑都在當前目錄下生成了一個名為myfifo的有名管道:
方式一:mkfifo("myfifo","rw");
方式二:mknod myfifo p
生成了有名管道後,就可以使用一般的文件I/O函數如open、close、read、write等來對它進行操作。下面即是一個簡單的例子,假設我們已經創建了一個名為myfifo的有名管道。
/* 進程一:讀有名管道*/
#include <stdio.h>
#include <unistd.h>
void main() {
FILE * in_file;
int count = 1;
char buf[80];
in_file = fopen("mypipe", "r");
if (in_file == NULL) {
printf("Error in fdopen.\n");
exit(1);
}
while ((count = fread(buf, 1, 80, in_file)) > 0)
printf("received from pipe: %s\n", buf);
fclose(in_file);
}
/* 進程二:寫有名管道*/
#include <stdio.h>
#include <unistd.h>
void main() {
FILE * out_file;
int count = 1;
char buf[80];
out_file = fopen("mypipe", "w");
if (out_file == NULL) {
printf("Error opening pipe.");
exit(1);
}
sprintf(buf,"this is test data for the named pipe example\n");
fwrite(buf, 1, 80, out_file);
fclose(out_file);
}

2.3.2 消息隊列
消息隊列用於運行於同一台機器上的進程間通信,它和管道很相似,是一個在系統內核中用來保存消息的隊列,它在系統內核中是以消息鏈表的形式出現。消息鏈表中節點的結構用msg聲明。
事實上,它是一種正逐漸被淘汰的通信方式,我們可以用流管道或者套介面的方式來取代它,所以,我們對此方式也不再解釋,也建議讀者忽略這種方式。

2.3.3 共享內存
共享內存是運行在同一台機器上的進程間通信最快的方式,因為數據不需要在不同的進程間復制。通常由一個進程創建一塊共享內存區,其餘進程對這塊內存區進行 讀寫。得到共享內存有兩種方式:映射/dev/mem設備和內存映像文件。前一種方式不給系統帶來額外的開銷,但在現實中並不常用,因為它控制存取的將是 實際的物理內存,在Linux系統下,這只有通過限制Linux系統存取的內存才可以做到,這當然不太實際。常用的方式是通過shmXXX函數族來實現利 用共享內存進行存儲的。
首先要用的函數是shmget,它獲得一個共享存儲標識符。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, int size, int flag);
這個函數有點類似大家熟悉的malloc函數,系統按照請求分配size大小的內存用作共享內存。Linux系統內核中每個IPC結構都有的一個非負整數 的標識符,這樣對一個消息隊列發送消息時只要引用標識符就可以了。這個標識符是內核由IPC結構的關鍵字得到的,這個關鍵字,就是上面第一個函數的 key。數據類型key_t是在頭文件sys/types.h中定義的,它是一個長整形的數據。在我們後面的章節中,還會碰到這個關鍵字。

當共享內存創建後,其餘進程可以調用shmat()將其連接到自身的地址空間中。
void *shmat(int shmid, void *addr, int flag);
shmid為shmget函數返回的共享存儲標識符,addr和flag參數決定了以什麼方式來確定連接的地址,函數的返回值即是該進程數據段所連接的實際地址,進程可以對此進程進行讀寫操作。
使用共享存儲來實現進程間通信的注意點是對數據存取的同步,必須確保當一個進程去讀取數據時,它所想要的數據已經寫好了。通常,信號量被要來實現對共享存 儲數據存取的同步,另外,可以通過使用shmctl函數設置共享存儲內存的某些標志位如SHM_LOCK、SHM_UNLOCK等來實現。

2.3.4 信號量
信號量又稱為信號燈,它是用來協調不同進程間的數據對象的,而最主要的應用是前一節的共享內存方式的進程間通信。本質上,信號量是一個計數器,它用來記錄對某個資源(如共享內存)的存取狀況。一般說來,為了獲得共享資源,進程需要執行下列操作:
(1) 測試控制該資源的信號量。
(2) 若此信號量的值為正,則允許進行使用該資源。進程將信號量減1。
(3) 若此信號量為0,則該資源目前不可用,進程進入睡眠狀態,直至信號量值大於0,進程被喚醒,轉入步驟(1)。
(4) 當進程不再使用一個信號量控制的資源時,信號量值加1。如果此時有進程正在睡眠等待此信號量,則喚醒此進程。
維護信號量狀態的是Linux內核操作系統而不是用戶進程。我們可以從頭文件/usr/src/linux/include/linux/sem.h 中看到內核用來維護信號量狀態的各個結構的定義。信號量是一個數據集合,用戶可以單獨使用這一集合的每個元素。要調用的第一個函數是semget,用以獲 得一個信號量ID。

struct sem {
short sempid;/* pid of last operaton */
ushort semval;/* current value */
ushort semncnt;/* num procs awaiting increase in semval */
ushort semzcnt;/* num procs awaiting semval = 0 */
}

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int flag);

key是前面講過的IPC結構的關鍵字,flag將來決定是創建新的信號量集合,還是引用一個現有的信號量集合。nsems是該集合中的信號量數。如果是創建新 集合(一般在伺服器中),則必須指定nsems;如果是引用一個現有的信號量集合(一般在客戶機中)則將nsems指定為0。

semctl函數用來對信號量進行操作。
int semctl(int semid, int semnum, int cmd, union semun arg);
不同的操作是通過cmd參數來實現的,在頭文件sem.h中定義了7種不同的操作,實際編程時可以參照使用。

semop函數自動執行信號量集合上的操作數組。
int semop(int semid, struct sembuf semoparray[], size_t nops);
semoparray是一個指針,它指向一個信號量操作數組。nops規定該數組中操作的數量。

下面,我們看一個具體的例子,它創建一個特定的IPC結構的關鍵字和一個信號量,建立此信號量的索引,修改索引指向的信號量的值,最後我們清除信號量。在下面的代碼中,函數ftok生成我們上文所說的唯一的IPC關鍵字。

#include <stdio.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
void main() {
key_t unique_key; /* 定義一個IPC關鍵字*/
int id;
struct sembuf lock_it;
union semun options;
int i;

unique_key = ftok(".", 'a'); /* 生成關鍵字,字元'a'是一個隨機種子*/
/* 創建一個新的信號量集合*/
id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666);
printf("semaphore id=%d\n", id);
options.val = 1; /*設置變數值*/
semctl(id, 0, SETVAL, options); /*設置索引0的信號量*/

/*列印出信號量的值*/
i = semctl(id, 0, GETVAL, 0);
printf("value of semaphore at index 0 is %d\n", i);

/*下面重新設置信號量*/
lock_it.sem_num = 0; /*設置哪個信號量*/
lock_it.sem_op = -1; /*定義操作*/
lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/
if (semop(id, &lock_it, 1) == -1) {
printf("can not lock semaphore.\n");
exit(1);
}

i = semctl(id, 0, GETVAL, 0);
printf("value of semaphore at index 0 is %d\n", i);

/*清除信號量*/
semctl(id, 0, IPC_RMID, 0);
}

semget()

可以使用系統調用semget()創建一個新的信號量集,或者存取一個已經存在的信號量集:
系統調用:semget();
原型:intsemget(key_t key,int nsems,int semflg);
返回值:如果成功,則返回信號量集的IPC標識符。如果失敗,則返回-1:errno=EACCESS(沒有許可權)
EEXIST(信號量集已經存在,無法創建)
EIDRM(信號量集已經刪除)
ENOENT(信號量集不存在,同時沒有使用IPC_CREAT)
ENOMEM(沒有足夠的內存創建新的信號量集)
ENOSPC(超出限制)
系統調用semget()的第一個參數是關鍵字值(一般是由系統調用ftok()返回的)。系統內核將此值和系統中存在的其他的信號量集的關鍵字值進行比較。打開和存取操作與參數semflg中的內容相關。IPC_CREAT如果信號量集在系統內核中不存在,則創建信號量集。IPC_EXCL當和 IPC_CREAT一同使用時,如果信號量集已經存在,則調用失敗。如果單獨使用IPC_CREAT,則semget()要麼返回新創建的信號量集的標識符,要麼返回系統中已經存在的同樣的關鍵字值的信號量的標識符。如果IPC_EXCL和IPC_CREAT一同使用,則要麼返回新創建的信號量集的標識符,要麼返回-1。IPC_EXCL單獨使用沒有意義。參數nsems指出了一個新的信號量集中應該創建的信號量的個數。信號量集中最多的信號量的個數是在linux/sem.h中定義的:
#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/
下面是一個打開和創建信號量集的程序:
intopen_semaphore_set(key_t keyval,int numsems)
{
intsid;
if(!numsems)
return(-1);
if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(sid);
}
};
==============================================================
semop()

系統調用:semop();
調用原型:int semop(int semid,struct sembuf*sops,unsign ednsops);
返回值:0,如果成功。-1,如果失敗:errno=E2BIG(nsops大於最大的ops數目)
EACCESS(許可權不夠)
EAGAIN(使用了IPC_NOWAIT,但操作不能繼續進行)
EFAULT(sops指向的地址無效)
EIDRM(信號量集已經刪除)
EINTR(當睡眠時接收到其他信號)
EINVAL(信號量集不存在,或者semid無效)
ENOMEM(使用了SEM_UNDO,但無足夠的內存創建所需的數據結構)
ERANGE(信號量值超出范圍)
第一個參數是關鍵字值。第二個參數是指向將要操作的數組的指針。第三個參數是數組中的操作的個數。參數sops指向由sembuf組成的數組。此數組是在linux/sem.h中定義的:
/*semop systemcall takes an array of these*/
structsembuf{
ushortsem_num;/*semaphore index in array*/
shortsem_op;/*semaphore operation*/
shortsem_flg;/*operation flags*/
sem_num將要處理的信號量的個數。
sem_op要執行的操作。
sem_flg操作標志。
如果sem_op是負數,那麼信號量將減去它的值。這和信號量控制的資源有關。如果沒有使用IPC_NOWAIT,那麼調用進程將進入睡眠狀態,直到信號量控制的資源可以使用為止。如果sem_op是正數,則信號量加上它的值。這也就是進程釋放信號量控制的資源。最後,如果sem_op是0,那麼調用進程將調用sleep(),直到信號量的值為0。這在一個進程等待完全空閑的資源時使用。
===============================================================
semctl()

系統調用:semctl();
原型:int semctl(int semid,int semnum,int cmd,union semunarg);
返回值:如果成功,則為一個正數。
如果失敗,則為-1:errno=EACCESS(許可權不夠)
EFAULT(arg指向的地址無效)
EIDRM(信號量集已經刪除)
EINVAL(信號量集不存在,或者semid無效)
EPERM(EUID沒有cmd的權利)
ERANGE(信號量值超出范圍)
系統調用semctl用來執行在信號量集上的控制操作。這和在消息隊列中的系統調用msgctl是十分相似的。但這兩個系統調用的參數略有不同。因為信號量一般是作為一個信號量集使用的,而不是一個單獨的信號量。所以在信號量集的操作中,不但要知道IPC關鍵字值,也要知道信號量集中的具體的信號量。這兩個系統調用都使用了參數cmd,它用來指出要操作的具體命令。兩個系統調用中的最後一個參數也不一樣。在系統調用msgctl中,最後一個參數是指向內核中使用的數據結構的指針。我們使用此數據結構來取得有關消息隊列的一些信息,以及設置或者改變隊列的存取許可權和使用者。但在信號量中支持額外的可選的命令,這樣就要求有一個更為復雜的數據結構。
系統調用semctl()的第一個參數是關鍵字值。第二個參數是信號量數目。
參數cmd中可以使用的命令如下:
·IPC_STAT讀取一個信號量集的數據結構semid_ds,並將其存儲在semun中的buf參數中。
·IPC_SET設置信號量集的數據結構semid_ds中的元素ipc_perm,其值取自semun中的buf

Ⅷ linux系統的進程間通信有哪幾種方式

  • 數據傳輸
    一個進程需要將它的數據發送給另一個進程,發送的數據量在一個位元組到幾M位元組之間

  • 共享數據
    多個進程想要操作共享數據,一個進程對共享數據

  • 通知事
    一個進程需要向另一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。

  • 資源共享
    多個進程之間共享同樣的資源。為了作到這一點,需要內核提供鎖和同步機制。

  • 進程式控制制
    有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,並能夠及時知道它的狀態改變。

  • Linux 進程間通信(IPC)的發展

    linux下的進程通信手段基本上是從Unix平台上的進程通信手段繼承而來的。而對Unix發展做出重大貢獻的兩大主力AT&T的貝爾實驗室及BSD(加州大學伯克利分校的伯克利軟體發布中心)在進程間通信方面的側重點有所不同。

    前者對Unix早期的進程間通信手段進行了系統的改進和擴充,形成了「system V IPC」,通信進程局限在單個計算機內;

    後者則跳過了該限制,形成了基於套介面(socket)的進程間通信機制。

    Linux則把兩者繼承了下來

  • 早期UNIX進程間通信

  • 基於System V進程間通信

  • 基於Socket進程間通信

  • POSIX進程間通信。

  • UNIX進程間通信方式包括:管道、FIFO、信號。

    System V進程間通信方式包括:System V消息隊列、System V信號燈、System V共享內存

    POSIX進程間通信包括:posix消息隊列、posix信號燈、posix共享內存。

    由於Unix版本的多樣性,電子電氣工程協會(IEEE)開發了一個獨立的Unix標准,這個新的ANSI Unix標准被稱為計算機環境的可移植性操作系統界面(PSOIX)。現有大部分Unix和流行版本都是遵循POSIX標準的,而Linux從一開始就遵循POSIX標准;

    BSD並不是沒有涉足單機內的進程間通信(socket本身就可以用於單機內的進程間通信)。事實上,很多Unix版本的單機IPC留有BSD的痕跡,如4.4BSD支持的匿名內存映射、4.3+BSD對可靠信號語義的實現等等。

    linux使用的進程間通信方式

  • 管道(pipe),流管道(s_pipe)和有名管道(FIFO)

  • 信號(signal)

  • 消息隊列

  • 共享內存

  • 信號量

  • 套接字(socket)

  • 管道( pipe )

    管道這種通訊方式有兩種限制,一是半雙工的通信,數據只能單向流動,二是只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。

    流管道s_pipe: 去除了第一種限制,可以雙向傳輸.

    管道可用於具有親緣關系進程間的通信,命名管道:name_pipe克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信;

    信號量( semophore )

    信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。

    信號是比較復雜的通信方式,用於通知接受進程有某種事件發生,除了用於進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD為了實現可靠信號機制,又能夠統一對外介面,用sigaction函數重新實現了signal函數);

    消息隊列( message queue )

    消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式位元組流以及緩沖區大小受限等缺點。

    消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠許可權的進程可以向隊列中添加消息,被賦予讀許可權的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式位元組流以及緩沖區大小受限等缺點。

    信號 ( singal )

    信號是一種比較復雜的通信方式,用於通知接收進程某個事件已經發生。

    主要作為進程間以及同一進程不同線程之間的同步手段。

    共享內存( shared memory )

    共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的 IPC 方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號量,配合使用,來實現進程間的同步和通信。

    使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。

    套接字( socket )

    套解口也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同機器間的進程通信

    更為一般的進程間通信機制,可用於不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。

    進程間通信各種方式效率比較

    類型

    無連接

    可靠

    流控制

    記錄消息類型

    優先順序

    普通PIPE N Y Y N

    流PIPE N Y Y N

    命名PIPE(FIFO) N Y Y N

    消息隊列 N Y Y Y

    信號量 N Y Y Y

    共享存儲 N Y Y Y

    UNIX流SOCKET N Y Y N

    UNIX數據包SOCKET Y Y N N

    注:無連接: 指無需調用某種形式的OPEN,就有發送消息的能力流控制:

    如果系統資源短缺或者不能接收更多消息,則發送進程能進行流量控制

    各種通信方式的比較和優缺點

  • 管道:速度慢,容量有限,只有父子進程能通訊

  • FIFO:任何進程間都能通訊,但速度慢

  • 消息隊列:容量受到系統限制,且要注意第一次讀的時候,要考慮上一次沒有讀完數據的問題

  • 信號量:不能傳遞復雜消息,只能用來同步

  • 共享內存區:能夠很容易控制容量,速度快,但要保持同步,比如一個進程在寫的時候,另一個進程要注意讀寫的問題,相當於線程中的線程安全,當然,共享內存區同樣可以用作線程間通訊,不過沒這個必要,線程間本來就已經共享了同一進程內的一塊內存

  • 如果用戶傳遞的信息較少或是需要通過信號來觸發某些行為.前文提到的軟中斷信號機制不失為一種簡捷有效的進程間通信方式.

    但若是進程間要求傳遞的信息量比較大或者進程間存在交換數據的要求,那就需要考慮別的通信方式了。

    無名管道簡單方便.但局限於單向通信的工作方式.並且只能在創建它的進程及其子孫進程之間實現管道的共享:

    有名管道雖然可以提供給任意關系的進程使用.但是由於其長期存在於系統之中,使用不當容易出錯.所以普通用戶一般不建議使用。

    消息緩沖可以不再局限於父子進程,而允許任意進程通過共享消息隊列來實現進程間通信,並由系統調用函數來實現消息發送和接收之間的同步,從而使得用戶在使用消息緩沖進行通信時不再需要考慮同步問題,使用方便,但是信息的復制需要額外消耗CPU的時間,不適宜於信息量大或操作頻繁的場合。

    共享內存針對消息緩沖的缺點改而利用內存緩沖區直接交換信息,無須復制,快捷、信息量大是其優點。

    但是共享內存的通信方式是通過將共享的內存緩沖區直接附加到進程的虛擬地址空間中來實現的,因此,這些進程之間的讀寫操作的同步問題操作系統無法實現。必須由各進程利用其他同步工具解決。另外,由於內存實體存在於計算機系統中,所以只能由處於同一個計算機系統中的諸進程共享。不方便網路通信。

    共享內存塊提供了在任意數量的進程之間進行高效雙向通信的機制。每個使用者都可以讀取寫入數據,但是所有程序之間必須達成並遵守一定的協議,以防止諸如在讀取信息之前覆寫內存空間等競爭狀態的出現。

    不幸的是,Linux無法嚴格保證提供對共享內存塊的獨占訪問,甚至是在您通過使用IPC_PRIVATE創建新的共享內存塊的時候也不能保證訪問的獨占性。 同時,多個使用共享內存塊的進程之間必須協調使用同一個鍵值。

Ⅸ Linux進程間通信的方式有哪些

第一種:管道通信
兩個進程利用管道進行通信時,發送信息的進程稱為寫進程;接收信息的進程稱為讀進程。管道通信方式的中間介質就是文件,通常稱這種文件為管道文件,它就像管道一樣將一個寫進程和一個讀進程連接在一起,實現兩個進程之間的通信。寫進程通過寫入端往管道文件中寫入信息;讀進程通過讀出端從管道文件中讀取信息。兩個進程協調不斷地進行寫和讀,便會構成雙方通過管道傳遞信息的流水線。
第二種:消息緩沖通信
多個獨立的進程之間可以通過消息緩沖機制來相互通信。這種通信的實現是以消息緩沖區為中間介質,通信雙方的發送和接收操作均以消息為單位。在存儲器中,消息緩沖區被組織成隊列,通常稱之為消息隊列。消息隊列一旦創建後即可由多進程共享,發送消息的進程可以在任意時刻發送任意個消息到指定的消息隊列上,並檢查是否有接收進程在等待它所發送的消息。若有則喚醒它,而接收消息的進程可以在需要消息的時候到指定的消息隊列上獲取消息,如果消息還沒有到來,則轉入睡眠等待狀態。
第三種:共享內存通信
針對消息緩沖需要佔用CPU進行消息復制的缺點,OS提供了一種進程間直接進行數據交換的通信方式。共享內存,顧名思義這種通信方式允許多個進程在外部通信協議或同步,互斥機制的支持下使用同一個內存段進行通信,它是一種最有效的數據通信方式,其特點是沒有中間環節,直接將共享的內存頁面通過附接映射到相互通信的進程各自的虛擬地址空間中,從而使多個進程可以直接訪問同一個物理內存頁面。

Ⅹ Linux進程間通信

進程間通信支持進程之間的通信,Linux支持進程間的多種通信機制,包含信號量、共享內存、消息
隊列、管道、UNIX域套接字等,這些機制可協助多個進程、多資源的互斥訪問、進程間的同步和消息傳
遞。在實際的Linux應用中,人們更多地趨向於使用UNIX域套接字,而不是System V IPC中的消息隊列等
機制。Android內核則新增了Binder進程間通信方式。
Linux內核5個組成部分之間的依賴關系如下。
·進程調度與內存管理之間的關系:這兩個子系統互相依賴。在多程序環境下,程序要運行,則必須
為之創建進程,而創建進程的第一件事情,就是將程序和數據裝入內存。
·進程間通信與內存管理的關系:進程間通信子系統要依賴內存管理支持共享內存通信機制,這種機
制允許兩個進程除了擁有自己的私有空間之外,還可以存取共同的內存區域。
·虛擬文件系統與網路介面之間的關系:虛擬文件系統利用網路介面支持網路文件系統(NFS),也
利用內存管理支持RAMDISK設備。
·內存管理與虛擬文件系統之間的關系:內存管理利用虛擬文件系統支持交換,交換進程定期由調度
程序調度,這也是內存管理依賴於進程調度的原因。當一個進程存取的內存映射被換出時,內存管理向虛
擬文件系統發出請求,同時,掛起當前正在運行的進程。
除了這些依賴關系外,內核中的所有子系統還要依賴於一些共同的資源。這些資源包括所有子系統都
用到的API,如分配和釋放內存空間的函數、輸出警告或錯誤消息的函數及系統提供的調試介面等。

熱點內容
ios遷移安卓用什麼助手 發布:2025-03-16 20:12:42 瀏覽:718
python異常值處理 發布:2025-03-16 20:12:42 瀏覽:579
POtn編程 發布:2025-03-16 20:06:11 瀏覽:774
競彩資金怎麼配置 發布:2025-03-16 20:01:57 瀏覽:9
在終端中打開編譯環境 發布:2025-03-16 19:57:34 瀏覽:600
sqlserver安裝資料庫 發布:2025-03-16 19:57:01 瀏覽:35
如何關掉廣告推送安卓 發布:2025-03-16 19:43:32 瀏覽:958
安卓如何關閉定時開機 發布:2025-03-16 19:23:54 瀏覽:292
酷派4g網路怎麼設置密碼 發布:2025-03-16 19:17:53 瀏覽:664
java培訓費用 發布:2025-03-16 19:17:12 瀏覽:586