C++ LinuxWebServer 2万7千字的面经长文(下)

Posted 忆_恒心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ LinuxWebServer 2万7千字的面经长文(下)相关的知识,希望对你有一定的参考价值。

⭐️我叫忆_恒心,一名喜欢书写博客的在读研究生👨‍🎓。
如果觉得本文能帮到您,麻烦点个赞👍呗!

Linux Web Server项目虽然是现在C++求职者的人手一个的项目,但是想要吃透这个项目,还是需要一定的基础的,以项目为导向,进行基础的学习。

涵盖了计算机网络(网络编程)常见的知识点和常见的操作系统知识。

参加过大大小小的互联网厂和银行的秋招和春招的笔试与面试,整理了下面的2万7千字的长文(😄都是干货,写作不易啊),喜欢,觉得有帮助的,欢迎订阅专栏,后续有很多优质的文章进行更新,有任何疑问,欢迎留言!

近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧,喜欢的小伙伴给个三连支持一下呗。👍⭐️❤️
Qt5.9专栏定期更新Qt的一些项目Demo
项目与比赛专栏定期更新比赛的一些心得面试项目常被问到的知识点。


C++ LinuxWebServer 2万7千字的面经长文(下)

三、网络编程的基础

GCC 工作流程

gcc 和g++的区别

.c为后缀的文件,gcc把它当作C程序,而g++帮它当作c++

编译阶段,g++会调用gcc, 对于C++代码,两者是等价的,但是因为gcc .命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的

库文件

库文件有两种,静态库和动态库(共享库),区别是库文件有两种,静态库和动态库(共享库),区别是:静态库在程序的链接阶段被复制到了程序中;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加
载到内存中供程序调用。

静态库的制作

动态库的制作

工作原理

静态库: GCC 进行链接时,会把静态库中代码打包到可执行程序中
动态库: GCC 进行链接时,动态库的代码不会被打包到可执行程序中
程序启动之后,动态库会被动态加载到内存中,通过 ldd list dynamic
dependencies

程序编译成可执行文件,动态库和静态库

静态库和动态库的优缺点

Makefile文件命名和规则

介绍一下make? 为什么使用make

1、包含多个源文件的项目在编译时有长而复杂的命令行,可以通过makefile保存这些命令行来简化该工作
2、make可以减少重新编译所需要的时间,因为make可以识别出哪些文件是新修改的
3、make维护了当前项目中各文件的相关关系,从而可以在编译前检查是否可以找到所有的文件

makefile:一个文本形式的文件,其中包含一些规则告诉make编译哪些文件以及怎样编译这些文件,每条规则包含以下内容:
一个target,即最终创建的东西
一个和多个dependencies列表,通常是编译目标文件所需要的其他文件
需要执行的一系列commands,用于从指定的相关文件创建目标文件

make执行时按顺序查找名为GNUmakefile,makefile或者Makefile文件,通常,大多数人常用Makefile
Makefile规则:

target: dependency dependency [..]    command    command    [..]
注意:command前面必须是制表符

例子:

editor: editor.o screen.o keyboard.o
	gcc -o editor editor.o screen.o keyboard.o
editor.o : editor.c editor.h keyboard.h screen.h
	gcc -c editor.c
screen.o: screen.c screen.h
	gcc -c screen.c
keyboard.o : keyboard.c keyboard.h
	gcc -c keyboard.c
clean:
	rm editor *.o

工作原理

变量

模式匹配

函数

GDB调试

gcc g Wall program.c o program

启动、退出、查看代码

启动和退出:qdb可执行程序,quit退出

给程序设置参数/获取参数: set args 10 20

show args

GDB 使用帮助: help

查看当前文件代码 list

设置显示的行数: show listsize

GDB命令-调试

运行GDB程序

start(程序停在第一行)

run(遇到断点才停)

c 继续运行,到下一个断点停

n 向下执行一行代码

p 变量名

s 向下单步调试

finish 跳出函数体

break断点

自动变量操作

display 变量名(自动打印指定变量的值)

i display

undisplay 编号

其他操作

set var 变量名=变量值(循环中用的较多)

until 跳出循环

GDB多进程调试

默认只能跟踪一个进程,可以在fork函数调用之气,通过指令设置GDB调试工具跟中父进程或者子进程,默认父进程

set follow-fork-mode [parent|child]

设置调试模式: set detach-on-fork [on | off]

默认为on,表示调试当前进程的时候,其他的进程继续运行,如果为off,调试当前进程的时候,其他进程被GDB挂起

查看调试的进程: info inferiors

切换当前调试的进程: inferior id

使进程脱离GDB调试: detach inferiors id

用gdb调试多线程程序
  gdb有一组命令可辅助多线程程序的调试。

info threads

显示当前可调试的所有线程。gdb会为每个线程分配一个ID,我们可以使用这个ID来操作对应的线程。ID前面有“*”的线程是当前被调试的线程。

thread ID

调试目标ID指定的线程。

