日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 嵌入式 > 嵌入式云IOT技術圈
[導讀]1、進程間通信簡述 進程間通信的幾種方式:無名管道、有名管道、消息隊列、共享內存、信號、信號量、套接字(socket)。 進程間通信是不同進程直接進行的一些接觸,這種接觸有簡單,有復雜。機制不同,復雜度也不同。通信是一個廣義上的意 義,不僅指大批量數(shù)

1、進程通信簡述

進程間通信的幾種方式:無名管道、有名管道、消息隊列、共享內存、信號、信號量、套接字(socket)。

進程間通信是不同進程直接進行的一些接觸,這種接觸有簡單,有復雜。機制不同,復雜度也不同。通信是一個廣義上的意 義,不僅指大批量數(shù)據(jù)傳送,還包括控制信息的傳送,但是使用的方法都是大同小異的。

如圖所示進程不是孤立的,不同的進程需要進行信息的交互和狀態(tài)的傳遞等,因此需要進程間通信。

2、管道

管道分為無名管道和有名管道兩種方式。管道是一種半雙工的通信方式,數(shù)據(jù)只能單向流動,但是無名管道和有名管道的區(qū)別是無名管道只能在具有親緣關系的進程間通信,有名管道則是在無親緣關系進程間通信。進程的親緣關系通常是指父子進程關系。管道是Linux支持的最初Unix IPC形式之一,管道與管道之間通信其實就是一個文件,但它不是一個普通的文件,它不屬于某種文件系統(tǒng),而是自立門戶,單獨構成一種文件系統(tǒng)而且只存在內存中。當一個進程向管道中寫的內容被管道另一端的進程讀出;寫入的內容每次都會被添加到管道緩沖區(qū)的末尾,并且每次都是從緩沖區(qū)的頭部讀出數(shù)據(jù)。如下圖所示。

那么,如何創(chuàng)建一條管道呢?下面,我們就來了解下FIFO函數(shù)。

FIFO不同于pipe函數(shù),因為它提供了一個路徑名與之關聯(lián),以FIFO的文件形式存在于文件系統(tǒng)中,這樣,即使與FIFO的創(chuàng)建進程不存在親緣關系的進程,只要可以訪問該路徑就能夠彼此通過FIFO互相通信,因此,通過FIFO不相關的進程也能交換數(shù)據(jù)。值得注意的是,F(xiàn)IFO嚴格遵循先進后出,和棧的原則一樣,對管道以及FIFO的讀總是從開始處返回數(shù)據(jù),對它們的寫則把數(shù)據(jù)添加到末尾。它們不支持諸如lseek()等文件定位操作。

需要包含的頭文件如下:

#include<sys/types.h>
#include<sys/stat.h>
#incldue<fcntl.h>
#include<unistd.h>

FIFO函數(shù)創(chuàng)建:

函數(shù)原型:

int mkfifo(const char *pathname,mode_t mode);

函數(shù)返回值 :

成功0,失敗-1

參數(shù)含義:

pathname為路徑名,創(chuàng)建管道的名字(該函數(shù)的第一個參數(shù)是一個普通的路徑名,也就是創(chuàng)建后FIFO的名字)。mode為創(chuàng)建fifo的權限(第二個參數(shù)與打開普通文件的open()函數(shù)中的mode參數(shù)相同)。

注:如果mkfido的第一個參數(shù)已經(jīng)是一個已經(jīng)存在的路徑名時,就會返回EEXIST錯誤,所以當我們調用的時候首先會檢查是否返回該錯誤,如果返回該錯誤那我們只需要直接調用打開FIFO的函數(shù)即可。

