Linux 互斥锁 递归锁 自旋锁 读写锁

2023-11-06

在多线程中,我们经常会要用到锁,那么,锁是什么,我们为什么要用到锁?回到问题的本质,我们在什么场景下会用到锁?锁是针对程序中的临界资源,也就是公共资源的,当我们有两个或多个线程同时对一个临界资源操作的时候,为了保证共享数据操作的完整性,我们要为这些公共资源加锁。

在Linux中常见的锁主要有互斥锁、自旋锁、读写锁,至于递归锁则是互斥锁的一个特例。

 


互斥锁(mutexlock)

在讲什么是互斥锁之前,我们先来看一下下面这段代码:

#includ <stdio.h>
#includ <pthread.h>

define THREAD_NUM 10

void *thread_proc(void *arg)
{
        int *pcount = (int*)arg;
    
        int i = 0;
        while(i ++ < 100000) {
                (*pcount) ++;
                usleep(1);
        }
}

int main()
{
        pthread_t thread_id[THREAD_NUM] = {0};

        int count = 0;
​
        int i = 0;
        for(i = 0; i < THREAD_NUM; i ++)
        {
                // 创建10个线程,每个线程对count实行自加到10万,count为这10个线程的一个共享资源
                pthread_create(&thread_id[i], NULL, thread_proc, &count);
        }

        // 每隔一秒打印一次count的值
        for(i = 0; i < 100; i ++) {
                printf("count --> %d\n", count);
                sleep(1);
        }

        return 0;
}

他的执行结果为下面这样: 

执行这段代码,我们本意是想最后打印出来的count值能到100万,但是实际上,最后我们打印出来的count值只会有99万多,那这是为什么呢?这就是由于多线程对同一个临界资源进行操作,我们代码是只有一行idx++,但是在这行代码翻译成汇编代码的时候就变成了这样:

 我们的一行idx++,翻译成汇编代码变成了3行。理想状态下,我们希望者行汇编代码能像下面这样顺序执行:

但是实际时,由于多线程的并发操作,使得部分时候执行的顺序变成了这样:

这就导致了我们上面的代码会出现最后的打印只有99万多的结果。为了确保不发生这种情况,我们就需要在对临界资源操作时加上锁。

Linux中的互斥锁是我们最常使用于线程同步的锁,标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁,通常情况下锁操作失败会将该线程睡眠等待锁释放时被唤醒。那么我们怎么使用互斥锁呢?pthread.h头文件中就提供了互斥锁的使用。

  • int pthread_mutex_init( pthread_mutex_t* mutex, const pthread_mutexattr_t* mutexattr );用于初始化互斥锁。mutexattr参数指定互斥锁的属性。如果将它设置为NULL,则表示默认属性。
  • int pthread_mutex_destroy( pthread_mutex_t* mutex);该函数用于销毁互斥锁,以释放其占用的内核资源。销毁一个已经枷锁的互斥锁将导致不可预期的后果。
  • int pthread_mutex_lock( pthread_mutex_t* mutex);该函数以原子操作的方式给一个互斥锁加锁。如果目标互斥锁已经被锁上,则pthread_mutex_lock调用将阻塞,直到该互斥锁的占有者将其解锁。
  • int pthread_mutex_unlock( pthread_mutex_t* mutex);该函数以原子操作的方式给一个互斥锁解锁。如果此时有其他线程正在等待这个互斥锁,则这些线程中的某一个将获得它。
  • int pthread_mutex_trylock( pthread_mutex_t* mutex);函数pthread_mutex_trylock和pthread_mutex_lock类似,不过它始终立即返回,而不论被操作的互斥锁是否已经被枷锁。相当于pthread_mutex_lock的非阻塞版本。当目标互斥锁未被加锁时,pthread_mutex_trylock对互斥锁执行加锁操作。当互斥锁已经被加锁时,pthread_mutex_trylock将返回错误码EBUSY。当然,pthread_mutex_trylock和pthread_mutex_lock的行为是针对普通锁而言的。

上面这些函数成功时返回0,失败则返回错误码。我们来在之前的代码中加入锁,再打印下结果:

#includ <stdio.h>
#includ <pthread.h>

define THREAD_NUM 10

pthread_mutex_t mutex;

