版權聲明:本文爲本文爲博主原創文章,轉載請註明出處。若有問題,歡迎指正。博客地址:https://www.cnblogs.com/wsg1100/
html
Linux系統中常見的進程間通信方式有管道、FIFO、共享內存、信號、套接字等方式。但在xenomai內核加入後,一個實時任務與非實時(普通Linux任務,如人機交互應用)之間該如何通信?linux
雖然xenomai任務自己也是一個linux任務,可以無障礙地使用linux提供的進程間通信方式,可是當實時任務調用這些服務接口的時候會觸發任務遷移,遷移到linux核,由linux接管調度並提供服務,Linux內核自己就只是軟實時內核,這樣必然會嚴重影響了xenomai實時任務實時性。編程
實時任務除了可使用Linux的進程間通信外(固然不建議使用),xenomai也提供了針對實時任務的進程間通信方式(Real-time IPC),其中包含一種跨域通信方式---XDDP(cross-domain datagram protocol跨域數據報協議)。跨域
RTIPC
以RTDM(實時設備驅動模型)下的Protocol Devices來實現,根據進程間通信狀況不一樣,rtipc提供三種進程間通信:dom
固然,並非說有了RTIPC,xenomai內核就沒有其它通信方式了,其實大部分posix標準通信方式xenoma內核均有實現,僅用於實時任務間,如:信號量(sem)、消息隊列(mq)、xddp/bufp/iddp、事件(event)、條件變量(cond)....,至於它們的內核實現,與RTIPC不一樣,能夠關注本博客後續文章。異步
因爲RTIPC以實時內核驅動模塊的形式來實現,因此要使用RTIPC,就得在內核構建編譯的時候配置,以下:socket
Xenomai/cobalt ---> Drivers ---> Real-time IPC drivers ---> <*> RTIPC protocol family [*] XDDP cross-domain datagram protocol [*] IDDP intra-domain datagram protocol (32) Number of IDDP communication ports [*] Buffer protocol (32) Number of BUFP communication ports
實時應用經過套接字來使用RTIPC,雖然接口與普通套接字接口同樣,可是參數須要根據xenomai提供的參數來使用,下面爲官方文檔簡單直譯。函數
建立套接字。線程
#include <rtdm/ipc.h> int socket(int domain, int type, int protocol);
參數:指針
domain:AF_RTIPC
地址族;
type:套接字類型,SOCK_DGRAM
(其他無效)
protocol:
IPCPROTO_XDDP
、IPCPROTO_IDDP
、IPCPROTO_BUFP
、IPCPROTO_IPC
默認協議(IPCPROTO_IDDP)。返回值:
返回一個套接字,出錯:除了用於socket(2)的標準錯誤代碼外,還可能返回如下特定錯誤代碼:
關閉一個套接字。
int close (int sockfd)
當套接字關閉並返回錯誤時,將解除阻塞在sendmsg或recvmsg的阻塞。
設置套接字選項。
#include <rtdm/ipc.h> int setsockopt(int sockfd, int level, int optname, const void * optval, socklen_t optlen )
針對XDDP套接字選項說明及參數配置以下:
/proc/xenomai/registry/rtipc/xddp/%s
)來打開通信端點,而不是用設備路徑名(/dev/rtpN
)
level
: SOL_XDDP
optname
:XDDP_LABEL
optval
:rtipc_port_label
指針optlen
:sizeof(struct rtipc_port_label)
struct rtipc_port_label { /** 端口標籤字符串,以null結尾。 */ char label[XNOBJECT_NAME_LEN]; };
XDDP_POLLSZ:XDDP本地內存池大小配置。默認狀況下,傳輸數據所需的內存是從xenomai的系統內存池中提取的,設定本地池大小會覆蓋默認大小。若是配置了非零大小,則在bind時才進行分配實際內存。 該池將爲未決數據提供存儲。綁定套接字後,不容許配置本地池大小。 可是,綁定以前容許進行多個配置調用。 將使用最後設置的值。
level
: SOL_XDDP
optname
:XDDP_POLLSZ
optval
:指向類型爲size_t的變量的指針,該變量表示綁定時保留的本地池大小,單位:字節。optlen
:sizeof(size_tl)
XDDP_BUFSZ :XDDP流緩衝區大小配置。除了發送數據報外,實時線程還能夠經過端口以面向字節的模式傳輸數據。爲套接字設置非零緩衝區大小時,啓用此功能。這樣,當任何發送函數使用MSG_MORE標誌時,實時數據會累積到流緩衝區中,發生如下狀況時緩衝區數據會被髮送出去:
Linux域中接收器被喚醒接收數據,
發送標誌中沒有MSG_MORE,
緩衝區已滿。(以先到者爲準)。
將* optval
設置爲0將禁用流緩衝區,在這種狀況下,全部發送都將在單獨的數據報中傳輸,而與MSG_MORE
無關。
注意:每一個套接字只有一個流緩衝區。當該緩衝區滿時,實時數據將中止積累,而且僅在數據報模式恢復發送操做。從Linux域端點消耗了流緩衝區中的部分或所有數據以後,可能會再次發生累積。在套接字生存期中,能夠屢次調整流緩衝區的大小;在刷新前一個緩衝區後恢復累積時,最新的配置更改將生效。
level
: SOL_XDDP
optname
:XDDP_BUFSZ
optval
:指向類型爲size_t的變量的指針,該變量表示綁定時保留的本地池大小,單位:字節。optlen
:sizeof(size_t)
XDDP_MONITOR:XDDP監視回調。對套接字安插用戶定義的回調函數,以便收集通道上發生的特定事件。此機制對於在執行其餘任務時異步監視通道特別有用。僅適用於內核空間任務。
level
: SOL_XDDP
optname
:XDDP_MONITOR
optval
:指向類型爲int (*)(int fd, int event, long arg)的函數的指針,其中包含用戶定義的回調函數的地址。在optval中傳遞NULL回調指針將禁用該功能。
optlen
:sizeof(size_t)
針對IDDP套接字選項說明及參數配置以下:
level
: SOL_IDDP
optname
:IDDP_LABEL
optval
:rtipc_port_label
指針optlen
:sizeof(struct rtipc_port_label)
struct rtipc_port_label { /** 端口標籤字符串,以null結尾。 */ char label[XNOBJECT_NAME_LEN]; };
level
: SOL_IDDP
optname
:IDDP_POLLSZ
optval
:指向類型爲size_t的變量的指針,該變量表示綁定時保留的本地池大小,單位:字節。`optlen
:sizeof(size_tl)
針對BUFP套接字選項說明及參數配置以下:
BUFP_BUFSZ:配置BUFP緩衝區大小,寫入BUFP的數據都被緩衝在每一個套接字的存儲區域中,必須配置該大小。綁定套接字後,不容許配置本地池大小。 可是,綁定以前容許進行多個配置調用。 將使用最後設置的值。
level
: SOL_BUFP
optname
:BUFP_BUFSZ
optval
:指向類型爲size_t的變量的指針,該變量表示綁定時保留的本地池大小,單位:字節。`optlen
:sizeof(size_tl)
BUFP_LABEL:設置BUFP端口標籤。以便以比使用普通數字端口值更具描述性的方式來鏈接套接字。
綁定套接字後,不容許分配標籤。 可是,在綁定以前容許屢次分配調用。 最後一個標籤集將被使用。
綁定一個RTIPC socket到一個端口。
int bind(int sockfd, const struct sockaddr_ipc *addr, socklen_t addrlen)
將套接字綁定到目標端口。
sockfd
:套接字文件描述符。
addr
:綁定套接字的地址(請參見struct sockaddr_ipc)。 該地址的含義取決於套接字所使用的RTIPC協議:
sipc_family
:必須是AF_RTIPC,sipc_port
爲-1或者0到CONFIG_XENO_OPT_PIPE_NRDEV-1之間的有效空閒端口號。若是sipc_port爲-1,bind將自動爲其分配一個空閒端口。
成功後,將爲該通訊通道保留僞設備/dev /rtpN
,其中N是分配的端口號。 非實時端應打開此設備以經過綁定的套接字交換數據。
若是使用了label,非實時經過僞設備/proc/xenomai/registry/rtipc/xddp/label
來與實時通信。
sipc_family
:必須是AF_RTIPC,sipc_port
爲-1或者0到CONFIG_XENO_OPT_IDDP_NRPORT-1之間的有效空閒端口號。若是sipc_port爲-1,bind將自動爲其分配一個空閒端口。
sipc_family
:必須是AF_RTIPC,sipc_port
爲-1或者0到CONFIG_XENO_OPT_BUFP_NRPORT-1之間的有效空閒端口號。若是sipc_port爲-1,bind將自動爲其分配一個空閒端口。
addrlen
:addr指向的結構體大小。
數據發送與接收。
ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
參數:
sockfd
:socket()建立的套接字.
buf
:發送/接收的數據;
len
:發送/接收的數據長度;
flags
:MSG_MORE發送標誌位,將帶有該標誌的數據包累積到緩衝區,而不是當即發出數據報,僅用於XDDP協議。
數據發送與接收。recvmsg()能作全部read()、sendto()能作到的事,一樣sendmsg()能作全部read()、sendto()能作到的事,具體使用方法查閱Linux相關資料。
recvmsg()從RTIPC套接字接收消息。
#include <rtdm/ipc.h> struct msghdr { void *msg_name; /* optional address */ socklen_t msg_namelen; /* size of address */ struct iovec *msg_iov; /* scatter/gather array */ size_t msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data, see below */ size_t msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags (unused) */ }; ssize_t recvmsg (int sockfd, struct msghdr *msg, int flags)
參數:
sockfd
: socket()建立的套接字。
msg
:消息頭將被複制到該地址,具體查閱資料。
flasgs
:MSG_DONTWAIT 非阻塞操做,若是沒有消息可接收時,不會阻塞,當即返回EWOULDBLOCK,只有實時應用能使用該標誌。
sendmsg()在RTIPC套接字上發送消息
#include <rtdm/ipc.h> ssize_t sendmsg (int sockfd, const struct msghdr *msg, int flags)
參數:
sockfd
: socket()建立的套接字。
msg
:傳達數據報的消息頭的地址,,具體查閱資料。
flasgs
:MSG_OOB給發送帶外消息;(帶外數據:容許發送端將傳送的數據標記爲高優先級)。
MSG_DONTWAIT 非阻塞操做,當沒法當即發送消息時(如內存不足),不會阻塞,而是當即返回EWOULDBLOCK。
MSG_MORE發送前先累積數據到緩衝區,而不是當即發出數據報,僅用於IPCPROTO_XDDP協議。只有實時應用能使用該標誌。
IPCPROTO_XDDP
:跨域數據報協議(RT<->NRT),實時Xenomai線程和常規Linux線程通信時使用,linux端經過read()、write()
讀寫/dev/rtp <minor>
來通信,Xenomai端經過套接字recvfrom()或read()
來接收數據,sendto()或write()
來發送數據。
一個LLinux任務與一個實時任務使用XDDP進行通信,實時任務向Linux任務發送消息,Linux任務收到後原樣發送出去,實時任務將收到的消息顯示出來(xenomai示例:xenomai3.0.8\demo\posix\cobalt\xddp-echo.c
)。
對於linux可經過打開固定rtipc端口的設備節點來與實時任務固定端口通信,這個端口是全局的,被使用了另外一個實時任務就沒法再使用。另外一種方式是設置XDDP端口標籤。實時程序設定XDDP端口的ASCII字符串名稱,設定後在非實時端,可經過設備名稱(/proc/xenomai/registry/rtipc/xddp/%s)來打開通信端點,而不是用設備路徑名(/dev/rtpN),其中的端口xenomai會自動分配。(xenomai示例:xenomai3.0.8\demo\posix\cobalt\xddp-label.c
)
同一系統的兩種方式儘可能不要混合使用,否則會發生以下狀況,程序1使用XDDP端口標籤配置了XDDP socket,此bind時系統爲該socket分配的是端口1,接着另外一個程序2開始建立另外一個XDDP socket,因爲指定了用端0來通信,但該端口已經被程序1佔用,就會綁定端口失敗,致使程序沒法正常運行。下面例子使用固定端口通信:
使用帶緩衝區方式與非實時應用通信,使用端口0,實時端:
#define XDDP_PORT 0 /*通信端口0*/ ..... /*1.建立一個XDDP(rt<->nrt)通信socket,AF_RTIPC、SOCK_DGRAM爲固定參數*/ s = socket(AF_RTIPC, SOCK_DGRAM, IPCPROTO_XDDP); if (s < 0) { perror("socket"); exit(EXIT_FAILURE); } /*2.配置socket s爲流緩衝通信,緩衝區大小爲1KB,設置爲零將禁用流緩衝,每次數據發送將單獨傳輸*/ streamsz = 1024; /* bytes */ ret = setsockopt(s, SOL_XDDP, XDDP_BUFSZ, &streamsz, sizeof(streamsz)); if (ret) fail("setsockopt"); /*3.將套接字s綁定到端口0*/ memset(&saddr, 0, sizeof(saddr)); saddr.sipc_family = AF_RTIPC; //固定參數 saddr.sipc_port = XDDP_PORT; //端口0 對應非實時讀寫的設備節點/dev/rtp0 ret = bind(s, (struct sockaddr *)&saddr, sizeof(saddr)); for (;;) { /*4.發送*/ for (b = 0; b < len; b++) { /*MSG_MORE表示:一字節一字節的將數據存到緩衝區*/ ret = sendto(s, msg[n] + b, 1, MSG_MORE, NULL, 0); if (ret != 1) fail("sendto"); /*若是不使用MSG_MORE,每一個字母將做爲一個數據包。Linux端段每次讀取只能讀取到一個字母,且符合FIFO*/ ret = sendto(s, msg[n] + b, 1, 0, NULL, 0); if (ret != 1) fail("sendto"); } /*4.接收數據*/ ret = recvfrom(s, buf, sizeof(buf), 0, NULL, 0); if (ret <= 0) fail("recvfrom"); } /* 5.關閉套接字*/ close(s);
非實時端:
#define _GNU_SOURCE /*使用asprintf()函數須要該宏*/ #include <stdio.h> #include <stdlib.h> #define XDDP_PORT 0 /*通信端口0*/ char buf[128],*devname; if (asprintf(&devname, "/dev/rtp%d", XDDP_PORT) < 0)/* /dev/rtp0 */ fail("asprintf"); /*1.打開設備 /dev/rtp0*/ fd = open(devname, O_RDWR); free(devname); for (;;) { /*2.讀/dev/rtp0*/ ret = read(fd, buf, sizeof(buf)); if (ret <= 0) fail("read"); /*3.寫/dev/rtp0來發送數據*/ ret = write(fd, buf, ret); if (ret <= 0) fail("write"); } close(fd);