Rhythmli's blog Rhythmli's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Rhythmli

知识就是财富
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • Nodejs

  • 博客搭建

  • CSDN迁移

    • Spring IOC
    • Ngnix 阿里云
    • 最长回文子串
    • 面试题xuexixiexue
    • 哈希集合和哈希映射的简单设计
    • leetcode2021.11.03
    • Leetcode2021.11.2
    • JUC学习
    • 869. 重新排序得到 2 的幂
    • Java自动装箱拆箱
    • 55. 跳跃游戏
    • 剑指 Offer II 085. 生成匹配的括号
    • 300. 最长递增子序列
    • Java并发编程之美 01
    • 134. 加油站
    • 139. 单词拆分
    • 岛屿类问题题解
    • 138. 复制带随机指针的链表
    • 347. 前 K 个高频元素
    • 剑指 Offer II 026. 重排链表
    • 剑指 Offer II 025. 链表中的两数相加
    • 剑指 Offer II 014. 字符串中的变位词
    • 剑指 Offer II 010. 和为 k 的子数组
    • 剑指 Offer II 009. 乘积小于 K 的子数组
    • 剑指 Offer II 008. 和大于等于 target 的最短子数组
    • 剑指 Offer II 007. 数组中和为 0 的三个数
    • 剑指 Offer II 006. 排序数组中两个数字之和
    • 剑指 Offer II 002. 二进制加法
    • 129. 求根节点到叶节点数字之和
    • 113.路径总和 II
    • leetcode18. 四数之和
    • 编译OpenCV 以及 openc_contrib 提示缺少boostdesc_bgm.i文件出错的解决
    • fork()浅学习
    • SSM 增删改查
    • springmvc helloworld
    • Spring01 hello实验
    • 树的DFS和BFS
    • leetcode——二分法
    • Halo博客搭建
    • 计算机视觉领域的一些牛人博客,超有实力的研究机构等的网站链接---转载
    • opencv+python+OpenPose姿态实时识别
    • 03.KNN算法 李航统计学习方法
    • 02.感知机 李航统计学习方法
    • 01.最小二乘法拟合 李航统计学习方法
    • Pycharm atplotlib.pyplot图像不显示解决方法
    • 论文阅读01 SVM+kNN图像分类
    • Java简单实现计算器——用数组实现栈
    • 【剑指Offer3】无重复字符的最长子串
    • 【剑指Offer5】最长回文字符串
    • TF-IDF求取文本相似度
    • JAVA_day02
    • JAVA_day01
    • 递归产生回文数
    • 中国象棋QT登录注册以及悔棋功能
    • STM32F4学习笔记(基础介绍篇)
    • leetcode_04 递归,回溯与分治
    • leetcode03_贪心算法
    • leetcode01--链表
    • leetcode_02栈
    • Docker学习入门
    • C/C++编译与链接 程序员的自我修养:链接 装载和库
    • Nginx简单学习
    • JAVA网络编程
    • JVM初步学习
    • Spring简单学习
    • 标准项目格式
    • 设计模式中的几个原则
    • Redis和IDEA简单创建及增删改查
    • Mybatis快速入门01
    • Redis全程学习笔记(附带学习的视频教程)
    • QT入门学习中最基础的那些事儿
    • QT中文输出错误问题:C2001
    • OOP:面向对象编程
    • LINUX常用命令集合(待续)
    • 《C和指针》简单学习笔记
    • 二叉树,栈存储及遍历小程序
    • 数据结构简单学习笔记
    • 扑克牌
    • C++动态内存和智能指针
    • 设计模式之简单工厂模式
    • leetcode_01数组
    • 嵌入式Linux移植应用
    • LINUX 进程与线程 信号量 通信
      • 操作系统 进程与线程 信号量 通信
        • 1 进程
        • 2.线程
        • 3.进程间通信
        • 5.信号量
      • 2.内存管理
        • 1.地址空间
        • 3.进程相关的几个函数
  • 技术
  • CSDN迁移
梁山话事人
最新推荐文章2025-08-18
目录

LINUX 进程与线程 信号量 通信

原文链接:https://blog.csdn.net/qq_39355828/article/details/110839777 (opens new window)

# 操作系统 进程与线程 信号量 通信

# 1 进程

进程模型:每个系统有自己的虚拟CPU,但CPU又在不同进程之间进行切换。一个进程就是一个正在执行的程序,包括程序寄存器和变量当前的值。

进程是某种类型的活动,它有程序。输入和输出以及状态。单个处理器可被若干进程共享,使用某种进程调度算法,决定何时停止一个进程的工作,并转而为另一个进程提供服务。

1.1进程的创建:系统初始化,正在运行的程序执行了创建进程的系统调用,用户请求创建一个新进程,一个批处理作业初始化。父进程(英语:Parent Process)指已创建一个或多个子进程的进程。

