第三章
pstack的使用
将C++类对象实例指针作为线程函数的参数
bind函数
auto newCallable = bind ( callable, arg_list) ;
我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。
假设我们将线程的基本功能都封装到一个Theard类中
class Thread {
void * threadFunc ( void * arg) ;
} ;
threadFunc作为类的非静态方法,被编译后第一个参数会变成this指针 不符合线程函数的签名要求了
如果使用std::thread类,可以使用非静态方法作为线程函数。但必须还要传入this指针 也可以使用bind
以下是线程类的示例
class Thread {
public :
Thread ( ) { } ;
~ Thread ( ) { } ;
void Start ( ) {
m_stop = false ;
m_spthread. reset ( new std:: thread ( std:: bind ( & Thread:: ThreadFunc, this , 8888 ) ) ) ;
}
void Stop ( ) {
m_stop = true ;
}
private :
void ThreadFunc ( int a) {
while ( ! m_stop) {
sleep ( 1 ) ;
printf ( "d\n" ) ;
}
}
private :
std:: shared_ptr< std:: thread> m_spthread;
bool m_stop;
} ;
std:: atomic_int value = 99 ;
互斥体
锁类型: 普通锁 对已加锁的对象再次加锁 会阻塞 (try_lock不会) 检错锁 一个线程对互斥体对象再次加锁,返回EDEADLK ,若是一个线程对某个互斥体加锁,另一个线程再次对其加锁,会阻塞。 可重入锁
条件变量
为什么需要条件变量?
如果我们需要判断一个多线程的共享条件是否满足,每次判断都要加锁和解锁,我们需要这样一种机制: 在条件不满足的情况下主动让出互斥体,一旦条件满足,可以被立刻唤醒。
为什么条件变量一定要和互斥体共享使用?
1 : pthread_mutex_lock ( & mutex) ;
2 : while ( false == ready) {
3 : pthread_cond_wait ( & cond, & mutex) ;
4 : }
5 : pthread_mutex_unlock ( & mutex) ;
1 : ready = true ;
2 : pthread_cond_signal ( & cond) ;
如果在 ready = false 的时候,Thread A 进入 while 循环,但是还没有执行 wait 的时候,thread B 执行了 ready = true 和 signal 唤醒,那么就出现条件变量唤醒 signal 先于 wait,那么相当于 Thread A 还没有被加入唤醒队列,这个时候,你已经 signal 唤醒了,那么这次唤醒自然就丢失了
问题就在于要确保检测条件和将线程挂入等待队列这两个操作是原子性的。
条件变量的虚假唤醒:
while ( tasks. empty ( ) ) {
pthread_cond_wait ( & mycv, & mymutex) ;
}
为什么要在条件变量醒来之后再次判断条件是否满足? 因为操作系统可能在某些情况下唤醒条件变量,这被称为虚假唤醒 底层原因: 当系统调用被信号中断时,会返回 - 1,并把errno设置为EINTR,很多这种系统调用被中断后都会再调用一次这个函数,但是cond_wait不能这么做,因为再调用的过程中可能错失信号,所以宁可虚假唤醒,也不能再次调用,以免陷入无穷等待中。
读写锁
第四章 网络编程
TCP通信基本流程
建立连接
TCP客户端
socket()
connect()
send()
recv()
close()
TCP服务端
socket()
bind()
listen()
accept()
recv()
send()
recv()
close()
bind函数
服务端bind函数如何选择绑定地址?
bindaddr. sin_addr. s_addr = htonl ( INADDR_ANY) ; 相当于0.0 .0 .0
如果我们不关心绑定的IP地址,底层协议栈会自动选择一个合适的IP地址。
客户端也可以使用bind函数选择绑定到某一个端口(0和不绑定的效果是一样的)
select函数
fd_set结构类型是一个 1024bit的数组。
long int __fds_bits[ 16 ] ;
使用位图法来表示状态,因此select支持的最大fd就是1024;
select使用注意事项:
select在调用前后可能会修改readfds,writefds,exceptfds这三个集合中的内容。所以每次重新调用select的时候需要清空set再添加。 超时时间也需要重新设置。 超时时间若被设置为0,select将立刻返回。设置为NULL,将一直阻塞,直到事件触发。 在Linux上,select必须设置为需要检测fd的最大值+1; select的缺点如下
每次调用都需要把fd集合从用户态复制到内核态 单个进程能够监视的文件描述符存在最大限制 需要重新设定集合参数 linux上select底层原理是使用了poll函数
socket阻塞和非阻塞模式
阻塞:条件不满足时阻塞当时线程 非阻塞:条件不满足时立即返回 执行程序流
send 与 recv
send函数在本质上并不是向网络上发送数据,而是将应用层发送缓冲区的数据拷贝到内核缓冲区中,至于数据什么时候会从网卡缓冲区中真正地发送到网络中,要根据TCP/IP协议栈的行为来定,如果socket禁用了nagel算法,存放到内核缓冲区中的数据就会被立刻发出去,否则会将多个小的数据包凑成一个足够大的才发出去。 recv函数是将内核缓冲区中的数据拷贝到应用程序的缓冲区中,拷贝完成后会将内核缓冲区中的数据移除。
第五章 常见网络调试命令
ifconfig
ping
telnet 交互式访问
netstat 查看网络连接
查看连接某ip地址端口最多的ip地址
netstat -nat | grep "10.206.0.13:22" | awk '{print $5 }' | awk -F: '{print $1 }' | sort | uniq -c | sort -nr | head -3
lsof 列出当前系统打开文件
查询1883端口状态
sudo lsof -i | grep 1883
可以用来恢复一些文件
nc netcat
curl 模拟HTTP请求
指定请求方法
curl -x POST -d 'data' 'www.baidu.com'
tcpdump
网络通信协议设计
TCP是流式协议,内容与内容之间没有明确的分界标志,需要人为给这些内容划分边界。
如何解决粘包问题?
TCP通过序列号和包重传机制保证数据包的有序和被正确发送到目的地。 只需要解决如何粘包的问题。 解决方法: 固定包长 以指定的字符串结尾的结束标志 包头或者是包体
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)