网络协议有很多协议族,常见的是AF_Inet、AF_Unix,前者是IPv4,后者用于本地通信。
Unix Socket用于环回地址通信,通信流程不经过网络层、数据链路层、物理层,不经过网络,只是内核缓冲区之间的数据拷贝,效率高一些。
一、基本结构
1 缓冲区
unix socket是基于文件系统和缓冲区实现的,内核中有个缓冲区队列unix_datas,长度取决于socket数量。
代码语言:c 复制#define AF_UNSPEC 0
#define AF_UNIX 1
#define AF_INET 2
#define PF_UNIX AF_UNIX
#define PF_INET AF_INET
//
extern struct unix_proto_data unix_datas[NSOCKETS];
struct unix_proto_data {
int refcnt; /* cnt of reference 0=free */
/* -1=not initialised -bgm */
struct socket *socket; /* socket we're bound to */
int protocol;
struct sockaddr_un sockaddr_un;//unix是基于文件系统实现的,这个保存了server socket监听的文件路径
short sockaddr_len; /* >0 if name bound */
char *buf;//数据
int bp_head, bp_tail;//循环队列
struct inode *inode;
struct unix_proto_data *peerupd; //unix连接的另一方缓冲区
struct wait_queue *wait; /* Lock across page faults (FvK) */
int lock_flag;
};
2 Unix协议
代码语言:c 复制struct proto_ops unix_proto_ops = {
unix_proto_init,
unix_proto_create,
unix_proto_dup,
unix_proto_release,
unix_proto_bind,
unix_proto_connect,
unix_proto_socketpair,
unix_proto_accept,
unix_proto_getname,
unix_proto_read,
unix_proto_write,
unix_proto_select,
unix_proto_ioctl
};
二、建立连接源码分析
1 创建socket
调用socket()系统调用时需要指定family,才能找到对应的操作函数集合。然后从unix_datas上分配一个空闲槽位用于接收消息。
代码语言:c 复制static int
unix_proto_create(struct socket *sock, int protocol)
{
struct unix_proto_data *upd;
dprintf(1, "UNIX: create: socket 0x%x, proto %dn", sock, protocol);
if (protocol != 0) {
dprintf(1, "UNIX: create: protocol != 0n");
return(-EINVAL);
}
//1 从unix_datas上分配一个空闲槽位
if (!(upd = unix_data_alloc())) {
printk("UNIX: create: can't allocate buffern");
return(-ENOMEM);
}
//2 分配buffer用于接收数据
if (!(upd->buf = (char*) get_free_page(GFP_USER))) {
printk("UNIX: create: can't get page!n");
unix_data_deref(upd);
return(-ENOMEM);
}
upd->protocol = protocol;
upd->socket = sock;
UN_DATA(sock) = upd;
upd->refcnt = 1; /* Now its complete - bgm */
dprintf(1, "UNIX: create: allocated data 0x%xn", upd);
return(0);
}
2 bind
unix socket是基于文件系统实现的,server socket调用bind时绑定文件路径并创建文件,client socket根据文件路径连接server socket。
代码语言:c 复制static int unix_proto_bind(struct socket* sock,
struct sockaddr* umyaddr,
int sockaddr_len) {
//1 基于文件系统实现,绑定一个文件路径
char fname[sizeof(((struct sockaddr_un*)0)->sun_path) 1];
struct unix_proto_data* upd = UN_DATA(sock);
unsigned long old_fs;
int i;
int er;
dprintf(1, "UNIX: bind: socket 0x%x, len=%dn", sock, sockaddr_len);
if (sockaddr_len <= UN_PATH_OFFSET ||
sockaddr_len > sizeof(struct sockaddr_un)) {
dprintf(1, "UNIX: bind: bad length %dn", sockaddr_len);
return (-EINVAL);
}
if (upd->sockaddr_len || upd->inode) {
printk("UNIX: bind: already bound!n");
return (-EINVAL);
}
er = verify_area(VERIFY_WRITE, umyaddr, sockaddr_len);
if (er)
return er;
//2 从用户空间拷贝数据
memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
upd->sockaddr_un.sun_path[sockaddr_len - UN_PATH_OFFSET] = '