在redis源碼的server.c文件中的initServer()方法中有這樣一段代碼:
/* Open the listening Unix domain socket. */
if (server.unixsocket != NULL) {
unlink(server.unixsocket); /* don't care if this fails */
server.sofd = anetUnixServer(server.neterr,server.unixsocket,
server.unixsocketperm, server.tcp_backlog);
if (server.sofd == ANET_ERR) {
serverLog(LL_WARNING, "Opening Unix socket: %s", server.neterr);
exit(1);
}
anetNonBlock(NULL,server.sofd);
}
根據註釋可以知道這段代碼是要打開unix域套接字進行監聽,但是打開之前做了一個判斷,即server.unixsocket是否爲null。這個值是什麼意思呢?又是從哪來的呢?看配置文件redis.conf:
# TCP listen() backlog.
#
# In high requests-per-second environments you need an high backlog in order
# to avoid slow clients connections issues. Note that the Linux kernel
# will silently truncate it to the value of /proc/sys/net/core/somaxconn so
# make sure to raise both the value of somaxconn and tcp_max_syn_backlog
# in order to get the desired effect.
tcp-backlog 511
# Unix socket.
#
# Specify the path for the Unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
unixsocket /tmp/redis.sock
unixsocketperm 700
可以看到redis中的unix域套接字默認是不打開的,這裏是取消註釋將其打開了,並定義了參數unixsocket和unixsocketperm。並截取了上面一部分配置tcp-backlog,這個參數之前在文章:套接字api值listen函數中着重提到過,這裏後面也會用到。可見方法中的值是來自配置文件。如果配置文件中沒打開,那麼server.unixsocket值就是爲null,也就不必打開unix域套接字進行監聽了。
繼續看if()裏面,調用了一個anetUnixServer()方法,三個參數都來自配置文件。
/**
* path: socket文件配置路徑,默認/tmp/redis.sock
* perm: 文件訪問權限,默認700
* backlog: listen方法參數,默認511
**/
int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
{
int s;
struct sockaddr_un sa;
if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)
return ANET_ERR;
memset(&sa,0,sizeof(sa));
sa.sun_family = AF_LOCAL;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa),backlog) == ANET_ERR)
return ANET_ERR;
if (perm)
chmod(sa.sun_path, perm);
return s;
}
static int anetCreateSocket(char *err, int domain)
{
int s;
if ((s = socket(domain, SOCK_STREAM, 0)) == -1) {
anetSetError(err, "creating socket: %s", strerror(errno));
return ANET_ERR;
}
/* Make sure connection-intensive things like the redis benchmark
* will be able to close/open sockets a zillion of times */
if (anetSetReuseAddr(err,s) == ANET_ERR) {
close(s);
return ANET_ERR;
}
return s;
}
看方法體發現這裏並沒有調用socketpair()方法創建一對unix域套接字,而是調用的anetCreateSocket()方法創建的,實際也就是socket()方法,只不過第一個參數是AF_LOCAL(AF_UNIX的別名),返回unix域套接字描述符並賦值給s。然而這時的unix域套接字是沒有名字的,無關進程還不能使用。
anetUnixServer()方法中還定義了一個struct sockaddr_un結構體,定義如下:
struct sockaddr_un {
sa_family_t sun_family; //AF_UNIX
char sun_path[108]; //pathname
};
該結構的sun_path成員保存一個地址,redis就將該結構體和之前生成的套接字描述符s一起傳給anetListen()方法。
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len, int backlog)
{
if (bind(s,sa,len) == -1) {
anetSetError(err, "bind: %s", strerror(errno));
close(s);
return ANET_ERR;
}
if (listen(s, backlog) == -1) {
anetSetError(err, "listen: %s", strerror(errno));
close(s);
return ANET_ERR;
}
return ANET_OK;
}
該方法中主要就是幹了兩件事,先調用bind()方法將創建的unix域套接字s綁到結構體包含的地址上,然後調用listen()方法開始監聽。當啓動redis服務時,就會用該路徑名創建一個socket文件,也就是這裏的/tmp/redis.sock文件。注意該文件無法打開,也不能由應用程序用於通信。
至此,redis中的unix域套接字部分就介紹到這裏,歡迎交流。