set scheduler-locking[off|on|step]

调试多线程程序时,默认除了被调试的线程在执行外,其他线程也在继续执行,但有的时候我们希望只让被调试的线程运行。这可以通过这个命令来实现。
  该命令设置sceduler-locking的值:
  off表示不锁定任何线程,即所有线程都可以继续执行,这是默认值。
  on表示只有当前被调试的线程会继续执行。
  step表示在单步执行的时候,只有当前线程会执行。

(gdb) info threads
//查看线程信息,当前被调试的是那个线程
(gdb) set scheduler-locking on
//不执行其他线程,锁定调试对象
(gdb)thread 2
//将调试切换到子线程,其ID为2

关于调试进程池或线程池程序的一个不错的方法:
   先将池中的进程个数或线程个数减少至一,以观察程序的逻辑是否正确,然后逐步增加进程或线程的数量,以调试进程或线程的同步是否正确。

Linux常用的命令:

https://blog.csdn.net/shenaisi/article/details/81488187

查看内存的命令。

free 查看当前内存的使用情况

查看进程的命令。

ps 显示瞬间进程的动态

删除文件的命令。

rm: 删除文件

杀死名字带特定字符串的进程命令。

一条命令,找出包含指定字符串的进程并杀死

ps -ef | grep jmeter | grep -v grep | cut -c 9-15 | xargs kill -s 9

vi编辑器常用的命令

:n,m w path/filename 保存指定范围文档( n表开始行,m表结束行)

:q! 对文件做过修改后,强制退出

:q 没有对文件做过修改退出

Wq或x 保存退出

dd 删除光标所在行

: set number 显示行号

:n 跳转到n行

:s 替换字符串 😒/test/test2/g /g全局替换 /也可以用%代替

/ 查找字符串

其他指令

Who:显示系统中有那些用户在使用。

​ -ami 显示当前用户

​ -u:显示使用者的动作/工作

​ -s:使用简短的格式来显示

​ -v:显示程序版本

Useradd/userdel:添加用户/删除用户

Uptime:显示系统运行了多长时间

文件IO

C 库函数

标准C库IO和linux系统IO的关系

虚拟地址空间

进程、线程、协程的区别

进程线程协程
定义资源分配和拥有的基本单位程序执行的基本单位用户态的轻量级线程,线程内部调度的基本单位
切换情况进程CPU环境(栈、寄存器、页表和文件句柄等)的保存以及新调度的进程CPU环境的设置保存和设置程序计数器、少量寄存器和栈的内容先将寄存器上下文和栈保存,等切换回来的时候再进行恢复
切换者操作系统操作系统用户
切换过程用户态->内核态->用户态用户态->内核态->用户态用户态(没有陷入内核)
调用栈内核栈内核栈用户栈
拥有资源CPU资源、内存资源、文件资源和句柄等程序计数器、寄存器、栈和状态字拥有自己的寄存器上下文和栈
并发性不同进程之间切换实现并发,各自占有CPU实现并行一个进程内部的多个线程并发执行同一时间只能执行一个协程,而其他协程处于休眠状态,适合对任务进行分时处理
系统开销切换虚拟地址空间,切换内核栈和硬件上下文,CPU高速缓存失效、页表切换,开销很大切换时只需保存和设置少量寄存器内容,因此开销很小直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快
通信方面进程间通信需要借助操作系统线程间可以直接读写进程数据段(如全局变量)来进行通信共享内存、消息队列

1、进程是资源调度的基本单位,运行一个可执行程序会创建一个或多个进程,进程就是运行起来的可执行程序

2、线程是程序执行的基本单位,是轻量级的进程。每个进程中都有唯一的主线程,且只能有一个,主线程和进程是相互依存的关系,主线程结束进程也会结束。多提一句:协程是用户态的轻量级线程,线程内部调度的基本单位

进程控制块(PCB)

为了管理进程,内核必须对每个继承所作的事情进行清楚的描述,内核为每个进程分配一个PCB进程控制快,维护进程先骨干的信息,Linux内核的进程控制块时task_struct结构体。

进程状态转换

三态模型

运行态: 进程占有处理器正在运行

就绪态: 进程具备运行条件,等待系统分配处理器以运行,在系统中处于就绪状态的进程可能有多个,通常将它们拍成一个队列,称为就绪队列。

阻塞态: 又称为wait等待态或睡眠sleep态,指进程不具备运行条件,正在等待某个事件完成。

五态模型

新建态:进程刚被创建时的状态,尚未进入就绪队列。

终止态: 进程完成任务到达正常结束点,或出现无法克服的错误而异常终止,或被操作系统及有终止权的进程所终止时所处的状态。进入终止态的进程以后不再执行,但依然保留在操作系统中等待善后。一旦其他进程完成了对终止态进程的信息抽取之后,操作系统将删除该进程。

查看进程

ps aux/ ajx

a: 显示终端上的所有进程,包括其他用户的进程

u: 显示进程的详细信息

