Linux进程、线程——保姆级助理解

目录

1、进程(Process)

1.1 进程基本概念:

1.2 进程分类

1.3 进程的特征

1.4 进程和程序的区别

1.5 进程的状态

1.6 进程的创建——Fork()函数

1.6.1 简介

1.6.2 使用

1.7 进程终止

2、线程(Thread)

1.1 线程基本概念:

1.2 线程的一些相关知识

1.3 多线程的缺点

1.4 创建线程

1.4.1 创建线程 pthread_create

1.4.2 结束线程 pthread_exit

1.4.3 线程等待 pthread_join

1.4.4 线程分离 pthread_detach

1.4.5 助理解线程等待函数和线程分离函数

通俗理解

3、进程和线程的区别

3.1 区别

3.2 举例 

3.3 总结


        学习Linux的同学都知道,进程、线程这两个东西,有些同学理解能力强的看书上的概念就可以理解是什么,或者有些初学者,怎么看都看不明白;别担心,下面我将分别讲解进程、线程的概念再举相应的生活中的例子助理解,废话不多说,开始吧。

1、进程(Process)

有些童鞋觉得进程离我们很远,其实不然,我们可以随时看到,最直观的是打开我们电脑任务管理器。如下图:

1.1 进程基本概念:

对于进程的概念,我讲出三种概念,那种便于理解就理解那种,希望能够帮助需要的人。

① 描述和管理程序的"运行过程"称为进程。在Windows中可以打开任务管理器查看进程

② 进程是程序在计算机上的一次执行活动。一个运行的程序,可能有多个进程。进程在操作系统中执行特定的任务。进程是正在执行的一个程序的实例,通常是由程序,数据集合和进程控制块三部分组成。

③进程是一个程序的执行实例,具有独立的内存空间和资源。操作系统为每个进程分配独立的资源,如内存、文件句柄等。进程之间是相互独立的,一个进程的崩溃不会影响其他进程。

为了便于理解,举一个生活中的例子,自行想象一下。

独立的房子:想象每个进程是一栋独立的房子,每栋房子有自己的院子、厨房、卧室等(内存和资源)。房子之间是独立的,一个房子的火灾不会直接影响到另一个房子。

1.2 进程分类

进程一般分为交互进程、批处理进程、守护进程。

①交互进程:是由shell启动的进程,它既可以在前台运行,也可以在后台运行。交互进程在执行过程中,交互进程在执行过程中,要求与用户进行交互操作。简单来说,就是用户需要给出某些参数或者信息,进程才能继续执行。

②批处理进程:与windows原来的批处理很类似,是一个进程序列。该进程负责按照顺序启动其它进程。

③守护进程:是执行特定功能或者执行系统相关任务的后台进程。守护进程只是一个特殊的进程,不是内核的组成部分。许多守护进程在系统启动时启动,直到系统关闭时才停止运行。而某些守护进程只是在需要时才会启动,比如FTP或者Apache服务等,可以在需要的时候才启动该服务。

1.3 进程的特征

1.动态性:进程是程序的一次执行过程,动态的产生,动态的消亡。

2.并发性:进程同其他进程一起向前推进。

3.异步性:每个进程按照自己各自的速度向前推进。

4.独立性:每个进程都是一个独立的实体,有自己独立的地址空间、资源和控制流。这意味着一个进程的执行不会直接影响其他进程的执行。

1.4 进程和程序的区别

1.动态与静态:进程是动态的,它是程序的一次执行过程;程序是静态的,它是一组指令的有序集合。

2.暂存与长存:进程是暂存的,它在内存上短暂驻留;程序是长存的,它在磁盘或者其他介质上长期保存。

3.程序和进程的对应关系:一个程序可能有多个进程(一个程序运行多次就会产生多个进程)。

1.5 进程的状态

1.运行状态(Running):进程已经占有CPU,并且在CPU上运行。

2.就绪状态(Ready):具有运行条件但没有CPU而暂时不能运行,处于就绪态的进程只要占有CPU便立马可以运行。

3.阻塞状态(Block):进程因为等待某项服务的完成或者信号而不得不停下来,例如调用系统调用等待执行结果、I/O操作操作、等待合作进程的信号......

实际上不同的操作系统有不同的进程状态,某些操作系统甚至具有新建态(new)和终止态(terminate):

Linux的进程状态:

1.可运行态:Linux没有就绪态,它把占有CPU的进程和处于就绪队列的进程的状态统称为可运行态。