1.2进程的终止:正常退出,出错退出,严重错误,被其他进程杀死

1.3进程的层次结构:进程和它的所有子进程以及后裔共同组成一个进程组。

Linux是一个树结构 Windows中是地位相同,每个进程地位都是相等的,句柄标识父与子进程

1.4进程的状态:运行态 就绪态,阻塞态

运行态和就绪态是由调度程序引起的,

1.5进程的实现:

# 2.线程

每个进程有一个进程空间和一个控制线程,在一个应用中也发生着许多活动。并行实体拥有共享同一个地址空间和所有可用数据的能力。进程比线程更轻量级,实现并行计算。

多个线程可以共享公共 内存,允许他们同时访问同一个文件夹,三个进程做不到。所有线程会有完全一样的内存空间

一个进程打开了文件,该文件对该进程中其他线程都可见

每个线程都有自己的堆栈

在用户空间中实现线程:内核对线程包一无所知,从内核角度考虑,但线程进程。用户级线程包可以在不支持线程的操作系统上实现。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#define NUMBER_OF_THREADS 10
void *print_hello_world(void *tid)
{
    printf("Hello World.Greeting from thread %d\n",tid);
    pthread_exit(NULL);
}
int main()
{
    pthread_t threads[NUMBER_OF_TH];
    int status,i;
    for(i=0;i<NUMBER_OF_THREADS;i++)
        printf();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

进程和线程有一个关键的差别:线程完成时,把线程信息保存在线程表中,调用线程带哦都程序来选择另一个要运行的线程。不需要陷入内核,不需要上下文切换。不需要对内存告诉缓存进行刷新。

某个程序调用或者跳转到了一条不存在的内存指令上,就会发生页面故障

在内核中实现线程:

# 3.进程间通信

两个或多个进程写某些共享数据,最后结果取决于运行的精准时序,称为竞争条件

临界区:避免竞争条件需要的是互斥条件:确保某个进程在使用文件夹或者共享变量时候,其他进程不会进行同样的操作。共享内存进行访问的程序片段称为临界区域,适当安排,使得两个进程不可能同时处于临界区内,避免竞争条件。

忙等待的互斥:

1.屏蔽中断:每个进程刚进入临界区后立即屏蔽所有中断,并在就要离开之前再打开所有中断。CPU只有发生时钟中断时候才会进行进程切换,屏蔽后的CPU不会被切换到其他中断。缺点是对于多核处理器不能使用。

2.锁变量:检查,如果内存被占用则设为1,反之为0,0表示临界区内没有进程。但可能会发生同时进入而造成竞争条件。

3.严格轮换法:

连续测试一个变量值直到某个值出现为止,称为忙等待

两个进程严格轮流进入临界区,

while(TRUE)
{
while(turn!=0);
critical_region();
turn=1;
noncritical_region();
}
1
2
3
4
5
6
7

4.Peterson解法

#define FALSE 0
#define TRUE 1
#define N 2
int turn;
int interested[N];
void enter_region(int process)
{
int other;//另一个进程号
other=1-process;//另一个进程
interested[process]=TRUE;//表示感兴趣
turn =process;//设置标志
while(turn==process&&interested[other]==TRUE);//空语句
}
void leave_region(int process)
{
    interested[process]=FALSE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5.信号量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-prqKfDHi-1607345871930)(C:\Users\123\AppData\Roaming\Typora\typora-user-images\1603195016154.png)]

  • 在缓冲区为空时,消费者不能再进行消费
  • 在缓冲区为满时,生产者不能再进行生产
  • 在一个线程进行生产或消费时,其余线程不能再进行生产或消费等操作,即保持线程间的同步
  • 注意条件变量与互斥锁的顺序 ​ 因此需要保持线程间的同步,即一个线程消费(或生产)完,其他线程才能进行竞争CPU,获得消费(或生产)的机会。对于这一点,可以使用条件变量进行线程间的同步:生产者线程在product之前,需要wait直至获取自己所需的信号量之后,才会进行product的操作;同样,对于消费者线程,在consume之前需要wait直到没有线程在访问共享区(缓冲区),再进行consume的操作,之后再解锁并唤醒其他可用阻塞线程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81HLnH8l-1607345871939)(C:\Users\123\AppData\Roaming\Typora\typora-user-images\1603195156901.png)]

