linux-cmd

文件

结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
要遵循FHS规范,规范定义的两级目录规范如下:
/home 每个账号在该目录下都有一个文件夹,进行数据的管理
/usr 有点像windows的program files和winNT结合的目录,主要包括系统的主要程序、本机端额外安装的软件、图形接口所需要的文档、额外的函数库、共享目录与文件等
/bin /usr/bin /usr/local/bin 存放执行挡 如可执行的指令等
/boot 存放linux开机会用到的文件
/dev 存放linux的任何装置和接口设备文档
/etc 存放系统设定文档 如账号密码文件、各种服务的起始档等
/lib /usr/lib /usr/local/lib 系统使用的函数库放置的目录
/mnt /media 是软盘和光盘预设挂载点的地方
/opt 主机额外安装软件所摆放的目录
/proc 该目录是一个虚拟档案系统,他放置的数据都是在内存中,所 以本身不占用任何的硬盘空间
/root 系统管理员的家目录
/sbin /usr/sbin /usr/local/sbin 放置一些只有系统管理员才能动用 的执行指令
/srv 一些服务启动之后,这些服务所需要取用的数据目录
/tmp 让一般使用者或者正在执行的程序暂时放置档案的地方
/var 主要针对系统执行过程中,常态性变动档案放置的目录
文档的路径有:绝对路径 (absolute) 与相对路径 (relative)。
绝对路径为:由根目录 (/) 开始写起的文件名或目录名称
相对路径为相对于目前路径的文件名写法。 ./表示相对当前路径,../表示相对于上一级目录的路径,~代表home目录,~account代表当前账号的home目录)

查看文件

  • 1
    2
    cat是一次性显示整个文件的内容,还可以将多个文件连接起来显示,它常与重定向符号配合使用,适用于文件内容少的情况;
    more和less一般用于显示文件内容超过一屏的内容,并且提供翻页的功能。more比cat强大,提供分页显示的功能,less比more更强大,提供翻页,跳转,查找等命令。而且more和less都支持:用空格显示下一页,按键b显示上一页
  • 1
    2
    3
    4
    5
    cat -b threadpool.cpp//对非空行显示行号 cat -b threadpool.cpp |less
    cat -n threadpool.cpp//对所有行均显示行号
    cat > newtxt.txt//创建新文件
    cat sir01.txt sir02.txt sir03.txt > sir04.txt //如果sir04.txt存在,则清空,并合并1 2 3 到4,否则就创建
    cat sir01.txt sir02.txt sir03.txt >> sir04.txt//追加 到 sir04.txt
  • 1
    2
    3
    4
    more -c threadpool.cpp//从顶部清屏后显示
    more +4 threadpool.cpp//从 第四行开始显示
    more -4 threadpool.cpp//每屏显示4行
    more +/threadpool threadpool.cpp//从 threadpool.cpp第一个 threadpool单词的前两行开始显示
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    less -N threadpool.cpp//显示行号
    less -M threadpool.cpp//显示百分比,页数以及总行数
    less -c threadpool.cpp//从顶部清空后显示
    less +4 ////从 第四行开始显示
    less -4//每屏显示4行

    动作命令:
    g 跳到第一行
    G 调到最后一行
    p n%调到 n%
    /pattern 搜索pattern单词
  • 1
    2
    3
    head -n 10 threadpool.cpp//显示前10行
    tail -n 10 threadpool.cpp//显示最后10行
    tail -n +10 threadpool.cpp
  • 1
    2
    3
    wc -l threadpool.cpp//统计文件行数 line
    wc -w threadpool.cpp//统计单词数
    wc -c //统计字符个数