x: 显示没有控制终端的进程

j: 列出与作业控制相关的信息

实时显示进程动态 top

可以对显示结果排序

  • M 根据内存使用量排序
  • P 根据 CPU 占有率排序
  • T 根据进程运行时间长短排序
  • U 根据用户名来筛选进程
  • K 输入指定的 PID 杀死进程

杀死进程

kill pid

kill -l 列出所有信号

kill -SIGKILL 进程ID

kill -9 进程ID

killall name 根据进程名杀死进程

进程退出、孤儿进程、僵尸进程

exit

孤儿进程

父进程运行结束,但子进程还在运行(未运行结束),这样的子进程就称为孤儿进程
Orphan Process )。

每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为 init ,而 init
进程会循环地 wait() 它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束
了其生命周期的时候, init 进程就会代表党和政府出面处理它的一切善后工作。
因此孤儿进程并不会有什么危害

僵尸进程

起因:每个进程结束之后,都会释放自己地址空间中的用户区数据,内核区的PCB没有办法自己释放,需要父进程释放。

进程终止时,父进程尚未回收,子进程残留资源(PCB)存放在内核中,编程僵尸进程。

危害性:

僵尸进程不能被 kill 9 杀死,这样就会导致一个问题,如果父进程不调用 wait()
或 waitpid() 的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,
但是系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进
程号而导致系统不能产生新的进程,此即为僵尸进程的危害,应当避免。

IO 五种IO模型:

https://blog.csdn.net/wuweiwuju___/article/details/105883115

进程间的通讯方式

重要的知识点

匿名管道

又叫无名管道

比如

统计一个目录中文件的数目命令:ls|wc -l 为了执行这个条命令,shell创建了两个进程分别执行lswc.

管道中的数据传递方向是单向的,一端用于写入,一端用于读取,管道是半双工的。

匿名管道只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用,

匿名管道的使用

查看管道缓冲区大小命令 ulimit -a

有名管道

起因:由于匿名管道没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO),也叫命名管道、FIFO文件,

它提供了一个路径名与之关联,以FIFO的文件形式存在与文件系统中。从管道中读取的顺序与写入顺序一样。

mkfifo 名字

两者的区别

  1. FIFO 在文件系统中作为一个特殊文件存在,但 FIFO 中的内容却存放在内存中。
  2. 当使用 FIFO 的进程退出后, FIFO 文件将继续保存在文件系统中以便以后使用
  3. FIFO 有名字,不相关的进程可以通过打开有名管道进行通信。

内存映射

用户通过修改内存就能修改磁盘文件。

信号

信号是时间发生时对进程的通知机制。

使用信号的两个主要的目的是:

  1. 让进程知道已经发生了一个特定的时期。
  2. 强迫进程执行它自己代码中的信号处理程序。

信号的特点:

简单

不能携带大量信息。

满足某个特定条件才发送。

信号中的几种状态: 产生、未决、递达

消息队列:

消息队列是有消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

共享内存

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与信号量,配合使用来实现进程间的同步和通信。(IPC进程间通信)

信号量

信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,实现进程、线程的对临界区的同步及互斥访问。

进程间通讯方式的小结

🔥🔥Linux下进程间通信方式?

  • 管道:

    • 无名管道(内存文件):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程之间使用。进程的亲缘关系通常是指父子进程关系。

    • 有名管道(FIFO文件,借助文件系统):有名管道也是半双工的通信方式,但是允许在没有亲缘关系的进程之间使用,管道是先进先出的通信方式。

  • 共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与信号量,配合使用来实现进程间的同步和通信。

  • 消息队列:消息队列是有消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  • 套接字:适用于不同机器间进程通信,在本地也可作为两个进程通信的方式。

  • 信号:用于通知接收进程某个事件已经发生,比如按下ctrl + C就是信号。

  • 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,实现进程、线程的对临界区的同步及互斥访问。

守护进程的创建步骤

(1)让程序在后台执行。方法是调用fork()产生一个子进程,然后使父进程退出。

(2)调用setsid()创建一个新对话期。控制终端、登录会话和进程组通常是从父进程继承下来的,守护进程要摆脱它们,不受它们的影响,方法是调用setsid()使进程成为一个会话组长。setsid()调用成功后,进程成为新的会话组长和进程组长,并与原来的登录会话、进程组和控制终端脱离。

(3)禁止进程重新打开控制终端。经过以上步骤,进程已经成为一个无终端的会话组长,但是它可以重新申请打开一个终端。为了避免这种情况发生,可以通过使进程不再是会话组长来实现。再一次通过fork()创建新的子进程,使调用fork的进程退出。

(4)关闭不再需要的文件描述符。子进程从父进程继承打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。首先获得最高文件描述符值,然后用一个循环程序,关闭0到最高文件描述符值的所有文件描述符。

(5)将当前目录更改为根目录。

(6)子进程从父进程继承的文件创建屏蔽字可能会拒绝某些许可权。为防止这一点,使用unmask(0)将屏蔽字清零。

