随笔-156  评论-223  文章-30  trackbacks-0
 
     摘要: 脚本概述   nginx是一款著名的开源web服务器,为方便升级与恢复,编写了一个简单的脚本,因为升级备份了可执行文件和配置文件(后缀名为old),所以可用于恢复。当升级时,若nginx正在运行,则不中断服务进行平滑升级,否则直接拷贝覆盖;当恢复时,若nginx正在运行,则不中断服务进行平滑恢复,否则直接拷贝覆盖。是否正在运行根据pid来判断,而pid从pid文件读取...  阅读全文
posted @ 2015-01-19 00:36 春秋十二月 阅读(2023) | 评论 (0)编辑 收藏
     摘要:    本文描述了一种简单的跨平台锁框架的设计与实现,该框架小巧实用、易于扩展,它的特点如下:      ● 实现了线程间互斥锁      ● 实现优化了单线程环境中的空锁和空级别锁      ● 支持编译时或运行时选择锁   ...  阅读全文
posted @ 2014-12-28 23:38 春秋十二月 阅读(2368) | 评论 (6)编辑 收藏
   众所周知,TLS是指线程局部存储,FIFO是Unix中的命名管道,可用于无关进程间的通信,而本文描述的TLS FIFO是指这样一种机制:如果一个线程在每次IO操作时,若没有连接,则先连接到FIFO服务端,再将连接关联到这个线程的TLS中,这里的连接即创建并打开唯一的FIFO,之后的读写就在这个FIFO连接上进行;当FIFO连接断开时,在下次IO操作时会自动重连。这样一来,用户程序就只要调用相关的IO操作,而不必管理连接,极大地简化了程序。使用FIFO通信前先要创建FIFO再打开它,其中创建是最重要的操作,结果有3种情况:成功、失败和已存在。

结构定义
typedef struct 
{
    int fd;
    char *name;
}ipc_fifo_t;
  fd存储FIFO文件描述符,name存储FIFO文件系统路径名。

接口函数
   创建FIFO
ipc_fifo_t* ipc_fifo_make(const char *path,mode_t mode);
  path指定FIFO路径,可以是绝对路径或相对路径,mode指定访问权限,若成功则返回一个FIFO结构,否则为NULL;通常被ipc_fifo_open调用。
   
   打开FIFO
int ipc_fifo_open(ipc_fifo_t **f,const char *path,int flag,mode_t mode);
  flag指定打开标志,如果包含了O_CREAT标志,那么调用ipc_fifo_make创建新的FIFO对象并在打开成功后替换*f,否则如果*f为空,就分配并初始化一个fifo结构;mode指定访问权限,仅当创建时生效。虽然f为输入输出参数,但操作失败时不会影响它,也就是说没有副作用。

  ● 发送数据   
ssize_t ipc_fifo_write(ipc_fifo_t *f,const void *data,size_t size);
  如果成功则返回已发送的字节数,否则返回-1,errno表示出错代码。
   
  ● 接收数据
ssize_t ipc_fifo_read(ipc_fifo_t *f,void *data,size_t size);
  如果成功则返回已发送的字节数,否则返回-1,errno表示出错代码。

  ● 关闭FIFO
void ipc_fifo_close(ipc_fifo_t *f);
  当通信结束的时候,应该调用此函数来关闭FIFO,它会先删除FIFO文件和关闭文件描述符,最后释放fifo结构。

  ● 获取TLS FIFO
ipc_fifo_t* ipc_fifo_tls_get();
  该函数一般被发送数据接口调用,若成功则返回一个FIFO结构,否则为NULL;每个线程对应一个FIFO对象,对于同一线程,获取的是同一个FIFO对象,而后便可调用ipc_fifo_write来发送数据。

工作流程
  创建FIFO
  适用于FIFO客户端和服务端,但服务端由于要异步处理众多FIFO客户端,因此要注意以下2个问题,这也是使用FIFO技术通信的一些细节。
  1)必须以非阻塞读写方式打开知名FIFO,即以O_CREAT|O_RDWR|O_NONBLOCK标志来调用ipc_fifo_open,这样才不会阻塞等待某个客户端以同步写方式打开知名FIFO而返回,因为它使用O_RDWR标志,这样自己既读又写,加上O_NONBLOCK,就立即返回了。
  2)必须以非阻塞只读方式打开对应客户端FIFO,即以O_RDONLY|O_NONBLOCK标志调用ipc_fifo_open,这样就不会阻塞接受客户端建立连接而返回。

  建立FIFO连接
  适用于FIFO客户端,被发送数据接口调用,考虑到服务端可能事先没有打开知名FIFO来监听连接,所以这里先以异步方式写打开知名FIFO如果成功则改以阻塞方式发送唯一路径名到服务端,如果发送完全后,接着以同步方式写打开唯一FIFO,这是为了等待服务端打开了对应的唯一FIFO。

  发送数据

  适用于FIFO客户端,当TLS中没有关联对应的FIFO时,则先调用fifo_tls_get进入建立FIFO连接流程,而后再发数据。
