Linux 第三十五章

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数C初学者入门训练题解CC的使用文章「初学」C++linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

互斥锁

scp指令

线程加锁的本质

可重入VS线程安全(多执行流并发执行同一段代码时造成的数据不一致)

死锁

线程同步

生产者消费者模型

条件变量

pthread_cond_wait

pthread_cond_signal

pthread_cond_broadcast

事例:火车票抢购


互斥锁

创建全局的锁

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//创建全局的锁,用宏去初始化

创建局部的锁

pthread_mutex_t mutex;//创建局部锁的时候不用宏初始化
pthread_mutex_init(&mutex);//初始化
pthread_mutex_destroy(&mutex);//局部的锁,不用时要销毁

scp指令

scp指令是用于在Linux系统中进行文件传输的命令,

其语法如下:

scp [选项] [源文件] [目标文件]
常用选项包括:

* -r:递归复制整个目录
* -P:指定端口号
* -p:保持源文件的修改时间、访问时间和权限信息
* -q:安静模式,不显示传输进度信息
示例用法:

1. 从本地复制文件到远程服务器:
scp /path/to/local/file username@remotehost:/path/to/remote/directory

2. 从远程服务器复制文件到本地:
scp username@remotehost:/path/to/remote/file /path/to/local/directory

3. 从远程服务器复制整个目录到本地:
scp -r username@remotehost:/path/to/remote/directory /path/to/local/directory

线程加锁的本质

关于加锁的原则,一般原则:谁加锁,谁解锁

可重入VS线程安全(多执行流并发执行同一段代码时造成的数据不一致)

可重入函数是线程安全函数的一种

线程安全不一定是可重入的,而可重入函数则一定是线程安全的。

如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。

死锁

原因

1)当两个线程需要都需要两把锁,而它们每一个线程只有一把锁,此时就会导致死锁

2)当一个线程在加锁的时候,没有解锁,又去加锁,这样就会导致死锁

…….

死锁必要条件(产生死锁的时候,这四个条件一定是满足的)

互斥条件:一个资源每次只能被一个执行流使用

请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放

不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺

循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系

避免死锁

破坏死锁的四个必要条件

加锁顺序一致

避免锁未释放的场景

资源一次性分配

线程同步

线程同步的概念:

在临界资源使用安全的前提下,让多线程执行具有一定的顺序性——同步

互斥:能够保证资源的安全性

同步:能够较为充分使用我们的资源

生产者消费者模型

3种关系

生产者之间关系,互斥

消费者之间关系,互斥

生产者与消费者之间关系,互斥和同步

本质:就是用锁和条件变量,维护

2种角色

生产者,消费者——线程或者进程

1个交易场所

内存空间

生产者消费者模型提高了数据处理的能力

条件变量

创建条件变量

pthread_cond_t cond;

全局的条件变量初始化

cond=PTHREAD_COND_INITIALIZER

局部条件变量的初始化

Pthread_cond_init(&cond);

局部条件变量需要销毁

pthread_cond_destory(&cond);

pthread_cond_wait

pthread_cond_wait() 是 POSIX 线程库中的一个函数,用于线程间的条件变量同步。它需要与互斥锁(mutex)和条件变量(condition variable)一起使用。
函数原型如下:

int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);

pthread_cond_wait() 的作用是让调用线程等待条件变量的满足。在调用该函数之前,
必须先使用 pthread_mutex_lock() 锁住一个互斥锁,以确保线程安全。
当条件变量满足时,pthread_cond_wait() 函数会解锁互斥锁并使线程进入等待状态,
直到被其他线程通过 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒。

使用示例:

#include <pthread.h>
// 全局互斥锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* thread_func(void* arg) {
    // 获取互斥锁
    pthread_mutex_lock(&mutex);
    // 等待条件变量满足
    pthread_cond_wait(&cond, &mutex);
    // 条件变量满足后的处理
    // 解锁互斥锁
    pthread_mutex_unlock(&mutex);
    return NULL;
}
int main() {
    pthread_t thread;
    // 创建线程
    pthread_create(&thread, NULL, thread_func, NULL);
    // 在某个时刻满足条件,并通知等待的线程
    pthread_mutex_lock(&mutex);
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    // 等待线程结束
    pthread_join(thread, NULL);
    return 0;
}