void *thread_proc(void *arg)
{
        int *pcount = (int*)arg;
    
        int i = 0;
        while(i ++ < 100000) {
                pthread_mutex_lock(&mutex);          // 加锁
                (*pcount) ++;
                pthread_mutex_unlock(&mutex);        // 解锁
                usleep(1);
        }
}

int main()
{
        pthread_t thread_id[THREAD_NUM] = {0};

        int count = 0;

        // 初始化互斥锁
        pthread_mutex_init(&mutex, NULL);
​
        int i = 0;
        for(i = 0; i < THREAD_NUM; i ++)
        {
                // 创建10个线程,每个线程对count实行自加到10万,count为这10个线程的一个共享资源
                pthread_create(&thread_id[i], NULL, thread_proc, &count);
        }

        // 每隔一秒打印一次count的值
        for(i = 0; i < 100; i ++) {
                printf("count --> %d\n", count);
                sleep(1);
        }

        return 0;
}

这里可以看到,可以达到我们想要的结果100万。

 


递归锁(recursivelock)

严格上讲递归锁只是互斥锁的一个特例,同样只能有一个线程访问该对象,但递归锁允许同一个线程在未释放其拥有的锁时反复对该锁进行加锁操作。windows下的临界区默认是支持递归锁的,而linux下的互斥量则需要设置参数PTHREAD_MUTEX_RECURSIVE,默认则是不支持的。我们先来看下下面的代码:

#include <stdio.h>
#include <pthread.h>
​
int count = 0;
​
pthread_mutex_t mutex;
​
void* thread_proc(void*)
{
        int i = 0;
        for (i=0; i<5000; i++)
        {
                pthread_mutex_lock(&mutex);
                pthread_mutex_lock(&mutex);
​
                count ++;
                printf("count = %d\n", count);
​
                pthread_mutex_unlock(&mutex);
                pthread_mutex_unlock(&mutex);      
        }
}
​
int main()
{
        pthread_t tid1, tid2;

        pthread_mutex_init(&mutex, NULL);
​
        pthread_create(&tid1, NULL, thread_proc, NULL);
        pthread_create(&tid2, NULL, thread_proc, NULL);
​
        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
​
        return 0;
}

这里,我们在互斥锁lock后又调用了一次lock,这时,程序就会死锁,不输出任何信息。

但是,如果我们这里使用的是递归锁的话,就不会有死锁的问题。

#include <stdio.h>
#include <pthread.h>
​
int count = 0;
​
pthread_mutex_t mutex;
​
void* thread_proc(void*)
{
        int i = 0;
        for (i=0; i<5000; i++)
        {
                pthread_mutex_lock(&mutex);
                pthread_mutex_lock(&mutex);
​
                count ++;
                printf("count = %d\n", count);
​
                pthread_mutex_unlock(&mutex);
                pthread_mutex_unlock(&mutex);      
        }
}
​
int main()
{
        pthread_t tid1, tid2;

        // 需要先定义一个pthread_mutexattr_t变量,用于设置锁的属性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
​
        //设置锁的属性
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&mutex, &attr);
​
        pthread_create(&tid1, NULL, thread_proc, NULL);
        pthread_create(&tid2, NULL, thread_proc, NULL);
​
        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
​
        return 0;
}

使用递归锁,结果就可以正确的输出1~10000。

 


自旋锁(spinlock)

同样用来标记只能有一个线程访问该对象,在同一线程多次加锁操作会造成死锁。使用硬件提供的swap指令或test_and_set指令实现,同互斥锁不同的是在锁操作需要等待的时候并不是睡眠等待唤醒,而是循环检测保持者已经释放了锁。这样做的好处是节省了线程从睡眠状态到唤醒之间内核会产生的消耗,在加锁时间短暂的环境下这点会提高很大效率。