posted @ 2014-12-01 00:13 春秋十二月 阅读(1312) | 评论 (2)编辑 收藏
     摘要:    Web服务器为了支持https访问,通常会使用第三方库openssl实现,而且为了高性能采用异步事件驱动模型,因此连接套接字被设为非阻塞类型,本文在nginx ssl模块的基础上,简化提取它的核心框架,使用面向对象的方式描述,从握手、读写和关闭3个方面进行了分析,由于这3个操作都是异步的,因此操作失败后要调用SSL_get_error来获取错误码,有如下4种情况。 &n...  阅读全文
posted @ 2014-04-11 17:26 春秋十二月 阅读(13766) | 评论 (0)编辑 收藏
模板
    1. 空基类优化
    2. 元编程技术
        2.1. 选择API
        2.2. 计算最值
        2.3. 类型选择
    3. 封装GCC原子操作
    4. 定制类对象的内存管理

算法
    1. 排序
        1.1. 改进的快速排序
        1.2. 原位统计排序     
    2. 多叉树
        2.1. 深度优先存储
        2.2. 迭代器的设计
        2.3. 前序遍历
        2.4. 后序遍历
        2.5. 兄弟遍历
        2.6. 叶子遍历
        2.7. 深度遍历 
    3. 优先级队列
        3.1. 原理
        3.2. 内幕
        3.3. 外观
    4. RSA加解密的证明
    5. DSA数字签名的推导
    6. 基于中国剩余定理优化RSA解密推论的证明
    7. 总结AES加密涉及的数学定理
    8. 为什么素检测存在概率多项式时间算法
    9. Blum数的基本定理及应用

GUI 
    1. MFC中的WM_COMMAND传递
    2. ATL和WTL中的消息反射
    3. 工作线程与消息循环
    4. 多窗口的组合与分离
        4.1. 接口
        4.2. 实现

跨平台
    1. 用户态自旋锁
    2. 互斥锁
    3. 信号量
    4. socket管道
    5. 锁框架的设计与实现

网络
    1. 运用状态机异步接收变长包
    2. 基于OpenSSL实现的安全连接
    3. TCP/IP FAQ
        3.1. 链路层、网络层和传输层
        3.2. 插口层和应用层
    4. Linux套接字与虚拟文件系统
        4.1. 初始化和创建
        4.2. 操作和销毁
    5. Linux ICMP消息的产生与转换
    6. nginx iocp
        6.1. tcp异步连接
        6.2. udp异步接收
        6.3. scm服务控制
    7. TCP分组丢失时的状态变迁
    8. 基于ENet实现可靠UDP通信的同步模型

Shell应用
    1. 自动生成并安装服务脚本
    2. nginx升级与恢复
    3. 使用awk定位反汇编输出
    4. 自动化批量编译
posted @ 2014-04-10 16:04 春秋十二月 阅读(1843) | 评论 (0)编辑 收藏
   为了方便更改系统tcp内核的一些参数,编写ktcpopt脚本如下
1#! /bin/bash 
2# ktcpopt 
3
4sed -i '/net.ipv4.tcp_syncookies\|net.ipv4.tcp_tw_reuse\|net.ipv4.tcp_tw_recycle\|net.ipv4.tcp_fin_timeout\|net.ipv4.tcp_max_syn_backlog\|net.ipv4.tcp_max_tw_buckets\|net.ipv4.ip_local_port_range/d' /etc/sysctl.conf
5
6sed -i '$a\net.ipv4.tcp_syncookies=1\nnet.ipv4.tcp_tw_reuse=1\nnet.ipv4.tcp_tw_recycle=1\nnet.ipv4.tcp_fin_timeout=30\nnet.ipv4.tcp_max_syn_backlog=8192\nnet.ipv4.tcp_max_tw_buckets=5000\nnet.ipv4.ip_local_port_range=10000 65000' /etc/sysctl.conf

  为了方便配置程序的崩溃调试,编写coredump脚本如下