内存、磁盘、IO

  • 1
    2
    tail -f log.log//实时查看log
    tail -100f log.log//实时查看Log的后100行, ctrl +c 退出
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
      top//
    top - 16:20:29 up 5:14, 1 user, load average: 0.21, 0.07, 0.04
    Tasks: 276 total, 2 running, 210 sleeping, 0 stopped, 0 zombie
    %Cpu(s): 2.0 us, 0.3 sy, 0.0 ni, 97.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
    KiB Mem : 4015636 total, 182740 free, 1430052 used, 2402844 buff/cache
    KiB Swap: 2097148 total, 2097148 free, 0 used. 2274092 avail Mem

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    2050 bliss 20 0 3149272 472176 98040 S 1.7 11.8 2:27.45 gnome-shell
    1861 bliss 20 0 450400 111008 48884 S 1.0 2.8 0:47.45 Xorg
    第一行:当前时间;系统运行天数;使用者个数;系统负载的平均值,后面分别为1分钟前,5分钟前,15分钟前进程的平均个数。这个数超过CPU数目时,说明负载过高
    第二行:任务:进程总数,运行进程总数,睡眠进程总数,被停止的进程总数,僵尸进程的进程总数
    第三行:cpu状态:us 用户空间占用CPU的百分比,sy内核空间,ni改变过优先级的进程,id空闲CPU,waIO等待占用CPU,hi硬件中断,si软件中断
    第四行:物理内存总量-空闲内存-使用中的内存-缓存内存
    第五行:swap交换分区 swap总量-


    可用内存数,这里有个近似的计算公式:第四行的内存free + 第四行的内存buffers + 第五行的swap cached

    6行为空
    7行:
    PID-进程ID
    USER-进程所有者
    NI-优先级-Nice值(负值高优先级,正值低优先级)
    VIRT-进程使用的虚拟内存总量 kb,VIRT=SWAP+RES
    RES 进程使用的、未被换出的物理内存大小,kb RES=CODE+data
    SHR 共享内存大小,kb
    S 进程状态。D=不可中断的睡眠状态R=运行 S=睡眠 T=跟踪/停止 Z=僵尸
    %CPU 上次更新到现在的CPU时间占用百分比
    %MEM 进程使用的物理内存百分比
    TIME+ 进程使用的CPU时间总计 1/100
    COMMAND 进程名称


    -H//开启线程查看
    动作命令:
    1 查看每个逻辑CPU的状况//不按1时,显示的是所有CPU的均值
    M 按照内存排序,默认从大到小,R反向
    P 根据CPU使用排序
    T 根据使用时间排序
    N 以 PID 的大小的顺序排列表示进程列表
    H 查看线程
    f 进入到选项框,带*的是显示项,d选择是否显示,s 表示按此排序

    top -H -p <pid>//top 查看单个进程的 线程资源
  • 1
    2
    //配合使用pmap  -d   + 进程号;查看进程内存映射情况
    pmap -d 1123
  • 1
    2
    3
    4
    top命令的监控最小单位是进程,所以看不到我关心的java线程数和客户连接数,而这两个指标是java的web服务非常重要的指标,通常我用ps和netstate两个命令来补充top的不足。

    ps -eLf | grep java | wc -l// 监控java线程数
    netstat -n | grep tcp | grep 侦听端口 | wc -l// 监控网络客户连接数
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    系统根目录/proc中,每一个数字子目录的名字都是运行中的进程的PID,进入任一个进程目录,可通过其中文件或目录来观察进程的各项运行指标,例如task目录就是用来描述进程中线程的,因此也可以通过下面的方法获取某进程中运行中的线程数量(PID指的是进程ID):
    ls /proc/PID/task | wc -l

    pmap PID//分析进程堆栈
    /*
    30281: top
    000055c102af6000 100K r-x-- top
    000055c102d0e000 4K r---- top
    000055c102d0f000 4K rw--- top
    000055c102d10000 160K rw--- [ anon ]
    000055c104132000 532K rw--- [ anon ]
    00007fcdc5a1a000 240K r-x-- libnss_systemd.so.2
    00007fcdc5a56000 2044K ----- libnss_systemd.so.2
    */
    cat /proc/your_PID/status //进程状态 进程状态、文件句柄数、内存使用情况
    /*
    Name: top
    Umask: 0022
    State: S (sleeping)
    Tgid: 30281
    Ngid: 0
    Pid: 30281
    PPid: 29894
    TracerPid: 0
    Uid: 1000 1000 1000 1000
    Gid: 1000 1000 1000 1000
    FDSize: 256
    Groups: 4 24 27 30 46 116 126 1000
    */
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    cat /proc/cpuinfo   //cpu的配置信息 CPU核心数,时钟频率、CPU型号
    /*
    processor : 0
    vendor_id : GenuineIntel
    cpu family : 6
    model : 158
    model name : Intel(R) Core(TM) i5-8300H CPU @ 2.30GHz
    stepping : 10
    microcode : 0xb4
    cpu MHz : 2303.997
    cache size : 8192 KB
    physical id : 0

    */
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    free//
    total used free shared buff/cache available
    Mem: 4015744 1030652 1603008 5472 1382084 2690460
    Swap: 2097148 0 2097148

    total 总计物理内存
    used 已使用
    free 未使用
    shared 多个进程共享的内存总额
    buffer/cache 磁盘缓存的大小
    free -m //单位 mb
    free -k //单位kb//默认
    free -b //单位b

    cat/proc/meminfo
    /*
    MemTotal: 4015744 kB
    MemFree: 1590312 kB
    MemAvailable: 2692140 kB
    Buffers: 270108 kB
    Cached: 952780 kB
    SwapCached: 0 kB

    */

    vmstat //Virtual Meomory Statistics 可对操作系统的虚拟内存、进程、IO读写、CPU活动等进行监视。它是对系统的整体情况进行统计,不足之处是无法对某个进程进行深入分析
    vmstat
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
    r b swpd free buff cache si so bi bo in cs us sy id wa st
    0 0 0 1576316 270224 1139764 0 0 1459 633 264 714 5 6 72 17 0

    procs r //等待运行的进程数
    pros b //处在非中断睡眠状态的进程数 指被资源阻塞的进程对列数

    memory swpd //已使用的虚拟内存大小
    memory free// 空闲的物理内存的大小
    buff// 用来做buffer(缓存,主要用于块设备缓存)的内存数,
    cache//用来做cache(缓存,主要用于缓存文件)的内存,

    swap si//Amount of memory swapped in from disk 从磁盘交换到内存的交换页数量,单位:KB/秒。
    so// Amount of memory swapped to disk从内存交换到磁盘的交换页数量,单位:KB/秒
    io bi//Blocks received from a block device 每秒从块设备接收到的块数,单位:块/秒 也就是读块设备。
    bo//Blocks sent to a block device 每秒发送到块设备的块数,单位:块/秒 也就是写块设备。
    system in//每秒的中断数,包括时钟中断
    system cs//每秒的环境(上下文)切换次数。

    cpu us//用户CPU时间(非内核进程占用时间)(单位为百分比) us的值比较高时,说明用户进程消耗的CPU时间多。
    sy//系统使用的CPU时间(单位为百分比)
    id// Time spent idle 空闲的CPU的时间(百分比)
    wa//Time spent waiting for IO. 等待IO的CPU时间 wait越大则机器io性能就越差。说明IO等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。
    st//Time stolen from a virtual machine
    //参数命令
    vmstat -f //显示系统启动以来的forks总数 包括fork、vfork和clone system calls
    vmstat -n 3//每隔3s刷新一次
    vmstat 3 5//每隔三秒更新一次,更新5次
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    iostat//

    Linux 4.15.0-64-generic (ubuntu) 20190930日 _x86_64_ (1 CPU)

    avg-cpu: %user %nice %system %iowait %steal %idle
    2.19 0.43 2.36 5.56 0.00 89.45

    Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn
    loop0 0.04 0.42 0.00 1117 0
    loop1 3.25 3.62 0.00 9573 0

    avg-cpu % usr cpu处于用户模式下的时间百分比
    %nice CPU处于带NICE值的用户模式下的百分比
    %system 系统模式下的百分比
    %iowait 处于等待输入输出完成时间的百分比 过高,表示硬盘存在I/O瓶颈
    %steal 管理程序维护另一个虚拟处理器时,虚拟CPU无意识等待事件百分比
    %idle CPU空闲时间百分比
    Device tps 该设备每秒的传输次数
    KB_read/s 每秒从设备(drive expressed)读取的数据量;
    kB_wrtn/s 每秒向设备(drive expressed)写入的数据量
    kB_read: 读取的总数据量;
    kB_wrtn:写入的总数量数据量;

    # 【每隔2秒刷新显示,且显示3次】
    iostat 2 3
    -c //显示 CPU情况
    -x//显示详细情况
    -d//显示磁盘使用情况 iostat -d sda1 显示指定磁盘信息
    iostat -m// 以M为单位显示信息

    iostat -d -x -k 1 1//
    Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
    loop0 0.03 0.00 0.32 0.00 0.00 0.00 0.00 0.00 14.56 0.00 0.00 10.34 0.00 0.22 0.00

    rrqm/s 每秒进行的merge的读操作数目
    wrqm/s 每秒进行的merge的写操作数目
    %util 一秒中有百分多少的时间用于IO 接近100表示IO请求太多,IO系统已经满负荷
    idle 小于
  • 1
    2
    3
    df  磁盘使用情况统计
    -h // 使用人类可读的格式
    -i //列出 inodes
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    sar //查看网络 IO
    sar -n DEV 1 2//一秒采集1次,采集2次
    095648秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
    095649秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00


    Average: IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
    Average: lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
    Average: ens33 0.51 0.00 0.03 0.00 0.00 0.00 0.00 0.00

    IFACE //设备名称
    rxpck/s//每秒接受到的数据包 几千正常
    txpck/s 每秒发送的包
    rxkB/s 每秒接收到的数据量
    txKB/s 每秒发送的数据量

    sar -n DEV -f /var/log/sa/sa07 查看7号的流量,后边sa07指的是那一天的,sar会以日期号为结尾保存,最多保存一个月的
    sar -q 查看历史负载 sar -q -f /var/log/sa07查看指定某天的负载
    sar -b 查看磁盘的(读写)
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
      ps 
    -e // 显示所有进程
    -f// 显示UID,PPIP,C与STIME栏位
    -p//指定进程识别码,并列出该进程的状况。
    -U<用户识别码>  列出属于该用户的进程的状况,也可使用用户名称来指定。
    -t<终端机编号>  指定终端机编号,并列出属于该终端机的进程的状况。
    -C<指令名称>  指定执行指令的名称,并列出该指令的进程的状况。
    x  显示所有进程,不以终端机来区分。
    -l或l  采用详细的格式来显示进程状况。
    -T//开启线程查看
    /*
    ps -T -t 5249 查看 5249 进程创建的线程
    PID SPID TTY TIME CMD
    5249 5249 tty2 00:00:10 firefox
    5249 5256 tty2 00:00:00 gmain
    5249 5257 tty2 00:00:00 gdbus

    */
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    netstat //显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。netstat是在内核中访问网络及相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告
    -t //显示TCP的连接状况
    -u//显示 UDP的连接状况
    -p //显示正在使用Socket的程序识别码和程序名称。
    -i//显示网卡
    -s//显示网络统计
    -l//处于listen状态的socket
    -e //显示扩展信息,例如uid等
    -c //每隔一个固定时间,执行该netstat命令

    netstat -ap | grep firfox//找出程序运行的端口数据
    tcp 0 0 ubuntu:48944 182.254.53.181:https ESTABLISHED 5249/firefox
    tcp 0 0 ubuntu:37916 182.254.59.158:https ESTABLISHED 5249/firefox
    unix 3 [ ] STREAM CONNECTED 76587 5418/firefox
    unix 3 [ ] SEQPACKET CONNECTED 77567 5249/firefox


    LISTEN:侦听来自远方的TCP端口的连接请求

    SYN-SENT:再发送连接请求后等待匹配的连接请求(如果有大量这样的状态包,检查是否中招了)

    SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认(如有大量此状态,估计被flood攻击了)

    ESTABLISHED:代表一个打开的连接

    FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认

    FIN-WAIT-2:从远程TCP等待连接中断请求

    CLOSE-WAIT:等待从本地用户发来的连接中断请求

    CLOSING:等待远程TCP对连接中断的确认

    LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认(不是什么好东西,此项出现,检查是否被攻击)

    TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认

    CLOSED:没有任何连接状态

    netstat -atnp | grep ESTA //打印 active 状态的连接 active 状态的套接字连接用 "ESTABLISHED" 字段表示
    sudo netstat -aple | grep ntp// 查看服务是否在运行
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    pidstat//监控全部或指定进程占用系统资源的情况,如CPU,内存、设备IO、任务切换、线程等
    u:默认的参数,显示各个进程的cpu使用统计
    -r:显示各个进程的内存使用统计
    -d:显示各个进程的IO使用情况
    -p:指定进程号
    -w:显示每个进程的上下文切换情况
    -t:显示选择任务的线程的统计信息外的额外信息
    -l:显示命令名和所有参数
    //每个进程的上下文切换情况
    bliss@ubuntu:~$ pidstat -w -p 5249
    Linux 4.15.0-64-generic (ubuntu) 20190930日 _x86_64_ (1 CPU)

    110102秒 UID PID cswch/s nvcswch/s Command
    1101021000 5249 1.35 6.54 firefox

    Cswch/s:每秒主动任务上下文切换数量
    Nvcswch/s:每秒被动任务上下文切换数量


    pidstat -T TASK TASK表示报告独立的task。
    pidstat -T CHILD CHILD关键字表示报告进程下所有线程统计信息
    pidstat -T ALL ALL表示报告独立的task和task下面的所有线程。

  • 统计某个文件夹下文件个数

    • 1
      ls /proc/5249/task | wc -l //某个进程创建了多少个线程
  • CPU参数

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      Linux查看物理CPU个数、核数、逻辑CPU个数

      # 总核数 = 物理CPU个数 X 每颗物理CPU的核数

      # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数


      # 查看物理CPU个数

      cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l


      # 查看每个物理CPU中core的个数(即核数)

      cat /proc/cpuinfo| grep "cpu cores"| uniq


      # 查看逻辑CPU的个数

      cat /proc/cpuinfo| grep "processor"| wc -l


      查看CPU信息(型号)
      cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c

