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 ...