1#! /bin/bash
2# coredump
3
4sed -i '/ulimit -c unlimited\|export core_path=\/tmp\/corefiles\|mkdir -p $core_path\|echo "0" > \/proc\/sys\/kernel\/core_uses_pid\|echo "$core_path\/%e" > \/proc\/sys\/kernel\/core_pattern/d' ~/.bashrc
5
6sed -i '$a\ulimit -c unlimited\nexport core_path=/tmp/corefiles\nmkdir -p $core_path\necho "0" > /proc/sys/kernel/core_uses_pid\necho "$core_path/%e" > /proc/sys/kernel/core_pattern' ~/.bashrc

   从以上2个脚本可以看出,为避免每次调用脚本时增加重复行以致配置文件逐渐变大,先删除已经存在的相关配置,再在末尾增加。这里使用了sed来实现直接在文件中删除和增加,其中由于删除操作的匹配模式有多个,因此使用了|符号并用\转义,并且第2个脚本模式文本中含有/符号,这与sed本身的模式限定符/存在歧义,因此也用了\转义;增加使用$a\命令来实现在文件末尾增加配置,并用\n换行。
posted @ 2014-03-24 18:44 春秋十二月 阅读(1369) | 评论 (0)编辑 收藏
脚本概述
   一般地,当在目标机器编译安装某个服务程序后,为了使服务能开机自启动和关机自停止,则需要将其添加为系统服务。但不同的Linux系统管理服务的方法不同,如Ubuntu使用update-rc.d命令,而RedHat则使用chkconfig命令。因此为了能自动识别系统的类型,减少人工控制,编写了一个简单的autosrv脚本,要求至少1个最多2个参数,特点如下:
   ● 第1个参数只能为install或uninstall,表示安装或卸载服务。
   ● 第2参数是可选的,表示系统名称,如果没有指定,那么会自动识别,若出现提示错误,则表示应该要显式指定系统名称了。

脚本实现
  1#! /bin/bash
  2# autosrv
  3
  4if [ $# -lt 1 ]; then
  5    echo "Usage: $(basename "$0") install | uninstall [sysname]"
  6    exit
  7elif [ "$1" != "install" -a "$1" != "uninstall" ]; then
  8    echo "The first parameter must be install or uninstall" 
  9    exit
 10fi
 11
 12action=$1
 13sysname=$2
 14srv_path=/etc/init.d/srv_name
 15
 16if [ -z "$sysname" ]; then
 17    sysname=`lsb_release -a | sed -n '2p' | awk '{if($0~/[Uu][Bb][Uu][Nn][Tt][Uu]/) print "ubuntu"; else if($0~/[Dd][Ee][Bb][Ii][Aa][Nn]/) print "debian"; else if($0~/[Rr][Ee][Dd][Hh][Aa][Tt]/) print "redhat"; else if($0~/[Cc][Ee][Nn][Tt][Oo][Ss]/) print "centos"; else print ""}'`
 18    if [ -z "$sysname" ]; then
 19        echo "Unknown system, please manual special it with the second parameter"
 20        exit
 21    fi
 22    echo "Current system is $sysname"
 23fi
 24
 25create_file_ubuntu_debian()
 26{
 27cat << END > $srv_path
 28#! /bin/bash
 29. /lib/lsb/init-functions
 30
 31END
 32cat srv_name.body >> $srv_path
 33}
 34
 35create_file_redhat_centos()
 36{
 37cat << END > $srv_path
 38#! /bin/bash
 39#chkconfig:2345 90 10
 40#description: srv name
 41
 42. /etc/rc.d/init.d/functions
 43
 44END
 45cat srv_name.body >> $srv_path
 46}
 47
 48chmod_file()
 49{
 50    chmod u+x $srv_path
 51}
 52
 53remove_file()
 54{
 55    rm -f $srv_path
 56}
 57
 58install_ubuntu_debian()
 59{
 60    create_file_ubuntu_debian
 61    chmod_file
 62    update-rc.d srv_name defaults 90 10
 63}
 64
 65uninstall_ubuntu_debian()
 66{
 67    update-rc.d -f srv_name remove
 68    remove_file
 69}
 70
 71install_redhat_centos()
 72{
 73    create_file_redhat_centos
 74    chmod_file
 75    chkconfig --add srv_name
 76}
 77
 78uninstall_redhat_centos()
 79{
 80    chkconfig --del srv_name
 81    remove_file
 82}
 83
 84case "$sysname" in
 85    ubuntu|debian)
 86    if [ "$action" = "install" ]; then
 87        install_ubuntu_debian
 88    else
 89        uninstall_ubuntu_debian
 90    fi
 91    ;;
 92
 93    redhat|centos)
 94    if [ "$action" = "install" ]; then
 95        install_redhat_centos
 96    else
 97        uninstall_redhat_centos
 98    fi
 99    ;;