network-program

0.socket

TCP

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int socket(int domain,int type,int protocol);
    /*
    domain 协议簇
    PF_INET IPV4
    PF_INET6 IPV6
    PF_LOCAL 本地通信
    Type 套接字类型
    SOCK_STREAM 面向连接的套接字
    SOCK_DGRAM 面向消息的套接字
    */
  • 端口号用于区分同一操作系统不同套接字,最大(0-65535),0-1023为知名端口。TCP套接字和UDP套接字不会共用端口号,允许重复。

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    //Ip地址表示
    struct{
    sa_family_t sin_family;
    uint16_t sin_port;//16 端口
    struct in_addr sin_adr;//32bitIp地址
    char sin_zero[8];//dumy
    }
    struct in_addr{
    In_addr_t s_addr;//32 bit IPV4地址
    }
    unsigned short htons(unsigned short)//short 2字节 端口转换 主机字节序->网络字节序
    unsigned short ntohs(unsigned short)//
    unsigned long htonl(unsigned long)//long 4字节 IP地址转换 主机字节序->网络字节序
    unsigned long ntohl(unsigned long)
    /*
    char* arr1="1234"
    主机字节序 小端--0x007EAC58 31 32 33 34
    网络字节序 大端--0x4030201(Ip地址)
    */
    in_addr_t inet_addr(const char* str)//将字符串返回32大端整数 IP地址,失败返回INADDR_NONE

    char* inet_ntoa(struct in_addr ar);//32bitIp地址 网络字节序 转为字符串IP


    int bind(int sockfd,struct sockaddr* myaddr,socklen_t addrlen)
    /*
    sockfd 套接字fd
    addrlen 第二个结构体变量长度
    */
    int listen(int sock,int backlog)
    /*
    sock希望进入到等待连接请求状态的套接字文件描述符
    backlog 连接请求等待队列的长度,=5时,最多使5个连接请求进入到队列
    */

    int accept(int sock,struct sockaddr* addr,socklen_t* addr_len)
    /*
    sock 服务器套接字fd
    addr 保存 请求连接的客户端地址信息的结构体
    addrlen 第二个addr结构体长度
    返回用于数据IO的套接字的fd,失败返回-1
    若等待队列为空,accept不会返回,直等到等待队列有新的客户端连接
    */
    int connect(int sock,struct sockaddr* servaddr,socklen_t addrlen);
    /*
    sock 客户端套接字fd
    servaddr 保存目标服务器地址信息的变量结构体
    addrlen 以字节为单位传递给第二个结构体参数servaddr地址变量长度
    出现如下情况返回:
    服务器收到连接请求
    发生断网等异常而中断请求
    */
    客户端在调用connect函数时,在内核中,给客户端套接字分配IP和随机端口
  • 客户端在调用connect函数时,在内核中,给客户端套接字分配IP和随机端口

  • TCP服务器

    • socket()->bind()->listen()->accept()->read()/write()->close()
  • TCP 客户端

    • socket()->connect()->read()/write()->close()
  • TCP IO缓冲

    • write 调用瞬间,数据将移至输出缓冲,read函数调用瞬间,从输入缓冲读取数据

    • IO缓冲在每个套接字中单独存在

    • IO缓冲在创建套接字时自动生成

    • 即使关闭套接字也会继续输出缓冲中遗留的数据

    • 关闭套接字将丢失输入缓冲中的数据

    • write函数 send函数不会在完成向对方主机的数据传输时返回,而是在数据移出到输出缓冲时 。TCP保证对输出缓冲数据的传输。所以,write在数据传输完成时返回

  • TCP套接字

    • 向10个客户端提供服务,守门的服务器套接字+10个服务器端套接字
  • TCP半关闭

    • 1
      2
      3
      4
      5
      6
      int shutdown(itn sock,int howto);
      /*
      SHUT_RD 断开输入流
      SHUT_WR 断开 输出流
      SHUT_RDWR 同时断开I/O流,分两次调用shudown
      */
    • close 函数同时断开IO流,同时发送EOF信息

