E-moss,程序员,爱好阅读和撸狗,主要从事iOS开发工做,公众号:知本集。
主要分享和编写技术方面文章,不按期分享读书笔记,亦可访问“知本集”Git地址:https://github.com/knowtheroot/KnowTheRoot_iOS,欢迎提出问题和讨论。
复制代码
Git地址:github.com/knowtheroot…git
最基本的单位是线程,一个任务包含一个或多个线程。程序员
线程(thread)定义了match中最小的执行单元。github
线程表示的是底层的机器寄存器状态以及各类调度统计数据。bootstrap
线程从设计上提供了调度所须要的大量信息,同时尽量地维持最小开销。bash
这里以简化的代码为例子:数据结构
struct thread {
/* 频繁读,但不多修改的字段 */
queue_chain_t links; /* 运行队列/等待队列的链表链接 */
wait_queue_t wait_queue; /* 所在的等待队列 */
...
...
/* thread_invoke中更新/使用的数据 */
vm_offset_t kernel_stack; /* 当前的内核栈 */
vm_offset_t reserved_stack; /* 预留的内核栈 */
...
...
/*
* 线程状态位
*/
#define TH_WAIT 0x01 /* 在队列中等待 */
#define TH_SUSP 0x02 /* 中止,或请求中止 */
#define TH_RUN 0x04 /* 正在运行或在运行队列中 */
#define TH_UNINT 0x08 /* 在不可中断的等待 */
#define TH_TERMINATE 0x10 /* 终止时同时运行 */
#define TH_TERMINATE2 0x20 /* 添加到终止队列 */
#define TH_IDLE 0x80 /* 空闲线程 */
...
...
/* 调度信息 */
sched_mode_t sched_mode; /* 调度模式 */
sched_mode_t saved_mode; /* 在被迫模式降级时保存的模式 */
...
...
/* 调度相关的状态位 */
integer_t sched_pri; /* 当前调度的优先级 */
integer_t priority; /* 基础优先级 */
integer_t importance ; /* 任务相关的重要性 */
...
...
/* 定时相关的数据结构 */
timer_data_t user_timer; /* 用户定时器 */
...
...
}
复制代码
能够发现,thread的数据结构是很是巨大的,所以大部分建立线程的时候都是从一个通用的模板复制而来,这个模板使用默认值填充这个数据结构。这个模板名为thread_template。
内核引导的过程当中,调用thread_bootstrap()方法负责填充这个模板。
thread_create_internal()函数会分配新的线程数据结构。
Mach API的thread_create() 就是经过thread_create_internal()实现的。架构
Mach将任务定义为线程的容器。
资源是在任务这个层次处理的,线程只能经过端口访问包含这个线程的任务中分配的资源和内存。函数
任务(task)是一种容器对象。虚拟内存空间和其余资源都是经过这个容器对象管理的。
资源被进一步抽象为端口。
所以资源的共享实际上至关于容许对对应端口进行访问。spa
每个BSD进程(也就是OS X进程)都在底层关联了一个Mach任务对象。操作系统
相对于线程,任务是一个比较轻量级的数据结构:
struct task {
/* 同步相关的信息 */
unit_32_t ref_count; /* 引用计数 */
boolean_t active; /* 任务尚未终止 */
boolean_t halting; /* 任务被中止 */
...
...
/* 杂项 */
vm_map_t map; /* 地址空间映射 */
...
...
/* 任务中的线程 */
queue_head_t threads; /* 用FIFO队列保存线程 */
int thread_count; /* 线程队列中的线程数 */
unit32_t active_thread_count; /* 活动的线程数 */
integer_t priority; /* 线程基础优先级 */
integer_t max_priority; /* 线程的最高优先级 */
...
...
//每一个任务都有本身私有的端口名称空间
struck ipc_space *itk_space;
...
...
#ifdef MACH_BSD
void *bsd_info; //指向BSD进程对象
#endif
}
复制代码
就自己而言,任务是没有生命的。任务存在的目的就是要成为一个或多个线程的容器。
任务中的线程都在threads成员中维护,这是一个包含thread_count个线程的队列。
大部分针对任务的操做实际上就是遍历给定任务中的全部线程,并对这些线程进行对应的线程操做。
帐本(ledger)是Mach任务的配额记帐和设置限制所须要的机制。
资源(通常指CPU资源和内存资源)能够在帐本间转移。
在任什么时候刻,内核都必须可以得到当前任务和当前线程的句柄。
内核分别经过:
两个函数来完成。
以上两个函数都是对“fast”版本的函数调用的宏。既:
Mach提供了完整的一套用于操做任务的API。
这里列举几个接口:
Mach任务相关API | 用途 |
---|---|
mach_task_self() | 获取任务端口,带有发送权限的名称 |
task_create(task_t target_task, ledger_array_t ledgers, mach_msg_type_number_t boolean_t task_t *child_task) | 以target_task为父任务建立一个child_task。 |
相似任务相关的API,Mach还提供了丰富的线程管理API。这些API大部分都和任务API的功能相似。
实际上,任务API一般的实现方法就是遍历任务中的线程列表,而后对每一个线程执行对应的操做。
这些调用大部分都是经过Mach消息实现的。
注意:phtread中的指POSLX的系统调用。
Mach内核提供了一组线程控制的函数,这些函数只能内核态中调用,这里暂时不作展开。
咱们特别关注一下线程建立的API。
这部分接口定义在<mach/ARCH/task.h>中:
Mach线程API | 用途 |
---|---|
thread_create(task_t parent, thread_act_t *child_act) | 在parent任务中建立一个线程,将结果返回在child_act中 |
thread_create_running(task_t parent, thread_state_flavor_t flavor, thread_state_t new_state, mach_msg_type_number_t nsCnt, thread_act_t *child_act) | 在parent任务中建立一个线程,其初始状态为new_state,thread_state_t与机器架构有关 |
注意第一个参数是task_t,这个参数表示建立线程的任务。
从Mach的角度看,线程能够建立在任何任务中,只要用户具备任务对应的端口便可。
当调用pthread_create()的时候,底层转而会调用Mach的APi调用thread_create()方法。
建立一个线程并不困难,可是要让线程作有意义的事情则不是那么容易了。这一内容将在后面讨论。