首页 > 系统相关 >第十一周Linux教材第十二章学习笔记——块设备I/O和缓冲区管理

第十一周Linux教材第十二章学习笔记——块设备I/O和缓冲区管理

时间:2023-11-19 11:33:06浏览次数:32  
标签:写入 第十二章 信号量 item 算法 mutex Linux 缓冲区

块设备I/O和缓冲区管理

本章讨论了块设备1/O和缓冲区管理;解释了块设备1/O的原理和T/O缓冲的优点;论述了Unix的缓冲区管理算法,并指出了其不足之处;还利用信号量设计了新的缓冲区管理算法,以提高1/O缓冲区的缓存效率和性能;表明了简单的PV算法易于实现,缓存效果好,不存在死锁和饥饿问题;还提出了一个比较Unix缓冲区管理算法和PV算法性能的编程方案。编程项目还可以帮助读者更好地理解文件系统中的I/O操作。

12.1 块设备I/O和缓冲区

  • 块设备 I/O:

  1. 概念: 块设备是一种按块(通常为固定大小的块)进行数据传输的设备,典型的例子是硬盘驱动器。块设备 I/O 涉及到对存储介质上的数据块的读取和写入。

  2. 块大小: 数据在块设备上以块为单位进行读写,块的大小通常是固定的,例如,一个典型的块大小可以是 512 字节或 4KB。

  3. 读取操作: 当应用程序请求从块设备读取数据时,操作系统会发起相应的块设备 I/O 操作,将数据块读取到内存中的缓冲区。

  4. 写入操作: 类似地,当应用程序希望向块设备写入数据时,操作系统将数据从内存缓冲区写入到块设备上的相应块。

  5. 缓存: 操作系统通常会使用缓存来存储最近读取或写入的块,以提高性能。这就是为什么在某些情况下,读取相同的块可能不会导致实际的物理 I/O 操作。

  • 缓冲区管理:

  1. 概念: 缓冲区是一个临时存储数据的区域,通常是为了减少对主存或磁盘等慢速设备的访问次数而引入的。缓冲区管理涉及到如何有效地使用和管理这些缓冲区。

  2. 读取缓冲: 当数据从块设备读取到内存时,通常会使用读取缓冲,将数据暂存于内存中,以便应用程序可以更快地访问。

  3. 写入缓冲: 类似地,当应用程序向块设备写入数据时,数据可能会首先被写入写入缓冲,然后由操作系统决定何时将缓冲中的数据写入实际的块设备。

  4. 缓存策略: 操作系统需要决定何时将数据从缓冲区写入块设备,以及何时从块设备读取新的数据到缓冲区。缓存策略的选择可以影响系统的性能。

  5. 脏页: 缓冲区中的数据可能与块设备上的数据不同步。当缓冲区中的数据被修改但尚未写回块设备时,该缓冲区被称为“脏页”。

12.2 Unix I/O缓冲区管理算法

  1. Unix系统中使用了一些经典的缓冲区管理算法,其中包括:

    • LRU(Least Recently Used): 最近最少使用算法,用于决定哪些缓冲区中的数据应该被替换。最久未被使用的数据会被最先替换。

    • FIFO(First-In-First-Out): 先进先出算法,按照数据进入缓冲区的顺序来选择替换哪些数据。

    • CLOCK算法: 一种近似LRU算法,通过维护一个时钟指针来模拟LRU的选择过程。

  2. 缓冲区刷新(Flush): 当应用程序对文件进行写操作时,数据通常首先被写入内核缓冲区而不是直接写入磁盘。缓冲区刷新是指将内核缓冲区中的数据写入到磁盘。这可以通过同步刷新或异步刷新来完成。

    • 同步刷新: 在写入系统调用完成之前,内核会等待数据被写入磁盘。这确保了数据的持久性,但可能导致性能下降。

    • 异步刷新: 内核会在后台将数据写入磁盘,而不阻塞应用程序。这提高了性能,但在发生系统崩溃时可能导致数据丢失。

  3. 预读和预写: 为了提高性能,Unix系统通常会使用预读和预写策略。预读是指在应用程序请求数据之前,系统会预先读取一些附近的数据到缓冲区。预写是指系统会提前将一些数据写入磁盘,而不仅仅在应用程序请求写入时才进行。