UDP

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    ssize_t sendto(int sock,void * buff,size_t nbytes,int flags, struct sockaddr* to,socklen_t addrlen);//
    /*
    sock 传输数据的UDP套接字fd
    buff 发送数据缓冲地址
    nbytes 发送的数据长度/bytes
    flags
    to 目标地址信息
    addrlen sockaddr 结构体长度
    返回值 返回传输的字节数 否则失败 -1
    需要向它传送目标地址信息
    */
    ssize_t recvfrom (int sock,void * buff,size_t nbytes,int flags,struct sockaddr* from,socklen_t * addrlen)
    /*
    sock 接受数据fd
    buff 接受数据缓冲地址
    nbytes 可接受的最大字节数
    flags
    from 保存 发送端地址信息
    addrlen 结构体长度
    */
  • UDP服务器

    • sock()->bind()->recvfrom()->sendto()
  • UDP客户端

    • sock()->sendto()->recvfrom()
    • 在调用sendto函数时检测到还未分配IP和端口,自动分配IP和端口号,IP主机IP,端口尚未使用的任意端口
  • 未连接UDP套接字VS已连接UDP套接字

    • 未连接套接字 sendto作用
      • 向UDP套接字注册目标IP和端口号
      • 传输数据
      • 删除UDP套接字中注册的目标地址信息
    • 每次调用都变更目标地址,可以重复利用同一UDP套接字向不同目标传输数据
    • 已连接UDP套接字
      • 通过connect注册目标IP和地址信息
      • sock()->connect()->sendto()->recvfrom()

