(一)C 语言基础知识: (1)static关键字的作用: 1.全局静态变量 在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。 静态存储区,在整个程序运行期间一直存在。 初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。 作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。 2. 局部静态变量 在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。 内存中的位置:静态存储区。 初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。 作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变。 3. 静态函数 在函数返回类型前加static,函数就定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。 函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突。 warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰。 4. 类的静态成员 在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存储一处,供所有对象共用。 5. 类的静态函数 静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。 (2) C 与C语言的区别: 设计思想上: C 是面向对象的语言,而C是面向过程的结构化编程语言 语法上: C 具有封装、继承和多态三种特性 C 相比C,增加多许多类型安全的功能,比如强制类型转换、 C 支持范式编程,比如模板类、函数模板等 (二)计算机操作系统: (1)进程与线程的概念,以及为什么要有进程线程,其中有什么区别,他们各自又是怎么同步的 ? 进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发。 线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位。每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。每个线程完成不同的任务,但是共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。 进程与线程的区别: 1.一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。 2.进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。) 3.进程是资源分配的最小单位,线程是CPU调度的最小单位; 4.系统开销:由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/o设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。类似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。可见,进程切换的开销也远大于线程切换的开销。 5.通信:由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。在有的系统中,线程的切换、同步和通信都无须操作系统内核的干预 6.进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂。 7.进程间不会相互影响 ;线程一个线程挂掉将导致整个进程挂掉 8.进程适应于多核、多机分布;线程适用于多核 。 进程间通信的方式: 进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。 1.管道: 管道主要包括无名管道和命名管道:管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信 1.1 普通管道PIPE: 1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端 2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间) 3)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。 1.2 命名管道FIFO: 1)FIFO可以在无关的进程之间交换数据 2)FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。 2. 系统IPC: 2.1 消息队列 消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标记。(消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点)具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息; 特点: 1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。 2)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。 3)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。 2.2 信号量semaphore 信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。 特点: 1)信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。 2)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。 3)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。 4)支持信号量组。 2.3 信号signal 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。 2.4 共享内存(Shared Memory) 它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等 特点: 1)共享内存是最快的一种IPC,因为进程是直接对内存进行存取 2)因为多个进程可以同时操作,所以需要进行同步 3)信号量 共享内存通常结合在一起使用,信号量用来同步对共享内存的访问 3.套接字SOCKET: socket也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之间的进程通信。 线程间通信的方式: 临界区:通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问; 互斥量Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问 信号量Semphare:为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。 事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作 。 (三)计算机网络: (1)TCP怎么保证可靠性,并且简述一下TCP建立连接和断开连接的过程? TCP保证可靠性: (1)序列号、确认应答、超时重传 数据到达接收方,接收方需要发出一个确认应答,表示已经收到该数据段,并且确认序号会说明了它下一次需要接收的数据序列号。如果发送发迟迟未收到确认应答,那么可能是发送的数据丢失,也可能是确认应答丢失,这时发送方在等待一定时间后会进行重传。这个时间一般是2RTT(报文段往返时间) 一个偏差值。 (2)窗口控制与高速重发控制/快速重传(重复确认应答) TCP会利用窗口控制来提高传输速度,意思是在一个窗口大小内,不用一定要等到应答才能发送下一段数据,窗口大小就是无需等待确认而可以继续发送数据的最大值。如果不使用窗口控制,每一个没收到确认应答的数据都要重发。 使用窗口控制,如果数据段1001-2000丢失,后面数据每次传输,确认应答都会不停地发送序号为1001的应答,表示我要接收1001开始的数据,发送端如果收到3次相同应答,就会立刻进行重发;但还有种情况有可能是数据都收到了,但是有的应答丢失了,这种情况不会进行重发,因为发送端知道,如果是数据段丢失,接收端不会放过它的,会疯狂向它提醒… (3)拥塞控制 如果把窗口定的很大,发送端连续发送大量的数据,可能会造成网络的拥堵(大家都在用网,你在这狂发,吞吐量就那么大,当然会堵),甚至造成网络的瘫痪。所以TCP在为了防止这种情况而进行了拥塞控制。 慢启动:定义拥塞窗口,一开始将该窗口大小设为1,之后每次收到确认应答(经过一个rtt),将拥塞窗口大小2。 拥塞避免:设置慢启动阈值,一般开始都设为65536。拥塞避免是指当拥塞窗口大小达到这个阈值,拥塞窗口的值不再指数上升,而是加法增加(每次确认应答/每个rtt,拥塞窗口大小 1),以此来避免拥塞。 将报文段的超时重传看做拥塞,则一旦发生超时重传,我们需要先将阈值设为当前窗口大小的一半,并且将窗口大小设为初值1,然后重新进入慢启动过程。 快速重传:在遇到3次重复确认应答(高速重发控制)时,代表收到了3个报文段,但是这之前的1个段丢失了,便对它进行立即重传。 然后,先将阈值设为当前窗口大小的一半,然后将拥塞窗口大小设为慢启动阈值 3的大小。 这样可以达到:在TCP通信时,网络吞吐量呈现逐渐的上升,并且随着拥堵来降低吞吐量,再进入慢慢上升的过程,网络不会轻易的发生瘫痪。 TCP建立连接和断开连接的过程:
三次握手:
Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J 1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
Client收到确认后,检查ack是否为J 1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K 1,并将该数据包发送给Server,Server检查ack是否为K 1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。 四次挥手: 由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。 1.数据传输结束后,客户端的应用进程发出连接释放报文段,并停止发送数据,客户端进入FIN_WAIT_1状态,此时客户端依然可以接收服务器发送来的数据。 2.服务器接收到FIN后,发送一个ACK给客户端,确认序号为收到的序号 1,服务器进入CLOSE_WAIT状态。客户端收到后进入FIN_WAIT_2状态。 3.当服务器没有数据要发送时,服务器发送一个FIN报文,此时服务器进入LAST_ACK状态,等待客户端的确认 4.客户端收到服务器的FIN报文后,给服务器发送一个ACK报文,确认序列号为收到的序号 1。此时客户端进入TIME_WAIT状态,等待2MSL(MSL:报文段最大生存时间),然后关闭连接。 今天的基础知识就分享到这里了,最近有点忙,没时间打理,过一段时间,好好排排版,欢迎关注一起学习~