netstat
netstat 是一个用来查看网络状态的重要工具.
语法:netstat [选项]
功能:查看网络状态
常用选项:
•
n 拒绝显示别名,能显示数字的全部转化成数字
•
l 仅列出有在 Listen (监听) 的服務状态
•
p 显示建立相关链接的程序名
•
t (tcp)仅显示 tcp 相关选项
•
u (udp)仅显示 udp 相关选项
•
a (all)显示所有选项,默认不显示 LISTEN 相关

pidof
在查看服务器的进程 id 时非常方便.
语法:pidof [进程名]
功能:通过进程名, 查看进程 id
TCP抓包
sudo tcpdump -i any tcp
注意:-i any 指定捕获所有网络接口上的数据包,tcp 指定捕获 TCP 协议的数据
包。i 可以理解成为 interface 的意思

如果你只想捕获某个特定网络接口(如 eth0)上的 TCP 报文,可以使用以下命令:
sudo tcpdump -i eth0 tcp

使用 host 关键字可以指定源或目的 IP 地址。例如,要捕获源 IP 地址为
192.168.1.100 的 TCP 报文,可以使用以下命令:
$ sudo tcpdump src host 192.168.1.100 and tcp
同时指定源和目的 IP 地址,可以使用 and 关键字连接两个条件:
sudo tcpdump src host 192.168.1.100 and dst host 192.168.1.200
and tcp
捕获特定端口的 TCP 报文
使用 port 关键字可以指定端口号。
例如,要捕获端口号为 80 的 TCP 报文(通常是 HTTP 请求),可以使用以下命令:
sudo tcpdump port 80 and tcp
保存捕获的数据包到文件
使用 -w 选项可以将捕获的数据包保存到文件中,以便后续分析。
sudo tcpdump -i eth0 port 80 -w data.pcap
这将把捕获到的 HTTP 流量保存到名为 data.pcap 的文件中。
•
了解:pcap 后缀的文件通常与 PCAP(Packet Capture)文件格式相关,这是一
种用于捕获网络数据包的文件格式
从文件中读取数据包进行分析
使用 -r 选项可以从文件中读取数据包进行分析
tcpdump -r data.pcap
这将读取 data.pcap 文件中的数据包并进行分析。
注意事项
• 使用 tcpdump 时,请确保你有足够的权限来捕获网络接口上的数据包。通常,你
需要以 root 用户身份运行 tcpdump。
• 使用 tcpdump 的时候,有些主机名会被云服务器解释成为随机的主机名,如果不
想要,就用-n 选项
• 主机观察三次握手的第三次握手,不占序号
自旋锁
1. 概述
自旋锁是一种多线程同步机制,用于保护共享资源。线程获取锁失败时,会 ** 循环自旋(不断检查锁状态)** 而非进入休眠,以此减少线程切换开销,适用于短时间锁竞争;但不合理使用会造成 CPU 资源浪费。
2. 原理
- 用共享标志位表示锁状态:true表示锁被占用,false表示锁可用。
- 线程尝试获取锁时:
- 若标志位为false,则将其设为true,占用锁并进入临界区。
- 若标志位为true,则在循环中持续自旋等待,直到锁被释放。
3. 优缺点
- 优点:
- 低延迟:避免线程休眠与唤醒,提升锁操作效率。
- 减少系统调度开销:等待线程不阻塞,无需上下文切换。
- 缺点:
- CPU 资源浪费:锁持有时间较长时,自旋线程会持续占用 CPU。
- 可能引发活锁:多线程同时自旋且无退避策略时,可能都无法获取锁。
4. 使用场景
- 锁占用时间极短的场景(如多线程对共享数据进行简单读写)。
- 系统底层,用于同步多个 CPU 对共享资源的访问。
5. 实现原理
依赖原子操作(如 CAS 指令)保证锁状态修改的原子性,避免并发冲突
注意事项
结论
自旋锁是一种适用于短时间锁竞争的同步机制,它通过避免线程休眠和上下文切换,显著降低了锁操作的开销,提升了效率。但它也存在明显缺点:长时间持有锁会导致 CPU 资源浪费,且在无退避策略的
1. pthread_spin_init() —— 初始化自旋锁
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
| 功能 | 初始化一个自旋锁,必须在使用前调用 |
| 参数 1 lock | 输入输出参数:指向pthread_spinlock_t变量的指针,用于存储初始化后的锁状态 |
| 参数 2 pshared | 输入参数:锁的共享范围- PTHREAD_PROCESS_PRIVATE:仅当前进程内线程共享(最常用)- PTHREAD_PROCESS_SHARED:可跨进程共享(需配合共享内存) |
| 返回值 | 成功返回0;失败返回非 0 错误码(如EINVAL、ENOMEM) |
| 注意 | 未初始化的锁不能使用;同一锁不能重复初始化 |
2. pthread_spin_destroy() —— 销毁自旋锁
int pthread_spin_destroy(pthread_spinlock_t *lock);
| 功能 | 销毁已初始化的自旋锁,释放资源 |
| 参数 lock | 输入输出参数:指向要销毁的pthread_spinlock_t变量的指针 |
| 返回值 | 成功返回0;失败返回非 0 错误码(如EINVAL表示锁无效) |
| 注意 | 不能销毁正在被其他线程持有的锁;销毁后不能再使用该锁,除非重新初始化 |
3. pthread_spin_lock() —— 阻塞式加锁
int pthread_spin_lock(pthread_spinlock_t *lock);
| 功能 | 阻塞式获取锁:获取不到则持续自旋等待,直到锁可用 |
| 参数 lock | 输入输出参数:指向要获取的自旋锁指针 |
| 返回值 | 成功返回0;失败返回非 0 错误码(如EDEADLK检测到死锁) |
| 注意 | 自旋锁不支持重入,同一线程多次加锁会导致死锁;持有锁的时间必须极短 |
4. pthread_spin_trylock() —— 非阻塞式尝试加锁
int pthread_spin_trylock(pthread_spinlock_t *lock);
| 功能 | 非阻塞式尝试加锁:获取不到则立即返回,不会自旋等待 |
| 参数 lock | 输入输出参数:指向要尝试获取的自旋锁指针 |
| 返回值 | – 成功获取锁:返回0- 锁被占用:返回EBUSY- 其他错误:返回非 0 错误码 |
| 注意 | 适合 “不想等待,失败则做其他事” 的场景;同样不支持重入 |
5. pthread_spin_unlock() —— 解锁
int pthread_spin_unlock(pthread_spinlock_t *lock);
| 功能 | 释放自旋锁,必须由持有锁的线程调用 |
| 参数 lock | 输入输出参数:指向要释放的自旋锁指针 |
| 返回值 | 成功返回0;失败返回非 0 错误码(如EPERM表示调用线程不持有该锁) |
| 注意 | 未持有锁的线程调用解锁,会导致未定义行为(如程序崩溃) |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 20;
// 1. 取消自旋锁变量注释
pthread_spinlock_t lock;
void *route(void *arg)
{
char *id = (char *)arg;
while (1)
{
// 2. 加锁:获取自旋锁,保护临界区
pthread_spin_lock(&lock);
if (ticket > 0)
{
usleep(1000); // 模拟售票耗时(短时间,符合自旋锁使用原则)
printf("%s sells ticket:%d\\n", id, ticket);
ticket–;
// 3. 解锁:释放自旋锁
pthread_spin_unlock(&lock);
}
else
{
// 4. 无票时也要解锁,避免死锁
pthread_spin_unlock(&lock);
break;
}
}
// 5. C语言中用NULL而非nullptr
return NULL;
}
int main(void)
{
// 6. 初始化自旋锁
pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE);
pthread_t t1, t2, t3;
// 创建3个售票线程
pthread_create(&t1, NULL, route, (void *)"thread 1");
pthread_create(&t2, NULL, route, (void *)"thread 2");
pthread_create(&t3, NULL, route, (void *)"thread 3");
// 7. 等待所有子线程执行完成(核心修复点)
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
// 8. 销毁自旋锁,释放资源
pthread_spin_destroy(&lock);
return 0;
}

