(1)string类的实现(使用strlen、strcpy、strcat、strcmp等,注意判NULL)。
(2)C++字符串和C字符串的转换:data()以字符形式返回字符串内容,但不添加’\0\;c_str()返回一个以’\0’结尾的字符数组;copy()把字符串内容复制或写入既有的c_string或字符数组内。
(3)string和int互转:snprintf()、strtol/strtoll/strtoull.
(4)经常使用成员函数:capacity()、max_size()、size()、length()、empty()、resize()html
(1)容器大小size()指元素个数,容量capacity()指分配的内存大小
(2)遍历:for(int i=0;i<a.size();++i
、for(iter=ivector.begin();iter!=ivector.end();iter++)
、for_each
(3)查找find()、删除erase()/pop_back()、增长insert()/push_front(),注意for 循环遍历删除时的坑:for语句条件里删除元素时,返回值指向已删除元素的下一个位置,不是删除元素时则直接++
(4)reserve()提早设定容量大小;swap()强行释放vector所占内存linux
(1)map内部自建一颗红黑树,具备对数据自动排序的功能。须回顾二叉树、红黑树。。。
(2)插入pair数据、数组方式插入、数据方式覆盖插入
(3)遍历:利用前向迭代器、利用反向迭代器、数组方式
(4)查找find()、删除erase()、排序less/greater程序员
(1)建立、插入元素、删除元素、查找元素等。。算法
(1)过程:预处理(prepressing),编译(compilation),汇编(assembly),连接(linking)
(2)预处理:主要处理那些源代码文件只可以的以”#”开始的预编译指令。好比“#include”、“#define”,过滤全部注释,添加行号,保留#pragma编译器指令等
(3)编译:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成和目标代码优化
(4)连接:把各个模块之间相互引用的部分都处理好,使得各个模块之间可以正确的衔接。
原理:把一些指令对其余符号地址的引用加以修正。连接过程主要包括了地址和空间分配、符号决议和重定位等
(5)静态连接库和动态连接库编程
1.动态连接库有利于进程间资源共享;
2.动态连接库升级容易,用静态库则须要从新编译;
3.许多进程或应用程序可在磁盘上共享动态库的一个副本,可节省内存和减小交换操做,节省磁盘空间;
4.静态连接库在编译的时候将库函数装载到程序中,执行速度更快。api
(6)g++和gcc数组
1.后缀为.c的,gcc将其看成C程序,而g++看成是C++程序;后缀为.cpp的,二者都认为是C++程序;
2.编译阶段,g++会自动调用gcc,二者等价;但由于gcc不能自动和C++程序使用的库连接,因此一般用g++来完成连接,因此一般直接用g++编译、连接;
3.extern”C”与gcc/g++并没有关系。缓存
(1)书写规则,第一部分为依赖关系,第二部分为生成目标的方法:安全
target : prerequisites
<tab>command <tab>command
target也就是一个目标文件,能够是.o文件,也能够是执行文件,还能够是一个标签(Label)。
prerequisites就是,要生成那个target所须要的文件或是目标。
command也就是make须要执行的命令(任意的Shell命令)。这里要注意的是在命令前面要加上一个tab键,不是空格,是按一个tab键按出来的空格。
(2)make clean
用于清除编译产生的二进制文件,保留源文件:bash
clean:
@echo "cleaning project" -rm main *.o @echo "clean completed"
在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续作后面的事
(3)变量,用$(objects)
的方式来使用
(4)$@
扩展成当前规则的目的文件名;$<
扩展成依靠列表中的第一个依靠文件;$^
扩展成整个依靠列表(除掉重复文件名)
(1)目标文件:源代码编译后可是没有进行连接的那些中间文件,好比win下的.obj文件、linux下的.o文件,与可执行文件的内容以及格式很相似。
目标文件中的内容至少有编译后的机器指令代码、数据。还包括链接时所须要的一些信息,好比符号表、调试信息、字符串等。通常,目标文件会将这些信息按照不一样的属性进行分段(其实就是多个必定长度的区域)。
(2)ELF文件主要由文件头(ELF header)、代码段(.text)、数据段(.data)、.bss段、只读数据段(.rodata)、段表(section table)、符号表(.symtab)、字符串表(.strtab)、重定位表(.rel.text)以下图所示:
(3)代码段与数据段分开的缘由:
1.权限分别管理。对进程来讲,数据段是可读写的,指令段是只读的。这样能够防止程序指令被改写。
2.指令区与数据区的分离有助于提升程序的局部性,有助于对CPU缓存命中率的提升。
3.当系统运行多个改程序的副本的时候,他们对应的指令都是同样的,此时内存只须要保留一份改程序的指令便可。固然,每一个副本进程的数据区域是不同的,他们是进程私有的
(4)阅读ELF文件的工具readelf;得到二进制文件里符号的工具nm;减小目标文件大小的工具strip
(1)经过跟踪系统调用观察程序在后台所作的事情
(2)跟踪信号传递
(3)统计系统调用
(1)经常使用调试命令
命令 | 描述 |
---|---|
backtrace(或bt) | 查看各级函数调用及参数 |
finish | 连续运行到当前函数返回为止,而后停下来等待命令 |
frame(或f) 帧编号 | 选择栈帧 |
info(或i) locals | 查看当前栈帧局部变量的值 |
list(或l) | 列出源代码,接着上次的位置往下列,每次列10行 |
list 行号 | 列出从第几行开始的源代码 |
list 函数名 | 列出某个函数的源代码 |
b 行号 | 在第几行设置断点 |
b 函数名 | 在函数处设置断点 |
next(或n) | 执行下一行语句 |
print(或p) | 打印表达式的值,经过表达式能够修改变量的值或者调用函数 |
quit(或q) | 退出gdb调试环境 |
set var | 修改变量的值 |
start | 开始执行程序,停在main函数第一行语句前面等待命令 |
step(或s) | 执行下一行语句,若是有函数调用则进入到函数中 |
(2)用gdb分析、定位coredump文件
实时显示系统中各个进程的资源占用情况
使用参考
列出刚刚那一时刻正在运行的进程快照
参数 | 功能 |
---|---|
a | 显示全部进程 |
-a | 显示同一终端下的全部程序 |
-A | 显示全部进程 |
c | 显示进程的真实名称 |
-N | 反向选择 |
-e | 等于“-A” |
e | 显示环境变量 |
f | 显示程序间的关系 |
-H | 显示树状结构 |
r | 显示当前终端的进程 |
T | 显示当前终端的全部程序 |
u | 指定用户的全部进程 |
-au | 显示较详细的资讯 |
-aux | 显示全部包含其余使用者的行程 |
-C<命令> | 列出指定命令的情况 |
–lines<行数> | 每页显示的行数 |
–width<字符数> | 每页显示的字符数 |
–help | 显示帮助信息 |
–version | 显示版本显示 |
(1)Valgrind包括以下工具:
1.Memcheck。这是valgrind应用最普遍的工具,一个重量级的内存检查器,可以发现开发中绝大多数内存错误使用状况,好比:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。
2.Callgrind。它主要用来检查程序中函数调用过程当中出现的问题。
3.Cachegrind。它主要用来检查程序中缓存使用出现的问题。
4.Helgrind。它主要用来检查多线程程序中出现的竞争问题。
5.Massif。它主要用来检查程序中堆栈使用中出现的问题。
6.Extension。能够利用core提供的功能,本身编写特定的内存调试工具
(2)linux下典型C程序内存空间布局:
(3)堆/栈的区别
1)申请方式: 栈区内存由系统自动分配,函数结束时释放;堆区内存由程序员本身申请,并指明大小,用户忘释放时,会形成内存泄露,不过进程结束时会由系统回收。
2)申请后系统的响应: 只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,不然将报异常提示栈溢出;堆区,空闲链表,分配与回收机制,会产生碎片问题(外部碎片)–>(固定分区存在内部碎片(分配大于实际),可变分区存在外部碎片(太碎没法分配))。
3)申请大小的限制:栈是1或者2M,能够本身改,可是最大不超过8M;堆,看主机是多少位的,若是是32位,就是4G
4)申请效率:栈由系统自动分配,速度较快,程序员没法控制;堆是由new分配的内存,通常速度较慢,并且容易致使内存碎片,可是用起来方便!
5)存储内容:栈,函数调用(返回值,各个参数,局部变量(静态变量不入栈));堆,通常在堆的头部用一个字节存放堆的大小,堆中的具体内容由程序员安排。
6)存取效率的比较:栈比堆快,Eg :char c[] = /”1234567890/”;char *p =/”1234567890/”;读取c[1]和p[1],c[1]读取时直接吧字符串中的元素读到寄存器cl中,而p[1]先把指针值读到edx中,再根据edx读取字符,多一次操做。
7)管理方式不一样:栈,数据结构中的栈;堆,链表
8)生长方向:栈,高到低;堆,低到高
(4)Valgrind安装/使用
使用参考
(1)OSI七层网络模型vs五层网络模型vsTCP/IP四层网络模型
七层 | 五层 | 四层 |
---|---|---|
应用层 | ||
表示层 | ||
会话层 | 应用层 | 应用层 |
传输层 | 运输层 | 传输层 |
网络层 | 网络层 | 网间层 |
数据链路层 | 数据链路层 | 网络接口 |
物理层 | 物理层 |
(2)TCP头格式和各字段说明
(3)TCP状态流转
(4)TCP超时重传
重传超时时间RTO设置,1s、2s、4s、8s、16s符合Karm算法。待研究。
(5)TCP滑动窗口
- “窗口”对应的是一段能够被发送者发送的字节序列,其连续的范围称之为“窗口”;
- “滑动”则是指这段“容许发送的范围”是能够随着发送的过程而变化的,方式就是按顺序“滑动”。
(6)TCP拥塞控制
经常使用方法:
1. 慢开始、拥塞避免
2. 快重传、快恢复
Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。
SO_REUSEADDR等选项,不太懂。
(1)字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。
(2)主机字节序就是咱们日常说的大端和小端模式:不一样的CPU有不一样的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫作主机序。常见的有两种:a) Little-Endian就是低位字节排放在内存的低地址端;b) Big-Endian就是高位字节排放在内存的低地址端。
(3)网络字节序:4个字节的32 bit值如下面的次序传输:首先是0~7bit,其次8~15bit,而后16~23bit,最后是24~31bit。这种传输次序称做大端字节序。因为TCP/IP首部中全部的二进制整数在网络中传输时都要求以这种次序,所以它又称做网络字节序。
(4)因此,当两台采用不一样字节序的主机通讯时,在发送数据以前都必须通过字节序的转换成为网络字节序后在进行传输。
(1)TCP/IP 网络数据以流的方式传输,数据流是由包组成,如何断定接收方收到的包是不是一个完整的包就要在发送时对包进行处理,这就是封包技术。封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部份内容了
(2)包头其实上是个大小固定的结构体,其中有个结构体成员变量表示包体的长度,这是个很重要的变量,其余的结构体成员可根据须要本身定义.根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完整的数据包
(3)利用底层的缓冲区来进行拆包时,因为TCP也维护了一个缓冲区,因此能够利用TCP的缓冲区来缓存发送的数据,这样一来就不须要为每个链接分配一个缓冲区了.对于利用缓存区来拆包,就是循环不停地接收包头给出的数据,直到收够为止,这就是一个完整的TCP包。
本书中讲解了四种网络IO模型:阻塞IO模型、非阻塞IO模型、多路IO复用模型、异步IO模型。
《Unix网络编程》一书中提到了五种IO模型,分别是:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO。
待学习!
tcpdump [ -adeflnNOpqStvx ] [ -c 数量 ] [ -F 文件名 ] [ -i 网络接口 ] [ -r 文件名] [ -s snaplen ]
[ -T 类型 ] [ -w 文件名 ] [表达式 ]
一些使用实例
(1)tcpdump -i eth0 host 192.168.0.250 —–在网口eth0上抓取主机地址为192.168.0.250的全部数据包。
(2)tcpdump -i eth0 net 192.168.0.0/24 —— 在网口eth0上抓取网络地址为192.168.0.0/24的全部数据包
(3)tcpdump -i eth0 port 80 —— 在网口eth0上抓取端口为80的全部数据包(注意,这里不区分是源端口仍是目的端口)
固然,咱们也能够指定源端口或目的端口
(4)tcpdump -i eth0 src port 80 and dst port6100 — 在网口eth0上抓取源端口为80且目的端口为6100的数据包,这里用到了and逻辑运算符,后面再介绍
(5)tcpdump -i eth0 icmp — 在网口eth0上抓取全部icmp协议的数据包
netstat [-a][-e][-n][-o][-p Protocol][-r][-s][Interval]
一些使用实例
(1)netstat -a:列出全部端口(包括未监听端口)
(2)netstat -at:列出全部tcp端口
(3)netstat -au:列出全部udp端口
(4)netstat -l:只显示监听端口
(5)netstat -lt:只列出全部监听 tcp 端口
(6)netstat -lu:只列出全部监听 udp 端口
(7)netstat -lx:只列出全部监听 UNIX 端口
(8)netstat -p:在 netstat 输出中显示 PID 和进程名称
(9)netstat -an:在 netstat 输出中不显示主机,端口和用户名。将会使用数字代替那些名称。
(10)netstat -c:持续输出 netstat 信息
lsof(list open files)是一个列出当前系统打开文件的工具。在linux环境下,任何事物都以文件的形式存在,经过文件不只仅能够访问常规数据,还能够访问网络链接和硬件。因此如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,不管这个文件的本质如何,该文件描述符为应用程序与基础操做系统之间的交互提供了通用接口。由于应用程序打开文件的描述符列表提供了大量关于这个应用程序自己的信息,所以经过lsof工具可以查看这个列表对系统监测以及排错将是颇有帮助的。
一些使用实例
(1)lsof -i :6666:查看6666端口如今运行状况
(2)lsof -a -u root -d txt:查看所属root用户进程所打开的文件,文件类型为.txt
(3)lsof/dev/tty1:监控打开的文件和设备。查看设备/dev/tty1被哪些进程占用
(4)lsof -c server:监控程序。查看指定程序server打开的文件
(5)lsof -u user_name:监控用户。查看指定用户user_name打开的文件
(1)多进程频繁上下文切换引发的额外开销可能会严重影响系统性能;进程间通讯要求复杂的系统级实现
(2)同一个进程内部的多个线程共享该进程的全部资源;经过线程能够支持同一个应用程序内部的并发,免去了进程频繁切换的开销;并发任务间通讯也更简单。
(3)多线程在的进程在内存中有多个栈,每一个栈对应一个线程,多个栈之间以必定的空白区域隔开,以备栈的增加,任何一个空白区域被填满都会致使栈溢出。
(1)线程建立:pthread_create函数
(2)线程退出:执行完成后隐式退出;由线程自己显示调用pthread_exit 函数退出;被其余线程用pthread_cancel函数终止
(3)pthread_join用于等待一个线程的结束,也就是主线程中要是加了这段代码,就会在加代码的位置卡主,直到这个线程执行完毕才往下走。通常都是pthread_exit在线程内退出,而后返回一个值。这个时候就跳到主线程的pthread_join了(由于一直在等你结束),这个返回值会直接送到pthread_join,实现了主与分线程的通讯。
(4)向线程传递参数
(5)得到线程id
(1)属性值不能直接设置,须使用相关函数进行操做,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数以前调用,以后必须用pthread_attr_destroy函数来释放资源。
(2)属性对象主要包括:做用域(scope)、栈尺寸(stack size)、栈地址(stack address)、优先级(priority)、分离的状态(detached state)、调度策略和参数(scheduling policy and parameters)。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程一样级别的优先级。
(1)互斥锁。经过加锁将原先分离的多个指令构成不可分割的一个原子操做
(2)条件变量。条件变量自己不是锁!但它也能够形成阻塞。一般与互斥锁配合使用,给多线程提供一个会合的场所。
(3)读写锁。与互斥量相似,但读写锁容许更高的并行性。其特性为:写独占,读共享。
(4)信号量。进化版的互斥锁(1–>N)。信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
(1)可重入函数特色:
1.不在函数内部使用静态或者全局数据
2.不返回静态或者全局数据,全部的数据都由函数调用者提供
3.使用本地数据,或者经过制做全局数据的本地拷贝来保护全局数据
4. 若是必须访问全局数据,使用互斥锁来保护
5.不调用不可重入函数
(2)不可重入函数特色:
1.函数中使用了静态变量,不管是全局静态变量仍是局部静态变量
2.函数返回静态变量
3.函数中调用了不可重入函数
4.函数中使用了静态的数据结构
5.调用了malloc/free函数,由于malloc函数是用全局链表来管理堆的
6.调用了标准I/O库函数,标准I/O库的不少实现都以不可重入的方式使用全局数据结构。
(3)_REENTRANT宏
在一个多线程程序里,默认状况下,只有一个errno变量供全部的线程共享。在一个线程准备获取刚才的错误代码时,该变量很容易被另外一个线程中的函数调用所改变。相似的问题还存在于fputs之类的函数中,这些函数一般用一个单独的全局性区域来缓存输出数据。
为解决这个问题,须要使用可重入的例程。可重入代码能够被屡次调用而仍然工做正常。编写的多线程程序,经过定义宏_REENTRANT来告诉编译器咱们须要可重入功能,这个宏的定义必须出现于程序中的任何#include语句以前。
_REENTRANT为咱们作三件事情,而且作的很是优雅:
1.它会对部分函数从新定义它们的可安全重入的版本,这些函数名字通常不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。
2.stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。
3.在error.h中定义的变量error如今将成为一个函数调用,它可以以一种安全的多线程方式来获取真正的errno的值。
(1)进程结构:代码段、数据段、堆栈段。堆栈段包括进程控制块PCB
(2)程序转换成进程步骤:
1.内核将程序读入内存,为程序分配内存空间
2.内核为该进程分配进程标识符(PID)和其余所需资源
3.内核为进程保存PID及相应的状态信息,把进程放到运行队列中等待执行。程序转化为进程后就能够被操做系统的调度程序调度执行了。
(1)fork()函数建立进程。仅仅被调用一次,却可以返回两次,它可能有三种不一样的返回值:
1.在父进程中,fork返回新建立子进程的进程ID;
2.在子进程中,fork返回0;
3.若是建立出错,fork返回-1
(2)exit()函数结束进程。
1.exit是函数,带参数,执行完后把控制权交给系统;return是函数执行完后的返回,执行完后把控制权交给调用函数
2.exit是正常终止进程;abort是异常终止
3.exit()会将缓冲区的数据写完后再退出;_exit()函数直接退出
(1)孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工做。
(2)僵尸进程:一个进程使用fork建立子进程,若是子进程退出,而父进程并无调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。
(1)linux或UNIX在系统引导时会开启不少服务,这些服务就叫做守护进程。守护进程是脱离终端而且在后台运行的进程
(2)建立守护进程步骤:
1.建立子进程,父进程退出(使子进程成为孤儿进程)
2.在子进程中建立新的会话(脱离控制终端)
3.改变当前目录为根目录
4.重设文件权限掩码
5.关闭文件描述符
6.守护进程的退出
(1)管道是一种半双工的通讯方式,数据只能单向流动,并且只能在具备亲缘关系的进程间使用。进程的亲缘关系一般是指父子进程或兄弟进程。
(2)无名管道和有名管道(FIFO)。前者用于父进程和子进程间的通讯,后者用于运行于同一台机器上的任意两个进程间的通讯。
消息队列用于运行于同一台机器上的进程间通讯,它和管道很类似,是一个在系统内核中用来保存消息的队列,它在系统内核中是以消息链表的形式出现。消息链表中节点的结构用msg声明。
(1)共享内存是运行在同一台机器上的进程间通讯最快的方式,由于数据不须要在不一样的进程间复制。一般由一个进程建立一块共享内存区,其他进程对这块内存区进行读写。
(2)获得共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不经常使用,由于它控制存取的将是实际的物理内存,在Linux系统下,这只有经过限制Linux系统存取的内存才能够作到,这固然不太实际。经常使用的方式是经过shmXXX函数族来实现利用共享内存进行存储的。
(3)使用共享内存优势:方便,函数接口简单,数据的共享使得进程间的数据不用传送,而是直接访问,也加快了效率。不像无名管道同样要求亲缘关系。
(4)使用共享内存缺点:没有提供同步机制,使得在使用共享内存进行进程间通讯时,每每要借助其余的手段来进行进程间的同步工做。
信号量又称为信号灯,它是用来协调不一样进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通讯。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取情况。
(1)ipcs -a列出本用户全部相关的ipcs参数 (2)ipcs -q列出进程中的消息队列 (3)ipcs -s列出全部信号量 (4)ipcs -m列出全部共享内存信息 (5)ipcs -l列出系统的限额 (6)ipcs -t列出最后的访问时间 (7)ipcs -u列出当前的使用状况