算法缺点:

  1. 效率低下;同时唤醒多组进程,但最后只有一组进程可以获取释放的缓冲区,其他所有被唤醒的进程必须重新进入休眠状态。
  2. 缓存效果不可预知;每个释放的缓冲区都有可能被获取。如果缓冲区由需要空闲缓冲区的进程获取,那么将会重新分配缓冲区,即使有些进程仍然需要当前的缓冲区。
  3. 可能会出现进程饥饿;
  4. 该算法使用只适用于单处理系统的休眠/唤醒操作。

12.3 新的I/O缓冲区管理算法

使用信号量的缓冲区管理算法:

使用信号量的缓冲区管理算法通常涉及到解决多线程或多进程之间的竞态条件和同步问题。信号量是一种用于线程或进程之间同步的机制,可以用于确保在共享资源上的互斥访问。

下面是一个简单的使用信号量的缓冲区管理算法的示例,其中涉及一个生产者-消费者场景:

信号量定义:

  1. 一个用于表示可用缓冲区数量的信号量(empty)。
  2. 一个用于表示已用缓冲区数量的信号量(full)。
  3. 一个用于互斥访问共享资源的信号量(mutex)。
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <pthread.h>
 4 #include <semaphore.h>
 5 
 6 #define BUFFER_SIZE 5
 7 
 8 int buffer[BUFFER_SIZE];
 9 sem_t empty, full, mutex;
10 int in = 0, out = 0;
11 
12 void produce(int item) {
13     // Produce an item
14     buffer[in] = item;
15     in = (in + 1) % BUFFER_SIZE;
16 }
17 
18 int consume() {
19     // Consume an item
20     int item = buffer[out];
21     out = (out + 1) % BUFFER_SIZE;
22     return item;
23 }
24 
25 void* producer(void* arg) {
26     int item;
27     while (1) {
28         // Produce an item
29         item = rand() % 100;
30         
31         // Wait for an empty buffer slot
32         sem_wait(&empty);
33         
34         // Wait for mutex to access the buffer
35         sem_wait(&mutex);
36         
37         // Produce the item into the buffer
38         produce(item);
39         
40         // Release mutex
41         sem_post(&mutex);
42         
43         // Signal that a buffer slot is full
44         sem_post(&full);
45     }
46     return NULL;
47 }
48 
49 void* consumer(void* arg) {
50     int item;
51     while (1) {
52         // Wait for a full buffer slot
53         sem_wait(&full);
54         
55         // Wait for mutex to access the buffer
56         sem_wait(&mutex);
57         
58         // Consume an item from the buffer
59         item = consume();
60         
61         // Release mutex
62         sem_post(&mutex);
63         
64         // Signal that a buffer slot is empty
65         sem_post(&empty);
66         
67         // Process the consumed item
68         printf("Consumed: %d\n", item);
69     }
70     return NULL;
71 }
72 
73 int main() {
74     // Initialize semaphores
75     sem_init(&empty, 0, BUFFER_SIZE); // Number of empty buffer slots
76     sem_init(&full, 0, 0); // Number of full buffer slots
77     sem_init(&mutex, 0, 1); // Mutex for buffer access
78 
79     // Create producer and consumer threads
80     pthread_t producer_thread, consumer_thread;
81     pthread_create(&producer_thread, NULL, producer, NULL);
82     pthread_create(&consumer_thread, NULL, consumer, NULL);
83 
84     // Wait for threads to finish (this example runs indefinitely)
85     pthread_join(producer_thread, NULL);
86     pthread_join(consumer_thread, NULL);
87 
88     // Destroy semaphores
89     sem_destroy(&empty);
90     sem_destroy(&full);
91     sem_destroy(&mutex);
92 
93     return 0;
94 }

