操作系统创建线程
Unix进程的设计思想,实现了 fork exec wait exit 四个精巧的系统调用来支持对进程的灵活管理。
父进程进程通过 fork 系统调用创建自身的副本(子进程);
称为“子进程”的副本可调用 exec 系统调用用另一个程序覆盖其内存空间,这样就可以执行新程序了;
子进程执行完毕后,可通过调用 exit 系统调用来退出并通知父进程;
父进程通过调用 wait 系统调用来等待子进程的退出
我们为什么不设计一个系统调用接口同时实现创建一个新进程并加载给定的可执行文件两种功能
拆分为两个系统调用后,可以灵活地支持 重定向 (Redirection) 等功能
但DOS/WINDOWS还有spawn类函数,
因为DOS是单任务的系统,它只能将"父进程"驻留在机器内再执行"子进程",这就是spawn类的函数
Windows 中, CreateProcess 函数用来创建一个新的进程和它的主线程,通过这个新进程运行指定的可执行文件
posix-spawn
POSIX 标准的 posix_spawn 系统调用则类似 Windows 的 CreateProcess 函数,不过对参数进行了简化
posix-spawn是基于POSIX标准的进程创建方法,可以方便地替代传统的fork()和exec()调用
开发语言创建线程-并发
仓颉语言中通过关键字 spawn 创建仓颉线程-即用户态协程
fork:除了必要的启动资源外,其他变量,包,数据等都继承自父进程,并且是copy-on-write的,也就是共享了父进程的一些内存页,因此启动较快,
但是由于大部分都用的父进程数据,所以是不安全的进程
spawn:从头构建一个子进程,父进程的数据等拷贝到子进程空间内,拥有自己的Python解释器,
所以需要重新加载一遍父进程的包,因此启动较慢,由于数据都是自己的,安全性较高
POSIX标准中的 posix_spawn 或者
Linux 上的 clone 系统调用
###进程方式
线程的并行: 独立的地址空间 独立的资源 相互隔离
线程的并发:进程的并发性指的是多个进程在同一时间段内交替执行的能力
进程
1.进程本身-组成:进程内部由哪些部分构成 Info
进程是动态--需要相应硬件资源支持--时间和空间的生命周期- 进程状态转换功能。
即硬件/虚拟资源 进行 动态绑定和解绑
进程ID 进程描述数据块PCB 进程实体(程序段-数据段和进程控制块Process Control Block)
生命周期状态::包括 创建进程、就绪状态、运行状态、阻塞进程、终止进程等 进程资源回收机制
2.进程相互制约--通信_多个进程之间的组织方式问题
同步
互斥
临界资源和临界区 信号量和P/V值 共享资源
竞态条件 死锁:
进程通信-进程之间的信息交换
1.共享存储-通过对这片共享空间进行写/读操作实现进程之间的信息交换
2.消息传递:进程通过系统提供的发送消息和接收消息两个原语进行数据交换
3.管道通信:管道通信是消息传递的一种特殊方式 (互斥、同步和确定对方的存在) I/O重定向 与 管道(pipe)
4.信号(Signals) 忽略 捕获 终止
方式: 顺序方式 链接方式 索引方式
链接方式和索引方式: 执行指针 就绪队列指针 阻塞队列指针
进程控制: 创建新进程、撤销已有进程、实现进程状态转换等功能 执行具有原子性
用“关中断指令”和“开中断指令”这两个特权指令实现原子性
3.进程调度-调度(scheduling)
指标: 周转时间(turn around),即进程完成时间(completion)与进程到达时间(arrival)的差值
调度机制
最短完成时间优先 STCF—_Shortest Time to Complet First
先来先服务FIFO
可抢占特性
基于时间片的轮转
最早截止时间优先(Earliest Deadline First,EDF)算法
操作系统通过硬件中断机制,支持分时多任务和抢占式调度机制
4.实现:
设计一些数据结构包含的内容及接口
基于应用名的应用链接、加载器、进程标识符 PidHandle、任务控制块 TaskControlBlock、任务管理器 TaskManager、处理器管理结构 Processor
线程的数据不一致问题和竞态条件问题的根本原因是 调度的不可控性
操作系统抽象: 地址空间 、 进程 以及 文件
文件 -文件描述符
内核提供的 sys_open 系统调用让该文件在进程的文件描述符表中占一项,并得到操作系统的返回值–文件描述符
管道: 新的系统调用 sys_pipe 和 sys_close 基于文件的管道
进程能够通过一个统一的接口来读写内核管理的持久存储设备,这样数据可以方便地被长久保存。
进程控制块中有一个 文件描述符表 ,在表中保存着多个 文件 记录信息。
每个文件描述符是一个非负的索引值,即对应文件记录信息的条目在文件描述符表中的索引
基于文件的统一I/O抽象,将标准输入/标准输出的访问改造为基于文件描述符,然后同样基于文件描述符实现一种父子进程之间的通信机制——管
将进程对于标准输入输出的访问修改为基于文件抽象的接口
文件操作
打开(open) :进程只有打开文件,操作系统才能返回一个可进行读写的文件描述符给进程,进程才能基于这个值来进行对应文件的读写;
读(read):进程可以基于文件描述符来读文件内容到相应内存中;
写(write):进程可以基于文件描述符来把相应内存内容写到文件中;
关闭(close) :进程基于文件描述符关闭文件后,就不能再对文件进行读写操作了,这样可以在一定程度上保证对文件的合法访问;
内核中支持命令行参数的解析和传递
IO-设备操作标识
让应用能便捷地访问外设
备驱动程序完成哪些功能:设备扫描/发现 设备初始化 通知设备 接收设备通知 卸载设备驱动时回收设备驱动资源
设备的硬件规范:
操作系统重点关注的是如何对I/O设备进行管理,软件程序员则主要关注I/O设备为软件提供的接口(interface)
件工程师看来,I/O设备就是一堆芯片、电源和其他电路的组
Python 多进程
根据不同的平台, multiprocessing 支持三种启动进程的方法
1.在 Windows 和 macOS 上: spawn 父进程会启动一个新的 Python 解释器进程
2.在 POSIX 系统上可用 : fork POSIX 上为默认值父进程使用 os.fork() 来产生 Python 解释器分叉(除 macOS 之外的)
fork 的代码应当显式地通过 get_context() 或 set_start_method() 来指定
3.支持通过 Unix 管道传递文件描述符的 POSIX 平台上可用,例如 Linux
当程序启动并选择 forkserver 启动方法时,将产生一个服务器进程
multiprocessing.set_start_method('spawn')
或者
ctx = multiprocessing.get_context('spawn')
multiprocessing 支持进程之间的两种通信通道 :队列和管道: Queue 类是一个近似 queue.Queue 的克隆 Pipe() 函数返回一个由管道连接的连接对
Pool 对象,它提供了一种快捷的方法,赋予函数并行化处理一系列输入值的能力,可以将输入数据分配给不同进程处理(数据并行)
进程池: concurrent.futures.ProcessPoolExecutor
生成进程:
创建一个 Process 对象然后调用它的 start() 方法来生成进程
进程通信:
队列和管道
进程间同步
共享内存: 可以使用 Value 或 Array 将数据存储在共享内存映射中
服务进程
Manager() 返回的管理器对象控制一个服务进程,该进程保存Python对象并允许其他进程使用代理操作它们
name pid is_alive()
start() run()
join()
terminate() kill() close()
Python 多线程
_thread --- 低层级多线程 API
threading ---高层级多线程 API
sched 模块定义了一个实现通用事件调度程序的类
sched模块还支持处理一些常见的用例,如定时任务、周期性任务和时间限制的任务
实例化定时器--enter() 方法执行定时任务
延迟执行
固定时间执行
执行多个任务--不同优先级执行不同任务
popen 的返回值是个标准 I/O 流 pclose 来终止
popen相当于是先创建一个管道,fork,关闭管道的一端,执行exec,返回一个标准的io文件指针。
system相当于是先后调用了fork, exec,wait来执行外部命令
popen本身是不阻塞的,要通过标准io的读取使它阻塞
system本身就是阻塞的。
I/O模型
编程语言的io接口,其实是依赖底层的操作系统的实现
IO从概念上来说,总共有5种:
(1)阻塞IO (blocking I/O)
(2)非阻塞IO (nonblocking I/O)
(3)IO多路复用 (I/O multiplexing (select and poll))
(4)事件驱动IO (signal driven I/O (SIGIO))
(5)异步IO (asynchronous I/O (the POSIX aio_functions))
准备数据到内核的缓冲区0--从缓冲区拷贝到用户空间
select,poll属于第三种-IO多路复用模型,
iocp属于第5种异步io模型
epoll,kqueue epoll和kqueue是更先进的IO复用模型
I/O多路复用模型就是通过一种机制,
一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
POSIX API
Windows API
进程管理接口:
exit:进程终止
fork:创建子进程
wait:等待子进程结束
文件管理接口:
open:打开文件
close:关闭文件
read:读取文件内容
write:写入文件内容
目录管理接口:
opendir:打开目录
readdir:读取目录项
closedir:关闭目录
网络通信接口:
socket:创建套接字
bind:绑定套接字到地址
listen:监听传入连接
accept:接受传入连接
线程和同步接口:
pthread_create:创建线程
pthread_join:等待线程结束
pthread_mutex_lock:加锁互斥量
pthread_cond_signal:发送条件变量信号
信号处理接口:POSIX信号库
signal:注册信号处理函数
kill:向进程发送信号
sigaction:设置信号处理动作
时间和日期接口:
time:获取当前时间
clock:获取时钟时间
strftime:格式化时间
共享内存接口:
shmget:获取共享内存标识符
shmat:连接到共享内存
shmdt:分离共享内存
ioctl 接口:
ioctl:控制设备操作
用 fcntl() 和ioctl() 的接口。
epoll 是Linux内核中实现的一种可扩展的I/O事件通知机制,
unix为kqueue)
FreeBSD中类似的实现是kqueue
种IO复用模式之select,poll,epoll,kqueue,iocp分析
POSIX
POSIX 是 可移植操作系统接口 Portable Operating System Interface
系统服务应用程序编程接口
POSIX主要分为四个部分:Base Definitions、System Interfaces、Shell and Utilities和Rationale
编程
裸机编程、 Linux 驱动编程以及 Linux 应用编程
Linux应用编程:
内核态和用户态,应用程序运行在用户态、而内核则运行在内核态。
系统调用是内核提供给应用层的编程接口,属于系统内核的一部分
库函数是属于应用层-运行在用户空间
开发 Linux 应用程序,
通过调用内核提供的系统调用
或使用 C 库函数来开发具有相应功能的应用程序
Linux系统调用接口非常精简(只有250个左右),
Linux 3.2.0版本提供了380个系统调用,而FreeBSD 8.0提供的系统调用超过450个
并发编程
Producer-Consumer模式、Pipeline模式和Future模式
Future模型是将异步请求和代理模式结合的产物
future 本质上表达一个还没有准备好的值
future、promise、delay 和 deferred 是指用于在某些并发编程语言中同步程序执行的构造。
future和promise起源于函数式编程和相关范例,
目的是将值(future)与其计算方式(promise)分离
future最大的好处是将函数的同步调用转换为异步调用,适用于一个交易需要多个子调用且这些子调用没有依赖的场景
C++ 异步编程
std::promise 是一个对象,可以保存某一类型 T 的值,该值在未来某个时间点可以被检索。
std::future 代表一个异步操作的结果
C++20中引入了coroutine,从语言层面而不仅是实现方法层面实现异步操
在Cangjie 仓颉编程语言
仓颉线程会返回一个Future<T>类型的结果
Future 和 Thread 类型
在Rust中,Future作为一个接口在rust 1.36加入标准库中
async:产生一个 Future 对象
在Java中 Future 模式是一种在并发编程中广泛使用的设计模式,用于表示一个可能还没有完成的异步操作的结果
在Java中,Future接口在java.util.concurrent包
在Go
编程中经常遇到在一个流程中需要调用多个子调用的情况,Go标准库并未直接提供Future或Promise这样的抽象概念
这些子调用相互之间没有依赖,如果串行地调用,则耗时会很长,此时可以使用Go并发编程中的future模式
Python
future对象,一般不会直接使用,它是task类的基类,task一部分的功能是future提供的,其是更低级的接口
concurrent.futures 模块
Future对象是submit任务(即带有参数的functions)到executor的实例
current.Futures提供了两种Executor的子类,各自独立操作一个线程池和一个进程池。这两个子类分别是:
- concurrent.futures.ThreadPoolExecutor(max_workers)
- concurrent.futures.ProcessPoolExecutor(max_workers)
在Scala中,可以使用Futures进行并行网络调用。
Future是一个表示异步计算结果的抽象,
bind和callback,类似与boost库的bind和function
并发设计模式
Producer-Consumer模式(生产者-消费者) 非常经典的多线程并发协作的模型 解藕,异步,平衡速度差异
Thread Pool模式(线程池): 这个模式就是为减少开启与关闭线程带来的开销
Master-Slave模式(主仆)
Pipeline模式(流水线)
Promise模式帮我们不需要等待耗时的求值操作,而是拿一个凭证。我们可以先去做些别的,最后再回来取值
mmutable Object模式(不可变对象): 过将对象变为只读的形式来保证线程安全,是比较好的无锁实现
参考
https://man7.org/linux/man-pages/man2/syscalls.2.html
https://github.com/JnuSimba/LinuxSecNotes/tree/master
标签:文件,调用,协程,Python,编程,接口,线程,进程
From: https://www.cnblogs.com/ytwang/p/18569838