假设缓冲区大小为10,生产者、消费者线程若干。生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。

  • items代表缓冲区已经使用的资源数,spaces代表缓冲区可用资源数

  • mutex代表互斥锁

  • buf[10] 代表缓冲区,其内容类型为item

  • in、out代表第一个资源和最后一个资源 `var items = 0, space = 10, mutex = 1; var in = 0, out = 0; item buf[10] = { NULL };

    producer { while( true ) { wait( space ); // 等待缓冲区有空闲位置, 在使用PV操作时,条件变量需要在互斥锁之前 wait( mutex ); // 保证在product时不会有其他线程访问缓冲区

        // product
        buf.push( item, in );  // 将新资源放到buf[in]位置
        in = ( in + 1 ) % 10;
    
        signal( mutex );  // 唤醒的顺序可以不同
        signal( items );  // 通知consumer缓冲区有资源可以取走
    }
    

    }

    consumer { while( true ) { wait( items ); // 等待缓冲区有资源可以使用 wait( mutex ); // 保证在consume时不会有其他线程访问缓冲区

        // consume
        buf.pop( out );  // 将buf[out]位置的的资源取走
        out = ( out + 1 ) % 10;
    
        signal( mutex );  // 唤醒的顺序可以不同
        signal( space );  // 通知缓冲区有空闲位置
    }
    

    }`

6.调度

有多个进程同时竞争CPU,只要有两个或更多的进程处于就绪状态,这种情况就会发生。完成选择工作的这一部分程序称为调度程序,使用的算法称为调度算法。

6.1先来先服务:

6.2最短作业优先

6.3最短剩余时间优先

交互式系统中的调度:

轮转调度 优先级调度 多级调度

# 2.内存管理

# 1.地址空间

把物理地址暴露给进程会带来严重的问题:1.用户程序破坏操作系统,使得系统慢慢停止运行2.同时运行多个程序几乎不可能

多个程序同时处于内存中,不影响,需要 保护和重新定位

地址空间为程序创造了一种抽象的内存,地址空间是一个进程可用于寻址内存的一套地址集合。

基址寄存器与界限寄存器

交换技术:处理内存超载,将空闲进程存储在磁盘上。不运行时不会使用内存。分配额外内存

虚拟内存:每个进程有自己的进程空间,

# 3.进程相关的几个函数

1.获得当前进程的进程ID:get_pid.c

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main()
{
    printf("The current process ID is %d\n",getpid());
    return 0;
}
1
2
3
4
5
6
7
8

2.进程控制相关函数:

2.1 fork和vfork

fork

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
   int count=0;
   pid_t pid;//此时仅有一个进程
   pid=fork();//此时有两个进程再同时运行
   if(pid<0)
   {
       printf("ERROR IN FORK!");
       exit(1);//fork出错时退出
   }
   else if(pid==0)
   printf("子进程数量 子进程的ID是 %d\n",count,getpid());
   else
   printf("子进程数量 子进程的ID是 %d\n",++count,getpid());
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

fork系统调用的作用是复制另一个进程,一个进程调用它,完成后出现两个几乎一模一样的进程,两次返回的区别是子进程的返回值是0,父进程的返回值则是新进程的进程ID。

fork与vfork之间有区别的,fork需要复制父进程的数据段,vfork不需要复制

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
    int count=1;
    int child;
    printf("Before create son,the father's count is: %d\n",count);
    child=vfork();//已经有两个进程在同时运行
    if(child<0)
    {
    printf("error in vfork!");
    exit(1);
    }
    if(child==0)//子进程
    {
    printf("This is son,his pid is: %d and the count is %d\n",getpid(),++count);
    }
    else
    printf("After son,This is father,his pid is : %d and the count is: %d, and the child is %d\n",getpid(),count,child);
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

wait函数

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
   pid_t pc,pr;
   if((pc==fork())<0)
   {
       printf("error in fork!\n");
       exit(1);
   }
   else if(pc==0)//子进程
   {
       printf("This is child process with pid of %d \n",getpid());
       sleep(10);
   }
   else //父进程
   {
       pr=wait(NULL);
       printf("I create a child with pid of %d\n",pr);
   }
    exit(0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

进程 资源分配单位

PCB

链表 树 哈希 三种方法进行进程控制

宏观并行微观串行

僵尸状态 已经释放所有的资源 死亡后被父进程回收 由父进程读到子进程的死因。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xsxCO5rt-1607346856174)(C:\Users\123\AppData\Roaming\Typora\typora-user-images\1603956490379.png)]

内存泄漏 进程活着 ,运行越久

cpulimit 控制进程的停止态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KH00MvqH-1607346856177)(C:\Users\123\AppData\Roaming\Typora\typora-user-images\1603957558318.png)]

fork时父进程返回子进程的PID 子进程返回0

编辑 (opens new window)
#LINUX#订阅专栏#查看详情
上次更新: 2026/08/11, 13:36:18
嵌入式Linux移植应用

← 嵌入式Linux移植应用

最近更新
01
Spring IOC
03-31
02
Git修改分支名
08-11
03
CSS给table的tbody添加滚动条
06-29
更多文章>
Theme by Vdoing | Copyright © 2019-2026 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式