这是一个简单的生产者-消费者模型,使用信号量确保在缓冲区上的互斥访问,并通过信号量控制缓冲区的状态。 empty 信号量跟踪可用的空缓冲区槽,full 信号量跟踪已用的缓冲区槽,而 mutex 信号量用于互斥访问共享资源(缓冲区)。

12.4 PV算法

PV算法(也称为P和V算法)是用于管理信号量的一组基本操作。这些操作包括:

  1. P(Produce)操作: 也称为wait操作,用于申请资源。如果资源可用,则减少信号量的值;否则,阻塞直到资源可用。

  2. V(Vaporate)操作: 也称为signal操作,用于释放资源。增加信号量的值,表示资源可用。

这两个操作是原子的,即它们在执行过程中不能被中断。PV算法通常与信号量一起使用,用于确保对共享资源的互斥访问。信号量是一个整数,用于表示可用的资源数量。当信号量的值为正时,表示有可用资源;当值为零时,表示资源已经被占用;当值为负时,表示有等待资源的进程或线程。

下面是PV算法的一般形式:

P操作(wait):

P(S):
  while S <= 0 do
    ; // 等待资源可用
  S = S - 1; // 申请资源

V操作(signal):

V(S):
  S = S + 1; // 释放资源

这里,S 是信号量。P(S) 操作尝试获取资源,如果资源不可用,则进入等待状态。V(S) 操作释放资源,增加信号量的值,从而允许其他等待资源的进程或线程继续执行。

PV算法是用于实现进程同步和互斥的基础机制,常用于多进程或多线程的操作系统中。这些算法有助于防止多个进程或线程同时访问共享资源,确保数据的一致性和正确性。

12.5 编程实践

PV操作代码

 1 #include <stdio.h>
 2 #include <pthread.h>
 3 
 4 int S = 1;  // 信号量
 5 
 6 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 7 pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
 8 
 9 // P(wait)操作
10 void P() {
11     pthread_mutex_lock(&mutex);
12 
13     while (S <= 0) {
14         // 等待资源可用
15         pthread_cond_wait(&condition, &mutex);
16     }
17 
18     // 申请资源
19     S--;
20 
21     pthread_mutex_unlock(&mutex);
22 }
23 
24 // V(signal)操作
25 void V() {
26     pthread_mutex_lock(&mutex);
27 
28     // 释放资源
29     S++;
30 
31     // 唤醒等待资源的线程
32     pthread_cond_signal(&condition);
33 
34     pthread_mutex_unlock(&mutex);
35 }
36 
37 void* producer(void* arg) {
38     while (1) {
39         P();  // 申请资源
40 
41         // 生产者的操作
42 
43         V();  // 释放资源
44     }
45     return NULL;
46 }
47 
48 void* consumer(void* arg) {
49     while (1) {
50         P();  // 申请资源
51 
52         // 消费者的操作
53 
54         V();  // 释放资源
55     }
56     return NULL;
57 }
58 
59 int main() {
60     pthread_t producer_thread, consumer_thread;
61 
62     // 创建生产者和消费者线程
63     pthread_create(&producer_thread, NULL, producer, NULL);
64     pthread_create(&consumer_thread, NULL, consumer, NULL);
65 
66     // 主线程等待线程结束
67     pthread_join(producer_thread, NULL);
68     pthread_join(consumer_thread, NULL);
69 
70     return 0;
71 }

运行结果

这段代码是一个简单的生产者-消费者模型,其中包含了P和V操作。由于代码中的生产者和消费者是无限循环的,所以程序将永远运行下去。

在这个简化的例子中,P操作和V操作的具体实现只是简单地增加或减少信号量S的值,并使用互斥锁和条件变量来确保对S的操作是原子的。

运行结果会是无限循环地执行生产者和消费者的操作。由于互斥锁的存在,每次只有一个线程能够执行P和V操作,从而保证对S的操作不会出现竞态条件。