2.阻塞态:Linux分为浅度阻塞和深度阻塞。浅度阻塞的进程可以被其他进程的信号或者时钟唤醒,反之深度阻塞的进程则不能。

3.僵尸态:进程终止运行时所处的状态,处于这个状态的进程会释放大部分资源。

4.挂起态:当调试程序时这个进程就处于挂起态。

1.6 进程的创建——Fork()函数

1.6.1 简介

fork()函数的功能是创建一个新的进程,新进程为当前进程的子进程,那么当前的进程为父进程。在一个函数中,可以通过fork()函数的返回值判断进程是在子进程中还是在父进程中。

①一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。

②一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己

③fork()函数调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
(1)在父进程中,fork()函数返回新创建子进程的进程ID
(2)在子进程中,fork()函数返回0
(3)如果出现错误,fork()函数返回一个负值;

1.6.2 使用

//示例
#include <stdio.h>
#include <unistd.h> int main()  
{pid_t pid;pid = fork();    //创建新进程if (pid  == 0) //返回子进程{ printf("child pid: %d\n", getpid());} else {printf("pid: %d\n", pid);//父进程中返回子进程的pidprintf("father pid: %d\n", getpid());}
}

一个父进程希望复制自己,使父子进程同时执行不同的代码段。
比如在网络服务程序中,父进程等待客户端的服务请求。当请求到达时,父进程调用fork()使子进程处理此请求;而父进程继续等待下一个请求。

②一个进程要执行一个不同的程序。这个在shell下比较常见,这种情况下,fork()之后一般立即接exec函数。