100
101    *)
102    echo "Currently only support ubuntu, debian, redhat and centos system"
103    exit
104    ;;
105esac
   从上可知,自动识别的方法是获取lsb_release -a返回的文本再使用awk来匹配ubuntu,redhat,debian,centos这几个子串(忽略大小写)。要注意的是,返回的文本可能有所不同。

   当系统安装了LSB模块时,返回结果如下
   

   没有安装时,返回结果如下
   
   无论哪种情况,要提取分析的都是第2行文本,因此使用了sed -n '2p'。srv_name.body是不同系统相同的用于生成最终服务脚本的部分代码文件,通常包含了start,stop,status,restart几个函数,只是没有包含前面的一部分,而这部分则由autosrv脚本来根据不同的系统生成不同的代码。
posted @ 2014-01-03 17:11 春秋十二月 阅读(1942) | 评论 (1)编辑 收藏
     摘要:    本文以系统的struct timeval为例,使其支持+、-、+=、-=4种算术运算和<,<=,>,>=,==、!= 6种关系运算,这样一来就免去了显式调用函数的麻烦,使代码简洁优雅,实现如下 Code highlighting produced by Actipro CodeHighlighter (freeware)htt...  阅读全文
posted @ 2013-10-28 23:37 春秋十二月 阅读(1529) | 评论 (2)编辑 收藏
   继上篇TCP/IP FAQ(1),本篇涵盖了域和协议、IP编址、插口、原始IP、unix域方面的问题与解答。

【Domain & Protocol】

1. 什么是域,它和协议有什么关系?
   域可以理解为一种容纳协议的空间,它的存在便于了协议的分类;域和协议是集合与元素的关系,一个域中的每个协议使用同类地址,并且每种地址只被一个域使用,一个域能由协议族或地址族常量唯一标识。
2. TCP/IP中有哪些域及其协议?
   internet、iso、route、ccitt、imp、network systems和unix,其中internet域含有ip,icmp,igmp,tcp和udp五种协议,unix域用于ipc通信。
3. 哪些地方会用到域?
   一个网络层协议必须分用输入数据报,并交给相应的传输层协议,由于域包含了协议族,因此必须从对应的域中找到合适的协议去处理。例如IP对应的域为internet,这个域对应的协议族为inetsw,当收到IP数据报时,就从inetsw中找到对应的传输层协议去输入处理。

【IP Addressing】
1. IP地址有哪几类?
   分为5类,A、B和C类用于单播;D类用于多播;E类用于实验。
2. sockaddr和sockaddr_in有什么区别联系?
   sockaddr是一种通用的用于接口编址信息的结构,它将硬件与协议的地址细节相对于接口层隐藏起来,成员依次为sa_len、sa_family和sa_data,而sockaddr_in成员依次为sin_len、sin_family、sin_port、sin_addr和sin_zero。它们的前两个成员其实是一样的,只是命名不同,而sockaddr_in中的sin_port标识传输层的端口,sin_addr标识IP层的地址,所以它是Internet协议的专用接口编址结构,sin_zero仅为填充用,因为sockaddr_in长度不应小于sockaddr。
3. 插口编址结构允许的最大长度是多少?
   sockaddr最后一个成员是可扩展的,这是C语言的通用技术,因为其第一个成员为u_char类型,所以最大长度是255。
4. 一个接口可以配置多个IP地址,是怎么实现的?
   ifaddr表示通用的接口地址,ifnet表示接口,它有个类型为ifaddr*的if_addrlist成员,表示当前接口上的地址链表。in_ifaddr表示Internet协议专用的接口地址,而IP属于Internet协议,因此用in_ifaddr表示,所有的IP地址组成一个类型为in_ifaddr*的链表。当增加一个IP地址时,就需要插入这两个链表中;当删除一个IP地址时,就需要从它们当中移除。