请注意,实际的生产者-消费者模型可能涉及更多的细节和同步机制,例如缓冲区,以确保正确的生产和消费过程。这只是一个基本的示例,实际应用中可能需要更多的考虑和改进。

向ChatGpt请求苏格拉底式询问

 

标签:写入,第十二章,信号量,item,算法,mutex,Linux,缓冲区
From: https://www.cnblogs.com/20211115fyq/p/17836350.html

相关文章

  • 在Linux环境安装redis步骤,且设置开机自动启动redis
    原创/朱季谦最近一直在学习redis相关知识,看了很多理论知识后,觉得还是要多动手操作,就如王阳明说的知行合一那样,因此,便决定在linux环境安装了redis,过程捣鼓了一番,也遇到了一些波折,但最后还是成功安装完成,顺便把步骤流程记录了下来,分享给有需要的小伙伴。1.首先,我在linux的/usr/local/......
  • linux环境安装可操作图库语言Gremlin的图框架HugeGraph
    原创/朱季谦 若你还没接触过图数据库,可能看到这个概念时,会比较蒙蔽。图是什么?图数据库又是什么?首先,在数据结构中,图是一种由顶点(vertex)集合及顶点间关系集合组成的一种非线性数据结构。而图数据库,则是以图这种具有点边结构来增、删、改、查之类操作的NoSQL数据库,它特别擅长处理大数......
  • Linux信号基本概念
    信号分为两大类:标准信号,用于内核向进程通知事件,信号范围为1~31实时信号信号产生后,会在稍后被传递给某进程,进程也会采取某措施来响应信号。在产生和到达期间,信号处于pending(等待)状态。有时需要确保一段代码不被传递来的信号所中断,这时可以把信号添加到进程的信号掩码中,阻塞......
  • linux - grep 查找匹配
    在文件中查找匹配的字符串或者模式1.在单个文件中查找给定的字符串grep"string"filename2.在多个文件中查找指定的字符串grep"this"demo_*3.-i选项忽略大小写敏感进行查找grep-i"string"filename4.使用正则表达式进行匹配查找grep"lines.*empty"demo_file5......
  • Linux与Sre入门建议
    https://www.w3schools.com/go/index.php......
  • 学习笔记10 第十二章自学归纳
    学习笔记10第十二章自学归纳块设备I/O缓冲区1.文件系统使用一系列I/O缓冲区作为块设备的缓存内存。当进程试图读取(dev,blk)标识的磁盘块时。它首先在缓冲区缓存中搜索分配给磁盘块的缓冲区。如果该缓冲区存在并且包含有效数据、那么它只需从缓冲区中读取数据、而无须再次从磁盘......
  • linux安装最新版本composer
    wgethttps://getcomposer.org/composer.pharmvcomposer.pharcomposerchmod+xcomposersudomvcomposer/usr/bin/composer-V切换为中国源composerconfig-grepo.packagistcomposerhttps://packagist.phpcomposer.com......
  • 第十二周Linux学习报告
    本周学习的是上周没有讲完的磁盘管理以及新学习的文件查找、打包压缩及解压磁盘管理1、修改挂载点的配置文件,目录在/etc/fstab,使用vim修 文件查找、打包压缩以及解压1、which命令查找符合条件的命令文件,可查看其是否存在以及执行的位置  2、locate命令可以让用户快速......
  • OS-Linux-程序安装与依赖
    OS-Linux-程序安装与依赖从源下载安装包sudoaptdownloadmysql查看安装依赖:apt-cacheapt-cachedependsxxx查看已安装程序依赖,如nginxsudoapt-getinstall--reinstall-d'apt-cachedependsnginxIgrep"依赖”lcut-d:-f2Itr-d“"......
  • C++ Primer学习笔记——第十二章
    第十二章动态内存前言在此之前,我们使用的程序中对象都有着严格定义的生存期:全局对象,在程序启动时分配,在程序结束是销毁。局部自动对象,当进入定义所在程序时创建,在离开块时销毁。局部static对象,在第一次使用前分配,在程序结束时销毁。显然这存在限制,为此C++支持动态分配对......