套接字选项

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    int getsockopt(int sock,int level,int optname,void * optval,socklen_t* optlen)
    /*
    sock
    level 查看可选项的协议层
    optname 可选项名
    optval 保存查看结果的缓冲地址值
    optlen 向optval传递的缓冲大小
    */
    int setsockopt(int sock,int level,itn optname,const void* optval,socklen_t* optlen);
  • TIEM_WAIT状态,服务器发送FIN后,客户端发送ACK,进入到TIME_WAIT状态,防止此ACK丢失,可以重发

  • 客户端每次动态分配端口号,不必担心TIME_WAIT状态,但是不管服务器、客户端只要先断开套接字都会有TIME_WAIT状态。

  • 1
    2
    3
    option=TRUE;
    optlen=sizeof(option)
    setopt(serv_sock,SOL_SOCKET,SO_REUSEADDR,(void*)&option,optlen);
  • 禁用NAGLE算法–TCP

  • 1
    2
    int opt_val=1;
    setopt(sock,IPPROTO_TCP,TCP_NODELAY,(void*)&opt_val,sizeof(opt_val));

多进程服务器

  • 1
    2
    3
    4
    5
    pid_t fork();
    /*
    父进程返回子进程ID
    子进程 返回0
    */
  • 调用fork之后,复制文件描述符,一个套接字对应多个文件描述符,需要关闭无关的套接字文件描述符

  • 僵尸进程

    • 子进程退出后,父进程没有调用wait函数或者wait_pid函数等待子进程结束,又没有显示忽略SIGCHLD信号

    • 占用资源和进程号

    • 销毁–向创建子进程的父进程传递子进程的exit参数值或return语句的返回值

      • 1
        2
        3
        4
        5
        6
        7
        pid_t wait(int * statloc);//函数
        if(WIFEXITED(status))//判断是否正常结束进程
        {
        WEXITSTATUS(status);//子进程结束返回值
        }

        //调用wait时,如果有子进程终止,那么子进程终止时传递的返回值将保存到statloc中,没有进程终止将阻塞
      • 1
        2
        3
        4
        5
        6
        pid_t waitpid(pid_t pid,int* statloc,it options)
        /*
        pid 等待终止的目标子进程id,-1时可以等待任意子进程
        options 传递头文件sys/wait.h文件中常量WNOHANG,即使没有子进程终止也不会进入到阻塞
        返回终止的子进程ID(或0),失败-1
        */
      • 1
        信号和signal函数

进程间通信

  • 管道

    • 1
      2
      3
      4
      5
      6
      7
      int pipe(int filedes[2]);
      /*
      成功时,返回0,失败-1
      filedesc[0] 通过管道接受数据
      filedesc[2] 通过管道发送数据
      */
      //管道单工
    • 管道只允许有血缘关系的进程间通信,内部保持同步机制,从而保证访问数据的一致性

    • 管道随进程,进程在,管道在,进程消失,管道对应的端口也关闭,两个进程都消失,管道也消失

IO复用 select

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //#define __FD_SETSIZE    1024 否则重新编译内核
    fd_set set;//bit为单位,保存需要监视的文件描述符[0-1024]
    FD_SET(1,&set);//设置1 bit为1
    FD_CLR(2,&set);//清空 第2bit
    FD_ZERO(&set); //清空 set

    int select(int maxfd,fd_set* readset,fd_set* write_set,fd_set* exceptset,const struct timeval* timeout)
    /*
    maxfd 监视对象文件描述符的数量 ---最大的描述符+1 (从0开始)
    readset 将所有关注“是否存在待读取数据”的文件描述符注册到fd_set变量,并传递其地址
    writeset “是否可传输无阻塞数据”
    exceptset "是否发生异常"
    timeout
    返回 -1 -错误,0-超时,>0 表示发生事件的文件描述符数量
    */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  
* select每次调用后,fd_set中保存着发生事件的文件描述符,其余均为0,所以,每次都需要对fd_set重新赋值中。timeout变为超时前剩余时间,每次都需要对timeout重新赋值

## 多种IO函数