5. 为什么配置IP主机地址和网络掩码可以是独立的?
   因为它们使用不同的命令实现,前者用SIOCSIFADDR,后者用SIOCSIFNETMASK。当配置IP主机地址时,如果没有指明网络掩码,那么网络掩码被设置成默认的。
6. 为什么访问IP接口地址使用的是UDP插口而不是原始IP插口?
   只有超级用户用程才能创建原始IP插口,而通过UDP插口,任何用户进程都能查看接口配置。

【Socket】
1. 为什么会存在插口层?
   从概念上讲,tcp/ip协议栈划分为链路、网络、传输和应用4层;但从实现上讲,在应用层和其下层中间,引入了一个插口层,作为进程和内核通信的桥梁,主要功能是将进程发送的与协议有关的请求映射到产生插口时指定的与协议有关的实现,从而屏蔽了不同协议处理的细节。
2. 为什么服务器进程总是要调用bind,客户进程能调用它吗?
   bind将一个本地地址同一个插口相关联,客户进程需要同一个已知地址建立连接或发送数据报到已知地址,如果不调用bind,服务器进程就无法在某个已知地址上接受TCP连接或接收UDP数据报。客户进程也能调用bind,这样便可以由应用程序而非内核来选择一个本地地址,其结果是只能接收目的地址为被绑定地址的数据包,但通常不必调用bind,因为内核会自动决定外出地址和临时端口。
3. 对于tcp和udp协议的插口,调用connect有什么区别联系?
   共同点是设定插口的外部地址(插口的地址存储在相关的协议控制块中),不同点如下
   1)tcp:与远端系统进行3次握手交互,如果插口是非阻塞的且连接正在进行中,那么返回EINPROGRESS,下次再调用则返回EALREADY;如果连接成功,无论是否阻塞,那么下次再调用会返回EISCONN;如果连接失败,那么下次再调用,则重新开始三次握手。
   2)udp:没有3次握手交互,直接设定外部地址,无论插口是否阻塞,调用会立即返回,多次调用则会替换老的外部地址。发送数据必须使用write或目的地址为空的sendto,若sendto目的地址非空,则返回EISCONN。如果没有事先调用connect,那么调用目的地址为空的sendto则会返回ENOTCONN。
4. 什么情况下调用close会阻塞?
   连接已建立且设置了SO_LINGER选项并延时值非零的阻塞插口。
5. 插口IO有哪些系统调用?
   发送有write、writev、sendto和sendmsg,接收有read、readv、recvfrom和recvmsg。注意,send和recv是库函数而非系统调用,前者调用sendto实现,后者调用recvfrom实现。
6. write、writev、read、readv与sendto、sendmsg、recvfrom、recvmsg有什么不同?
   1)前面4个适合于任何描述符,而后面4个只能用于插口。
   2)前面4个不支持标志,而后面4个支持。
   3)前面前2个不支持目的地址、后2个不支持源地址,而后面前2个支持目的地址、后2个支持源地址。
   4)前面4个不支持控制信息,而后面第2个和第4个支持。
7. 如何断开已连接的udp插口,允许调用sendto向其它主机发送数据?
    由于系统并没有提供形如disconnect的断连API,但connect内部实现是先断连,再调用对应协议的PRU_CONNECT请求处理,因此向connect传递无效的外部地址结构(如IP=0.0.0.0,Port=0),虽然这样会导致结果失败,但先前的断连成功,对应pcb的外部地址被设为INADDR_ANY,所以调用sendto就不会返回EISCONN。

【Raw IP】
1. 怎么使用原始IP,它有哪些用途及应用?
   创建SOCK_RAW类型的原始插口,就能使用原始IP机制,它有下列用途:
   1)发送和接收ICMP和IGMP报文,如ping程序和多播路由守护程序。
   2)构造自己的IP首部,如路由跟踪程序。
   3)设计基于IP的新的传输层协议,如gated程序。
2. 协议值为PROTOCOL_RAW(255)的原始插口能收到什么类型的IP数据报?
   由于255是非零的保留值,这样的IP数据报在网络中不会存在,原始IP输入处理协议比较测试失败,因此收不到任何类型的IP数据报。
3. 协议值为0的原始插口能收到什么类型的IP数据报?
   由于协议值为0,原始IP输入处理忽略了协议比较测试,因此能收到任何类型的IP数据报。
4. 如何处理收到的IP数据报?
   遍历Internet PCB表,依次从协议值、本地地址和外部地址三项来比较IP数据报和每个PCB,将IP数据报复制追加到所有匹配的PCB对应的插口缓存中,并唤醒等待的进程。
