Wayland協議解析 三 Wayland的工作原理

首先,需要了解wayland的工作原理,需要了解幾個其他的內容。

  1. int mkstemp(char *template); // 創建臨時文件 (標C接口)
  2. long int strtol(const char *nptr,char **endptr,int base); // 字符串轉數字
  3. ssize_t sendmsg (int s, const struct msghdr *msg, int flags); // 可以往其他進程發送fd的函數

衆所周知,子進程會繼承父進程已經打開的文件描述符fd,但是fork之後的是不會被繼承的,這個時候是否無能無力了?答應是NO。Linux提供了一個系統調用sendmsg,藉助它,可以實現進程間傳遞文件描述符fd,而且不僅限於父進程到子進程。sendmsg函數的原型如下:

 

#include <sys/socket.h>

ssize_t sendmsg(int socket, const struct msghdr *message, int flags);

ssize_t recvmsg(int socket, struct msghdr *message, int flags);

 

recvmsg函數用來接收fd,這裏的socket必須爲UnixSocket(AF_UNIX),在Linux上執行man 7 unix,並搜索SCM_RIGHTS,即看到有關說明:Send or receive a set of open file descriptors from another process。

 

通過sendmsg發送的fd,並不是將fd值傳遞給目標進程,而是活生生地在目標進程空間裏複製指向同一個file結構體的fd,所以不要期望在兩個進程中,fd值相同。

 

具體的使用示例,請baidu或google關鍵詞:sendmsg fd,即可找到,這裏就不多說了。

 

  1. int ftruncate(int fd,off_t length);   // 修改文件大小
  2. epoll函數  // 實現一個fd監聽多個fd的效果

int epoll_create(int size)   // 創建epoll fd描述符

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) // 監聽其他的文件描述符

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)   // 啓動監聽

 

https://baike.baidu.com/item/epoll%E5%87%BD%E6%95%B0/10792875

 

  1. int socket(int domain, int type, int protocol); // 創建一個網絡描述符.如果第一個參數是AF_UNIX, 獲取到的描述符可以傳遞文件描述符(通過sendmsg函數), 實現本地的進程間通信

int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);

// 在上一個函數使用AF_UNIX參數創建時, bind函數指定一個文件路徑來實現進程間通信, 使用結構體(sockaddr_un)保存文件路徑

int listen( int sockfd, int backlog); // 啓動監聽網絡套接字

int accept( int fd, struct socketaddr* addr, socklen_t* len); // 接受連接進來的客戶端

 

  1. /* Request notification for delivery of signals in MASK to be

performed using descriptor FD.*/

extern int signalfd (int __fd, const sigset_t *__mask, int __flags) // 產生信號監聽fd

 

/* Clear all signals from SET.  */

extern int sigemptyset (sigset_t *__set) __THROW __nonnull ((1));

 

/* Set all signals in SET.  */

extern int sigfillset (sigset_t *__set) __THROW __nonnull ((1));

 

/* Add SIGNO to SET.  */

extern int sigaddset (sigset_t *__set, int __signo) __THROW __nonnull ((1));

 

// 讓信號在被處理之前程序都處於阻塞狀態

extern int sigprocmask (int __how, const sigset_t *__restrict __set,

                    sigset_t *__restrict __oset)

// 以上API  創建一個信號的fd

 

  1. Libffi庫,FFI(Foreign Function Interface)允許以一種語言編寫的代碼調用另一種語言的代碼,而libffi庫提供了最底層的、與架構相關的、完整的FFI。libffi的作用就相當於編譯器,它爲多種調用規則提供了一系列高級語言編程接口,然後通過相應接口完成函數調用,底層會根據對應的規則,完成數據準備,生成相應的彙編指令代碼。

 

 

我來描述一下,wayland是怎麼把上面的知識點給聯繫起來的。

首先,我們必須要清楚wayland是一個CS架構的協議,所以肯定涉及到通信,但是wayland的CS侷限於同一個操作系統,因爲底層會利用操作系統的特性來傳遞進程中的文件描述符(文件句柄),接下來我們分爲服務器端和客戶端來分析:

  1. 首先服務器端創建一個epoll_fd,爲什麼需要創建呢?因爲服務器端會監聽非常多的fd,而epoll_fd是linux最高效的用來監聽大量fd的方法(sylixos操作系統的epoll_fd是模擬出來的,不具有linux那樣的高效特性,但是確實還是支持,只是不能去select epoll_fd)。
  2. 服務器端創建一個綁定到指定文件(默認文件名爲wayland-%d,路徑由XDG_RUNTIME_DIR環境變量指定)的socket,並把這個socket添加到epoll_fd,在此之前,服務器端還需要註冊很多客戶端能用的服務(通過這個接口註冊:wl_global_create),並開始等待客戶端的連接,隨後連接進來的socket也會添加到epoll_fd裏面一起監聽。
  3. 這時客戶端可以開始連接服務器端了,通過wl_display_connect返回客戶端wl_display句柄,這個結構體內部就包含了socket連接的fd,並可以和服務器進行通信了。
  4. 客戶端調用的第一個請求只能是獲取服務器端有哪些服務可以使用(因爲其他的接口都是各種服務提供的請求):wl_display_get_registry,該請求返回一個結構體wl_registry,就是我前面說的客戶端所有的interface對象實際上都是wl_proxy。然後給這個結構體設置事件處理函數,這樣客戶端在接收到服務器端的消息的時候會轉換成函數調用,調用到這個事件處理函數。(這裏我要說明一點。客戶端和服務器通信的數據全是函數調用的序列化數據(包括函數調用的參數及函數名(函數名用一個數字代表)),接收端再反序列化爲函數調用,最後去調用該函數),這個函數調用用到了libffi的接口,函數序列化數據傳遞用的sendmsg接口,可以額外傳遞文件描述符,但是傳遞方式和一般的不太一樣,所以纔會大費周章的把fd做爲特殊的參數類型,就是在這裏用的。

 

以上就是簡單的描述wayland的工作原理,下面開始更詳細的描述wayland的工作原理。不過,在進行更詳細的原理介紹之前,我必須要給大家把wayland通過的接口介紹一遍,其實wayland接口明白之後,讀者應該就能明白大概的原理了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章