(7)处理SIGCHLD信号。对于服务器进程,在请求到来时往往生成子进程处理请求。如果子进程等待父进程捕获状态,则子进程将成为僵尸进程(zombie),从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。这样,子进程结束时不会产生僵尸进程。

线程

经典语句

进程是 CPU 分配资源的最小单位,线程是操作系统调度执行的最小单位。

线程同步

  • 线程的主要优势在于,能够通过全局变量来共享信息。不过需要注意的是:必须确保多个线程不会同时修改同一变量,或者某一线程不会读取正在由其他线程修改的变量。
  • 临界区是指访问某一共享资源的代码片段,并且这段代码的执行应为原子操作,也就是同时访问同一共享资源的其他线程不应终端该片段的执行。
  • 线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作,而其他线程则处于等待状态。

条件变量互斥锁

互斥量:

  • 为避免线程更新共享变量时出现问题,可以使用互斥量( mutex 是 mutual exclusion的缩写)来确保同时仅有一个线程可以访问某项共享资源。可以使用互斥量来保证对任意共享资源的原子访问。
  • 互斥量有两种状态:已锁定( locked )和未锁定 unlocked )。任何时候,至多只有一个线程可以锁定该互斥量。试图对已经锁定的某一互斥量再次加锁,将可能阻塞线程或者报错失败,具体取决于加锁时使用的方法。
  • 一旦线程锁定互斥量,随即成为该互斥量的所有者,只有所有者才能给互斥量解锁。一般情况下,对每一共享资源(可能由多个相关变量组成)会使用不同的互斥量,每一线程在访问同一资源时将采用如下协议:
    1. 针对共享资源锁定互斥量
    2. 访问共享资源
    3. 对互斥量解锁

四、几种典型的锁

读写锁

  • 多个读者可以同时进行读
  • 写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
  • 写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

互斥锁

一次只能一个线程拥有互斥锁,其他线程只有等待

互斥锁是在抢锁失败的情况下主动放弃CPU进入睡眠状态直到锁的状态改变时再唤醒,而操作系统负责线程调度,为了实现锁的状态发生改变时唤醒阻塞的线程或者进程,需要把锁交给操作系统管理,所以互斥锁在加锁操作时涉及上下文的切换。互斥锁实际的效率还是可以让人接受的,加锁的时间大概100ns左右,而实际上互斥锁的一种可能的实现是先自旋一段时间,当自旋的时间超过阀值之后再将线程投入睡眠中,因此在并发运算中使用互斥锁(每次占用锁的时间很短)的效果可能不亚于使用自旋锁

条件变量

互斥锁一个明显的缺点是他只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,他常和互斥锁一起使用,以免出现竞态条件。当条件不满足时,线程往往解开相应的互斥锁并阻塞线程然后等待条件发生变化。一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。总的来说互斥锁是线程间互斥的机制,条件变量则是同步机制。

图解操作系统里面 妈妈叫孩子吃饭的例子

自旋锁

如果进线程无法取得锁,进线程不会立刻放弃CPU时间片,而是一直循环尝试获取锁,直到获取为止。如果别的线程长时期占有锁,那么自旋就是在浪费CPU做无用功,但是自旋锁一般应用于加锁时间很短的场景,这个时候效率比较高。

五、易混点

互斥锁和条件变量锁通常一起使用

条件变量

实现多个线程间的同步,当条件不满足时,相关线程一直被阻塞,直到某种条件出现,这些线程才会被唤醒

互斥锁

多个线程访问同一资源时,为了保证数据的一致性,最简单的方式就是使用 mutex(互斥锁)

🔥简单的来说:

互斥锁是线程间互斥的机制,条件变量则是同步机制。

在C++的实现中

 condition_variable cv;// 条件变量 
 mutex myMutex;// 互斥锁

举个🌰

采用三个线程交替打印ABC

知识储备

mutex 实现互斥锁

unique_lock 上独占互斥所

condition_variable 条件变量

通知

notify_one 通知一个等待的线程

notify_all 通知所有等待的线程

等待

wait 阻塞当前线程,直到条件变量被唤醒

wait_for 阻塞当前线程,直到条件变量被唤醒,或到指定时长后

wait_until 阻塞当前线程,直到条件变量被唤醒,或直到抵达指定时间点

 #include<iostream>
 #include<thread>
 #include<mutex>
 #include<condition_variable>
 using namespace std;
 condition_variable cv;// 条件变量 
 mutex myMutex;// 互斥锁 
 int flag = 0;
void printA()
	//与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。 
 	unique_lock<mutex> lk(myMutex); 
 	for(int i = 0; i < 10; ++i)
 		if(flag != 0)
 			cv.wait(lk);
		 
		flag = 1;
		cout<<"线程一打印 "<<endl;
		cout<<i<<endl;
		cv.notify_all();
	 
	 cout<<"线程一打印完毕"<<endl;
 
 