5. 如何发送数据?
   先填充IP首部,如果未设置IP_HDRINCL选项,那么由内核填充,否则由应用程序在发送前填充,再交给IP协议输出处理。
6. 内核何时会调用原始输入?
   当收到的协议类型为除IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP和IPPROTO_IGMP外的IP数据报时调用。

【Unix domain】
1. 使用unix域的原因有哪些?
   1)当通信双方在同一主机上时,使用unix域插口的速度比tcp和udp插口要快很多。
   2)支持同一主机进程间传递描述符。
2. unix域和internet域有什么不同?
   1)编址结构不同:前者是sockaddr_un,与文件系统路径名关联,而后者是sockaddr_in,与IP地址和端口关联。
   2)协议控制块不同:前者是unpcb,没有全局的pcb链表,而后者是inpcb,有全局的pcb双向循环链表。
3. 如果一个unix域服务器在bind后unlink了被绑定的路径名,会发生什么情况?
   因为connect内部实现查找路径名失败,所以连接失败,但listen会成功,因为bind会创建新的vnode和pcb关联,且PRU_LISTEN请求实现只检查vnode是否为空。
4. 如果一个unix域服务器在终止时没有unlink被绑定的路径名,会发生什么情况?
   因为connect内部实现虽能找到路径名但找不到相关的插口,所以连接被拒绝。
5. 系统调用socketpair和pipe有什么区别联系?
   共同点是使用unix域,即socket调用第1参数为AF_UNIX,不同点如下
   1)前者是双工的,因为两个插口标志都为读写,且它们的pcb相互指向对方;后者是单工的,因为一个插口标志为只读,另一个为只写,写插口的pcb指向读插口的pcb。
   2)前者支持数据报和流式插口,后者仅支持流式插口。
6. unix域是怎么实现传递描述符的?
   描述符存储在控制信息cmsghdr内,cms_level=SOL_SOCKET且cms_type=SCM_RIGHTS,unix域的发送请求实现将描述符转换为file指针,这个过程叫内部化,接收请求实现把file指针转换为最小的没有使用的描述符,这个过程叫外部化。
posted @ 2013-09-03 15:52 春秋十二月 阅读(2346) | 评论 (1)编辑 收藏
   TCP/IP FAQ系列,以经典的4.4BSD-Lite实现为准,参考《TCP/IP协议详解》3卷 ,加入个人的思考理解,理清主干,不深究细枝末节,皆在总结基本原理和实现。本篇涵盖了数据链路层、ARP、RARP、IP、ICMP、TCP、UDP方面的问题与解答。

【Data Link】
1. 环回接口地址必须是127.0.0.1吗?
   形如127.x.x.x的A类IP都可作为环回接口的地址,但常用的是127.0.0.1。
2. 环回接口为什么没有输入处理?
   发送到环回接口的数据报实质上被送到网络层的输入队列中,因此数据报没有离开网络,也就不可能从链路上接收到目标地址为环回接口地址的数据帧,所以不存在输入处理。
3. SLIP、环回和以太网接口,三者有何不同?
   SLIP和环回接口没有链路层首部和硬件地址,环回接口没有输入处理,而以太网接口都有。
4. SLIP和以太网接口如何分用输入帧,环回接口如何分用输出分组?
   SLIP将帧直接放进IP输入队列中,以太网接口则根据帧类型字段放到对应的协议输入队列中,环回接口则按目的地址族放到对应的输入队列中。
5. 接口和地址有什么关联?
   一个接口的编址信息包括主机地址、广播地址和网络掩码,当内核初始化时,每个接口分配一个链路层地址,可以配置有多个相同或不同的网络层地址,例如2个IP地址,或者1个IP地址、1个OSI地址。

【ARP & RARP】
1. 何时发送ARP请求,何时应答ARP请求?
   当单播发送IP数据并且查询ARP高速缓存失败时,就会广播一个询问目的主机硬件地址的ARP请求;当接收到ARP请求的主机就是该请求所要查找的目的主机或目的主机的ARP代理服务器时,就会单播一个ARP应答。
2. 为什么两者的以太网帧类型不同?
   ARP值为0x0806,RARP为0x8035,其实对于发送方来说,利用ARP的op字段可以区分RARP,但对于接收方,由于ARP实现在内核中,而RARP一般实现为服务器,所以为了更易区分,就单独用另一个值标识。
