linux网络

1,interrupt.h

     在该中断头文件中定义了结构体struct bh_struct:

struct bh_struct{

    void (*routine)(void *);                 ------------软中断处理函数

    void *data;                                   ------------软中断数据

};

     也定义了使能位,long bh_active , bh_mask;

     另外也定一个了软中断需要处理的数组:

     struct bh_struct bh_base[32];

     目前只有8个软中断类型需要处理,这8个软中断类型用枚举定义如下:

      enum{

           TIMER_BH = 0 , ------------定时器软中断

           CONSOLE_BH , 

           TQUEUE_BH , 

           SERIAL_BH , 

           NET_BH            -----------网络软中断

     在kernel/softirq.c中定义了do_bottom_half()函数,该函数被定期执行,每次执行都会看看bh_base中有没有被使能的软中断,如果有的话就会调用bh_struct.routine(bh_struct.data)来处理

 

2,net.h

     该头文件定义了socket核心结构体,结构体如下:

     struct socket{

               short type ;           ----------socket类型; SOCK_DGRAM(UDP数据报) , SOCK_STREAM(TCP数据流)

               socket_state state ;      ---------socket状态

               long   flags;

               struct proto_ops *ops;      ---------该socket可以进行操作的函数指针结构体

               void *data;

              struct socket *next;             ---------socket组成链表的指针

              struct wait_queue **wait;     --------等待在该socket上的阻赛队列

              struct inode *inode ;            --------- 该socket所隶属的索引节点,其实socket就是inode结构体中的一个结构体

              struct fasync_struct *fasync_list ;     ----------异步通知队列               

     };

      socket_state表示socket的状态,socket有如下几个状态:

           SS_FREE                                  -------该socket还未分配

           SS_UNCONNECTED                -------该socket还未有任何连接

           SS_CONNECTING                    -------该socket正在连接中

           SS_CONNECTED                     -------该socket已经被连接

           SS_DISCONNECTING              -------该 socket正在断开连接

 

     proto_ops结构体定义了可以对一个socket进行操作的函数集合,这些函数有:

           

                                                                            proto_ops结构体中函数集合
函数定义函数备注
int (*create)(struct socket*sock , int protocol) 
int (*dup)(struct socket *newsock , socket *oldsock ) 
int (*release)(strcut socket *sock , struct *peer) 
int (*bind)(struct socket *sock , struct socketaddr *umyaddr, int sockaddr_len)将sock绑定到地址umyaddr上去,绑定到底是什么意思
int (*connect)(struct socket *sock , struct socketaddr *umyaddr , int socketaddr_len,int flags)将本地sock连接到远程地址umyaddr上面去
int (*accept)(struct socket *sock , struct socket *newsock , int flags)从sock的已建立连接队列中提取一个已经建立好的连接,并给这个连接分配一个新的套接字,新套接字的指针为newsock
int (*getname)(struct socket*sock , struct sockaddr *uaddr , int *sockaddr_len , int peer) 
int (*read)(struct socket*sock , char *ubuf , int size , int nonblock)从sock中读取size个字节的数据到用户地址空间ubuf处,sock开始读取的起始位置为哪里?
int (*write)(struct socket *sock , char *ubuf , int size , int nonblock)从用户地址空间ubuf处开始写入size个字节到socket中,写入套接字的起始地址在哪里?
int (*listen)(struct socket *sock , int len)服务器套接字监听客户端的连接请求,len指定内核需要为套接字保持的队列长度
int (*send)(struct socket *sock , void *buff , int len , int nonblock , int flags)从用户空间buff处开始向套接字socket写入len个字节的数据,写入套接字的起始地址在哪里?
int (*recv)(struct socket *sock , void *buff , int len , int nonblock , int flags)从socket中读取len个字节的数据到用户空间buff处,sock开始读取的起始位置在哪里?
int (*shutdown)(struct socket *sock , int flags)该函数只用来关闭本地套接字到远程套接字的连接,注意该函数只是不让客户端向服务端发送数据,但是依然可以接收服务端传送过来的数据,所以该函数仅仅是修改一下标志位

         该头文件还声明了几个函数,如下所示:

                                                                                  net.h声明的函数
函数名函数备注
int sock_register(int family , struct proto_ops *ops)往内核中注册一个协议族,该协议族包括了proto_ops中各个函数的具体实现,该函数在初始化网络栈的时候调用
int sock_unregister(int family)从内核中取消一个协议族,对应协议族中的proto_ops也没有了
struct socket *sock_alloc(void)分配一个socket结构,socket结构本质上位于inode结构体中,所以本质上是分配一个索引节点;inode和socket中有指针互相引用着
void sock_release(socket *sock)释放一个socket结构体,本质上是释放一个inode

                

3,protocols.c

      该文件定义链路层所使用协议的初始化函数,核心数据结构有如下两个:

struct net_proto{

       char *name ;                                           --------协议名称

       void (*init_func)(struct net_proto*);         ---------协议启动函数

}

struct net_proto protocols[] = { {"UNIX",unix_proto_init} , {"INET" , init_proto_init} , {"DDP",atalk_proto_init} };    -------协议数组,协议启动函数是什么时候注册进去的

 

4,socket.c

     static struct file_operations socket_file_ops = {
               sock_lseek,                                                          --------该函数在网络中没有实现
               sock_read,                                                           --------socket读,从socket中读取数据到用户空间中
               sock_write,                                                           --------socket写,将用户空间数据写入到socket中
               NULL,            /* readdir */                                     --------网络操作没有定义读目录操作
               sock_select,                                                         ---------选择操作 ????
               sock_ioctl,                                                            ---------对网络输入输出进行控制
               NULL,            /* mmap */                                      --------网络没有内存映射?
               NULL,            /* no special open code... */           --------网络套接字不用打开,socket系统调用已经起到了打开网络的作用
               sock_close,                                                          --------网络关闭操作
               NULL,            /* no fsync */
               sock_fasync
      };

      如上结构体是允许网络进行的文件操作,也即将网络也当作文件来看待

 

      static struct proto_ops *pops[16];   --------该数组定义了网络协议族操作函数集合。比如对于INET域为inet_proto_ops,对于UNIX域为unix_proto_ops;

      static int socket_in_use = 0 ;         --------该全局变量定义了正在使用中的套接字个数

      int sys_socketcall(int call , long *args);     ------该函数为所用网络系统调用的总入口函数,call字段用来路由不同的内核函数, args为输入参数指针,整个函数描述罗列如下:

 

                                             网络操作系统调用一览

                                            sys_socketcall(call , args)

call被路由到的函数函数备注
SYS_SOCKETsock_socket(int family,int type,int protocol)

创建一个socket

       1,根据family选择一个域操作函数集合(ops),inet_proto_ops/unix_proto_ops

       2,type为socket的类型,SOCK_STREAM/SOCK_DGRAM

       3,分配一个inode

       4,分配一个socket , 将inode/socket用指针互相引用起来

       5,初始化socket.type , socket.ops

       6,调用INET层创建sock结构

       7,为inode分配一个文件描述符,在file数据结构中会初始化该索引节点可以进行的文件操作,索引节点指针,读写指针位置

SYS_BINDsock_bind(int fd , struct sockaddr *umyaddr , int addrlen)

将一个本地IP地址绑定到本地套接字上,该套接字由文件描述符fd指定

       1,调用INET层bind函数

SYS_CONNECTsock_connect(int fd , struct sockaddr *uservaddr , int addrlen)

建立本地套接字(fd)到远程服务器的连接,远程服务器地址由sockaddr指定

        1,调用INET层connect函数

SYS_LISTENsock_listen(int fd , int backlog)

该函数用于服务器端为接收客户端连接作准备,本质上也是调用INET层listen函数

该函数一般是服务器端调用

SYS_ACCEPTsock_accept(int fd , struct sockaddr *upeer_sockaddr , int *upeer_addrlen)

        该函数用于服务器端在监听套接字(fd)上监听客户端的连接请求,如果有客户端的连接请求过来,该函数返回与客户端连接的新套接字的文件描述符,注意此时对于一个新过来的客户端连接,是新建立一个套接字,原套接字依然用于监听客户端连接请求

         1,根据fd取得监听套接字sock

         2,创建新套接字newsock(用来和客户端连接通信)

         3,用sock信息初始化newsock(INET层dup)

         4,调用INET层accept完成与远端的连接建立过程

         5,为newsock分配文件描述符

         6,如果upeer_sockaddr填写了,这里会填进去客户端的地址(调用INET层getname完成)

SYS_GETSOCKNAMEsock_getsockname(int fd , struct sockaddr *usockaddr , int *usockaddr_len)

该函数用于获取本地IP地址和端口号,本地IP地址和端口号是绑定于套接字(fd)sock结构中的,地址信息会在最后放到usockaddr中去

         1,调用INET层getname函数

SYS_GETPEERNAMEsock_getpeername(int fd , struct sockaddr *usockaddr , int *)

该函数用于获得远端IP地址和端口号,远端IP地址和端口号也是绑定与套接字(fd)sock结构中的,远端地址信息在最后会放到usockaddr中去

         1,调用INET层getname函数

SYS_SENDsock_send(int fd , void *buff , int len , long flags)

该函数将用户空间buff开始的len个字节数据写入到socket中,socket由fd指定,这里的疑问是套接字中写入起始地址在哪里

         1,调用INET层send函数

SYS_SENDTOsock_sendto(int fd , void *buff , int len , long flages , struct sockaddr*addr , int addr_len)

该函数将用户空间buff处开始的len个字节数据写入套接字中,不过该函数可以指定远端IP地址,但是对于TCP来说指定的远端IP地址必须是之前建立连接的IP地址(不明白,一个socket本来不就唯一对应一个IP地址吗)

         1,调用INET层sendto函数

SYS_RECVsock_recv(int fd , void *buff , int len , unsigned flags)

该函数从套接字中读取len个字节的数据到用户空间buff开始的位置,套接字由fd指定

         1,调用INET层recv函数

SYS_RECVFROMsock_recvfrom(int fd,void *buff , int len , unsigned flags , struct sockaddr*addr,int *addr_len)

该函数从套接字中读取len个字节的数据到用户空间buff开始的位置,同时返回远端的IP地址并放到addr中,该函数主要用于UDP

         1,调用INET层recvfrom函数

         

SYS_SHUTDOWNsock_shutdown(int fd , int how)

该系统调用用来半关闭本地连接,用来告诉远端本地数据已经完全传输完毕,关闭本地到远程的连接通道。但是远程到本地的连接通道还没有关闭,远端依然可以给本地传送数据,该函数主要是设置相关标志位,不涉及网路数据的传输

         1,调用INET层inet_shutdown函数

SYS_SETSOCKOPTsock_setsockopt(int fd , int level , int optname , char *optval , int  optlen)该函数用来设置套接字属性
SYS_GETSOCKOPTsock_getsockopt(int fd , int level , int optname , char *optval , int *optlen)该函数用来提取套接字属性

 

        上面列出来的是网络栈给用户暴漏的系统调用接口,下面介绍一些内核内部使用的函数:

                                                                                         socket.c中其他函数
函数名函数备注
int move_addr_to_kernel(void *uaddr , int ulen , void *kaddr)

该函数的作用是将用户空间的数据拷贝到内核空间;

用户空间的起始地址为uaddr , 数据字节长度为ulen , 内核空间被写入的起始地址为kaddr

int move_addr_to_user(void * kaddr , int klen , void * uaddr , int *ulen)

该函数的作用是将内核空间的数据拷贝到用户空间;

内核空间的起始地址为kaddr , 被拷贝数据字节长度为min(klen , *ulen),用户空间被写入的起始地址为uaddr

int get_fd(struct inode *inode)该函数的作用是为索引节点inode分配一个文件描述符
struct socket *socki_lookup(struct inode *inode)返回索引节点inode中的套接字socket节点的指针
struct socket *socki_lookup(int fd , struct **pfile)返回文件描述符fd指向的索引节点inode中socket节点的指针
struct socket *sock_alloc(void)

分配一个新的套接字;

       1,向内核申请一个inode

       2,设置该inode->i_mode = S_IFSOCK;

                        inode->i_sock = 1;

       3,sock = &inode->u.socket_i;(socket本质上是inode中的一个结构体)

       4,设置sock属性,sock上面的等待队列就是inode上的等待队列,设置指向inode的指针

       5,sock_in_use ++

int sock_read(struct inode *inode , struct file *file , char *ubuf , int size)

从套接字inode->u.socket_i中读取size个字节的数据到用户空间ubuf处,套接字中读取的起始地址是啥

        1,调用INET层read

int sock_write(struct inode *inode , struct file *file , char *ubuf,int size)

从用户空间ubuf处开始写入size个字节的数据到inode->u.socket_i中去,套接字中被写入的起始地址是啥

        1,调用INET层write

int sock_select(struct inode *inode , struct file *file , int sel_type , select_table *wait)

该函数干嘛的?

        1,调用INET层select

int sock_register(int family , struct proto_ops *ops)

将网络协议族注册进pops数组中

        1,选择一个空闲的ops[i]

        2,ops[i] = ops

        3,ops[i]->family = family

 

该函数何时被调用,proto_ops到底是怎么初始化的

int sock_unregister(int family)

从网络协议族中撤销family网络协议

         1,从ops数组中找到family网络协议ops[i]

         2,ops[i]=null

void proto_init(void)

该函数用来初始化网络部分协议

实现方式为依次调用protocols数组中每个元素的init_func函数

void sock_init(void)

该函数是系统网络栈初始化总入口函数,在start_kernel()函数中被调用

          1,清空pops数组             

          2,proto_init()                  --------------网络协议族初始化

          3,dev_init()                    --------------网卡驱动程序初始化

          4,bh_base[NET_BH].routine = net_bh;     -----网络中断下半部初始化,net_bh到底是啥

                enable_bh(NET_BH)     ----------使能网络中断下半部

  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值