1.进程与线程
 linux中调度与执行代码流的基础单位是线程.
 我们通过父进程产生新的子进程,其实是产生一个新的线程.不过这个线程属于一个新的线程组,且是线程组的组长.
 我们通过兄弟线程p产生新的线程q.也是获得新线程的方法.不过这个新线程q和兄弟线程p在同一个线程组.
每个线程拥有一个task_struct对象实例来记录信息和管理自身.每个线程拥有一个独立的线程id.
 每个线程必然属于一个线程组,每个线程组拥有一个组长.
2.线程的状态
 (1). TASK_RUNNING
 要么线程正在cpu中执行,要么线程位于cpu运行队列,可供调度执行.
 (2). TASK_INTERRUPTIBLE
 线程由于需要阻塞等待某些条件的满足而位于相应的等待队列,无法被调度执行.但若系统或其他线程给线程发了信号,且希望线程立即处理信号时,线程可被唤醒以便执行信号处理.唤醒并执行信号处理后,线程会从阻塞处继续运行,应用代码应负责进一步判断是否应该继续.
 (3). TASK_UNINTERRUPTIBLE
 线程由于需要阻塞等待某些条件的满足而位于相应的等待队列,无法被调度执行.系统或其他线程给线程发了信号时,线程也无法被唤醒.
 (4). TASK_TRACE
 调试追踪用途.
 (5). TASK_STOPPED
 调试追踪用途.
 (6). EXIT_ZOMBIE
 一般表示线程已经退出,但其task_struct依然未被回收的阶段.
3.线程id
 每个线程拥有独立的id,称为线程id.
 通过父进程产生子进程时,linux内核实际产生一个新线程,但这个新线程属于一个新的线程组,且自己是线程组的组长.其线程id就是这个线程组组长的id.
linux内核采用哈希表方式来通过id快速定位到task_struct结构.
 为了实现通过线程id,快速定位到线程的task_struct结构,linux内核准备了一个全局哈希表p1.每个线程需将其<id,task_struct对象指针>注册到此哈希表.
 为了实现通过线程组组长id,快速定位到线程组组长的task_struct结构,linux内核准备了一个全局哈希表p2.每个线程组组长需将其<id,task_struct对象指针>注册到此哈希表.
为了快速获得一个线程组下的所有成员,p2哈希表的链式结构的节点额外包含一个链表结构.通过此链表结构可以将此节点代表的线程组的每个成员链接起来.
当基于父进程产生子进程时,产生的新线程.除了形成一个新的线程组外.还将隶属于父进程所在的进程组,隶属于父进程所在的会话,隶属于父进程所在的终端对象.这部分主要服务于信号处理,不太常用,不做深入分析.
4.线程关系
 (1). 父子关系
 基于父进程创建子进程时,产生的新线程的task_struct结构中real_parent指向执行fork调用的task_struct实例(父进程).
 基于兄弟线程创建新线程时,产生的线程的task_struct结构中real_parent和执行fork调用的task_struct中real_parent一致(和兄弟的父相同).
 新线程产生时,其task_struct中real_parent和parent是一致的.
(2). 兄弟关系
 基于兄弟线程创建新线程时,新线程和创建其的线程是兄弟关系.
对代表某个线程的task_struct而言,其children字段指向一个双向链表,链表中包含线程的所有孩子线程.
 对代表某个线程的task_struct而言,其sibling字段是一个双向链表的节点,该双向链表中的所有其他成员均是线程的兄弟线程.
5.基于兄弟线程产生新线程
 新线程共享兄弟线程的信号处理,用户态虚拟内存空间,打开文件结构.共享的意思是共享的资源仅需递增引用数即可.
6.基于父进程产生子进程
 父子进程的用户态虚拟地址空间是独立的,独立的意思是父子进程拥有各自独立的页表结构,拥有各自独立的用户态线性区管理结构.新进程页表的内容设置参考父进程的页表来.
 但针对父进程页表中属于私有映射的页表项,在处理时,会分别取消父子进程中对应页表项的可写权限下.这样,后续父子进程需对相应线性地址执行写访问时,将在缺页异常处理中通过写时拷贝来解决异常.
7.线程退出
 线程退出时,会重新为其孩子线程选择父亲.
 在线程所在线程组尚有存活的线程时,选择其中一个作为其孩子线程的父亲.
 在线程所在线程组无存活的线程时,选择init全局线程作为其孩子线程的父亲,init会对已经释放的线程执行回收调用,来释放其task_struct实例.
一般而言linux中非detach线程退出时,其task_struct结构依然存在,直到执行回收调用时,才释放.
 而detach线程,一般退出时,直接释放其task_struct,无需执行回收调用.