进程是操作系统中程序执行的基本单位。一个进程包括了多个组成部分,主要可以分为以下几个方面:1. 程序代码:•进程包含了一段可执行的程序代码,也就是实际执行的指令序列。2. 进程控制块 (PCB):•PCB 是操作系统用来管理进程的数据结构,包含了操作系统控制和管理进程所需的所有信息。•PCB 包括的信息通常有: •进程标识符(PID)•进程状态(运行、就绪、阻塞等)•CPU 寄存器的值(包括程序计数器 PC)•内存管理信息(如页表)•访问权限•等待的资源和条件变量•优先级•父进程和子进程的链接•时间片•I/O 状态信息3. 内存空间:•进程有自己的私有内存空间,包括: •代码段:存放程序的机器指令。•数据段:存放程序的全局变量和静态变量。•堆:动态分配的内存区域。•栈:用于函数调用时的局部变量和函数参数。4. 打开的文件描述符:•进程可以打开文件和其他 I/O 资源,每个打开的资源都有一个文件描述符。•文件描述符是用于标识这些资源的小整数。5. 环境变量:•进程可以拥有自己的环境变量集,这些变量通常用于配置程序的行为。6. 资源:•进程可以占用系统资源,如 CPU 时间、内存、磁盘空间等。7. 线程:•多线程进程中的每个线程都是进程的一部分,共享进程的大部分资源,但有自己的栈空间和寄存器状态。进程的状态进程的状态通常包括:•运行:进程正在 CPU 上执行。•就绪:进程准备好执行,但等待 CPU 时间片。•阻塞:进程因等待某种事件(如 I/O 操作完成)而暂时不能执行。进程的生命周期进程通常经历以下阶段:1. 创建:当一个程序启动时,操作系统创建一个新的进程。2. 执行:进程在其生命周期内执行程序代码。3. 结束:进程完成其任务或遇到错误时终止
线程是操作系统中程序执行的基本单位之一,它是进程内的一个执行实体。一个线程通常包括以下几个组成部分:1. 线程标识符 (TID):•每个线程都有一个唯一的标识符,称为线程 ID (TID),用于区分同一进程中的不同线程。2. 线程控制块 (TCB):•TCB 是操作系统用来管理线程的数据结构,包含了操作系统控制和管理线程所需的所有信息。•TCB 包括的信息通常有: •线程状态(运行、就绪、阻塞等)•CPU 寄存器的值(包括程序计数器 PC)•栈指针•优先级•等待的资源和条件变量•线程间通信的信息3. 栈空间:•每个线程都有自己的独立栈空间,用于存储函数调用时的局部变量和函数参数。4. CPU 寄存器状态:•线程在执行时,会使用 CPU 的寄存器。当线程被切换时,这些寄存器的值会被保存在线程的 TCB 中。5. 线程局部存储 (TLS):•TLS 是一种特殊的存储区域,其中的变量只能被同一个线程访问。6. 资源引用:•线程可以持有对资源的引用,如文件描述符、互斥锁等。线程与进程的关系线程与进程的主要区别在于它们如何共享资源:•共享资源:•线程通常共享同一进程中的大部分资源,如内存空间、打开的文件描述符、环境变量等。•这意味着一个线程可以访问另一个线程的全局变量和静态变量。•独立状态:•每个线程都有自己的栈空间和寄存器状态,这意味着线程之间可以独立执行。•调度和切换:•线程间的切换通常比进程间的切换开销小,因为线程共享大部分资源,不需要切换地址空间。线程的状态线程的状态通常包括:•运行:线程正在 CPU 上执行。•就绪:线程准备好执行,但等待 CPU 时间片。•阻塞:线程因等待某种事件(如 I/O 操作完成或等待互斥锁)而暂时不能执行。线程的生命周期线程通常经历以下阶段:1. 创建:当一个进程启动时,最初只有一个主线程。可以通过创建新的线程来扩展并发能力。2. 执行:线程在其生命周期内执行程序代码。3. 结束:线程完成其任务或遇到错误时终止。总结
进程线程
1.进程的概念
2.进程 和 程序
将 存放 硬盘中程序 ,加载到内存中,运行起来,就是进程
eg:
QQ
3.进程的组成
进程 = PCB + text|data|bss|堆栈
PCB --- 操作系统 Process control block
struct task_struct
{
....
}
//宏观并行
//微观串行
4.进程的状态 //伴随进程的生命周期
三态模型 (就绪态 -- 执行态 --阻塞态)
linux操作系统 (R-S-D-T-Z)
命令
top
ps -elf | grep a.out
ps aux |grep a.out
pstree -sp pid号
kill
killall
5.创建进程 //编程
fork
复制 //
注意:
1.fork之后,父进程与子进程 都是从fork下一话话开始执行 //pc (Program Counter)
2.fork之后,返回值
父子进程,各自返回了一次
3.fork之后,父子进程运行顺序
取决于 操作系统调度
4.fork之后,父子进程 各自拥有独立的4g内存空间
父进程和子进程之间数据不共享,独立的。
5.fork之后,程序处理的逻辑
//父进程 原本独立要运行的代码
pid = fork();
//都可以执行到的部分
if (pid >0)
{
//父进程
}else if (pid == 0)
{
}
//都会执行的部分
6.进程的执行
a.子进程做与父进程相同的事情 --- 创建子进程 执行任务即可
b.子进程做与父进程不同的事情 --- fork+exec
exec
l vs v //主要是第二个参数 的传参方式不同
p //表示寻找可以执行文件 是通过PATH环境变量
e //e表示可以给要执行的程序,传递一个环境变量
7.回顾
//1.创建进程 -- fork
//2.进行的执行
a.子进程与父进程做相同的时间
eg: 12306 淘宝
b.子进程做的事情与父进程不相同
eg: shell
//shell --- fork --shell+exec
exec函数族 //启动一个新的程序
execl
execv //"~" /home/linux
execlp
execvp
execle
execvpe
//3.进程退出
两大类:
正常退出
a.return 从main函数
b.exit
atexit // 注册 "退出清理函数" 的函数
IO缓冲清理
c._exit
异常退出 ---信号结束掉
abort
signal
//4.wait
wait //功能:
//1.获取子进程退出状态
//2.回收资源 //会让僵尸态的子进程销毁
注:
1.wait的本身 是一个 阻塞操作
会使调用者 阻塞
2.父进程要获得子进程的退出状态
子进程
exit(退出状态值)
退出状态值 只有最低8位有效 [0~255]
父进程
wait(&status)
获取到退出状态值
WIFEXITED() //先判断是否为正常退出
WEXITSTATUS() //获取到exit传递的退出状态值
练习:
举例:
无人机
1.控制模块
2.视频模块
3.传输模块
4.存储模块
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:
等待子进程状态发生变化
参数:
@pid
pid = -1 //表示等待所有子进程
pid > 0 //表示等待 指定的子进程状态改变
eg:
100 //表示等待pid号为100的子进程状态改变
@wstatus //表示获取到 子进程 状态信息
@options //选项
//可以不阻塞 WNOHANG //hang on
//默认是阻塞 0
wait(&wstatus) <=> waipid(-1,&wstatus,0)
非阻塞调用:
waitpid(-1,&wstatus,WNOHANG); //表示非阻塞调用
非阻塞 和 阻塞
1.阻塞
会父进程处理逻辑
2.非阻塞
父进程 会去查看 子进程状态改变
但是,如果没有发生改变,父进程不阻塞,
整个程序继续往下
非阻塞 必须 套在循环中处理 //轮询
总结:
1.wait 和 waitpid都是 等待子进程状态改变
2.wait 是一种阻塞调用
3.waitpid 可以实现非阻塞调用
进程退出:
处理方式
wait //阻塞方式 --- 调用进程 一般不做额外的事情
waitpid //非阻塞的方式 --- 调用进程 逻辑一般不受影响
waitpid 想要处理到子进程
必须套在循环中
练习:
创建3个子进程
子进程打印 自己的pid号之后,退出
父进程 回收 第二个子进程
//waitpid //回收指定的子进程
---------------------------------------------------------
1.创建 ---fork
//创建一个子进程
//创建多个子进程
//创建好之后,能区分父子进程代码的逻辑
2.执行
a.做与父进程相同的事情
b.做与父进程不同的事情
fork + exec //minishell
3.进程的退出
return
exit
_exit
//atexit
4.两个特殊状态
孤儿进程 --- 没有危害 ,不需要处理
僵尸态进程 --- 处理
wait
waitpid
---------------------------------------------------------
多任务的技术: 线程
1.什么是线程
线程 --轻量级的进程
light weight
2.为什么需要线程?
进程的产生 ----- fork
线程 --- 轻量级的进程
进程 --- 重量级的进程
线程 成为 CPU执行的最小单位
进程 成为 资源分配的基本单位
线程
创建 和 调度 时空开销都比进程小
3. 线程组成
线程tid //thread
程序计数器
寄存器集合
栈
4. 线程与进程的关系
a.线程 是 存在于 进程中的
b.线程 共享了进程的资源 (代码段,数据段,打开一些文件,信号等)
c.线程结束,不一定导致进程结束
5.线程的编程
类似与进程过程
线程函数
Red hat / IBM
//thinkpad
ubuntu
contos //服务器上 --- linux系统运维
red hat 实现的一套线程函数 //NPTL线程库
NPTL (New Posix Thread Library)
a.线程的创建 pthread_create
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
功能:该函数可以创建指定的一个线程。
参数:
@thread 线程id,需要实现定义并由该函数返回。
@attr 线程属性,一般是NULL,表示默认属性。(可结合性+分离属性)
默认(可结合性) -- 自己手动回收
分离属性 -- 系统自动回收
@start_routine -- 线程执行函数 (线程回调函数)
//指向[指针函数的] 函数指针。
本质上是一个函数的名称即可。
称为
th 回调函数,是线程的执行空间。
{
}
//注: 线程回调函数 --完成线程任务功能的函数
// 需要调用者 自己实现
@arg 回调函数的参数,即参数3的指针函数参数。
返回值:成功 0
失败 错误码
练习:
创建两个线程
要求,两个线程,打印不一样内容
注:
1. 主函数所在的执行流 --- 主线程
2. 其它的线程 --- 子线程(次线程)
3. 各个线程间 低位是平等 //对等
获得线程tid:
pthread_self(); //在那个线程中调用,获得的就是那个线程的tid
练习:
主函数中定义一个 栈上的变量
int a=1;
创建两个线程
线程1 对 a 加1
线程2 对 a 加2
练习:
定义一个全局变量
int a=1;
创建两个线程
线程1 对 a 加1
线程2 对 a 加2
b.线程的执行 --- 就体现在线程的 执行函数(回调函数)上
c.线程的退出
方式1
pthread_exit
void pthread_exit(void *retval)
功能:
结束调用的线程
参数:
@retval //退出状态值 //传的是,退出状态值 对应的地址
注意:
1.pthread_exit 本身表示结束线程
如果用在main函数中 表示结束主线程
主线程结束 并不表示进程
此时,
主线程执行流结束,进程会在其余线程都结束后,结束
d.线程的资源回收
int pthread_join(pthread_t thread, void **retval);
功能:
等待线程结束
参数:
@thread --- 线程tid
@retval --- 用来保存,退出状态值,所在空间的地址
返回值:
成功 0
失败 错误码
注:
线程退出时,可以带出退出状态值,
但是传的是,退出状态值对应空间的地址