对照linux内核4.8.6的代码进行研究的。
1:socket进程间通信
unix的发展史上 ,AT&T的贝尔实验室和伯克利软件中心(BSD)做出了重大的贡献。AT&T 实现的 SysV IPC , BSD对socket的实现。
2:系统调用socket
像SysV IPC一样,socket有个总入口,sys_socketcall(); socket实现的核心代码在 内核中的目录为 net/socket.c中
/*
* System call vectors.
*
* Argument checking cleaned up. Saved 20% in size.
* This function doesn't need to set the kernel lock because
* it is set by the callees.
* System call vectors.
*
* Argument checking cleaned up. Saved 20% in size.
* This function doesn't need to set the kernel lock because
* it is set by the callees.
*/
#define AL(x) ((x) * sizeof(unsigned long)) //用于后面设置获取数据大小
static const unsigned char nargs[21] = {
AL(0), AL(3), AL(3), AL(3), AL(2), AL(3),
AL(3), AL(3), AL(4), AL(4), AL(4), AL(6),
AL(6), AL(2), AL(5), AL(5), AL(3), AL(3),
AL(4), AL(5), AL(4)
AL(0), AL(3), AL(3), AL(3), AL(2), AL(3),
AL(3), AL(3), AL(4), AL(4), AL(4), AL(6),
AL(6), AL(2), AL(5), AL(5), AL(3), AL(3),
AL(4), AL(5), AL(4)
};
SYSCALL_DEFINE2 宏定义将 传入的第一参数 转换为 sys_##socketcall 即得到 相应的调用接口
SYSCALL_DEFINE2
(socketcall
, int, call, unsigned long __user *, args)
{
unsigned long a[AUDITSC_ARGS];
unsigned long a0, a1;
int err;
unsigned int len;
if (call < 1 || call > SYS_SENDMMSG)
return -EINVAL;
unsigned long a[AUDITSC_ARGS];
unsigned long a0, a1;
int err;
unsigned int len;
if (call < 1 || call > SYS_SENDMMSG)
return -EINVAL;
len = nargs[call]; //参数个数乘以unsignd long
if (len > sizeof(a))
return -EINVAL;
return -EINVAL;
/* copy_from_user should be SMP safe. */
if (copy_from_user(a, args, len)) //将需要传递的所有参数都放入args中,参数个数不受限制,分发是根据具体端口,取出传递不同的参数。
return -EFAULT;
err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
if (err)
return err;
return err;
a0 = a[0];
a1 = a[1]; //将a中参数从a中取出来,方便后面操作
//根据call的值来进行分发比较区分 举例:#define SYS_SOCKET 1 /* sys_socket(2) */
//参数传递到接口后,根据接口数据类型进行转换。 这样的架构对于我们写应用程序的时候,不确定参数,进行分发的时候也是一个好的建议。内核代码不仅可以用于学习内核操作原理,更值得我们去学习他的代码实现方式,架构。对于自己的代码编写很有好处
switch (call) {
case SYS_SOCKET:
err = sys_socket(a0, a1, a[2]);
break;
case SYS_BIND:
err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
break;
case SYS_CONNECT:
err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
break;
case SYS_LISTEN:
err = sys_listen(a0, a1);
break;
case SYS_ACCEPT:
err = sys_accept4(a0, (struct sockaddr __user *)a1,
(int __user *)a[2], 0);
break;
case SYS_GETSOCKNAME:
err =
sys_getsockname(a0, (struct sockaddr __user *)a1,
(int __user *)a[2]);
break;
case SYS_GETPEERNAME:
err =
sys_getpeername(a0, (struct sockaddr __user *)a1,
(int __user *)a[2]);
break;
case SYS_SOCKETPAIR:
err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
break;
case SYS_SEND:
err = sys_send(a0, (void __user *)a1, a[2], a[3]);
break;
case SYS_SENDTO:
err = sys_sendto(a0, (void __user *)a1, a[2], a[3],
(struct sockaddr __user *)a[4], a[5]);
break;
case SYS_RECV:
err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
break;
case SYS_RECVFROM:
err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
(struct sockaddr __user *)a[4],
(int __user *)a[5]);
break;
case SYS_SHUTDOWN:
err = sys_shutdown(a0, a1);
break;
case SYS_SETSOCKOPT:
err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
break;
case SYS_GETSOCKOPT:
err =
sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
(int __user *)a[4]);
break;
case SYS_SENDMSG:
err = sys_sendmsg(a0, (struct user_msghdr __user *)a1, a[2]);
break;
case SYS_SENDMMSG:
err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]);
break;
case SYS_RECVMSG:
err = sys_recvmsg(a0, (struct user_msghdr __user *)a1, a[2]);
break;
case SYS_RECVMMSG:
err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3],
(struct timespec __user *)a[4]);
break;
case SYS_ACCEPT4:
err = sys_accept4(a0, (struct sockaddr __user *)a1,
(int __user *)a[2], a[3]);
break;
default:
err = -EINVAL;
break;
}
return err;
case SYS_SOCKET:
err = sys_socket(a0, a1, a[2]);
break;
case SYS_BIND:
err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
break;
case SYS_CONNECT:
err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
break;
case SYS_LISTEN:
err = sys_listen(a0, a1);
break;
case SYS_ACCEPT:
err = sys_accept4(a0, (struct sockaddr __user *)a1,
(int __user *)a[2], 0);
break;
case SYS_GETSOCKNAME:
err =
sys_getsockname(a0, (struct sockaddr __user *)a1,
(int __user *)a[2]);
break;
case SYS_GETPEERNAME:
err =
sys_getpeername(a0, (struct sockaddr __user *)a1,
(int __user *)a[2]);
break;
case SYS_SOCKETPAIR:
err = sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
break;
case SYS_SEND:
err = sys_send(a0, (void __user *)a1, a[2], a[3]);
break;
case SYS_SENDTO:
err = sys_sendto(a0, (void __user *)a1, a[2], a[3],
(struct sockaddr __user *)a[4], a[5]);
break;
case SYS_RECV:
err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
break;
case SYS_RECVFROM:
err = sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
(struct sockaddr __user *)a[4],
(int __user *)a[5]);
break;
case SYS_SHUTDOWN:
err = sys_shutdown(a0, a1);
break;
case SYS_SETSOCKOPT:
err = sys_setsockopt(a0, a1, a[2], (char __user *)a[3], a[4]);
break;
case SYS_GETSOCKOPT:
err =
sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
(int __user *)a[4]);
break;
case SYS_SENDMSG:
err = sys_sendmsg(a0, (struct user_msghdr __user *)a1, a[2]);
break;
case SYS_SENDMMSG:
err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]);
break;
case SYS_RECVMSG:
err = sys_recvmsg(a0, (struct user_msghdr __user *)a1, a[2]);
break;
case SYS_RECVMMSG:
err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3],
(struct timespec __user *)a[4]);
break;
case SYS_ACCEPT4:
err = sys_accept4(a0, (struct sockaddr __user *)a1,
(int __user *)a[2], a[3]);
break;
default:
err = -EINVAL;
break;
}
return err;
}
2.1 sys_socket的实现
int socket(int domain,int type, int protocol);
一般前两个参数确定以后,函数流程也就确定了,除非一些特殊情况,一般设置protocol为0.
函数范围的是一个正整数,文件描述符,这个文件描述符与一个代表这个文件插口的数据结构相关联,并不是根据磁盘上的某个文件相关联。
后面根据这个文件描述符,就可以找到相关联的数据接口进行一些操作。
sys_bind的实现
socket 创建以后还不够,只有一个文件号,还需要一个地址,与文件号相关联。好比,买了手机以后,还要买个电话号码,才能与外界通信。
int bind(int sockfd, struct sockaddr *myaddr, socklen_t len);
sys_listen的实现
若想建立服务器的地位,必须使用listen接口,可以监听所有其他插口对自己的连接,对client作出回应。
int listen(int sockfd, int backlog); //允许连接队列的最大值。
sys_connect的实现
clent向server发送连接请求。
int connect(int sockfd, struct sockaddr *serv_addr, addrlen_t len);
sys_accept的实现
server接受client的请求。
int accept(int sockfd, struct sockaddr *addr, socklen_t *len);
重点在这里,这里会返回一个新的fd,这个相当于另开了一个服务单门和这个地址的客户端进行服务,每个client都有这种待遇。新开个服务与之通信。
C/S之间的数据传输接口:
有连接的: read() , write(), 以及sendto send recv recvfrom
无连接的:sendto send recv recvfrom。 由于无连接的数据传输时不可靠的,数据次序是不确定的,不适合用 read和write来实现。