Linux 第四章 进程通信

news/2024/9/25 19:53:23/

文章目录

  • 一、引言
  • 二、进程的同步与互斥
    • 2.1 进程合作
    • 2.2 共享资源
    • 2.3 临界区与临界资源
    • 2.4 同步机构设计准则
  • 三、互斥的软件方法
  • 四、硬件指令机制
  • 五、信号量机制
    • 5.1 整型信号量
    • 5.2 结构型信号量
    • 5.3 用信号量实现互斥
    • 5.4 用信号量实现同步
  • 六、经典进程同步问题
    • 6.1 生产者—消费者问题
    • 6.2 哲学家就餐问题
    • 6.3 读者—写者问题
  • 七、进程通信
    • 7.1 共享存储区系统
    • 7.2 管道通信系统
    • 7.3 消息传递系统
    • 7.4 客户机 - 服务器系统


一、引言

现代操作系统通过进程管理实现多道程序并发执行,但并发执行的进程可能因为合作或竞争资源而需要同步。

二、进程的同步与互斥

2.1 进程合作

  • 合作进程需要协调运行以完成任务。例如,一个进程可能负责计算,另一个进程则负责输入输出。
代码示例:
// 进程 P1 和 P2 合作计算 X = fun1(y) * fun2(z)
// P1 计算 fun1(y)
int X, y, z;
X = fun1(y);// P2 计算 fun2(z)
int result = fun2(z);
X *= result;// 注释: fun1 和 fun2 是需要实现的函数,用于计算表达式的结果

2.2 共享资源

  • 并发进程可能因竞争独占性资源而相互制约,如打印机。