FIFO比pipe函數(shù)打開的時候多了一個打開操作open;如果當時打開操作時為讀而打開FIFO時,若已經(jīng)有相應進程為寫而打開該FIFO,則當前打開操作將返回成功;否則,可能阻塞到有相應進程為寫而打開該FIFO;或者,成功返回。另一種情況就是為寫而打開FIFO時,若已經(jīng)有相應進程為讀而打開該FIFO,則當前打開操作將成功返回;否則可能會阻塞直到有相應進程為讀而打開該FIFO;或者,返回ENIO錯誤。

下面我們使用FIFO實現(xiàn)進程間的通信。

(1)打開一個文件,管道的寫入端向文件寫入數(shù)據(jù);管道的讀取端從文件中讀取出數(shù)據(jù)。

fifo_write.c

#include <stdio.h>
#include <string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define P_FIFO  "txt"

int main()
{
    int fd;
    //要寫入有名管道的數(shù)據(jù)
    char buf[20] = "hello write_fifo";
    int ret=0;
    //創(chuàng)建有名管道,并賦予訪問有名管道的權限
    ret = mkfifo(P_FIFO,0777);
    //創(chuàng)建失敗
    if(ret < 0)
    {
        printf("create named pipe failed.\n");
        return -1;
    }
    fd = open(P_FIFO,O_WRONLY);
    if(fd < 0)
    {
        printf("open failed.\n");
        return -2;
    }
    //寫入數(shù)據(jù)到有名管道
    //第一個參數(shù)為有名管道文件描述符
    //第二個參數(shù)為寫入有名管道的數(shù)據(jù)
    //第三個參數(shù)為寫入有名管道的數(shù)據(jù)長度
    write(fd,buf,sizeof(buf));
    //關閉有名管道
    close(fd);
    return 0;
}

fifo_read.c

#include <stdio.h>
#include <string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define P_FIFO  "txt"

int main()
{
    int ret;
    int fd;
    char buf[20];
    //打開有名管道
    //第一個參數(shù)為有名管道文件路徑
    //第二個參數(shù)表明是以讀取方式并以非阻塞方式打開有名管道
    //O_RDONLY讀取模式
    //O_NONBLOCK非阻塞方式
    fd = open(P_FIFO,O_RDONLY);
    if(fd<0)
    {
        printf("open fail\n");
        return -1 ;
    }
    //循環(huán)讀取有名管道
    while(1)
    {
        memset(buf,0,sizeof(buf));
        if(read(fd,buf,sizeof(buf)) == 0)
        {
            printf("nodata.\n");
        }
        else
        {
            printf("getdata:%s\n",buf);
            break;
        }
   }
   close(fd);
   return 0;
}

下面先將fifo_write.c和fifo_read.c分別編譯成fifo_write和fifo_read兩個可執(zhí)行程序:

接下來,先運行fifo_write,然后打開另一個終端,接著運行fifo_read,運行fifo_write的時候,可以看到程序阻塞在終端:

下面打開另外一個終端運行fifo_read

切換到另外一個終端,在終端輸入ls –l可以看到由于fifo_write中創(chuàng)建了管道文件txt,從前面的字串prwxr-xr-x中的p可以知道,這是一個管道文件,如下圖所示:

運行fifo_read,這時候,可以看到從管道中獲取的字符串hello write_fifo,如下圖所示:

管道讀取結束后,fifo_write這個程序也就不會在阻塞在終端了,如下圖所示:

寫管道程序還要注意,一旦我們創(chuàng)建了FIFO,就可以用open去打開它,可以使用open、read、close等去操作FIFO和pipe有相同之處,當打開FIFO時,非阻塞標志(O_NONBLOCK)將會對讀寫產(chǎn)生如下影響:

  • 1、沒有使用O_NONBLOCK:訪問要求無法滿足時進程將阻塞。如試圖讀取空的FIFO,將導致進程阻塞;
  • 2、使用O_NONBLOCK:訪問要求無法滿足時不阻塞,立即出錯返回,errno是ENXIO。

3、消息隊列