void printB()
	unique_lock<mutex> lk(myMutex);
	for(int i = 0; i< 10; ++i)
		if(flag !=1)
			cv.wait(lk);
		
		flag = 2;
		cout<<"线程二打印 "<<endl;
		cout<<i<<endl;
		cv.notify_all();
	
	cout<<"线程二打印完毕"<<endl;


void printC()
	unique_lock<mutex> lk(myMutex);
	for(int i = 0; i< 10; ++i)
		if(flag !=2)
			cv.wait(lk);
		
		flag = 0;
		cout<<"线程三打印 "<<endl;
		cout<<i<<endl;
		cv.notify_all();
	
	cout<<"线程三打印完毕"<<endl;


int main()
	thread th1(printA);
	thread th2(printB);
	thread th3(printC);
	
	th1.join();
	th2.join();
	th3.join();
	
	cout<<"main thread"<<endl; 
	return 0;

同时看到了小林的一篇好文 [C++并发编程之互斥锁和条件变量的性能比较](C++ 并发编程之互斥锁和条件变量的性能比较)

信号量

信号量其实是一个整形计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据。

信号量也可以在线程间实现互斥同步:

互斥的方式:可保证任意时刻只有一个线程访问共享资源。

同步的方式:可保证线程A应在线程B之前执行。

信号量 通常也表示资源的数量,对应的变量是一个sem型。

由两个院子操作的系统函数来控制信号量

分别是:

P操作:将sem1,相减后,如果sem<0,则进程/线程进入阻塞等待,否则继续,表明P操作可能会阻塞。

V操作:将sem1,相加后,如果sem<=0,唤醒一个等待中的线程/进程,表明V操作不会阻塞。

举个栗子🌰:

对于两个并发线程,互斥信号量的值仅取1. 0和-1三个值,分别表示:

  • 如果互斥信号量为1,表示没有线程进入临界区;
  • 如果互斥信号量为0, 表示有-个线程进入临界区;
  • 如果互斥信号量为-1,表示一个线程进入临界区,另一个线程等待进入。

六、总结

在并发编程中,如果资源数量比较少,采用条件变量和互斥量的情况比较多,值得注意的是条件变量一般用于同步问题,互斥量用于用于线程间互斥的机制。比如要保证一个资源一次只能被一个线程使用,用互斥量,而完成了使用需要执行某种顺序时候采用条件变量

生产者和消费者模型

IP地址转换函数

#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);

TCP通信流程

服务器端

// TCP 通信的流程
// 服务器端 (被动接受连接的角色)
1. 创建一个用于监听的套接字
- 监听:监听有客户端的连接
- 套接字:这个套接字其实就是一个文件描述符
2. 将这个监听文件描述符和本地的IP和端口绑定(IP和端口就是服务器的地址信息)
- 客户端连接服务器的时候使用的就是这个IP和端口
3. 设置监听,监听的fd开始工作
4. 阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个和客户端通信的套接字
(fd)
5. 通信
- 接收数据
- 发送数据
6. 通信结束,断开连接

客户端

1. 创建一个用于通信的套接字(fd)
2. 连接服务器,需要指定连接的服务器的 IP 和 端口
3. 连接成功了,客户端可以直接和服务器通信
- 接收数据
- 发送数据
4. 通信结束,断开连接

UDP通信

端口复用

端口复用最常用的用途是:

  1. 防止服务器重启时之前绑定的端口还未释放
  2. 程序突然退出而系统没有释放端口

查看网络相关信息的命令

netstat

-a 所有的socket

-p 显示正在使用socket的程序的名称

-n 直接使用IP地址,而不通过域名服务器

管道读写特点

总结:
读管道:
管道中有数据,read返回实际读到的字节数。
管道中无数据:
写端被全部关闭,read返回0(相当于读到文件的末尾)
写端没有完全关闭,read阻塞等待

写管道:
    管道读端全部被关闭,进程异常终止(进程收到SIGPIPE信号)
    管道读端没有全部关闭:
        管道已满,write阻塞
        管道没有满,write将数据写入,并返回实际写入的字节数

Linux上的五种IO模型

阻塞 blocking

非阻塞 no blocking

IO复用(IO multiplexing)

信号驱动

Linux 用套接口进行信号驱动 IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO事件就绪,进
程收到SIGIO 信号,然后处理 IO 事件。

内核在第一个阶段是异步,在第二个阶段是同步;与非阻塞IO的区别在于它提供了消息通知机制,不需
要用户进程不断的轮询检查,减少了系统API的调用次数,提高了效率。

异步

可以调用 aio_read 函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方
式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。

七、参考文献

  1. web服务器项目部分问题汇总
  2. 关于操作系统的支持图片来源
  3. 自己的代码仓库
  4. TCP/IP网络编程
  5. Linux高性能服务器编程—游双
  6. 社长的WebServer
  7. 牛客网LinuxWeb服务器 (😢这个写的比较简单,但是作为课程,过一过基础也是挺不错的。)

