第七章 :7.1 socket进程间通信-系统调用socket

来源:互联网 发布:mac os 10.10 dmg 编辑:程序博客网 时间:2024/05/29 19:31
对照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.
*/

#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)
};
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;

     len = nargs[call];   //参数个数乘以unsignd long
     if (len > sizeof(a))
          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;

     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;
}
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来实现。






1 0