3. 设计RARP服务器有哪些问题?
   一是怎么发送以太网帧以响应请求,这与系统相关。二是当存在多个服务器时,同时发送响应帧会造成以太网冲突,这可以通过分主从服务器和随机延时来优化避免。
4. ARP在等待应答时,它会如何处理发往给定目的的多个报文?
   在大多数的实现中,在等待一个ARP应答时,只将最后一个报文发给特定目的主机。Host Requirements RFC要求实现中必须防止这种类型的ARP洪泛,建议最高速率是每秒一次。
5. 免费ARP有什么作用?
   一般的ARP请求用于查询目标硬件地址,并等待应答。而免费的ARP发出请求并不一定期望应答,这可以有两方面的作用:
   1)一个主机可以确定是否存在相同IP地址的另一主机
   2)当本机硬件地址改变时,通知其它主机更新ARP高速缓存。
6. ARP如何映射一个IP多播地址?
   先获取IP多播地址的低23位,再与常量0x01005e7f0000按位或,结果就是对应的多播硬件地址。

【IP】
1. 何时何地分片?
   当数据报长度大于链路接口MTU且DF=0时,开始分片,分片可发生在源主机,也可发生在中途路由器。若需要分片但DF=1,则向源主机发送ICMP不可达差错。
2. 如何分片?
   1)计算每个分片的数据长度(不含IP首部),除后一个分片外,其它分片数据长度为8字节的倍数。
   2)除复制对应数据外,还复制原始分组的首部及(部分)选项到新的每个分片中,更新新分片首部的头部长度、总长度、MF标志和偏移量。如果原始分组已经是分片,那么MF=1,否则最后一个分片MF=0,其余MF=1。
3. 何时何地重装?
   由于分片可以有不同的路由,而且中途路由器可能再次分片,因此只有目标主机才能重装所有分片。当接收端第一次收到一个MF或偏移量非零的分组时,则该分组就是一个必须被重装的分片,于是开始重装。
4. 如何重装?
   1)使用4元组{源地址,目标地址,协议,16位标识}为唯一标识查找当前分片所属的数据报(分片表),如果没有找到,则创建分片表,按偏移量将当前分片插入到分片表,并启动重装定时器。
   2)如果重装定时器超时后,还没有组装好一个完整的IP数据报,此时如果已经收到第一个分片,则向源主机返回ICMP超时差错,最后丢弃收到的所有分片;否则,提交数据给适当的传输层处理。
5. 哪些分组能被转发,何时转发?
   到达非最终目的地系统的分组,且当系统配置为可转发或分组包含源路由时,才能被转发,但下列类型的分组除外:1)链路层广播 2)环回分组 3)网络0和E类目标地址 4)D类目标地址。

【ICMP】
1. ICMP报文有哪些类型,何时何地生成这些报文?
   包括请求、应答、差错和重定向4种,其中前两者可统一为查询类。请求当需要查询的时候由进程生成,应答由当内核收到请求报文时生成,当主机发出的数据报无法成功地提交给目的主机时,目的主机或中间路由器的IP或传输协议生成差错报文,并返回给原来的系统。
2. 内核怎么处理收到的ICMP报文?
   ICMP是一种传输层协议,其协议号为1,当IP层收到一个ICMP报文时,分用交给ICMP协议输入处理,ICMP协议输入根据其类型分别处理:1)请求---生成适当的应答报文 2)差错---提交给适当的传输层协议处理 3)应答---提交给等待ICMP报文的进程 4)重定向---更新路由表,并提交给等待的进程。
3. 怎么发送ICMP报文?
   构造ICMP报文-->计算ICMP检验和-->封装到IP数据报中-->提交给IP协议输出处理,对于用户进程,须使用原始IP机制才能发送。
4. 哪些情况不会产生ICMP差错报文,为什么?
   1)ICMP差错报文:违反此条可能导致差错引起差错,无休止循环下去。
   2)源地址不是单播地址的IP数据报:违反此条导致差错可能同时发到多个主机。
   3)目的地址是广播或多播地址的IP数据报:违反此条导致多个主机可能同时响应。
   4)作为链路层广播的数据报:违反此条导致多个主机可能同时响应。
   5)不是IP分片的第一片:违反此条可能导致产生多个ICMP差错,每个分片一个。
   由此可见,违反以上几条都会引起网络风暴。

【TCP & UDP】
1. 为什么TCP首部存在首部长度字段,而UDP却没有?
   TCP首部存在选项,如mss,timestame,nop和wscale等。
