进程与线程
參考:http://www.cnblogs.com/blueclue/archive/2010/07/16/1778855.html
首先比較Linux进程和线程的创建的差别,以此展开:
创建进程:(1)调用fork(),为子进程新建内核栈、pthread_info和task_struct,复制父进程的大部分的參数,採用写时复制(copy-on-write)辅助父进程的资源,改动子进程如pid、ppid等重要资源。(2)调用exec()为子进程分配地址空间,加载运行程序。
创建线程:与进程创建没有大的差别,唯一不同的就是,在调用clone()的fork()參数表里指明共享的资源。
标志 含义
CLONE_PARENT 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”
CLONE_FS 子进程与父进程共享同样的文件系统,包含root、当前文件夹、umask
CLONE_FILES 子进程与父进程共享同样的文件描写叙述符(file descriptor)表
CLONE_NEWNS 在新的namespace启动子进程,namespace描写叙述了进程的文件hierarchy
CLONE_SIGHAND 子进程与父进程共享同样的信号处理(signal handler)表
CLONE_PTRACE 若父进程被trace,子进程也被trace
CLONE_VFORK 父进程被挂起,直至子进程释放虚拟内存资源
CLONE_VM 子进程与父进程执行于同样的内存空间
CLONE_PID 子进程在创建时PID与父进程一致
CLONE_THREAD Linux 2.4中添加以支持POSIX线程标准,子进程与父进程共享同样的线程群
为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是由于,线程共享同样的内存空间。不同的线程能够存取内存中的同一个变量。所以,程序中的全部线程都能够读或写声明过的全局变量。假设曾用fork() 编写过重要代码,就会认识到这个工具的重要性。为什么呢?尽管fork() 同意创建多个进程,但它还会带来下面通信问题:怎样让多个进程相互通信,这里每一个进程都有各自独立的内存空间。对这个问题没有一个简单的答案。尽管有很多不同种类的本地IPC (进程间通信),但它们都遇到两个重要障碍:
- 强加了某种形式的额外内核开销,从而减少性能。
- 对于大多数情形,IPC不是对于代码的“自然”扩展。通常极大地添加了程序的复杂性。
双重坏事: 开销和复杂性都非好事。假设以前为了支持 IPC而对程序大动干戈过,那么您就会真正赞赏线程提供的简单共享内存机制。因为全部的线程都驻留在同一内存空间,POSIX线程无需进行开销大而复杂的长距离调用。仅仅要利用简单的同步机制,程序中全部的线程都能够读取和改动已有的数据结构。而无需将数据经由文件描写叙述符转储或挤入紧窄的共享内存空间。仅此一个原因,就足以让您考虑应该採用单进程/多线程模式而非多进程/单线程模式。
为什么要用线程?
与标准 fork()相比,线程带来的开销非常小。内核无需单独复制进程的内存空间或文件描写叙述符等等。这就节省了大量的CPU时间,使得线程创建比新进程创建快上十到一百倍。由于这一点,能够大量使用线程而无需太过于操心带来的CPU 或内存不足。使用 fork() 时导致的大量 CPU占用也不复存在。这表示仅仅要在程序中有意义,通常就能够创建线程。
当然,和进程一样,线程将利用多CPU。假设软件是针对多处理器系统设计的,这就真的是一大特性(假设软件是开放源代码,则终于可能在不少平台上执行)。特定类型线程程序(尤其是CPU密集型程序)的性能将随系统中处理器的数目差点儿线性地提高。假设正在编写CPU很密集型的程序,则绝对想设法在代码中使用多线程。一旦掌握了线程编码,无需使用繁琐的IPC和其他复杂的通信机制,就行以全新和创造性的方法解决编码难题。全部这些特性配合在一起使得多线程编程更有趣、高速和灵活。
什么是线程?
- 专业点的说法,线程被定义为一个独立的指令流,它本身的运转由操作系统来安排,可是,这意味着什么呢?
- 对软件开发人员来说,解释线程最好的描写叙述就是“procedure”能够独立于主程序执行。
- 再进一步,设想一个包括了大量procedure的主程序,然后想象全部这些procedure在操作系统的安排下一起或者独立的执行,这就是对于多线程程序的一个简单描写叙述。
- 问题是,它是怎样实现的呢?
- 在弄懂线程之前,第一步要搞清楚Unix进程。进程被操作系统创建,并须要相当多的“开支”,进程包括例如以下程序资源和程序执行状态信息:
- 进程ID,进程群组ID,用户ID,群组ID
- 环境
- 工作文件夹
- 程序指令
- 寄存器
- 栈
- 堆
- 文件描写叙述符
- 信号动作
- 共享库
- 进程间通信工具(比如消息队列,管道,信号量,共享内存)
Unix进程 Unix进程内部的线程
- 线程使用和在进程内的生存,仍由操作系统来安排而且独立的实体来执行,非常大程度上是由于它们为可执行代码的存在复制了刚刚好的基本资源。
- 这个独立的控制流之所以能够实现,是由于线程维护着例如以下的东西:
- 栈指针
- 寄存器
- 调度属性(比如规则和优先级)
- 等待序列和堵塞信号
- 线程拥有的数据
- 所以,总的来说,Unix环境里的线程有例如以下特点:
- 它生存在进程中,并使用进程资源;
- 拥有它自己独立的控制流,前提是只要它的父进程还存在,而且OS支持它;
- 它只复制能够使它自己调度的必要的资源;
- 它可能会同其他与之同等独立的线程分享进程资源;
- 假设父进程死掉那么它也会死掉——或者类似的事情;
- 它是轻量级的,由于大部分的开支已经在它的进程创建时完毕了。
- 由于在同一进程内的线程分享资源,所以:
- 一个线程对共享的系统资源做出的改变(比如关闭一个文件)会被全部的其他线程看到;
- 指向同一地址的两个指针的数据是同样的;
- 对同一块内存进行读写操作是可行的,但须要程序猿作明白的同步处理操作。