-
Notifications
You must be signed in to change notification settings - Fork 0
/
WebServer项目(五)-笔记.txt
253 lines (171 loc) · 9.15 KB
/
WebServer项目(五)-笔记.txt
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
WebServer项目(五)-知识补充
---------------------多进程知识点------------------------------>
fork(),
我是0,所以我是子进程
我是1(>0),所以我是父进程
进程->数据(内存中)
程序->2进制文件(磁盘中)
进程退出
exit(0) 标准c库 , 会刷新行缓存
_exit(0) linux库 ,它不会......
孤儿进程:没有爹,会被init领养,init进程会循环地wait()这个子进程
(没有危害)
僵尸进程:父未死子先死,父不对子进行回收,子变僵尸进程
(有危害,僵尸进程过多会占用cup过量资源)
(僵尸进程不能被kill -9 杀死,只能等父进程结束后,僵尸进程变成孤儿进程,让init收留)
进程回收:
父有义务对子进行回收,
当调用wait函数时,父会阻塞,等待所以子结束进行回收;
当调用waitpid函数时,父可以阻塞也可以不阻塞,还可以指定等待哪个子结束
进程间通信(IPC):
1.匿名管道: 运用案例:linux命令 ls | wc -l 管道:内存的缓存 管道是半双工的
2.有名管道: 通过一个文本进行读写,而匿名管道只是通过缓存读写
如果read返回值>0,说明读到了多少字节,如果==0,说明已读到文件末尾;
write和read一样;
还有就是:如果管道已满,write就会阻塞在这
3.内存映射
原理:将磁盘文件的数据映射到内存,用户只要修改内存,系统就可以自动修改或同步磁盘文件.
通过这个原理,我们可以很容易完成进程间的通信,为什么?怎么做?
答:你想,我们将同一块磁盘文件,分别映射到两个进程的内存中,当一个进程修改它这块被映射的内存时,这部分被修改的内存会同步到磁盘文件中,另一个进程就可以通过读取映射的磁盘文件,进而完成进程间的通信
1)mmap映射
2)munmap接触映射
4.信号
是进程间通信的最古老的方式之一,是事件发生后对进程的通知机制,也称软件中断,
信号的优先级别基本上是最高的,如果有信号来了,先放下手头的事,首先要去处理信号,
当信号处理完了,再回过头来继续中断前的事情
补充知识点:段错误(核心已转储)->表示访问了非法内存
补充知识点:定时器,案例:电脑一秒能数多少个数?
1)alarm
```cpp
#include<stdio.h>
#include<unistd.h>
int main()
{
alarm(1);//定时器,1s
int i = 0;
while(1){
printf("%d\n",i++);
}
return 0;
}
```
2)setitimer也是一种定时器,他可以做到,比如:2s发送一次信号,第一次发送信号在3s之后
5.消息队列
6.共享内存
原理:两个进程共享一段内存,一个进程往这段内存中写,另一个进程从这段内存中读
与共享内存有关的linux命令: ipcs
共享内存与内存映射的区别:共享内存是通过一段内存进行通信;内存映射是通过一个文件(如:test.txt)进行通信
7.信号量
8.Socket
------------------------------------------------------------->
-----------------------多线程知识点--------------------------->
return 0;等价于exit(0); 表示进程退出
pthread_join();主线程去回收子线程
pthread_exit(NULL);表示线程退出(让主线程退出,当主线程退出时,不会影响其他进程,
只有所有线程都退出,进程才退出)
pthread_detch(tid)线程分离--->子线程不需要主线程回收,自己就可以回收自己
父线程调用pthread_join回收某个子线程,只要子线程没结束,父线程就会一直阻塞在那,直到等到“他等的那个子线程”.
父线程调用pthread_exit(),相当于退出整个进程;也就是说:当父线程退出时,整个进程就会结束,最后面的return 0;不会调用。
线程同步:
互斥量(互斥锁)
读写锁:适合读多写少的情况
读写锁的特点:可以同时读,但是不能同时读写,也不能同时写写
-如果有其他线程度数据,则允许其他线程“读”操作,但是不允许写
-如果有其它线程写数据,则其他线程都不允许读、写操作
-写是独占的,写的优先级高
死锁
生产者消费者模型:
1)条件变量#include <condition_variable>
std::condition_variable g_cv_full; // 缓冲区满的条件变量
std::condition_variable g_cv_empty; // 缓冲区空的条件变量
比如:通知消费者线程队列非空;通知生产者线程队列不满
条件变量有一个wait方法:
eg:g_cv_empty.wait(lock, []{ return !g_queue.empty(); }); // 等待队列非空
wait()方法会先获取该互斥锁lock,然后检查条件判断函数的返回值。
如果条件不满足,则线程阻塞等待,并释放该互斥锁。
当有其他线程调用notify_one()或者notify_all()方法通知条件变量时,该线程将被唤醒并重新尝试获取互斥锁。
2)信号量(pv操作)#include <semaphore.h>
sem_t g_sem; // 信号量,控制并发的数量
------------------------------------------------------------->
-----------------------网络编程知识点------------------------>
MAC
物理网卡唯一标识
0.0.0.0是子网IP
255.255.255.255是广播地址
只有进行网络通信,才会使用到端口
ip4字节,port2字节
客户端需要知道服务器的ip和port才能进行连接
TCP,UDP在传输层;IP,ICMP在网络层,ARP和RARP在数据链路层;HTTP是应用层
《-------------------inet_pton与inet_ntop的语法----------------》
inet_pton和inet_ntop都是网络编程中的函数,它们都包含在头文件<arpa/inet.h>中。下面是它们的语法:
1. inet_pton
```cpp
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
```
参数说明:
- af:地址族,可以是AF_INET(IPv4)或者AF_INET6(IPv6)
- src:要转换的IP地址,可以是点分十进制形式的字符串,也可以是16进制、32进制表示的字符串。
- dst:转换后的二进制形式存储的位置。
返回值说明:
- 成功返回1;
- 如果该地址族不支持,则返回-1并设置errno为EAFNOSUPPORT;
- 如果src格式错误,则返回0并设置errno为EINVAL。
2. inet_ntop
```cpp
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
```
参数说明:
- af:地址族,可以是AF_INET(IPv4)或者AF_INET6(IPv6)。
- src:要转换的IP地址,可以是二进制形式的IPv4或IPv6地址。
- dst:转换后的点分十进制形式存储的位置。
- size:dst缓冲区大小。
返回值说明:
- 成功返回一个指向目标缓冲区的指针;
- 如果该地址族不支持,则返回NULL并设置errno为EAFNOSUPPORT;
- 如果size太小,则返回NULL并设置errno为ENOSPC。
《----------------------------------------------------》
《---------------------三握四挥--------------------》
C S
SYN=1
------------------------->
对发
自收
SYN=1 ACK=1
<-------------------------
自发 收
对发 收
ACK=1
------------------------->
对收
自发
《----------------------------------------------------》
《-----------------端口复用-----------------------》
主动断开的一方会有TIME_WAIT状态,在linux中一般是(30s)x2,只有多了1min以后,这个端口才会被释放掉,
但是在1min以内,端口是被占用的,如果我们想要重启这个服务器,那我们可以使用端口复用技术去复用这个端口
端口复用的代码应该放在服务器程序的 **bind() 函数调用之前** 。
在调用 bind() 函数之前,可以使用 setsockopt() 函数来设置 SO_REUSEADDR 和 SO_REUSEPORT 选项,
以便在服务器程序退出后可以立即重新启动,而无需等待一段时间,并且多个进程可以同时绑定到同一个端口上。
在bind绑定ip 端口之前,加上这两句话就行:
```cpp
int optval = 1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));//lfd监听的文件描述符
```
《-----------------IO多路复用----------------------》
IO多路复用(还是同步的):就是select,poll和epoll
epoll的两种工作模式:
默认是水平触发(LT),还有边沿触发(ET)
工作方式的区别:
水平触发(LT):当有数据到来,你这次没读完,下次接着提醒你读
边沿触发(ET):当有数据到来,你这次没读完,下次也不再提醒你了(只有当你的数据到来时,才会再次提醒你读)
根据工作方式的不同我们的做法:
水平触发(LT):有数据到来时,我们就read读一次
边沿触发(ET):有数据到来时,我们加个while(1)循环,将read放在while循环中,一次性读完
两者谁更好?
边沿触发(ET)效率更高,因为ET减少了epoll事件被重复触发的次数
------------------------------------------------------------->
《-----------------两种高效的事件处理模式----------------------》
服务器程序通常需要处理三类事件:I/O事件,信号及定时事件.
有两种两种高效的事件处理模式:Reactor和Proactor,同步I/O模型通常用于实现Reactor模式,异步I/O模型通常用于实现Proactor模式.
------------------------------------------------------------->