消息隊列(也叫做報文隊列)提供了一個進程向另一個進程發(fā)送一個數(shù)據(jù)塊的方法。每個數(shù)據(jù)塊都被認為含有一個類型,接收進程可以獨立地接收含有不同類型的數(shù)據(jù)結構。我們可以通過發(fā)送消息來避免命名管道的同步和阻塞問題。但是消息隊列與命名管道一樣,每個數(shù)據(jù)塊都有一個最大長度的限制。

打開或者創(chuàng)建消息隊列的內核持續(xù)性要求每個消息隊列都在系統(tǒng)范圍內對應唯一的鍵值,所以,要獲得一個消息隊列的描述字,只需要提供該消息隊列的鍵值即可。

消息讀寫操作非常簡單,對于開發(fā)人員來說,每個消息都類似如下的數(shù)據(jù)結構:

struct msgbuf
{
 long mtype;
 char mtext[1];
};

3.1、msgget函數(shù)

該函數(shù)用來創(chuàng)建或者訪問一個消息隊列。

int msgget(key_t key, int msgflg);

與其他的IPC機制一樣,程序必須提供一個鍵來命名某個特定的消息隊列。msgflg是一個權限標志,表示消息隊列的訪問權限,它與文件的訪問權限一樣。msgflg可以與IPC_CREAT做或操作,表示當key所命名的消息隊列不存在時創(chuàng)建一個消息隊列,如果key所命名的消息隊列存在時,IPC_CREAT標志會被忽略,成功則返回一個以key命名的消息隊列的標識符(非零整數(shù)),失敗時返回-1。

3.2、msgsnd函數(shù)

該函數(shù)用來向消息隊列發(fā)送一個消息。

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

將發(fā)送的消息存儲在msgp指向的msgbuf結構中,消息大小由msgsz指定。對發(fā)送的消息來說,有意義的msgflg標準為IPC_NOWAIT,指明在消息隊列沒有足夠的空間容納要發(fā)送的消息時,msgsnd是否等待。造成msgsnd()等待的條件有兩種:當前消息的大小與當前消息隊列中的字節(jié)數(shù)之和超過了消息隊列的總容量;當前消息隊列的消息數(shù)不小于消息隊列的總容量,此時,雖然消息隊列中的消息數(shù)目并不多,但基本上都只有一個字節(jié)。調用成功的時候返回0,失敗返回-1.

3.3、msgrcv函數(shù)

該函數(shù)用來從一個消息隊列獲取消息。

int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

msgrcv函數(shù)前面三個參數(shù)和msgsnd函數(shù)的三個參數(shù)一樣不做講解。msgtype可以實現(xiàn)一種簡單的接收優(yōu)先級。如果msgtype為0,就獲取隊列中的第一個消息。如果它的值大于零,將獲取具有相同消息類型的第一個信息。如果它小于零,就獲取類型等于或小于msgtype的絕對值的第一個消息。msgflg用于控制當隊列中沒有相應類型的消息可以接收時將發(fā)生的事情。當調用成功時,該函數(shù)返回放到接收緩存區(qū)中的字節(jié)數(shù),消息被復制到由msg_ptr指向的用戶分配的緩存區(qū)中,然后刪除消息隊列中對應的消息;失敗則返回-1.

3.4、msgctl函數(shù)

該函數(shù)用來控制消息隊列。

int msgctl(int msgid, int command, struct msgid_ds *buf);

該系統(tǒng)調用對由msqid標識的消息隊列執(zhí)行cmd操作,共有三種cmd操作:

  • IPC_STAT:把msgid_ds結構中的數(shù)據(jù)設置為消息隊列的當前關聯(lián)值,即用消息隊列的當前關聯(lián)值覆蓋msgid_ds的值。
  • IPC_SET:如果進程有足夠的權限,就把消息列隊的當前關聯(lián)值設置為msgid_ds結構中給出的值。
  • IPC_RMID:刪除消息隊列。buf是指向msgid_ds結構的指針,它指向消息隊列模式和訪問權限的結構。成功返回0,否則返回-1。