自旋锁的实现是为了保护一段短小的临界区操作代码,主要是用于在SMP上保护临界区,保证这个临界区的操作是原子的,从而避免并发的竞争冒险。在Linux内核中,自旋锁通常用于包含内核数据结构的操作,你可以看到在许多内核数据结构中都嵌入有spinlock,这些大部分就是用于保证它自身被操作的原子性,在操作这样的结构体时都经历这样的过程:上锁-操作-解锁。如果内核控制路径发现自旋锁“开着”(可以获取),就获取锁并继续自己的执行。相反,如果内核控制路径发现锁由运行在另一个CPU上的内核控制路径“锁着”,就在原地“旋转”,反复执行一条紧凑的循环检测指令,直到锁被释放。 自旋锁是循环检测“忙等”,即等待时内核无事可做(除了浪费时间),进程在CPU上保持运行,所以它保护的临界区必须小,且操作过程必须短。不过,自旋锁通常非常方便,因为很多内核资源只锁1毫秒的时间片段,所以等待自旋锁的释放不会消耗太多CPU的时间。

自旋锁的初始化有两种方式:

  • pthread_spin_lock_t lock = SPIN_LOCK_UNLOCKED;自旋锁的宏常量初始化。
  • pthread_spinlock_init(spin_lock_t* lock)函数初始化。lock是读写锁的spin_lock_t结构指针。

自旋锁的加锁和解锁:

  • int pthread_spinlock_lock(pthread_spinlock_t* lock);获取一个自旋锁。如果该自旋锁当前没有被其它线程所持有,则调用该函数的线程获得该自旋锁.否则该函数在获得自旋锁之前不会返回。如果调用该函数的线程在调用该函数时已经持有了该自旋锁,则结果是不确定的。
  • int pthread_spinlock_trylock(pthread_spinlock_t* lock);尝试获取一个自旋锁。如果无法获取则理解返回失败。
  • int pthread_spinlock_unlock(pthread_spinlock_t* lock);用于释放自旋锁。

在使用方法上,自旋锁和互斥锁差不多,这里还用上面互斥锁的那个例子:

#includ <stdio.h>
#includ <pthread.h>

define THREAD_NUM 10

pthread_spinlock_t spinlock;

void *thread_proc(void *arg)
{
        int *pcount = (int*)arg;
    
        int i = 0;
        while(i ++ < 100000) {
                pthread_spin_lock(&spinlock);          // 加锁
                (*pcount) ++;
                pthread_spin_unlock(&spinlock);        // 解锁
                usleep(1);
        }
}

int main()
{
        pthread_t thread_id[THREAD_NUM] = {0};

        int count = 0;

        // 初始化自旋锁
        pthread_spin_init(&spinlock, PTHREAD_PROCESS_SHARED);
​
        int i = 0;
        for(i = 0; i < THREAD_NUM; i ++)
        {
                // 创建10个线程,每个线程对count实行自加到10万,count为这10个线程的一个共享资源
                pthread_create(&thread_id[i], NULL, thread_proc, &count);
        }

        // 每隔一秒打印一次count的值
        for(i = 0; i < 100; i ++) {
                printf("count --> %d\n", count);
                sleep(1);
        }

        return 0;
}

互斥锁和自旋锁的应用:

互斥锁用于临界区持锁时间比较长的操作,比如下面这些情况都可以考虑

  • 临界区有IO操作
  • 临界区代码复杂或者循环量大
  • 临界区竞争非常激烈
  • 单核处理器

至于自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下,自旋锁一般用于多核的服务器。

 


读写锁(rwlock)

高级别锁,区分读和写,符合条件时允许多个线程访问对象。处于读锁操作时可以允许其他线程和本线程的读锁, 但不允许写锁, 处于写锁时则任何锁操作都会睡眠等待。常见的操作系统会在写锁等待时屏蔽后续的读锁操作以防写锁被无限孤立而等待,在操作系统不支持情况下可以用引用计数加写优先等待来用互斥锁实现。 读写锁适用于大量读少量写的环境,但由于其特殊的逻辑使得其效率相对普通的互斥锁和自旋锁要慢一个数量级。值得注意的一点是按POSIX标准在线程申请读锁并未释放前本线程申请写锁是成功的,但运行后的逻辑结果是无法预测。

读写锁中的读操作可以共享,写操作是排它的,读可以有多个在读,写只有唯一个在写,写的时候不允许读操作。对于读数据较修改数据频繁的应用,用读写锁代替互斥锁可以提高效率。因为使用互斥锁时,即使是读出数据(相当于操作临界区资源)都需要上互斥锁;而采用读写锁则允许在任一时刻多个读出。