读写锁

在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会
比较少。相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴
随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效
率。那么有没有一种方法,可以专门处理这种多读少写的情况呢?
有,那就是读写锁。
1.pthread_rwlockattr_setkind_np() —— 设置读写锁优先级
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
-
项说明 功能 设置读写锁的优先级策略(读者优先 / 写者优先),属于非标准扩展(_np 表示 non-portable)。 参数 attr 输入参数:指向 pthread_rwlockattr_t 类型的属性对象指针,用于存储优先级设置。 参数 pref 输入参数:优先级选项,共 3 种:- PTHREAD_RWLOCK_PREFER_READER_NP:默认,读者优先,可能导致写者饥饿- PTHREAD_RWLOCK_PREFER_WRITER_NP:写者优先(存在 BUG,实际表现与读者优先一致)- PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP:写者优先,且写者不能递归加锁 返回值 成功返回 0;失败返回非 0 错误码(如 EINVAL 表示参数无效)。 注意 该接口为非标准扩展,不同系统实现可能存在差异;优先级策略会影响读写锁的竞争行为,需谨慎选择。
2. pthread_rwlock_init() —— 初始化读写锁
c
运行
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
| 功能 | 初始化读写锁,必须在使用前调用。 |
| 参数 rwlock | 输入输出参数:指向 pthread_rwlock_t 类型的读写锁指针,用于存储初始化后的锁状态。 |
| 参数 attr | 输入参数:指向读写锁属性对象的指针;若为 NULL,则使用默认属性(读者优先)。 |
| 返回值 | 成功返回 0;失败返回非 0 错误码(如 ENOMEM 表示内存不足)。 |
| 注意 | 未初始化的读写锁不能使用;同一锁不能重复初始化。 |
3. pthread_rwlock_destroy() —— 销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
| 功能 | 销毁已初始化的读写锁,释放系统资源。 |
| 参数 rwlock | 输入输出参数:指向要销毁的 pthread_rwlock_t 类型的读写锁指针。 |
| 返回值 | 成功返回 0;失败返回非 0 错误码(如 EBUSY 表示锁仍被线程持有)。 |
| 注意 | 不能销毁正在被其他线程持有的锁;销毁后不能再使用该锁,除非重新初始化。 |
4. pthread_rwlock_rdlock() —— 获取读锁(共享锁)
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
| 功能 | 获取读锁(共享锁),多个线程可同时持有读锁,写锁会被阻塞。 |
| 参数 rwlock | 输入输出参数:指向要获取读锁的 pthread_rwlock_t 类型的读写锁指针。 |
| 返回值 | 成功返回 0;失败返回非 0 错误码(如 EDEADLK 检测到死锁)。 |
| 注意 | 读锁是共享的,适合读多写少的场景;持有读锁时不能再获取写锁,否则会导致死锁。 |
5. pthread_rwlock_wrlock() —— 获取写锁(独占锁)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
| 功能 | 获取写锁(独占锁),写锁持有期间,所有读锁和写锁请求都会被阻塞。 |
| 参数 rwlock | 输入输出参数:指向要获取写锁的 pthread_rwlock_t 类型的读写锁指针。 |
| 返回值 | 成功返回 0;失败返回非 0 错误码(如 EDEADLK 检测到死锁)。 |
| 注意 | 写锁是独占的,持有写锁时不能再获取读锁或写锁,否则会导致死锁;写锁持有时间应尽量短。 |
6. pthread_rwlock_unlock() —— 释放读写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
| 功能 | 释放读锁或写锁,必须由持有锁的线程调用。 |
| 参数 rwlock | 输入输出参数:指向要解锁的 pthread_rwlock_t 类型的读写锁指针。 |
| 返回值 | 成功返回 0;失败返回非 0 错误码(如 EPERM 表示调用线程不持有该锁)。 |
| 注意 | 未持有锁的线程调用解锁会导致未定义行为(如程序崩溃);解锁后其他线程可竞争获取锁。 |
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <cstdlib>
#include <ctime>
// 共享资源
int shared_data = 0;
// 读写锁
pthread_rwlock_t rwlock;
// 读者线程函数
void *Reader(void *arg)
{
//sleep(1); //读者优先,一旦读者进入&&读者很多,写者基本就很难进入了
int number = *(int *)arg;
while (true)
{
pthread_rwlock_rdlock(&rwlock); // 读者加锁
std::cout << "读者-" << number << " 正在读取数据,数据是: " << shared_data << std::endl;
sleep(1); // 模拟读取操作
pthread_rwlock_unlock(&rwlock); // 解锁
}
delete (int*)arg;
return NULL;
}
// 写者线程函数
void *Writer(void *arg)
{
int number = *(int *)arg;
while (true)
{
pthread_rwlock_wrlock(&rwlock); // 写者加锁
shared_data = rand() % 100; // 修改共享数据
std::cout << "写者- " << number << " 正在写入。新的数据是: " << shared_data << std::endl;
sleep(2); // 模拟写入操作
pthread_rwlock_unlock(&rwlock); // 解锁
}
delete (int*)arg;
return NULL;
}
int main()
{
srand(time(nullptr)^getpid());
pthread_rwlock_init(&rwlock, NULL); // 初始化读写锁
// 可以更高读写数量配比,观察现象
const int reader_num = 2;
const int writer_num = 2;
const int total = reader_num + writer_num;
pthread_t threads[total]; // 假设读者和写者数量相等
// 创建读者线程
for (int i = 0; i < reader_num; ++i)
{
int *id = new int(i);
pthread_create(&threads[i], NULL, Reader, id);
}
// 创建写者线程
for (int i = reader_num; i < total; ++i)
{
int *id = new int(i – reader_num);
pthread_create(&threads[i], NULL, Writer, id);
}
// 等待所有线程完成
for (int i = 0; i < total; ++i)
{
pthread_join(threads[i], NULL);
}
pthread_rwlock_destroy(&rwlock); // 销毁读写锁
return 0;
}

读者优先(Reader-Preference)
在这种策略中,系统会尽可能多地允许多个读者同时访问资源(比如共享文件或数
据),而不会优先考虑写者。这意味着当有读者正在读取时,新到达的读者会立即被
允许进入读取区,而写者则会被阻塞,直到所有读者都离开读取区。读者优先策略可
能会导致写者饥饿(即写者长时间无法获得写入权限),特别是当读者频繁到达时。
写者优先(Writer-Preference)
在这种策略中,系统会优先考虑写者。当写者请求写入权限时,系统会尽快地让写者
进入写入区,即使此时有读者正在读取。这通常意味着一旦有写者到达,所有后续的
读者都会被阻塞,直到写者完成写入并离开写入区。写者优先策略可以减少写者等待
的时间,但可能会导致读者饥饿(即读者长时间无法获得读取权限),特别是当写者
频繁到达时。
网硕互联帮助中心


评论前必须登录!
注册