最后,最后
如果觉得有用,麻烦三连👍⭐️❤️支持一下呀,希望这篇文章可以帮到你,你的点赞是我持续更新的动力

mysqld 服务每天在 ec2 服务器上停止一次

【中文标题】mysqld 服务每天在 ec2 服务器上停止一次【英文标题】:mysqld service stops once a day on ec2 server 【发布时间】:2012-08-20 08:00:12 【问题描述】:

环境详情:

Server: Amazon ec2 Linux
Web Server: Apache
Web Framework: Django with mod_wsgi

以下我在 mysql_err.log 文件中找到。

The InnoDB memory heap is disabled
120823  3:21:40 InnoDB: Mutexes and rw_locks use GCC atomic builtins
120823  3:21:40 InnoDB: Compressed tables use zlib 1.2.3
120823  3:21:40 InnoDB: Using Linux native AIO
120823  3:21:41 InnoDB: Initializing buffer pool, size = 128.0M
InnoDB: mmap(137363456 bytes) failed; errno 12
120823  3:21:41 InnoDB: Completed initialization of buffer pool
120823  3:21:41 InnoDB: Fatal error: cannot allocate memory for the buffer pool
120823  3:21:41 [ERROR] Plugin 'InnoDB' init function returned error.
120823  3:21:41 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
120823  3:21:41 [ERROR] Unknown/unsupported storage engine: InnoDB
120823  3:21:41 [ERROR] Aborting

看起来系统内存不足以将内存分配给缓冲池。当我使用Amazon ec2 micro instance 时也发生了同样的错误,所以我搬到了small instance。它可以正常工作几天,但现在又一天中断一次。是否有永久解决方案?我可以移动到中等实例,但问题是它会被修复吗?我应该减小innodb_buffer_pool_size,首选尺寸是多少?

cat /proc/meminfo 的结果如下(可能会有所帮助):

MemTotal:        1697824 kB
MemFree:          125744 kB
Buffers:          109704 kB
Cached:           481408 kB
SwapCached:            0 kB
Active:          1212396 kB
Inactive:         266840 kB
Active(anon):     888192 kB
Inactive(anon):       76 kB
Active(file):     324204 kB
Inactive(file):   266764 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 4 kB
Writeback:             0 kB
AnonPages:        888144 kB
Mapped:            15604 kB
Shmem:               144 kB
Slab:              63752 kB
SReclaimable:      53680 kB
SUnreclaim:        10072 kB
KernelStack:         800 kB
PageTables:        16436 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      848912 kB
Committed_AS:    1417140 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       10988 kB
VmallocChunk:   34359725168 kB
DirectMap4k:     1748992 kB
DirectMap2M:           0 kB

操作系统版本(uname -a): Linux ip-10-246-134-149 3.2.21-1.32.6.amzn1.x86_64 #1 SMP Sat Jun 23 02:32:15 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

我检查了ps aux 命令,发现服务器只剩下 15MB 的内存,这些是当时正在运行的httpd 进程:

free -m的结果

total       used       free     shared    buffers     cached
Mem:          1657       1628         29          0          3         19
-/+ buffers/cache:       1605         51
Swap:          895        875         20

ps aux的结果