在上面的示例中,主线程通过 pthread_cond_signal() 唤醒等待的线程。当满足条件时,等待的线程会被唤醒并继续执行。注意,在调用 pthread_cond_wait() 之前必须加锁(pthread_mutex_lock()),并在条件满足后解锁(pthread_mutex_unlock())。

pthread_cond_signal

pthread_cond_signal() 是 POSIX 线程库中的一个函数,用于唤醒等待在条件变量上的一个线程。它会选择一个等待的线程,并通知该线程条件已经满足,使得该线程从等待状态恢复到运行状态。

函数原型如下:

int pthread_cond_signal(pthread_cond_t* cond);
pthread_cond_signal() 的作用是发送一个信号给等待在条件变量上的某个线程,
使其从等待状态返回。如果有多个线程等待在条件变量上,那么只有其中一个线程会被唤醒,
具体哪个线程被唤醒则不确定。

pthread_cond_broadcast

pthread_cond_broadcast() 是 POSIX 线程库中的一个函数,用于广播唤醒所有等待在条件变量上的线程。当条件满足时,可以使用 pthread_cond_broadcast() 函数来唤醒所有等待在该条件变量上的线程,让它们从等待状态返回到运行状态。
 

函数原型如下:

int pthread_cond_broadcast(pthread_cond_t* cond);
pthread_cond_broadcast() 的作用是向所有等待在条件变量上的线程发送信号,
使它们全部被唤醒。所有被唤醒的线程都会尝试重新获取互斥锁,并继续执行。

1.让线程在进行等待的时候,会自定释放锁

2.线程被唤醒的时候,是在临界区内唤醒的,当线程被唤醒的时候,线pthread_cond_wait返回的时候要重新申请并持有锁

3.当线程被唤醒的时候,重新申请并持有锁的本质是也要参与锁的竞争

单纯的互斥,能保证数据的安全,不一定合理高效!

事例:火车票抢购

test_cond

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;

pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//条件变量
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//锁

int tickets=1000;


void* threadroutine(void* args)
{
    string name=static_cast<const char*>(args);
    while(true)
    {
        // sleep(1);
        pthread_mutex_lock(&mutex);//加锁

        if(tickets>0)
        {
            cout<<name<<", get a ticket: "<<tickets--<<endl;
            usleep(100);
        }
        else
        {
            cout<<"没票了"<<name<<endl;
            pthread_cond_wait(&cond,&mutex);//让线程满足条件变量,才被唤醒
            //1.让线程在进行等待的时候,会自动释放锁
            //2.线程被唤醒的时候,是在临界区内唤醒的,当线程被唤醒的时候,线程pthread_cond_wait返回的时候
            //要重新申请并持有锁
            //3.当线程被唤醒的时候,重新申请并持有锁的本质是也要参与锁的竞争
        }
        pthread_mutex_unlock(&mutex);
    }
}

//主线程
int main()
{
    pthread_t t1,t2,t3;
    pthread_create(&t1,nullptr,threadroutine,(void*)"thread - 1");
    pthread_create(&t2,nullptr,threadroutine,(void*)"thread - 2");
    pthread_create(&t3,nullptr,threadroutine,(void*)"thread - 3");

    sleep(5);//主线程5s之后,开始唤醒一个线程
    while(true)
    {
        // sleep(1);
        // pthread_cond_signal(&cond);

        sleep(20);//休息20s再放一批票
        pthread_mutex_lock(&mutex);
        tickets+=1000;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);//让一个线程被唤醒,才后这个线程在去抢票
    }


    
    pthread_join(t1,nullptr);
    pthread_join(t2,nullptr);
    pthread_join(t3,nullptr);
    return 0;
}