通過上面的函數(shù)我們清楚如何去創(chuàng)建一個消息隊列那我們簡單的來看一個案例。

(1)創(chuàng)建一條消息隊列msg_get.c

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main(void)
{
 int  msgid ; 
 //創(chuàng)建消息隊列,注意,創(chuàng)建后面要有IPC_CREAT標志
 msgid = msgget(0x123456 , IPC_CREAT | 0777);
 if(msgid < 0)
 {
  perror("msgget fail");
  return -1 ; 
 }
 printf("success ... ! \n");
 return 0  ;
}

運行結果:

那消息隊列呢?怎么查看?使用ipcs –q命令可以查看到剛剛我們創(chuàng)建的消息隊列0x123456。

(2)向消息隊列發(fā)送消息 msgsend.c

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main(void)
{
    int msgid ;
    msgid = msgget(0x123456 , 0);
    if(msgid == -1)
    {
        perror("create msg queue fail");
        return -1 ;
    }
    printf("open msg success ... \n");
    int ret ;
    char *p = "hello world" ;
   //發(fā)送hello world到消息隊列0x123456
   //在這里可以直接發(fā)送
    ret = msgsnd(msgid , p , strlen(p) , 0);
    if(ret == -1)
    {
        perror("send msgid fail");
        return -2 ;
    }
    return 0 ;
}

運行結果:

使用ipcs –p命令查看:

(3)獲取消息隊列中的信息 msgrecv.c 在上面msgsend.c的基礎上,這個例程將上面發(fā)送到消息隊列的信息讀取回來。

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main(void)
{
    int msgid ;
    msgid = msgget(0x123456 , 0);
    if(msgid == -1)
    {
        perror("create msg queue fail");
        return -1 ;
    }
    printf("open msg success ... \n");
    int ret ;
    char buffer[1024] = {0};
    //接收消息隊列中的信息
    ret = msgrcv(msgid , buffer , 11 , 0 , 0);
    if(ret == -1)
    {
        perror("recv msgid fail");
        return -2 ;
    }
    printf("ret: %d  buffer:%s \n" , ret , buffer);
    return 0 ;
}

運行結果,如圖所示:

那么,如何刪除一個消息隊列呢?先用ipcs –q查看消息隊列,如圖所示:

有兩種方法:

  • 1、使用命令ipcrm –q msqid 刪除消息隊列,如圖所示

  • 2、使用msgctl函數(shù),寫IPC_RMID標志刪除消息隊列

(4)刪除消息隊列 msgrm.c

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main(void)
{
    int  msgid ;
    msgid = msgget(0x123456 , 0);
    if(msgid < 0)
    {
        perror("msgget fail");
        return -1 ;
    }
    printf("success ... ! msgid:%d \n" , msgid);
    //寫IPC_RMID標志
    if(msgctl(msgid , IPC_RMID , NULL) == 0)
    {
        printf("remove success ... \n");
    }
    return 0  ;
}

運行結果,如圖所示:

使用系統(tǒng)提供的API的方式,可以將消息隊列刪除。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等缺點,相對于管道通信有很大的改觀,而且消息隊列對數(shù)據(jù)的順序處理也是非常有條理性的不會產(chǎn)生混雜性。

往期精彩

Linux 進程必知必會

【Linux系統(tǒng)編程】IO標準緩沖區(qū)

【Linux系統(tǒng)編程】可重入和不可重入函數(shù)

韋東山:6000字長文告訴你如何學習linux

會C/C++就可以開發(fā)Linux/Android應用程序?Yoxios了解一下!

覺得本次分享的文章對您有幫助,隨手點[在看]并轉發(fā)分享,也是對我的支持。

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

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

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統(tǒng) 散熱

根據(jù)LED驅動電源的公式,電感內電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數(shù)校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