* ```c++
ssize_t send(int sockfd,const void* buf,size_t nbytes,int flags);
ssize_t recv(int sockfd,void* buf,size_t nbytes,int flags);
/*
可选项
MSG_OOB 传输带外数据
MSG_PEEK 验证输入缓冲中是否存在接收的数据
MSG_DNOTWAIT 调用IO函数时不阻塞,用于非阻塞I/O
MSG_WAITTAIL 防止函数返回,直到接收全部请求的字节数
*/
  • MSG_OOB并不能传输带外数据,督促消息处理

  • MSG_PEEK | MSG_DNOTWAIT 验证输入缓冲中是否存在接收的数据,读取到的数据并不会从输入缓冲中删除

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    struct iovec{
    void * iov_base;//缓冲地址
    size_t iov_len;//缓冲大小
    }
    //将分散在多个缓冲中的数据一块发送,减少IO函数调用次数
    ssize_t writev(int filedesc,const struct iovec* iov,int iovcnt)
    /*
    filedesv 传输对象的fd
    iov iovec 保存着待发送数据的位置和size
    iovcnt 数据长度

    返回发送的字节数 或者-1
    */
    ssize_t readv(int filedesc,cosnt struct iovec* iov,int iovcnt);
    /*
    返回成功接收的字节数 或者 -1
    */

套接字和标准IO

  • 创建套接字时操作系统会准备I/O缓冲,仅用于维持TCP协议存在。TCP丢失的数据。。。。套接字的删除缓冲

  • 标准I/O提高性能

    • 传输的数据量—-多个小文件中包含的头信息多
    • 数据向输出缓冲移动的次数
    • 缺点
      • 不容易双向通信
      • 有可能频繁调用flush函数
      • 需要以FILE结构体指针的形式返回文件描述符
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    FILE* fdopen(int files,const char* mode)//文件描述符转为FILE*
    /*
    fildes 需要转换的文件描述符
    mode “w” "a"
    r 打开只读文件,r+打开可读写文件,都要求文件必须存在
    w 打开只写文件,w+打开可读写文件,都会将文件长度清零,文件不存在时会创建文件
    a 打开只写文件,a+打开可读写文件,都会将写的内容加到文件后面,文件不存在时创建文件
    */

    int fileno(FILE* stream)//FILE* 转为fd

关于IO分离的其他内容

  • 针对于FILE指针的半关闭

    • 1
      2
      FILE* readfp=fdopen(clnt_sock,"r");
      FILE* writefp=fdopen(clnt_sock,"w");
    • 针对于任意一个FILE指针调用fclose函数时,文件描述符都会关闭(读+写)

    • 销毁所有文件描述符后,套接字才销毁

    • 1
      2
      3
      4
      5
      6
      7
      int dup(int fildes);
      int dup2(int fildes,int fildes2);
      /*
      fildes 需要复制的文件描述符
      fildes2 明确指定文件描述符整数值
      失败返回-1,成功返回复制的文件描述符
      */
    • 无论复制多少文件描述符,都应调用shutdown函数发送EOF并进入半关闭状态

多线程服务器实现

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int pthread_create(pthread_t* restrict thread,const pthread_attr_t* restrict arrt,void *(* start_routine)(void*),void* restrict arg);
    /*
    thread 保存新创建线程ID的变量地址
    attr 传递线程属性参数,null时,默认属性
    start_rountine 线程处理函数 函数指针
    arg 线程处理函数的参数地址

    成功时返回0,失败时返回其他值
    */

    int pthread_join(pthread_t thread,void** status)
    /*
    thread 该参数值ID的线程终止后才会从该函数返回
    status 保存线程的main函数返回值的指针变量地址值
    */
  • 1
    2
    3

    struct hostent* gethostbyname(const char* hostname);//线程非安全函数
    struct hostent* gethostbyname_r(const char* name,struct hostent* result,char* buff,int buflen,int* h_errnop)//线程安全函数
  • 线程安全函数后缀_r

  • 或者声明头文件前定义 _REENTRANT 宏(重入)

  • 或者编译时 gcc -D_REENTRANT mythread.c -o mthread -lpthreadz

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    int pthread_init(pthread_mutex_t* mutex,const pthread_mutexattr_t* arrt)
    int pthread_mutex_destory(pthread_mutex_t* mutex);
    /*
    mutex创建互斥量时传递保存互斥量的变量地址值,销毁时传递需要销毁的互斥量地址值
    attr 传递即将创建的互斥量属性值,没有特别需要指定的属性时null
    */

    int pthread_mutex_lock(pthread_mutex_t* mutex)
    int pthread_mutex_unlock(pthread_mutex_t* mutex);

    int pthread_join(pthread_t thread);
    /*
    等待线程终止,且引导线程销毁。线程终止前,调用该函数的线程进入到阻塞状态
    */
    int pthread_detach(pthread_t thread);
    /*

    */
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int sem_init(sem_t* sem,int pshared,unsigned int value);
    int sem_destory(sem_t* sem);
    /*
    sem 创建信号量时传递保存信号量的变量地址值、销毁时传递需要销毁的信号量变量的地址值
    pshared 传递其他值时,创建可由多个进程共享的信号量;0时,创建只允许一个进程内部使用的信号量
    。同一进程内的线程同步=0
    value 指定新创建的信号量的初始值
    */
    int sem_post(sem_t* sem);
    int sem_wait(sem_t* sem);
    /*
    传递保存信号量读取值的变量地址值,传递给sem_post时信号量+1,sem_wait 信号量-1
    信号量的值不能小于0。在信号量值为0的情况下,调用sem_wait函数时,调用函数的线程将进入阻塞状态(函数未返回)
    */

1. epoll

  • epoll_create

  • 1
    2
    3
    4
    5
    6
    int epoll_create(int size)
    /*
    select 中为了存放监视对象文件描述符,声明了一个fd_set变量。epoll类似,由操作系统负责保存监视对象文件描述符,需要向操作系统请求创建保存文件描述符的空间。
    linux2.6.8之后忽略,内核根据情况调整epoll 一个实例的大小
    成功时返回epoll实例文件描述符,失败-1
    */
  • epoll_ctl

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    /* epoll中监视对象事件 的结构*/
    struct epoll_event{
    __uint32_t events;
    epoll_data_t data;
    }
    typedef union epoll_data{
    void* ptr;
    int fd;
    __UInt32_t u32;
    __uint64_t u64;
    } epoll_data_t;
    /*
    events:
    EPOLLIN :读取数据
    EPOLLOUT:s输出缓冲为空,可以立即发送数据
    EPOLLPRI:收到OOB的情况
    EPOLLRDHUP:断开连接或者半关闭情况,边缘触发时非常有用
    EPOLLERR:发生错误
    EPOLLET:边缘触发的方式得到事件通知
    EPOLLONESHOT:发生一次事件后,相应文件描述符不在收到事件通知。需要向epoll——ctl函数第二个参数
    传递EPOLL_CTL_MOD,再次设置事件
    */
    /*
    epfd epoll实例的文件描述符 epoll_create返回值
    op 监视对象的操作
    fd 监视对象的文件描述符
    event 监视对象的事件类型
    */
    epoll_ctl(int epfd,int op,int fd,struct epoll_event * event);
    /*
    op
    EPOLL_CTL_ADD 文件描述符注册到epoll实例
    EPOLL_CTL_DEL 从epoll例程中删除文件描述符
    EPOLL_CTL_MOD 更改注册的文件描述符的关注事件发生的情况
    */
  • epoll_wait

  • 1
    2
    3
    4
    5
    6
    7
    8
    /*
    返回发生事件的文件描述符的个数
    epfd epoll文件描述符
    events 保存事件发生的文件描述符集合的结构体地址
    maxevents 第二个参数中可以保存的最大事件数
    timeout 1/1000秒为单位的等待事件,-1永不超时,一直等待直至发生事件
    */
    int epoll_wait(int epfd,struct epoll_evetn* events,int maxevents,int timeout)
  • 条件触发–默认方式

    • 只要输入缓冲有数据就会一直通知该事件
    • select模型———-条件触发
  • 边缘触发

    • 边缘触发输入缓冲收到数据时仅注册1次事件,即使输入缓冲中还有数据,也不会再次进行注册。接受数据时,仅仅注册一次事件。所以一旦发生事件,需要读取输入缓冲中的全部数据。

    • 通过以下方式验证输入缓冲是否为空

      • read函数返回-1,且全局变量errno=EAGAIN
    • 设置文件描述符为非阻塞式

      • 1
        2
        3
        4
        5
        int fcntl(int filedes,int cmd,....)
        /*
        int flag=fcntl(fd,F_GETFL,0);//获取之前的属性
        fcntl(fd,F_SETFL,flag|O_NONBLOCK);//设置非阻塞
        */
      • 边缘触发下,以阻塞方式工作的read&write函数有可能引起服务器端的长时间停顿。边缘触发一定采用非阻塞read&write

    • 可以分离接受数据和处理数据的时间点

Appendix

  • 读panda有感

  • threadpool

    • 线程池的任务队列中存放的是IThreadHandle* ,有threadhandle接口,即线程处理函数

      • 1
        2
        3
        4
        5
        typedef Queue::CQueue<IThreadHandle *> queue_handle_t;//线程处理函数
        queue_handle_t m_taskqueue;

        typedef std::vector<pthread_t> vector_tid_t;//创建的线程ID
        vector_tid_t m_thread;
    • 在CThreadPool构造函数中调用create_threadpool 来产生一定数量的线程,并保存创建好的线程的id,每个线程都相应函数都为process_task。这里利用领导者/跟随者模式从任务队列中取出任务并执行thread_handle。

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        CThreadPool::CThreadPool(size_t threadnum, size_t tasknum): 
        m_taskqueue(tasknum), m_hasleader(false)
        {
        m_threadnum = (threadnum > THREADNUM_MAX)? THREADNUM_MAX: threadnum;
        create_threadpool();
        }
        void CThreadPool::create_threadpool()
        {
        pthread_attr_t thread_attr;
        pthread_attr_init(&thread_attr);
        for(size_t i = 0; i < m_threadnum; i++){
        pthread_t tid = 0;
        if(pthread_create(&tid, &thread_attr, process_task, (void *)this) < 0){
        errsys("create thread[%d] filed\n", (int)i);
        continue;
        }

        m_thread.push_back(tid);
        }
        pthread_attr_destroy(&thread_attr);
        trace("create thread pool, thread number %d\n", (int)m_thread.size());
        }
        void *CThreadPool::process_task(void * arg)
        {
        CThreadPool &threadpool = *(CThreadPool *)arg;
        while(true){
        threadpool.promote_leader();
        IThreadHandle *threadhandle = NULL;
        int ret = threadpool.m_taskqueue.pop(threadhandle);
        threadpool.join_follwer();

        if(ret == 0 && threadhandle)
        threadhandle->threadhandle();//IThreadHandle 接口存在threadhandle处理函数
        }

        pthread_exit(NULL);
        }
    • 领导者/跟随者

      • [两种高效的并发模式]: https://blog.csdn.net/ZYZMZM_/article/details/98055416#LeaderFollowers_41

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        void CThreadPool::promote_leader()
        {
        Pthread::CGuard guard(m_identify_mutex);//m_identify_mutex 加锁,析构时,释放锁
        while(m_hasleader){ // more than one thread can return
        m_befollower_cond.wait(m_identify_mutex);
        /*
        如果m_hasleader =true,直接返回,表示当前已存在领导者
        否则,m_befollower_cond条件变量释放m_identify_mutex锁,但是阻塞并等待被唤醒,之后在join_follwer中可以获得锁继续执行,设置m_hasleader=false,并唤醒m_befollower_cond,释放锁m_identify_mutex,之后promote_leader中m_befollower_cond.wait(m_identify_mutex);被唤醒并加锁m_identify_mutex,由于m_hasleader为false,跳出循环
        */
        }
        m_hasleader = true;
        }
        void CThreadPool::join_follwer()
        {
        Pthread::CGuard guard(m_identify_mutex);
        m_hasleader = false;
        m_befollower_cond.signal();
        }
      • 关于pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之间,也可以放在pthread_mutex_lock和pthread_mutex_unlock之后,但是各有有缺点。

      [再谈互斥锁与条件变量!]: https://blog.csdn.net/brian12f/article/details/73882166?utm_source=copy

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        pthread_mutex_lock
        xxxxxxx
        pthread_cond_signal
        pthread_mutex_unlock
        /*
        缺点:在某下线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)然后又回到内核空间(因为cond_wait返回后会有原子加锁的 行为),所以一来一回会有性能的问题。但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。
        所以在Linux中推荐使用这种模式。
        */
        pthread_mutex_lock
        xxxxxxx
        pthread_mutex_unlock
        pthread_cond_signal
        /*
        优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了
        缺点:如果unlock和signal之前,有个低优先级的线程正在mutex上等待的话,那么这个低优先级的线程就会抢占高优先级的线程(cond_wait的线程),而这在上面的放中间的模式下是不会出现的。
        */
  • epoll异步框架

    • 在CEvent的构造函数中创建一个eventwait_thread 等待线程。

      • 1
        2
        if(pthread_create(&tid, NULL, eventwait_thread, (void *)this) == 0)
        m_detectionthread = tid;
    • 在eventwait_thread 线程中调用epoll_wait函数,并根据监控结果,将文件描述符,事件类型,封装成任务CEvent *,push到线程池的任务的队列中。CEvent继承至IThreadHandle,有重写的threadhandle的函数

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        void *CEvent::eventwait_thread(void *arg)
        {
        CEvent &cevent = *(CEvent *)(arg);//
        if(INVALID_FD(cevent.m_epollfd)){
        seterrno(EINVAL);
        pthread_exit(NULL);
        }
        for(;;){
        int nevent = epoll_wait(cevent.m_epollfd, &cevent.m_eventbuff[0], EventBuffLen, -1);
        if(nevent < 0 && errno != EINTR){
        errsys("epoll wait error\n");
        break;
        }
        for(int i = 0; i < nevent; i++){
        int fd = cevent.m_eventbuff[i].data.fd;
        EventType events = static_cast<EventType>(cevent.m_eventbuff[i].events);
        if(cevent.pushtask(fd, events) == 0x00){
        cevent.m_ithreadpool->pushtask(&cevent);//将CEvent* 对象push到任务队列中,有重写thread_handle函数
        }
        }
        }
        pthread_exit(NULL);
        }
        void CEvent::threadhandle()
        {
        int fd = 0x00;
        EventType events;
        if(poptask(fd, events) < 0){
        return;
        }
        CNetObserver *observer = get_observer(fd);
        if(observer == NULL)
        return;
        /*
        * 关闭时递减引用计数。在对象的所有回调处理完时真正释放
        */
        if(events & ECLOSE){
        cleartask(fd);
        observer->subref();
        }else{
        if(events & EERR){
        observer->handle_error(fd);
        }
        if(events & EIN){
        observer->handle_in(fd);
        }
        if(events & EOUT){
        observer->handle_out(fd);
        }
        }
        /*
        * unregister_event 执行后于handle_close将会出现当前套接字关闭后
        * 在仍未执行完unregister_event时新的连接过来,得到一样的描述符
        * 新的连接调用register_event却未注册进入epoll。同时han_close中
        * 关闭了套接字,unregister_event中epoll删除关闭的套接字报错
        */
        if(observer->subref_and_test()){
        unregister_event(fd);
        observer->handle_close(fd);
        observer->selfrelease();
        }
        }
      • CEvent中threadhandle,调用的是和每个fd所绑定的CNetObserver*的处理函数

        • 1
          2
          3
          4
          typedef std::map<int, CNetObserver *> EventMap_t;
          typedef std::map<int, EventType> EventTask_t;
          EventMap_t m_eventreg;
          EventTask_t m_events;
      • CNetObserver 继承至INetObserver,一方面负责fd的事件处理函数,另一方面负责相应的fd和事件处理函数的同步,为了确保CNetObserver * 的正确销毁时机,用了引用计数功能。

        • 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          int CEvent::record(int fd, EventType type, IEventHandle *handle)
          {
          CNetObserver *newobserver = new CNetObserver(*handle, type);
          assert(newobserver != NULL);

          Pthread::CGuard guard(m_eventreg_mutex);
          m_eventreg[fd] = newobserver;
          return 0;
          }
          class CNetObserver: public INetObserver
          {
          friend class CEvent;
          public:
          CNetObserver(INetObserver &, EventType);
          ~CNetObserver();
          inline void addref();//
          inline void subref();
          inline bool subref_and_test();
          inline void selfrelease();
          inline EventType get_regevent();
          inline const INetObserver *get_handle();

          protected: //负责fd的事件处理函数
          void handle_in(int);
          void handle_out(int);
          void handle_close(int);
          void handle_error(int);

          private:
          EventType m_regevent;
          INetObserver &m_obj;

          int32_t m_refcount;
          Pthread::CMutex m_refcount_mutex;
          };
    • 在CEvent中负责事件的处理函数,负责向线程池的任务队列Push_task,一个单例的CEventProxy 进一步封装CEvent

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        class CEventProxy: public IEvent
        {
        public:
        static CEventProxy *instance();
        int register_event(int, IEventHandle *,EventType);
        int register_event(Socket::ISocket &, IEventHandle *,EventType);
        int shutdown_event(int);
        int shutdown_event(Socket::ISocket &);

        private:
        CEventProxy(size_t neventmax);
        ~CEventProxy();

        private:
        IEvent *m_event;
        };
    • 为了确保在INetObserver中可以注册事件,关闭事件,在IEventHandle相应接口中调用CEventProxy中的CEvent实现相应接口

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        class IEventHandle: public INetObserver
        {
        public:
        // desc: 注册进入事件中心
        // param: fd/套接字描述符 type/事件类型
        // return: 0/成功 -1/失败
        int register_event(int fd, EventType type = EDEFULT);

        // desc: 注册进入事件中心
        // param: socket/套接字对象 type/事件类型
        // return: 0/成功 -1/失败
        int register_event(Socket::ISocket &socket, EventType type = EDEFULT);

        // desc: 关闭事件
        // param: fd/套接字描述符
        // return: 0/成功 -1/失败
        int shutdown_event(int fd);

        // desc: 关闭事件
        // param: socket/套接字对象
        // return: 0/成功 -1/失败
        int shutdown_event(Socket::ISocket &);
        };

        int IEventHandle::register_event(int fd, EventType type)
        {
        return CEventProxy::instance()->register_event(fd, this, type);
        }
  • HTTP

    *