2. 为什么这两种协议首部前面都是源和目的端口?
   当TCP收到一个ICMP差错时,必须检查两个端口号以决定差错对应于哪个连接;只有当UDP套接口连接到对端时,用户进程才会收到ICMP差错,例如当服务器未运行时,返回的ICMP端口不可达消息。
3. 当收到TCP或UDP数据包时,怎么提交给应用层?
   插口由进程调用socket或accept创建,关联到对应的PCB(协议控制块)上,通配匹配数由本地和外部IP地址确定,有3种取值:0--本地和外部IP都不为*、1--本地或外部IP有一个为*和2--本地和外部IP都为*。与UDP不同的是,TCP还有自己的PCB。
   1)TCP:先扫描Internet PCB,查找最小通配匹配数的插口,如果没找到,那么响应RST包;再查看对应的TCP PCB,若不存在则响应RST包,否则若TCP 状态为关闭,则丢弃;最后交付给找到的对应插口。
   2)UDP:这里要分2种情况,对于目的地为广播或多播地址的IP数据报,交付给所有匹配的插口;对于目的地为单播的IP数据报,扫描Internet PCB,查找具有最小通配匹配数的插口,如果没有找到,则向源主机发送ICMP端口不可达差错。如果有多个插口有相同的最小通配匹配数,那么具体由哪个插口接收依赖于不同的实现。
4. 计算首部检验和时,为什么要引入伪首部?
   这是因为考虑到IP层的可能差错,TCP和UDP需要验证数据包是否被递送到正确的协议和目的主机。
5. UDP何时会计算检验和,如何区分是否使用了检验和?
   UDP的检验和是可选的,当系统没有禁止(udpcksum非零)时,发送方会计算检验和,接收方还须输入分组检验和非零时才会计算检验和。如果检验和字段非零,那么就使用了,反之没有。
6. 在TCP状态迁移中,哪些状态在什么情况下可直接转到CLOSED状态?
   SYN_SENT在连接定时器超时后,FIN_WAIT_2在FIN_WAIT_2定时器超时后。
7. 为什么TCP需要持续(persist)定时器、FIN_WAIT_2定时器和2MSL定时器?
   1)因为连接对端发送的窗口通告为ACK报文,而ACK是不会确认的,允许TCP继续发送数据的窗口更新可能会丢失,所以需要设定persist定时器,在超时后发送1字节的数据,判定对端接收窗口是否已打开。
   2)因为在正常情况下,当连接主动关闭时,会由FIN_WAIT_1状态进入FIN_WAIT_2状态等待接收对端的FIN报文,但对方可能一直不发送FIN,所以需要FIN_WAIT_2定时器避免连接永远滞留在FIN_WAI_2状态。
   3)因为当连接主动关闭进入TIME_WAIT状态后,将等待2个MSL时间,在这段时间内,TCP可以重发丢失的ACK,丢弃来自新连接替身的迟到的报文段以防止被曲解,所以需要2MSL定时器,超时后关闭连接。
8. 当TCP发送数据,调用ip_output返回ENOBUFS差错时,可能会发生什么情况?
   当提交给网络层因为内存不足发送失败时,数据包被丢弃。如果丢弃的是数据报文,重传定时器超时后数据将被重传;如果丢弃的是纯ACK报文,对端收不到ACK时会重传对应的数据报文;如果丢弃的是RST报文,当对端重传导致发送RST报文的数据报文时,将再次生成RST报文。
9. TCP何时发送ACK报文?
   对于数据、SYN和FIN报文,发送ACK,但对于纯ACK和RST报文,不会发送;另外当遇以下情况时,则立即发送。
   1)200ms延时ACK定时器超时;2)收到失序的报文段;3)三次握手收到了SYN;4)收到了FIN。
10. TCP何时发送RST报文?
   1)当收到报文段,但没有找到对应的internet pcb或tcp pcb。
   2)当连接处于LISTEN状态时,收到了ACK报文段。
   3)当连接处于SYS_SENT状态时,收到了错误的ACK报文段(ack小于等于iss或大于snd_max)。
   4)当连接被动关闭时(状态大于CLOSE_WAIT),收到了数据。
   5)当连接处于SYN_RCVD状态时,收到了错误的ACK报文段(ack小于snd_una或大于snd_max)。
posted @ 2013-08-25 10:50 春秋十二月 阅读(2519) | 评论 (1)编辑 收藏
仅列出标题
共16页: First 6 7 8 9 10 11 12 13 14 Last