读写锁的初始化有两种方式:

  • pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;宏常量初始化。
  • pthread_rwlock_init(pthread_rwlock_t*, pthread_rwattr_t*);函数初始化。rwlock是读写锁的pthread_rwlock_t结构指针,attr是读写锁的属性结构指针,不需要别的属性默认为NULL。

读写锁的加锁和解锁:

  • int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);该函数为读写锁的读锁
  • int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);该函数为读写锁的写放
  • pthread_rwlock_unlock(pthread_rwlock_t*);该函数为读写锁的释放。
  • int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);该函数用于销毁读写锁。

获取读写锁的读操作有两种方式:

  • 阻塞式:pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
  • 非阻塞式:pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);如果获取不到锁,会立即返回错误EBUSY。

如果对应的读写锁被其它写者持有,或者读写锁被读者持有,该线程都会阻塞等待。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 互斥锁 递归锁 自旋锁 读写锁 的相关文章

  • 如何使用MemoryCache代替Timer来触发一个方法?

    以下方法通过等待已运行操作的结果来处理并发请求 对数据的请求可能会使用相同 不同的凭据同时出现 对于每组唯一的凭据 最多可以有一个GetCurrentInternal呼叫正在进行中 当准备就绪时 该呼叫的结果将返回给所有排队的服务员 pri
  • 使用Physics.Raycast 和Physics2D.Raycast 检测对象上的点击

    我的场景中有一个空的游戏对象 带有 2D 组件盒碰撞器 我将脚本附加到该游戏对象 void OnMouseDown Debug Log clic 但是当我点击我的游戏对象时 没有任何效果 你有什么想法 如何检测我的盒子碰撞器上的点击 使用光
  • Unix网络编程澄清

    我正在翻阅这本经典书籍Unix网络编程 https rads stackoverflow com amzn click com 0139498761 当我偶然发现这个程序时 第 6 8 节 第 179 180 页 include unp h
  • 为 Visual Studio 2013 编译 Tesseract

    我正在尝试使用tesseract在 Visual Studio 2013 中 我在链接器 gt 输入 不是 libtesseract302 static lib 中使用 libtesseract302 lib 一切都正常 并且已编译并运行
  • 如何在 C# 中从 UNIX 纪元时间转换并考虑夏令时?

    我有一个从 unix 纪元时间转换为 NET DateTime 值的函数 public static DateTime FromUnixEpochTime double unixTime DateTime d new DateTime 19
  • 如何从 .resx 文件条目获取注释

    资源文件中的字符串有名称 值和注释 The ResXResourceReader类让我可以访问名称和值 有办法看评论吗 你应该能够得到Comment via ResXDataNode class http msdn microsoft co
  • 将 System.Windows.Input.KeyEventArgs 键转换为 char

    我需要将事件参数作为char 但是当我尝试转换 Key 枚举时 我得到的字母和符号与传入的字母和符号完全不同 如何正确地将密钥转换为字符 这是我尝试过的 ObserveKeyStroke this new ObervableKeyStrok
  • 存储来自其他程序的事件

    我想将其他应用程序的事件存储在我自己的应用程序中 事件示例 打开 最小化 Word 或打开文件时 这样的事可能吗 运行程序 http msdn microsoft com en us library ms813609 aspx and 打开
  • C# Dns.GetHostEntry 不返回连接到 WiFi 的移动设备的名称

    我有一个 C 中的 Windows 窗体应用程序 我试图获取列表中所有客户端的主机名 下面给出的是 ra00l 来自此链接的代码示例 GetHostEntry 非常慢 https stackoverflow com questions 99
  • 未经许可更改内存值

    我有一个二维数组 当我第一次打印数组的数据时 日期打印正确 但其他时候 array last i 的数据从 i 0 到 last 1 显然是一个逻辑错误 但我不明白原因 因为我复制并粘贴了 for 语句 那么 C 更改数据吗 I use g
  • 如何将整数转换为 void 指针?

    在 C 中使用线程时 我面临警告 警告 从不同大小的整数转换为指针 代码如下 include
  • 如何在 Blackberry Cascades 中显示具有特定号码的电话板

    我正在使用带有 C QT 和 QML 的 Blackberry Cascades 10 Beta 3 SDK 以及 Blackberry 10 Dev Alpha Simulator 和 QNX Momentics IDE 并且我正在尝试实
  • 使用 Moq 使用内部构造函数模拟类型

    我正在尝试模拟 Microsoft Sync Framework 中的一个类 它只有一个内部构造函数 当我尝试以下操作时 var fullEnumerationContextMock new Mock
  • 如何使用 Mongodb C# 驱动程序连接多个集合

    我需要将 3 个集合与多个集合合并在一起 lookup我在 C 驱动程序中尝试过 它允许我 lookup用户采集但无法执行秒 lookup用于设置集合 有人可以帮忙吗 db Transactions aggregate lookup fro
  • std::async 与重载函数

    可能的重复 std bind 重载解析 https stackoverflow com questions 4159487 stdbind overload resolution 考虑以下 C 示例 class A public int f
  • (de)从 CSV 序列化为对象(或者最好是类型对象的列表)

    我是一名 C 程序员 试图学习 C 似乎有一些内置的对象序列化 但我在这里有点不知所措 我被要求将测试数据从 CSV 文件加载到对象集合中 CSV 比 xml 更受青睐 因为它更简单且更易于人类阅读 我们正在创建测试数据来运行单元测试 该集
  • Server.MapPath - 给定的物理路径,预期的虚拟路径

    我正在使用这行代码 var files Directory GetFiles Server MapPath E ftproot sales 在文件夹中查找文件 但是我收到错误消息说 给定物理路径但虚拟路径 预期的 我对在 C 中使用 Sys
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 编译时“strlen()”有效吗?

    有时需要将字符串的长度与常量进行比较 例如 if line length gt 2 Do something 但我试图避免在代码中使用 魔法 常量 通常我使用这样的代码 if line length gt strlen Do somethi
  • memset 未填充数组

    u32 iterations 5 u32 ecx u32 malloc sizeof u32 iterations memset ecx 0xBAADF00D sizeof u32 iterations printf 8X n ecx 0