apache   21123  0.1  1.2 394652 20464 ?        S    19:35   0:06 /usr/sbin/httpd
apache   21146  0.1  1.2 394280 20796 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21152  0.1  1.2 394284 21560 ?        S    19:38   0:05 /usr/sbin/httpd
apache   21155  0.2  1.4 396244 24528 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21156  0.1  1.1 392552 20344 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21157  0.1  1.1 394284 18884 ?        S    19:38   0:05 /usr/sbin/httpd
apache   21159  0.1  1.4 396200 25040 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21161  0.1  1.2 394856 21724 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21162  0.1  1.3 394864 22400 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21163  0.1  1.3 394860 22204 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21164  0.1  1.1 392560 19204 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21165  0.1  1.3 394832 22280 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21166  0.1  1.3 395276 22932 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21172  0.2  1.4 396320 24820 ?        S    19:38   0:06 /usr/sbin/httpd
apache   21174  0.2  1.7 400672 29452 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21178  0.1  1.4 400540 25304 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21179  0.2  1.6 400580 27856 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21184  0.1  1.7 400628 29320 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21185  0.1  1.6 397944 27292 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21186  0.1  1.5 397960 25648 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21187  0.1  1.7 400576 29120 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21191  0.1  1.4 400576 24400 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21193  0.1  1.4 400536 24940 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21194  0.1  1.5 400572 26096 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21203  0.1  1.6 400580 28808 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21206  0.1  1.7 400584 29732 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21207  0.1  1.6 400576 27940 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21224  0.1  1.2 400624 20768 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21225  0.1  1.6 400576 28468 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21226  0.1  1.6 400576 28048 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21228  0.1  1.4 400572 23880 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21237  0.1  1.5 400628 26124 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21265  0.1  1.6 400536 28592 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21276  0.1  1.2 400544 21456 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21277  0.1  1.3 400624 22676 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21278  0.1  1.6 400536 27360 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21282  0.1  1.4 400612 24996 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21292  0.1  1.4 400532 24780 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21302  0.2  1.2 400540 21332 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21303  0.1  1.3 400628 22228 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21305  0.2  1.2 400536 21116 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21306  0.1  1.3 400572 22380 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21307  0.1  1.1 397956 20056 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21308  0.1  1.2 400624 21520 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21319  0.1  1.1 400540 19468 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21320  0.1  1.3 400628 22712 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21335  0.1  1.0 400540 17236 ?        S    19:39   0:05 /usr/sbin/httpd
apache   21336  0.1  1.3 400628 22188 ?        S    19:39   0:06 /usr/sbin/httpd
apache   21352  0.1  1.1 394276 18972 ?        S    19:40   0:04 /usr/sbin/httpd
apache   21356  0.1  1.1 394280 19028 ?        S    19:40   0:05 /usr/sbin/httpd
apache   21358  0.1  1.1 394280 19004 ?        S    19:40   0:05 /usr/sbin/httpd
apache   21361  0.2  0.7 400452 12632 ?        S    19:40   0:06 /usr/sbin/httpd
apache   21610  0.2  1.6 400536 27660 ?        S    19:46   0:06 /usr/sbin/httpd
apache   21643  0.2  1.3 400156 23272 ?        S    19:55   0:04 /usr/sbin/httpd
apache   21647  0.2  1.0 400544 17556 ?        S    19:57   0:05 /usr/sbin/httpd
apache   21654  0.2  1.5 400188 26884 ?        S    19:58   0:05 /usr/sbin/httpd
apache   21719  0.3  1.9 400192 32264 ?        S    20:14   0:03 /usr/sbin/httpd
apache   21725  0.2  2.0 400044 35340 ?        S    20:15   0:03 /usr/sbin/httpd
apache   21738  0.0  0.8 257648 13792 ?        S    20:26   0:00 /usr/sbin/httpd

有人知道为什么会有这么多httpd进程吗?

【问题讨论】:

看这里***.com/questions/10284532/… 我检查了我已经关闭了 895MB 的交换空间,我把它打开了,让我们看看它是否有效?? 这个崩溃现在每周发生一次,而不是交换 895Mb 是 ON。 答案对我来说不走运,我尝试了一切,最后我已经转移到 Amazon Ec2 Ubuntu 12.04 (Medium Instance),并且该网站现在可以正常运行两周了。 嗨,我在 AWS 上哪里可以找到这个文件。我每天不止一次注意到这一点。 【参考方案1】:

使用 50% 的可用 RAM 进行测试:

您可以将 innodb_buffer_pool_size 降低到非常低,看看是否有帮助:

#/etc/my.cnf 
innodb_buffer_pool_size = 1M

经验法则是将 innodb_buffer_pool_size 设置为可用 RAM 的 50% 以进行低内存测试。这意味着您启动服务器和所有除了 MySQL InnoDB。看看你有多少内存。然后将其中的 50% 用于 InnoDB。

一次尝试多个低内存设置:

http://paragasu.wordpress.com/2008/12/02/very-low-memory-mysql-5-mycnf-configuration/

更可能的罪魁祸首是该服务器上的其他任何东西,例如网络服务器。

阿帕奇?

您使用的是 Apache 和/或其他网络服务器吗?如果是这样,请尝试减少其 RAM 使用量。例如,在 Apache conf 中,考虑像这样的低 RAM 设置:

StartServers 1
MinSpareServers 1
MaxSpareServers 5
MaxClients 5

并像这样限制请求:

MaxRequestsPerChild 300

然后重新启动 Apache。

mod_wsgi:

如果您使用带有 mod_python 的 Apache,请切换到带有 mod_wsgi 的 Apache。

皮普勒:

如果它仍在发生,则可能您的 Django 正在稳步增长。尝试使用 Pymler 进行 Django 内存分析:

http://www.rkblog.rk.edu.pl/w/p/profiling-django-object-size-and-memory-usage-pympler/

特区:

您对每天一次失败和每周一次失败的报告可能指向某种每天或每周运行的 cron 作业。例如,可能有一个占用大量 RAM 的批处理,或者数据库转储等。

要跟踪 RAM 使用情况并在 MySQL 死亡前一小时查找 RAM 峰值,请查看 SAR,这是一个很棒的工具:http://www.thegeekstuff.com/2011/03/sar-examples/

【讨论】:

感谢您的回答,我会将其标记为已接受,一旦我对其进行测试,我仍然会奖励您额外的赏金。 innodb_buffer_pool_size 提示对我有用。我在 EC2 t2.micro 实例上运行了一个非常不稳定的 wordpress 实例。 Mysqlq 每隔几个小时就会崩溃一次并给出Error connecting to database 错误。我在 my.cnf 中添加了:innodb_buffer_pool_size = 200K,现在它更稳定了。【参考方案2】:

您必须减少 innodb_buffer_pool_size =

Innodb 错误解决方法:

110603  7:34:15 [ERROR] Plugin ‘InnoDB’ init function returned error.
110603  7:34:15 [ERROR] Plugin ‘InnoDB’ registration as a STORAGE ENGINE failed.
110603  7:34:15 [ERROR] Unknown/unsupported storage engine: InnoDB
110603  7:34:15 [ERROR] Aborting

10603  7:34:15 [Note] /usr/sbin/mysqld: Shutdown complete

I moved the ib_logfile0 and ib_logfile01 to bak and start Mysql again. Now this time, it is working fine

[root@xxx mysql]# mv ib_logfile0 ib_logfile0-bak
[root@xxx mysql]# mv ib_logfile1 ib_logfile1-bak

来源:http://www.onaxer.com/tag/error-plugin-innodb-init-function-returned-error/

【讨论】:

面临同样的问题。它可以正常工作一段时间,然后再次出现同样的问题。建立数据库连接时出错。我必须手动 SSH 进入服务器并启动 mysql 服务【参考方案3】:

就像其他人提到的那样,问题似乎是您的系统在 RAM 上运行不足,因此 MySQL 正在崩溃。以下是如何缩小系统内存的使用范围以及如何从数据库故障中恢复。

看看collectd 及其插件。一些适用的可能是processes plugin 和memory plugin。通过这些,您可以查看系统的内存使用情况以及哪些进程占用了大部分内存。

根据您运行 Django 的方式,您可以将工作进程配置为仅处理一定数量的请求然后终止。这样,如果您的应用程序中存在某种内存泄漏,它将不会持续超过该数量的请求。例如,如果您使用Gunicorn,则可以使用--max-requests 选项。将其设置为 500 将在处理 500 个请求后删除工作人员。

以上结合统计数据收集将向您展示一些有趣的内存使用趋势。

至于数据库宕机,你可以设置进程监控,这样如果 MySQL 死了,它会自动重新启动。最新版本的 Ubuntu 中的 MySQL 使用 Upstart 来做到这一点。如果进程终止,Upstart 将立即将其恢复。如果您使用的另一个发行版没有内置此功能,请查看Supervisor。虽然这并不能解决问题,但至少会减轻其影响。这不应被视为解决方法,而是一种让您的应用程序在出现问题时保持运行的方法。

【讨论】:

【参考方案4】:

当我遇到类似的问题时,我真的很沮丧,因为我的用户看到这条丑陋的消息建立数据库连接时出错。我发现this repo 没有解决确切的问题,对我来说就像一个魅力(暂时)。之后,我的朋友对其进行了调试,他通过一些配置更改对我的服务器进行了微调。但是我仍然每 10 分钟将此脚本添加到我的 crontab 中,然后检查服务器是否崩溃(对于我的情况,每当我在服务器上运行 VNCServer 时最终崩溃),然后重新启动它

【讨论】:

【参考方案5】:

我在此讨论中找到答案并为我工作:https://www.digitalocean.com/community/questions/mysql-server-keeps-stopping-unexpectedly?answer=26016

您必须同时将innodb_buffer_pool_sizemy.conf 中的my.conf 上的32M 相结合/etc/mysql/my.cnf,您还可能需要修改/etc/apache2/mods-enabled/mpm_prefork.conf 以减少由阿帕奇;

<IfModule mpm_prefork_module>
    StartServers     3
    MinSpareServers  3
    MaxSpareServers  5
    MaxRequestWorkers 25
    MaxConnectionsPerChild  0
</IfModule>

【讨论】:

【参考方案6】:

通过添加新的交换空间来增加可用 RAM 也可能有所帮助。步骤是here

确保您创建的 /swapfile 的大小小于显示的可用空间

df -h

例如对我来说 df- h 的输出是:

Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1      7.8G  1.2G  6.3G  16% /
none            4.0K     0  4.0K   0% /sys/fs/cgroup
udev            492M   12K  492M   1% /dev
tmpfs           100M  336K   99M   1% /run

所以我用 2 G 创建了

sudo fallocate -l 2G /swapfile

然后启动服务

sudo /etc/init.d/mysql restart

希望这会有所帮助。一切顺利。

【讨论】:

请注意,使用 fallocate 就足够了——在挂载交换文件之前,您会丢失 3-4 个命令。更多信息在这里:digitalocean.com/community/tutorials/…

以上是关于C++ LinuxWebServer 2万7千字的面经长文(下)的主要内容,如果未能解决你的问题,请参考以下文章

《绝地求生》玩家排名预测(2万5千字~大型综合实战)

C++ 之string类常用接口功能解析(7千字长文带你玩懂string!)

C++ LinuxWebServer项目数据库连接池与注册登录

3万6千字爆肝,前端进阶不得不了解的函数式编程开发,含大量实例,手写案例,所有案例均可运行

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

c++之引用(五千字长文详解!)