③fork()函数出错可能有两种原因:
(1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN
(2)系统内存不足,这时errno的值被设置为ENOMEM

每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。

fork()函数执行完毕后,出现两个进程。

vfor()函数

(1)vfork
基本功能和fork相同
(2)区别:
vfork()函数创建新进程的主要目的是exec一个新程序。
vfork()函数并不复制父进程的地址空间,并和父进程的内存数据share一起用。
vfork()函数保证子进程先运行。
当子进程调用exit()或exec()后,父进程往下执行。

1.7 进程终止

进程终止的5种正常情况
①在main中执行return(在main函数中会结束当前进程;子函数中,会返回调用当前函数的调用位置,进程从下个C语句开始执行)
②调用_exit 或_Exit(结束当前的进程,不对缓存区刷新)
③调用exit(结束当前的进程,并且会刷新缓存区,关闭没有关闭的文件等)
④进程的最后一个线程执行了返回语句
⑤进程的最后一个线程调用了pthread_exit函数

进程的3种异常终止方式

①调用abort,产生SIGABRT信号

进程收到某些信号

最后一个线程对“取消”请求作出响应

2、线程(Thread)

1.1 线程基本概念:

对于线程的概念,也是进程一样,讲多个概念。

①线程是进程中的一个执行单元,一个进程可以包含多个线程。线程共享进程的资源,如内存和文件句柄,但每个线程有自己的程序计数器、寄存器和栈。

②线程是包含在进程内。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,线程之间资源是共享的。

③线程是CPU直接运行的实体,是CPU调度的基本单位。一个进程可以有多个执行路径,这些路径叫做线程。多个线程可以同时共享CPU,从而实现并发。

生活中的例子:

房子里的房间:想象一个进程是一栋房子,线程是房子里的房间。房子里的房间(线程)共享房子的院子、厨房等(进程的资源),但每个房间有自己的家具(程序计数器和栈)。房间之间可以并行使用资源,但一个房间的操作不会直接影响到其他房间。

1.2 线程的一些相关知识

①如果只有一个线程,任务就是顺序执行的。一个进程可以多个线程,每条线程并行执行不同的任务,引入多线程可以在执行某个任务的过程中,执行其他任务。所以在耗时多任务中,应用非常广泛。

②线程通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源。如:虚拟地址空间,文件描述符和信号处理等等

③线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。

④同一进程中的多个线程有各自的调用栈(call stack)寄存器环境(register context)本地存储(thread-local storage)


⑤在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。

单线程程序:整个进程只有一个线程,不适用多线程技术时都是单线程程序,这个线程称为主线程。

多线程程序:整个进程有至少两个线程,多个线程当中一定存在一个主线程。

1.3 多线程的缺点

多线程并不是完美的,它也会带来许多让人头疼的问题。例如程序调试起来是非常困难的,因为是多个执行流并发执行的;并发的过程难以控制,因为CPU的调度是随机的,我们不能预测线程安全问题,这也是最严重的问题,当多个线程同时访问同一份资源时,就会产生数据冲突、数据不一致等等问题。

1.4 创建线程

1.4.1 创建线程 pthread_create

创建线程是多线程编程的第一步,理解线程创建是多线程编程的关键;mian 函数运行时,系统会自动创建一个线程,称为主线程。通过 pthread_create创建的线程,称为子线程。创建线程的函数如下。

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

线程创建函数包含四个变量,分别为:

1. 一个线程变量名,被创建线程的标识;thread用于存储新线程标识符的变量。

2.线程的属性指针,缺省为NULL即可;attr用于设置新线程属性的指针,通常可以传入NULL以使用默认属性。

3.被创建线程的程序代码;start_routine新线程的入口函数,是线程执行的起点。

4. 程序代码的参数;arg传递给入口函数start_routine的参数。

返回值

1.若线程创建成功,返回0。

2.若线程创建失败,返回非0的错误码。

pthread_create函数的使用示例

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>// 线程函数
void* thread_function(void* arg) 
{int thread_num = *((int*)arg);printf("Hello from thread %d\n", thread_num);pthread_exit(NULL);
}int main() 
{pthread_t threads[5];int thread_args[5];int result_code;// 创建5个线程for (int i = 0; i < 5; ++i) {thread_args[i] = i;printf("Main: creating thread %d\n", i);result_code = pthread_create(&threads[i], NULL, thread_function, &thread_args[i]);if (result_code != 0) {printf("Error creating thread %d, error code: %d\n", i, result_code);exit(EXIT_FAILURE);}}// 等待所有线程结束for (int i = 0; i < 5; ++i) {result_code = pthread_join(threads[i], NULL);if (result_code != 0) {printf("Error joining thread %d, error code: %d\n", i, result_code);exit(EXIT_FAILURE);}}printf("Main: all threads completed.\n");return 0;
}

1.4.2 结束线程 pthread_exit

线程通过调用pthread_exit()函数终止执行,就如同进程在结束时调用exit函数一样。这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。

void pthread_exit(void *retval);//retval用于存放线程结束的退出状态

1.4.3 线程等待 pthread_join

pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。

  • 作用:等待一个线程结束,并回收该线程的资源。
  • 特点:这个函数会阻塞调用它的线程,直到被等待的线程终止。
  • 使用场景:当你需要确认一个线程已经结束并获取它的退出状态时使用。
int pthread_join(pthread_t thread, void **retval);
/*示例:
假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:pthread_t thread    //thread是要等待结束的线程的标识
void **thread_return     //指针thread_return指向的位置存放的是终止线程的返回状态。*/
//调用实例:
pthread_join(thread, NULL);

1.4.4 线程分离 pthread_detach

即主线程与子线程分离,子线程结束后,资源自动回收。

在POSIX线程库(pthread)中,pthread_detach函数用于将一个线程的状态设置为分离(detachedstate)状态。一个分离状态的线程在终止后,其资源会立即被回收,而不需要其他线程调用pthread_join来等待它的结束。这样可以避免资源泄漏,同时也简化了线程管理。

返回值:pthread_detach() 在调用成功完成之后返回零。其他任何返回值都表示出现了错误。如果检测到以下任一情况,pthread_detach()将失败并返回相应的值。
 

  • 作用:将一个线程设置为分离状态。分离状态的线程在结束时,系统会自动回收它的资源。
  • 特点:调用这个函数不会阻塞调用它的线程。分离状态的线程结束后,资源会自动回收,不需要通过 pthread_join 手动回收。
  • 使用场景:当你不关心线程的退出状态,只希望它结束时能自动回收资源时使用。

1.4.5 助理解线程等待函数和线程分离函数

通俗理解

  • pthread_join:相当于你等你的朋友回家,等他回家了,你才能做其他事情。
  • pthread_detach:相当于你告诉朋友,不用告诉你他什么时候回家,他回家了自己开门,你继续做你的事情就好。

3、进程和线程的区别

3.1 区别

①内存空间:

进程:每个进程有自己独立的内存空间。
线程:同一进程中的线程共享内存空间。

②资源开销:

进程:创建和切换进程的开销较大。
线程:创建和切换线程的开销较小。

③通信方式:

进程:进程之间通信需要使用进程间通信机制(IPC),如管道、信号、共享内存等。
线程:线程之间直接共享内存,通信更方便,但需要注意同步和互斥。

④独立性:

进程:一个进程崩溃不会直接影响其他进程。
线程:一个线程的异常可能导致整个进程崩溃。

3.2 举例 

举一个生活中的对比助于理解
独立的公司(进程)和公司内部的部门(线程):
进程:每个公司(进程)有自己的办公室、设备和资源(内存和资源)。一个公司的倒闭(进程崩溃)不会直接影响到其他公司(进程)。
线程:每个公司 (进程) 内部可能有多个部门(线程),部门之间共享公司 (进程) 的资源(内存)。一个部门的错误操作(异常)可能会影响整个公司 (进程) 。

3.3 总结

①进程是操作系统分配资源和运行程序的基本单位,具有独立的内存空间和资源。

②线程是进程中的执行单元,共享进程的资源,但有独立的执行路径。

③进程和线程的主要区别在于资源的独立性和通信方式,进程之间更加独立,但通信复杂;线程之间通信方便,但需要注意同步和互斥。

④通过这些对比和生活中的类比,可以更好地理解进程和线程的概念及其应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/42273.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git管理源代码、git简介,工作区、暂存区和仓库区,git远程仓库github,创建远程仓库、配置SSH,克隆项目

学习目标 能够说出git的作用和管理源代码的特点能够如何创建git仓库并添加忽略文件能够使用add、commit、push、pull等命令实现源代码管理能够使用github远程仓库托管源代码能够说出代码冲突原因和解决办法能够说出 git 标签的作用能够使用使用git实现分支创建&#xff0c;合并…

排序 -- 冒泡排序和快速排序

一、 交换排序 1、基本思想 所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换排序的特点是&#xff1a;将键值较大的记录向序列的尾部移动&#xff0c;键值较小的记录向序列的前部移动。 2、常见的交换排序 1、冒泡…

《征服数据结构》SparseArray

摘要&#xff1a; 1&#xff0c;SparseArray的介绍 2&#xff0c;SparseArray的代码实现 1&#xff0c;SparseArray的介绍 前面我们讲过《ArrayMap》&#xff0c;用它来实现哈希表&#xff0c;其中存放key和value的数组长度是存放散列表数组长度的二倍。 在哈希表中如果key值是…

【Python】已解决:(Python xml库 import xml.dom.minidom导包报错)‘No module named dom’

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;&#xff08;Python xml库 import xml.dom.minidom导包报错&#xff09;‘No module named dom’ 一、分析问题背景 在使用Python处理XML文件时&#xff0c;xml…

中科曙光:新智生产力引擎,是一台精密的AI发动机

每一个时代的新生产力释放&#xff0c;都需要新的发动机来释放新动能。比如蒸汽机之于畜力、燃油机之于蒸汽机&#xff0c;发动机的每一次进化&#xff0c;都为新兴工业体系奠定了更强大的生产力底座。 如今&#xff0c;AI作为新质生产力的关键引擎&#xff0c;带来了史无前例的…

240706_昇思学习打卡-Day18-基于MindSpore的GPT2文本摘要

240706_昇思学习打卡-Day18-基于MindSpore的GPT2文本摘要 今天做一个根据一段文章提取摘要的提取器&#xff0c;基于nlpcc2017摘要数据&#xff0c;内容为新闻正文及其摘要&#xff0c;就是训练集及标签。 首先我们来预装以下MindSpore环境 %%capture captured_output # 实验…

LabVIEW干涉仪测向系统

开发了一套基于LabVIEW的软件系统&#xff0c;结合硬件设备&#xff0c;构建一个干涉仪测向实验教学平台。该平台应用于信号处理课程&#xff0c;帮助学生将理论知识与实际应用相结合&#xff0c;深化对信号处理核心概念的理解和应用。 项目背景&#xff1a; 当前信号处理教学…

昇思25天学习打卡营第十四天|Pix2Pix实现图像转换

训练营进入第十四天&#xff0c;今天学的内容是Pix2Pix图像转换&#xff0c;记录一下学习内容&#xff1a; Pix2Pix概述 Pix2Pix是基于条件生成对抗网络&#xff08;cGAN, Condition Generative Adversarial Networks &#xff09;实现的一种深度学习图像转换模型&#xff0c…

为RK3568或RK3288开发板创建交叉编译环境{采用amd64的ubuntu系统配置交叉编译arm64开发环境}(保姆级包括安装QT)超详细记录版

为RK3568或RK3288开发板创建交叉编译环境{采用amd64的ubuntu系统配置交叉编译arm64开发环境}【保姆级包括安装QT】超详细记录版 Chapter1 为RK3568或RK3288开发板创建交叉编译环境{采用amd64的ubuntu系统配置交叉编译arm64开发环境}(保姆级包括安装QT)超详细记录版一. 安装QT程…

picgo+gitee图床配置

node.js安装 刚开始顺着picgo操作,直接跳转到了node.js官网 下载的时候直接 Next,然后可以自定义安装路径,我的安装路径是C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Node.js 然后再在安装路径的根目录下新建两个文件夹,nodecache 和 nodeglobal, 如图所示:…

C++(Qt)-GIS开发-简易瓦片地图下载器

Qt-GIS开发-简易瓦片地图下载器 文章目录 Qt-GIS开发-简易瓦片地图下载器1、概述2、安装openssl3、实现效果4、主要代码4.1 算法函数4.2 瓦片地图下载url拼接4.3 多线程下载 5、源码地址6、参考 更多精彩内容&#x1f449;个人内容分类汇总 &#x1f448;&#x1f449;GIS开发 …

全面解析 TypeScript 泛型的二三事

2024年了相信大家都已经在日常开发的过程中使用上了 TypeScript 了。TypeScript 增强了代码可靠性和可维护性&#xff0c;确保减少运行时错误并提高开发人员的工作效率。 TypeScript 通过类型声明 使得 javascript 拥有了强类型校验。而泛型的是类型声明中最重要的一环&#x…

06浅谈大语言模型可调节参数TopP和TopK

浅谈大模型参数TopP和TopK 大语言模型中的temperature、top_p和top_k参数是用来控制模型生成文本时的随机性和创造性的。下面分享一下topP和topK两个参数的意义及逻辑&#xff1b; top K&#xff08;Top-K Sampling&#xff09; 作用&#xff1a;只从模型认为最可能的k个词中选…

时间、查找、打包、行过滤与指令的运行——linux指令学习(二)

前言&#xff1a;本节内容标题虽然为指令&#xff0c;但是并不只是讲指令&#xff0c; 更多的是和指令相关的一些原理性的东西。 如果友友只想要查一查某个指令的用法&#xff0c; 很抱歉&#xff0c; 本节不是那种带有字典性质的文章。但是如果友友是想要来学习的&#xff0c;…

Amesim中删除计算结果保存计算文件

前言 Amesim在工程应用中计算的结果文件有时会很大&#xff0c;为了节省电脑存储空间&#xff0c;项目结束后可以将计算结果删除进行保存以存档。 操作步骤 具体操作步骤如下&#xff1a; Step1&#xff1a;在①File下打开&#xff08;Open&#xff09;需要删除计算结果的项…

DAMA学习笔记(四)-数据建模与设计

1.引言 数据建模是发现、分析和确定数据需求的过程&#xff0c;用一种称为数据模型的精确形式表示和传递这些数据需求。建模过程中要求组织发现并记录数据组合的方式。数据常见的模式: 关系模式、多维模式、面向对象模式、 事实模式、时间序列模式和NoSQL模式。按照描述详细程度…

第1节、基于太阳能的环境监测系统——MPPT充电板

一、更新时间&#xff1a; 本篇文章更新于&#xff1a;2024年7月6日23:33:30 二、内容简介&#xff1a; 整体系统使用太阳能板为锂电池充电和系统供电&#xff0c;天黑后锂电池为系统供电&#xff0c;本节主要介绍基于CN3722的MPPT太阳能充电模块&#xff0c;这块主要是硬件…

AE的首选项设置

打开AE,点击 编辑->首选项->常规 显示 点击“所有关键帧”&#xff0c;这样显示路径就会显示所有关键帧 导入 将序列素材改为25帧每秒&#xff0c;作为以后制作的默认 媒体和磁盘缓存 根据个人需求选择磁盘缓存的文件夹&#xff0c;如果d盘空间充足&#xff0c;就改成…

Git中两个开发分支merge的原理

一 分支合并 1.1 原理 分支合并&#xff1a;就是将A分支修改后且commit的内容&#xff0c;合并到B分支&#xff0c;这些修改且提交的内容和B分支对应的内容和位置进行比较&#xff1a; 1.不一样的话&#xff0c;提示冲突&#xff0c;需要人工干预。 2.一样的话&#xff0c;…

网页封装APP:让您的网站变身移动应用

网页封装APP&#xff1a;让您的网站变身移动应用 随着移动设备的普及&#xff0c;越来越多的人开始使用移动设备浏览网站。但是&#xff0c;传统的网站设计并不适合移动设备的屏幕尺寸和交互方式&#xff0c;这导致了用户体验不佳和流失。 有没有办法让您的网站变身移动应用&…