火车票抢购,其中会同时出现多个人(线程)去抢购票,所有的抢票的过程一定要是原子的,所以需要加互斥锁。还有一个问题,在上面的抢票过程中,会在一段时间增加票的数量,但总不能让某一个人(线程)把这些票都抢光吧!所以就需要条件变量,当这次票完的时候,将这个线程阻塞住,只有被唤醒才可以继续参与抢票。

 🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸  

  • 19
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LINUX设备驱动第三版_ 前言 第一章 设备驱动程序简介 设备驱动程序的作用 内核功能划分 设备和模块的分类 安全问题 版本编号 许可证条款 加入内核开发社团 本书概要 第二章 构造和运行模块 设置测试系统 Hello World模块 核心模块与应用程序的对比 编译和装载 内核符号表 预备知识 初始化和关闭 模块参数 在用户空间编写驱动程序 快速参考 第三章 字符设备驱动程序 scull的设计 主设备号和次设备号 一些重要的数据结构 字符设备的注册 open和release scull的内存使用 read和write 试试新设备 快速参考 第四章 调试技术 内核中的调试支持 通过打印调试 通过查询调试 通过监视调试 调试系统故障 调试器和相关工具 第五章 并发和竞态 scull的缺陷 并发及其管理 信号量和互斥体 completion 自旋锁 锁陷阱 除了锁之外的办法 快速参考 第六章 高级字符驱动程序操作 ioctl 阻塞型I/O poll和select 异步通知 定位设备 设备文件的访问控制 快速参考 第七章 时间、延迟及延缓操作 度量时间差 获取当前时间 延迟执行 内核定时器 tasklet 工作队列 快速参考 第八章 分配内存 kmalloc函数的内幕 后备高速缓存 get_free_page和相关函数 vmalloc及其辅助函数 per-CPU变量 获取大的缓冲区 快速参考 第九章 与硬件通信 I/O端口和I/O内存 使用I/O端口 I/O端口示例 使用I/O内存 快速参考 第十章 中断处理 准备并口 安装中断处理例程 实现中断处理例程 顶半部和底半部 中断共享 中断驱动的I/O 快速参考 第十一章 内核的数据类型 使用标准C语言类型 为数据项分配确定的空间大小 接口特定的类型 其他有关移植性的问题 链表 快速参考 第十二章 PCI驱动程序 PCI接口 ISA回顾 PC/104和PC/104+ 其他的PC总线 SBus NuBus 外部总线 快速参考 第十三章 USB驱动程序 USB设备基础 USB和Sysfs USB urb 编写USB驱动程序 不使用urb的USB传输 快速参考 第十四章 Linux设备模型 kobject、kset和子系统 低层sysfs操作 热插拔事件的产生 总线、设备和驱动程序 类 各环节的整合 热插拔 处理固件 快速索引 第十五章 内存映射和DMA Linux的内存管理 mmap设备操作 执行直接I/O访问 直接内存访问 快速参考 第十六章 块设备驱动程序 注册 块设备操作 请求处理 其他一些细节 快速参考 第十七章 网络驱动程序 snull设计 连接到内核 net_device结构细节 打开和关闭 数据包传输 数据包的接收 中断处理例程 不使用接收中断 链路状态的改变 套接字缓冲区 MAC 地址解析 定制 ioctl 命令 统计信息 组播 其他知识点详解 快速参考 第十八章 TTY驱动程序 小型TTY驱动程序 tty_driver函数指针 TTY线路设置 ioctls proc和sysfs对TTY设备的处理 tty_driver结构详解 tty_operations结构详解 tty_struct结构详解 快速参考 参考书目 9112405-1_o.jpg (85.53 KB, 下载次数: 50)
### 回答1: 《Linux+, 第七章: Linux 文件系统》是《Linux+认证教程》中的第七章,重点介绍了Linux操作系统中的文件系统。在这一章中,读者将了解到Linux文件系统的基本概念、常用的文件系统类型以及文件系统的管理和维护等内容。 首先,本章开始介绍了Linux文件系统的基本概念,包括文件和目录的概念以及在Linux中的表示方式。读者将学习到如何使用文件路径和文件名来访问文件和目录。同时,还介绍了Linux系统中的特殊目录,如根目录、用户主目录和共享目录等。 接着,本章继续讲解了常见的文件系统类型。Linux支持多种文件系统类型,如EXT4、XFS、Btrfs等。读者将了解到每种文件系统类型的特点和适用场景,并学习如何在Linux中创建和格式化不同类型的文件系统。 然后,本章讲解了文件系统的管理和维护。读者将学习如何使用命令行工具来管理和操作文件系统,如创建、删除、复制和移动文件等。同时,还介绍了文件系统的权限和属性配置,如设置文件的拥有者和权限等。 最后,本章还涉及了文件系统的备份和恢复。读者将学习如何使用备份工具来对文件系统进行备份,以及如何从备份中恢复数据。 通过学习本章内容,读者将深入了解Linux文件系统的原理和操作方法。这对于Linux系统管理员和开发人员来说非常重要,因为文件系统是Linux操作系统中最基础和核心的部分之一。掌握了文件系统的知识,读者将能够更好地管理和维护Linux系统,并解决文件系统相关的问题。 ### 回答2: 《Linux头歌》是一本关于Linux操作系统的技术书籍,第七章主要讲述了Linux文件系统的管理和操作。本章主要涵盖了以下几个方面的内容。 首先,本章介绍了Linux文件系统的基本概念和特点。Linux文件系统采用了类似于树状结构的层次目录结构,以根目录"/"为起点,通过不同的目录和文件来组织和管理数据。同时,Linux文件系统还具有对文件和目录进行权限管理的功能,确保安全性。 接着,本章详细介绍了如何在Linux系统中创建、删除、复制和移动文件和目录。通过命令行工具或图形界面工具,用户可以轻松地对文件和目录进行操作,满足不同的需求。此外,还介绍了文件和目录的属性和权限设置,确保文件的安全性。 然后,本章讲解了Linux文件系统中的硬链接和软链接的概念和用法。硬链接是指多个文件指向同一个物理存储空间,而软链接是指一个文件指向另一个文件,类似于快捷方式。用户可以根据需要使用不同的链接方式,帮助管理和组织文件。 最后,本章还介绍了Linux文件系统的挂载和卸载操作。在Linux系统中,用户可以通过挂载操作将外部设备或远程文件系统与本地文件系统连接起来,以便访问和操作外部文件。同时,也可以通过卸载操作安全地断开与外部文件系统的连接。 总之,第七章《Linux头歌》详细介绍了Linux文件系统的管理和操作技巧,帮助读者更好地理解和掌握Linux操作系统的文件系统特性和使用方法。无论是初学者还是有一定经验的用户,都可以从中获得实用的知识和技能,提高对Linux系统的操作能力。 ### 回答3: 《Linux内核设计与实现》是一本经典的Linux内核相关书籍,其中第七章主要介绍了Linux内核的关键组成部分——系统调用及库函数。系统调用是操作系统内核提供给用户程序的一组接口,它们允许用户程序通过内核来访问底层系统资源和服务。这一章内容对于深入理解 Linux 内核的使用和开发具有重要意义。 首先,第七章详细介绍了系统调用的实现原理和机制。作者解析了系统调用表的结构和功能,并且详细说明了Linux内核是如何通过系统调用号来定位和调用相应的系统调用函数的。这些系统调用函数在内核空间中运行,并能够处理用户程序发起的请求,实现具体的功能。 其次,本章还介绍了用户空间和内核空间之间的切换机制。当用户程序发起系统调用的时候,会触发用户态和内核态之间的转换,在转换过程中,内核会根据系统调用号找到对应的系统调用函数,并执行相应的操作。这一章详细分析了切换过程的细节以及相关的数据结构。 此外,本章还对一些常用的系统调用进行了详细介绍,如文件操作相关的系统调用(如open、read、write等),进程管理相关的系统调用(如fork、execve、wait等),以及网络通信相关的系统调用(如socket、bind、connect等)。对于开发者来说,熟悉这些系统调用的使用方法和原理非常重要,能够帮助他们更好地设计和开发Linux内核的应用程序。 总之,第七章《Linux内核设计与实现》详细介绍了Linux内核的系统调用和库函数的实现原理和机制,以及常用系统调用的使用方法。通过学习这一章的内容,读者能够深入理解Linux内核的工作原理,对于使用和开发Linux内核应用程序具有指导意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值