随机推荐

  • VM虚拟机提示“vmware tools 的安装无法手动启动,自动安装正在进行中”

    VM虚拟机提示 vmware tools 的安装无法手动启动 自动安装正在进行中 装了个VMWARE虚拟机 但vmware tools总也装不上 提示 vmware tools 的安装无法手动启动 自动安装正在进行中 网上搜索下 解决方法如
  • cannot connect to 192.168. 由于目标计算机积极拒绝,无法连接或者AndroidStudio通过WIFI连接手机调试

    由于在公司只有一根数据线 不能同时两个手机用 一个自己的 一个测试机 那有人说 不会再买个吗 穷啊 买不起 AS可以通过WIFI 不需要数据线就可以连接到手机 这个很符合现在的我 嘻嘻 本以为很简单粗暴 但中间遇到了一些坑 并解决了 在此记
  • 学习笔记(一)数据挖掘概念与技术

    1数据仓库与数据库 数据仓库是一种用于长期存储数据的仓库 这些数据来自多个数据源 是经过组织的 以便支持管理决策 这些数据在一种统一的模式下存放 并且通常是汇总的 数据仓库提供一些数据分析能力 称作联机分析处理 数据库 传统关系型数据库的主
  • 从数据爬取到构建基于知识图谱的问答系统(前端展示)

    项目介绍 项目地址 整理了很久 一定要给个star呀 博主目前南京大学在读研究生 有问题欢迎咨询 bravezhangw 163 com
  • 数据中台盛行,DataOps兴起,数据架构才是未来

    导读 在数字化转型的浪潮下 数据架构获得了越来越多的关注 作为企业架构中的关键纽带 数据架构解决了业务与数据间的映射 规范了应用架构中的数据集成关系 指导了技术架构的技术选型 在企业中发挥着不可或缺的作用 伴随DataOps等场景的出现 数
  • 【Leetcode】P5612 从仓库到码头运输箱子

    Leetcode P5612 从仓库到码头运输箱子 你有一辆货运卡车 你需要用这一辆车把一些箱子从仓库运送到码头 这辆卡车每次运输有 箱子数目的限制 和 总重量的限制 给你一个箱子数组 boxes 和三个整数 portsCount maxB
  • es 局部更新 DSL 语句

    curl XPOST 192 168 1 47 9200 sub refresh 更新 根据条件 curl XPOST 192 168 1 4 9200 sub update by query pretty H Content Type a
  • 02-07GRE真题及答案解析整理

    02 07年GRE真题及答案解析整理 2002年11月23日GRE笔考题 VERBAL部分 Section 1 填空 1 Although she gives badly titles to her musical compositions
  • std::match_result

    英文文档 https cplusplus com reference regex match results 以下是我对此英文文档的翻译 经过一定的加工 改动和取舍 并添加了我自己的理解 可能翻译的比较差 请轻喷 有空我会更新的 match
  • 数据库的添加与查询

    创建数据库 使用数据库 创表 添加数据 1 查询所有学生的信息 2 查询姓名是李白的学生信息 3 查询1班是上海的学生 4 查询家乡是北京或者上海的学生 5 查询小乔的年龄 6 查询家乡不在北京的学生 7 查询年龄小于20的女同学 8 查询
  • Obsidian 从本地到云端

    原文 https www ftls xyz posts obcsapi fc simple 作者 恐咖兵糖 Obsidian 从本地到云端 Obsidian 作为本地笔记软件 在使用了多端同步插件 remotely save插件情况下 我选
  • Flask类视图的使用

    1 add url rule函数 之前我们接触的视图都是函数 所以一般简称视图函数 其实视图也可以基于类来实现 类视图的好处是支持继承 但是类视图不能跟函数视图一样 写完类视图还需要通过app add url rule 函数来进行注册 ap
  • Python如何自动操作电脑桌面应用程序

    前言 本文是该专栏的第2篇 后面会持续分享python的各种黑科技知识 值得关注 熟悉python的朋友 都知道python可以做自动化 比如说selenium pyppeteer airtest等等 但你是否听说过python可以来自动操
  • Office 365 官方原版镜像下载

    中文说明 专业增强版 简体中文版 文件名称 O365ProPlusRetail img 下载地址 https officecdn microsoft com db 492350F6 3A01 4F97 B9C0 C7C6DDF67D60 m
  • “M OP N“ 数值问题

    M OP N 数值问题 问题描述 获得用户输入的一个字符串 格式为 M OP N 其中 M和N是任何数字 OP代表一种操作 表示为如下四种 加减乘除 根据OP 输出M OP N的运算结果 统一保存小数点后2位 注意 M和OP OP和N之间可
  • 腾讯AI Lab开源大规模高质量中文词向量数据,800万中文词随你用

    感谢阅读腾讯AI Lab第45篇文章 本文将介绍大规模高质量的中文词向量数据的开源情况 今日 腾讯AI Lab 宣布开源大规模 高质量的中文词向量数据 该数据包含800多万中文词汇 相比现有的公开数据 在覆盖率 新鲜度及准确性上大幅提高 为
  • 第八章 坐标系统变换

    一 使用QPainter绘图 http blog csdn net hellozex article details 78361311 二 坐标系统变换 在QPainter的默认坐标系中 点 0 0 位于坐标绘制设备的左上角 x坐标向右 y
  • 为什么http协议需要服务器,http协议、web服务器、并发服务器(上)

    1 HTTP格式 每个HTTP请求和响应都遵循相同的格式 一个HTTP包含Header和Body两部分 其中Body是可选的 HTTP协议是一种文本协议 所以 它的格式也非常简单 1 1 HTTP GET请求的格式 GET path HTT
  • 网络安全专业名词解释

    1 Burp Suite 是一款信息安全从业人员必备的集成型的渗透测试工具 它采用自动测试和半自动测试的方式 通过拦截HTTP HTTPS的Web数据包 充当浏览器和相关应用程序的中间人 进行拦截 修改 重放数据包进行测试 是Web安全人员
  • Linux 互斥锁 递归锁 自旋锁 读写锁

    在多线程中 我们经常会要用到锁 那么 锁是什么 我们为什么要用到锁 回到问题的本质 我们在什么场景下会用到锁 锁是针对程序中的临界资源 也就是公共资源的 当我们有两个或多个线程同时对一个临界资源操作的时候 为了保证共享数据操作的完整性 我们