IPC: Unix Domain Socket
要讓系統中的兩個 process 彼此溝通該如何處理?過去常見的守法有 pipeline、shared memory 等,不過其實我一個都沒有用過。我比較喜歡的作法是 socket programming,在兩個 process 間建立 network socket,因為這樣的概念最簡單(我一向很懶)。
問題是,使用 network socket 會造成效率上的浪費,儘管兩個 process 都在同一台機器(localhost)上,但是還是會到 kernel 的 TCP/IP Stack 裏面逛個一圈,這在執行上勢必會有效能的浪費。既然都已經在本機了,那又何必經過網路層的處理呢?今天要介紹的 IPC 技術就是一個解決這問題的方案: Unix Domain Socket。從下面的範例上來看,寫法和 network socket 幾乎一模一樣。所以就不多做介紹了,直接看程式吧。
問題是,使用 network socket 會造成效率上的浪費,儘管兩個 process 都在同一台機器(localhost)上,但是還是會到 kernel 的 TCP/IP Stack 裏面逛個一圈,這在執行上勢必會有效能的浪費。既然都已經在本機了,那又何必經過網路層的處理呢?今天要介紹的 IPC 技術就是一個解決這問題的方案: Unix Domain Socket。從下面的範例上來看,寫法和 network socket 幾乎一模一樣。所以就不多做介紹了,直接看程式吧。
上面的程式應該很好懂,基本上建立 socket 的流程和過去 network socket 的流程一模一樣,除了使用 AF_UNIX 以及 address 上給的是檔案名稱這兩件事情。在程式範例裏面,寫法類似 UDP,但其實還可以使用 connect 以及 send 來處理,因為概念類似,就不另外提供範例了。#include <stdio.h> // for printf() and fprintf() #include <sys/socket.h> // for socket(), bind(), and connect() #include <sys/un.h> // for struct sockaddr_un #include <stdlib.h> // for atoi() and exit() #include <string.h> // for memset() #include <unistd.h> // for close() #include <sys/time.h> // for struct timeval {} #include <fcntl.h> // for fcntl() #include <sys/epoll.h> int main( int argc, char *argv[] ) { struct sockaddr_un unixSocketAddr; // Unix Domain Socket int unfd; // Unix Socket FD struct sockaddr_un unixSocketDstAddr; // Unix Domain Socket struct sockaddr_un unixSocketSrcAddr; // Unix Domain Socket socklen_t unixSocketAddrLen; // Socket Length int epfd; // EPOLL File Descriptor. struct epoll_event ev; // Used for EPOLL. struct epoll_event events[120]; // Used for EPOLL. int noEvents; // EPOLL event number. int i, j, k, len, running = 1; const int cBufferSize = 1024; char msg[cBufferSize]; if ( argc < 3 ) { fprintf( stderr, "Usage: %s\n", argv[0]); exit(1); } // Create epoll file descriptor. // Standard Input + Bind Socket epfd = epoll_create( 2 ); // Create Unix domain socket + Bind + add to the EPOLL set memset( &unixSocketAddr, 0, sizeof( unixSocketAddr ) ); unixSocketAddr.sun_family = AF_UNIX; strcpy( unixSocketAddr.sun_path, argv[1] ); if( ( unfd = socket( AF_UNIX, SOCK_DGRAM, 0 ) ) < 0 ) { perror("socket error."); exit(1); } unlink( argv[1] ); if ( bind( unfd, (struct sockaddr *)&unixSocketAddr, sizeof( unixSocketAddr ) ) == -1 ) { perror("bind error."); exit(1); } ev.data.fd = unfd; ev.events = EPOLLIN | EPOLLET; epoll_ctl( epfd, EPOLL_CTL_ADD, unfd, &ev ); // Create destination Unix domain socket memset( &unixSocketDstAddr, 0, sizeof( unixSocketDstAddr ) ); unixSocketDstAddr.sun_family = AF_UNIX; strcpy( unixSocketDstAddr.sun_path, argv[2] ); // Add STDIN into the EPOLL set. ev.data.fd = STDIN_FILENO; ev.events = EPOLLIN | EPOLLET; epoll_ctl( epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev ); printf( "Starting server: \n" ); while ( running ) { printf( "Input (q to quit): " ); fflush( stdout ); noEvents = epoll_wait( epfd, events, FD_SETSIZE , -1 ); printf( "Get %d events.\n ", noEvents ); for( i = 0 ; i < noEvents; i++ ) { if( events[i].events & EPOLLIN && STDIN_FILENO == events[i].data.fd ) { memset( msg, cBufferSize, 0 ); fgets( msg, cBufferSize, stdin ); if( strlen( msg ) == 2 && msg[0] == 'q' && msg[1] == '\n' ) { running = 0; continue; } len = sendto( unfd, msg, strlen( msg ), 0, ( struct sockaddr * ) &unixSocketDstAddr, sizeof( unixSocketDstAddr ) ); if( len < 0 ) { perror( "Send Error!!\n" ); } } else if( events[i].events & EPOLLIN && unfd == events[i].data.fd ) { memset( msg, cBufferSize, 0 ); len = recvfrom( unfd, msg, cBufferSize, 0, ( struct sockaddr * ) &unixSocketSrcAddr, &unixSocketAddrLen ); if( len < 0 ) { perror( "Recv Error!!\n" ); } for( j = 0 ; j < len ; j++ ) { printf( "%02X ", msg[j] ); if( j % 16 == 15 ) { printf( "\n" ); } } printf( "\n" ); } else { printf( "Unknown %d!\n", events[i].data.fd ); } } } close( unfd ); return 0; } ...
留言
張貼留言