代码示例:
int x = 1; // x 表示打印机的空闲数量void P1() {while (1) {x = x - 1;if (x < 0) {  // 如果请求打印机时它正忙,则等待// 等待打印机变为空闲的逻辑代码}// 使用打印机的逻辑代码x = x + 1;}
}void P2() {while (1) {x = x - 1;if (x < 0) {  // 如果请求打印机时它正忙,则等待// 等待打印机变为空闲的逻辑代码}// 使用打印机的逻辑代码x = x + 1;}
}
// 注释: 此代码示例展示了两个进程如何通过一个共享变量来控制对打印机的访问

2.3 临界区与临界资源

  • 临界资源一次仅允许一个进程使用,需要通过进入和退出临界区来控制。

代码示例:

int resource = 1; // 表示临界资源是否可用void critical_section() {while (resource) {// 循环等待资源变为空闲}resource = 0; // 占用资源// 访问临界资源的代码resource = 1; // 释放资源
}
// 注释: 此示例代码展示了如何使用一个简单的忙等待机制来实现对临界资源的访问控制

2.4 同步机构设计准则

  • 包括空闲让进、忙则等待、有限等待和让权等待。

三、互斥的软件方法

  • 早期通过软件方法实现临界区互斥访问。
    代码示例:
int turn = 0; // 令牌变量,用于控制两个进程的互斥访问void P0() {while (1) {while (turn != 0) {// 等待获取令牌}// 进入临界区critical_section();turn = 1; // 退出临界区,通知下一个进程可以进入remainder_section();}
}void P1() {while (1) {while (turn != 1) {// 等待获取令牌}// 进入临界区critical_section();turn = 0; // 退出临界区,通知下一个进程可以进入remainder_section();}
}
// 注释: 此代码示例展示了使用令牌机制来实现两个进程之间的互斥

四、硬件指令机制

  • 利用硬件指令解决临界区问题。

代码示例:

int lock = 0; // 锁变量,用于控制对临界区的访问void P1() {while (1) {while (TS(lock)) {// 测试并设置锁,如果锁已经被设置,则忙等待}// 进入临界区critical_section();lock = 0; // 退出临界区,释放锁remainder_section();}
}void P2() {while (1) {while (TS(lock)) {// 测试并设置锁,如果锁已经被设置,则忙等待}// 进入临界区critical_section();lock = 0; // 退出临界区,释放锁remainder_section();}
}
// 注释: TS是测试并设置的硬件指令,此代码示例展示了如何使用硬件指令来实现互斥

五、信号量机制

  • 信号量用于实现进程同步。

5.1 整型信号量

  • 使用整型变量表示信号量。

代码示例:

Semaphore s = 1;void wait(Semaphore s) {while (s <= 0) {// 忙等待,直到信号量大于0}s = s - 1; // 信号量减1
}void signal(Semaphore s) {s = s + 1; // 信号量加1
}
// 注释: 此代码示例展示了整型信号量的wait和signal操作

5.2 结构型信号量

  • 不存在忙等待问题的进程同步机制。

代码示例:

Semaphore s = {.value = 1, .L = NULL};void wait(Semaphore s) {s.value--;if (s.value < 0) {// 如果信号量小于0,则进程进入等待队列block(s.L);}
}void signal(Semaphore s) {s.value++;if (s.value <= 0) {// 如果信号量小于等于0,则唤醒等待队列中的一个进程wakeup(s.L);}
}
// 注释: 此代码示例展示了结构型信号量的wait和signal操作,包括阻塞和唤醒进程

5.3 用信号量实现互斥

  • 使用信号量实现进程互斥。

代码示例:

Semaphore mutex = 1;void P1() {while (1) {wait(mutex);  // 请求互斥信号量// 进入临界区critical_section();signal(mutex);  // 释放互斥信号量// 退出临界区remainder_section();}
}void P2() {while (1) {wait(mutex);  // 请求互斥信号量// 进入临界区critical_section();signal(mutex);  // 释放互斥信号量// 退出临界区remainder_section();}
}
// 注释: 此代码示例展示了如何使用信号量来实现两个进程之间的互斥

5.4 用信号量实现同步

  • 解决进程间的同步问题。

代码示例:

Semaphore plate = 1, apple = 0, orange = 0;void father() {while (1) {prepare_an_apple();wait(plate);  // 请求盘子信号量put_an_apple_in_the_plate();signal(apple);  // 释放苹果信号量}
}void mother() {while (1) {prepare_an_orange();wait(plate);  // 请求盘子信号量put_an_orange_in_the_plate();signal(orange);  // 释放橘子信号量}
}void son() {while (1) {wait(orange);  // 请求橘子信号量get_an_orange_from_the_plate();signal(plate);  // 释放盘子信号量eat();}
}void daughter() {while (1) {wait(apple);  // 请求苹果信号量get_an_apple_from_the_plate();signal(plate);  // 释放盘子信号量eat();}
}
// 注释: 此代码示例展示了如何使用信号量来解决生产者和消费者之间的同步问题

六、经典进程同步问题

  • 解决进程同步的经典问题。

6.1 生产者—消费者问题

  • 生产者与消费者通过缓冲区共享数据。

代码示例:

Semaphore empty = n, full = 0, mutex = 1;
Product buffer[n];void producer() {while (1) {produce_a_new_product();wait(empty);  // 等待空的缓冲区wait(mutex);  // 请求互斥信号量buffer[in] = product;in = (in + 1) % n;signal(mutex);  // 释放互斥信号量signal(full);  // 释放产品信号量}
}void consumer() {while (1) {wait(full);  // 等待产品wait(mutex);  // 请求互斥信号量product = buffer[out];out = (out + 1) % n;signal(mutex);  // 释放互斥信号量signal(empty);  // 释放空缓冲区信号量consume_product();}
}
// 注释: 此代码示例展示了生产

6.2 哲学家就餐问题

  • 多个哲学家围绕圆桌就餐。
    代码示例:
Semaphore fork[5];void philosopher(int i) {while (1) {think();wait(fork[i]);  // 拿起左边的叉子wait(fork[(i+1) % 5]);  // 拿起右边的叉子eat();signal(fork[i]);  // 放下左边的叉子signal(fork[(i+1) % 5]);  // 放下右边的叉子}
}
// 注释: 此代码示例展示了如何使用信号量来解决哲学家就餐问题

6.3 读者—写者问题

  • 读者和写者共享文件。

代码示例:

Semaphore rmutex = 1, wmutex = 1, readcount = 0;void reader() {while (1) {wait(rmutex);  // 请求读者互斥信号量if (readcount == 0) wait(wmutex);  // 如果没有读者在读,则需要请求写者互斥信号量readcount++;  // 增加读者数量signal(rmutex);  // 释放读者互斥信号量// 读取文件的代码wait(rmutex);  // 请求读者互斥信号量readcount--;  // 减少读者数量if (readcount == 0) signal(wmutex);  // 如果所有读者读完,则释放写者互斥信号量signal(rmutex);  // 释放读者互斥信号量}
}void writer() {while (1) {wait(wmutex);  // 请求写者互斥信号量// 写入文件的代码signal(wmutex);  // 释放写者互斥信号量}
}
// 注释: 此代码示例展示了读者优先的读者写者问题的信号量解决方案

七、进程通信

  • 进程通信的方式。

7.1 共享存储区系统

  • 通过内存中的共享存储区域进行通信。

代码示例:

SharedMemory shm;void writer() {attach_shared_memory(shm);  // 将共享内存附加到进程地址空间// 写入共享内存的代码detach_shared_memory(shm);  // 从进程地址空间分离共享内存
}void reader() {attach_shared_memory(shm);  // 将共享内存附加到进程地址空间// 从共享内存读取数据的代码detach_shared_memory(shm);  // 从进程地址空间分离共享内存
}
// 注释: 此代码示例展示了共享存储区系统进行进程间通信的方法

7.2 管道通信系统

  • 使用管道文件进行通信。

代码示例:

Pipe pipe;void writer() {write_to_pipe(pipe, data);  // 将数据写入管道
}void reader() {data = read_from_pipe(pipe);  // 从管道读取数据
}
// 注释: 此代码示例展示了管道通信系统进行进程间通信的方法

7.3 消息传递系统

  • 通过消息传递进行通信。

代码示例:

void send_message(int destination, Message message) {send(destination, message);  // 向目标进程发送消息
}void receive_message(Message *message) {receive(message);  // 接收消息
}
// 注释: 此代码示例展示了消息传递系统进行进程间通信的方法

7.4 客户机 - 服务器系统

使用客户机-服务器模型进行通信。

代码示例:

Socket client_socket, server_socket;void client() {connect_to_server(client_socket, server_socket);  // 客户端连接到服务器send_request(client_socket);  // 发送请求receive_response(client_socket);  // 接收响应
}void server() {server_socket = listen_for_client_connection();  // 服务器监听客户端连接client_socket = accept_connection(server_socket);  // 接受连接receive_request(client_socket);  // 接收请求send_response(client_socket);  // 发送响应
}
// 注释: 此代码示例展示了客户机-服务器系统进行进程间通信的方法

http://www.ppmy.cn/news/1530426.html

相关文章

linux静态路由表

使用ip route命令临时添加静态路由表 使用ip route命令添加静态路由。例如&#xff0c;要添加一条到达192.168.1.0/24网络的静态路由&#xff0c;通过网关192.168.0.1&#xff0c;您可以执行&#xff1a; ip route add 192.168.1.0/24 via 192.168.0.1 dev enp1s0编辑网络配置…

数据结构之结构体

1.求 sizeof(name1)&#xff1f;(晟安信息) struct name1{ char str; //1 short x;//2 int num;//4 }; 答案&#xff1a;8字节 2.(电工时代) typedef struct _a { char c1;//1 long i;//8 char c2;//1 double f;//8 }a; typedef struct _b { char c1;//1 char c2;//1 long i;//…

浅拷贝和深拷贝(Java 与 JavaScript)

一、Java 浅拷贝和深拷贝 在Java中&#xff0c;浅拷贝和深拷贝的主要区别在于对对象的引用和内容的复制方式。 浅拷贝 Java 的类型有基本数据类型和引用类型&#xff0c;基本数据类型是可以由 CPU 直接操作的类型&#xff0c;无论是深拷贝还是浅拷贝&#xff0c;都是会复制出…

matlab中如何进行强制类型转换

在MATLAB中&#xff0c;强制类型转换通常用于将一种数据类型转换为另一种数据类型。这在进行数值计算时尤其有用&#xff0c;尤其是当你需要确保变量具有特定类型以符合函数或操作的输入要求时。MATLAB提供了几种不同的方法来执行强制类型转换。 1. 使用函数进行转换 MATLAB提…

基于Java,SpringBoot和Vue的仓库管理商品管理电商后台管理系统

摘要 基于Java、Spring Boot和Vue的仓库管理系统是一个现代化的库存管理解决方案&#xff0c;旨在提高仓库运营效率和准确性。系统采用Java作为后端开发语言&#xff0c;结合Spring Boot框架简化配置和部署过程&#xff0c;实现业务逻辑和数据处理。前端使用Vue.js构建用户界面…

什么是网络安全网络安全包括哪几个方面学完能做一名黑客吗?

&#x1f91f; 基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 提及网络安全&#xff0c;很多人都是既熟悉又陌生&#xff0c;所谓的熟悉就是知道网络安全可以保障网络服务不中断。那么到底什么是网络安全?网络安全包括哪几个…

【Unity踩坑】Textmesh Pro是否需要加入Version Control?

问题&#xff1a;如果Unity 项目中用到了Textmesh pro&#xff0c;相关的文件是否也需要签入呢&#xff1f; 回答&#xff1a; 在使用 Unity 的 Version Control&#xff08;例如 Plastic SCM 或 Git&#xff09;时&#xff0c;如果你的项目中使用了 TextMesh Pro&#xff0c…

有限元方法仿真弹性体 (Finite Element Method, FEM)

Finite Element Method (FEM, 有限元方法) 时一种将物体看作许多有体积的微小单元来进行仿真的方法。比如将二维图形拆分成若干三角形&#xff0c;将三维体拆分成若干四面体。这种方法能够很好的模拟弹性体的形变等特点&#xff0c;能够在数学上证明其仿真方法一定收敛到解析解…