《UNIX網路程式設計 卷1》 筆記 描述符傳遞技術

2021-08-01 17:28:26 字數 3419 閱讀 7758

linux提供了一種從乙個程序向另乙個程序傳遞任意開啟的描述符的技術,這兩個程序可以無親緣關係。這種技術要求首先在這兩個程序之間建立乙個unix域套接字,然後使用sendmsg跨套接字傳送乙個特殊的訊息,這個訊息由核心來處理,會把開啟的描述符傳遞到接收程序。

先來看看要使用的資料結構和函式。

struct msghdr ;

struct cmsghdr ;

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

msghdr結構表示資料訊息首部,msg_control指向輔助資料,msg_controllen指明了輔助資料的長度(包括輔助資料首部)。

cmsghdr結構體表示輔助資料首部。為了傳遞描述符,我們將cmsg_level取值為sol_socket,cmsg_type取值為scm_rights,實際的輔助資料長度為4位元組(描述符的大小)。為此我們定義了乙個表示輔助資料的聯合體:

union  control_un;
傳遞描述符的具體步驟如下:

1. 如果是父子程序之間傳遞描述符,則父程序呼叫socketpair函式

建立乙個流管道。如果是無親緣關係的程序,則程序之間使用unix域套接字通訊,就像上節我們給出的客戶與伺服器之間通訊的程式一樣。

2. 傳送程序開啟乙個描述符,建立乙個msghdr結構,其中的輔助資料含有待傳遞的描述符,然後呼叫sendmsg函式傳送描述符。即使之後程序呼叫close函式關閉描述符,但是對於接收程序它仍然保持開啟的狀態。因為傳送乙個描述符會使該描述符的引用計數加1。

3. 接收程序呼叫recvmsg函式

接收描述符。這個描述符的值並不一定和傳送程序傳送的描述符的值相同,但是它們都指向核心中相同的檔案表項。

傳送描述符的函式write_fd實現如下,引數fd是unix域套接字描述符,引數sendfd是要傳送的描述符。

ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd)

control_un;

struct cmsghdr *cmptr;

msg.msg_name = null;

msg.msg_namelen = 0;

iov[0].iov_base = ptr;

iov[0].iov_len = nbytes;

msg.msg_iov = iov;

msg.msg_iovlen = 1;

msg.msg_control = control_un.control;

msg.msg_controllen = sizeof(control_un.control);

cmptr = cmsg_firsthdr(&msg);

cmptr->cmsg_len = cmsg_len(sizeof(int));

cmptr->cmsg_level = sol_socket;

cmptr->cmsg_type = scm_rights;

*((int *)cmsg_data(cmptr)) = sendfd; /*要傳遞的描述符*/

return sendmsg(fd, &msg, 0); /*傳送資料*/

}ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd)

接收描述的函式read_fd的實現如下:

ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)

control_un;

struct cmsghdr *cmptr;

msg.msg_name = null;

msg.msg_namelen = 0;

iov[0].iov_base = ptr;

iov[0].iov_len = nbytes;

msg.msg_iov = iov;

msg.msg_iovlen = 1;

msg.msg_control = control_un.control;

msg.msg_controllen = sizeof(control_un.control);

/*讀取資料*/

if ((n = recvmsg(fd, &msg, 0)) <= 0)

return n;

/*解析出輔助資料*/

if ((cmptr = cmsg_firsthdr(&msg)) != null &&

cmptr->cmsg_len == cmsg_len(sizeof(int))) else

*recvfd = -1;

return n;

}ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)

書中給出了乙個描述符傳遞的例子,實現了兩個程式mycat和openfile。mycat程式建立乙個流管道,呼叫fork函式建立乙個子程序,然後在子程序中呼叫execl函式執行openfile程式,將流管道一端的描述符(呼叫execl函式後已經開啟的描述符不會關閉)和要開啟的檔案路徑通過execl函式的傳給openfile程式。openfile程式開啟檔案,然後將它的描述符通過流管道傳遞給父程序。父程序讀取描述符,將檔案輸出到標準輸出。

mycat程式的主體功能由my_open函式實現,**如下:

int my_open(const char *pathname, int mode)

close(sockfd[1]);

waitpid(childpid, &status, 0); /*等待子程序終止*/

if (wifexited(status) == 0)

err_quit("child did not terminate");

if ((status = wexitstatus(status)) == 0) /*子程序正常終止*/

read_fd(sockfd[0], &c, 1, &fd); /*讀取子程序傳遞的檔案描述符*/

else

close(sockfd[0]);

return fd;

}

mycat程式的**如下:

int main(int argc, char **argv)

openfile程式的**如下:

int main(int argc, char **argv)

如注釋所示:通過流管道傳送描述符(輔助資料)時,我們總是傳送至少1位元組資料。

《UNIX網路程式設計 卷1》 筆記 UNIX域協議

unix域協議並不是乙個實際的協議族,而是在單個主機上客戶程序和伺服器程序之間通訊的一種方法。unix域使用的套接字結構如下 struct sockaddr un int main int argc,char argv 執行結果如下 可以看到繫結的路徑名 tmp 123 現在是乙個套接字檔案。uni...

《UNIX網路程式設計 卷1》 筆記 高階I O函式

本節我們關注稱為 高階i o 的各個函式和技術。首先是在i o操作上設定超時。在涉及套接字的i o操作上設定超時的方法有以下3種 1.呼叫alarm函式,在定時時間超時產生sigalrm訊號,打斷i o操作。首先我們給出要使用的signal1函式的實現,這個函式不是系統呼叫signal的包裹函式,而...

《UNIX網路程式設計 卷2》 筆記 管道

管道是最初的unix ipc形式,它的侷限性在於沒有名字,只能在有親緣關係的程序間使用。後來,fifo出現了,fifo也稱為有名管道。管道和fifo都使用read和write函式訪問。include int pipe int fd 2 管道由pipe函式建立,返回兩個描述符 fd 0 用來讀,fd ...