进程与线程--原理

        所谓白话即是将事物的原理用通俗易懂的语言表达出来,接下来我们就说一说我们平时用到的进程与线程在操作系统中是如何被管理以及调度的。

        其实操作系统本质上的意义就是如何让我们更方便的来使用这些如 cpu、内存、网卡 等物理设施,给我们的生活带来便利或更优质的生活享受。如我们打开电脑后,启动操作系统,安装应用就可以在线看电视或者打游戏。或者对于我们技术人员来讲,在我们的开发过程中,假如我们要读取硬盘中的数据,我们直接调用read系统调用就可以,我们无需去关心磁头的移动与柱面扇区如何移动才能读出数据。或者我们分配内存,我们直接调用malloc系统调用就可以分配内存,我们也无需关心内存条到底还有多少空闲。

        所以操作系统就是将我们从那些琐碎的重复的劳动中解放出来,方便我们的生活与工作。 那么我们开始今天的话题,先说一下进程,然后再说一线程,最后再总结一下进程与线程的区别与应用方面的考虑。

        什么是进程?

        我们的计算机要为我们的生活服务,我们一边写着博客,同时还得聊着QQ,还得帮我收取邮件,我们希望计算机同时能帮我们做到这些事情,那么就有了进程。进程就是在操作系统中做某件事情的一段程序的实例,现在我的电脑里就运行着浏览器进程、QQ客户端进程、邮件进程还有其他操作系统的一些基础进程,所以计算机能在我写博客的时候收取邮件还能发送QQ消息,当然对于cpu来讲,某一个时刻只会运行一个进程,但是我们的操作系统有任务调度程序,让cpu根据调度来在这些进程之间不断的切换。在一秒钟之内,操作系统就会在不同的进程中切换很多次,这也是对于我们人来讲,感觉就像是计算机在同时做这几件事情,但对于cpu来讲他是在不同进程之间的执行来回切换。

      进程在操作系统中是如何管理的?

      我们通过上面知道了,操作系统中运行着各种不同任务的进程,有收发邮件的、有聊天的、有浏览器的,但是操作系统是如何让这些进程不断的来回切换并且是如何切换的呢。

      我们知道其实计算机的就是在不断的进行计算,对数据进行处理。cpu发送指令从内存中读取程序指令然后通过总线放入不同的寄存器,然后再从寄存器中读入cpu进行运算然后再放入寄存器或写入内存。

      其实一切的程序都是如此运行,当在操作系统中运行一个程序就是启动一个进程,在操作系统中运行一个进程的时候,如果是第一次运行,操作系统就会为此进程分配内存空间,这些空间包括不可更改的指令空间(文本段),就是存储要运行的程序的指令的;数据段,变量的值都存在这里;还有栈(堆栈)空间,这里存放在调用函数时局部变量在计算过程中的变化以及结果,随着函数调用或结束,这里的空间也在增加或缩小;还有堆空间,程序中动态分配的内存空间将分配在这里。而后操作系统将指令空间的内存地址放入寄存器,由cpu开始读取执行,沿着代码段执行,在执行过程中为了保存结果或执行进度将变量的值或函数的调用过程放入相应的空间以及指令的执行进度放入相应的寄存器。

       在操作系统中系统维护着一张进程表,这里存储着系统中运行的所有进程,每一项进程都占用一个进程表项。这个表项数据结构中存储着进程的很多重要信息,如在系统调度让cpu运行下一的进程时,把当前进程运行时的镜像全部保存下来,为了当再次运行此进程时恢复当时运行的场景。比如 程序计算器(程序执行到哪) 堆栈指针(执行过程中的变相值) 各寄存器中的值 文件的打开状态 等一切运行时的信息,还有进程本身的一些信息 比如运行此进程的帐号以及优先级之类的属性信息。

       所以,操作系统是根据调度程序来决定运行哪个进程,当要运行某个进程时,会把当时正在运行的进程时的一起用于恢复当时场景的数据都保存在系统维护的进程表中对应的进程信息存储的数据结构中,当调度程序再次运行此进程时,将保存的这些信息改放入寄存器的放入对应的起存期,恢复到堆栈的恢复到堆栈,继续执行上一次运行指令的下一次指令。

      什么是线程?

     之所以会有线程,是因为在进程中运行多种活动时,当进行某一项活动时比如读取磁盘数据,此时进程就处于阻塞状态,整个事情的进展就处于暂时的停止状态,如果我们将进程中的多种工作分派给不同的线程去处理,比如一个线程在向内存中写入数据,一个线程在分析数据,这样就不会在读取数据的时候无法分析数据了。这就是为什么会有线程的概念。

     其次线程可以共享进程中的资源比如打开的文件、全局变量等公共资源,另外线程比进程更轻量级,创建和销毁比进程要快10-100倍。这两个也是线程之所以存在的原因。

     线程在进程的内存空间中有自己的内存空间用来临时保存自己的堆栈或寄存器内容或程序计数器,用来恢复继续运行,进程中不同的线程不像不同的进程之间存在着很大的独立性,所有线程都有完全一样的地址空间,这意味着线程之间共享全局变量。由于各个线程都可以访问进程地址空间中的任意内存地址,所以一个线程可以修改读取甚至删除另一线程的堆栈,线程之间是没有保护的。因为不同的线程肯定来自同一进程也就是同一用户,他们之间不会有敌意,他们之间还可以共享打开的文件集、子进程、以及相关信号。而不同的线程之间的通信就复杂的多,同一进程中的多个线程是相互信任的,而不同的进程之间是不信任的。

其实线程就是进程中的又一执行单位,进程中不同的线程只是运行的进度不同,其他都是可以共享的。

进程空间地址存储的内容 线程空间地址中存在的内容
地址空间 程序计数器
全局变量 寄存器
打开的文件集 堆栈
子进程 状态
账户信息  
优先级  
信号  

     那么什么时候该使用线程?因为我们知道线程是在进程中的运行时不断交替运行的单位,当某一线程遇到io阻塞,这个时候让当前线程等待总线上的数据,保存当前线程的现场(堆栈和寄存器),运行另一线程。所以当某项工作大多时进行的都是cpu运算,那么多线程显然毫无意义,而且还会增加线程之间切换保存线程的额外工作,所以cpu密集型的工作不适合用线程。但是当工作中有运算和iO处理工作时,显然将这些工作分配给多线程是可以不浪费cpu宝贵的运算时间,让cpu忙起来提高工作效率的好办法。

    那么操作系统如何管理线程的呢?我们应该如何利用这种机制呢?

    第一种是操作系统不知道进程中运行着多线程,我们在用户空间调用库函数在进程中运行和调度多个线程,通过维护一张线程表来管理这些线程,操作系统拿这个进程就当是普通的进程。

     这种方式的优点就是操作系统计算不支持多线程机制,我们也可以照样使用多线程来高效的完成我们的工作。我们可以在进程中通过自己的算法来调度线程的运行,而且在进程中创建或销毁线程比在操作系统内核空间中要节约资源,无需像在内核中发生大量的上下文切换以及现场保存工作。

     这种方式的缺点就是由于操作系统并不知道进程中的多线程,所以当进程中的某一个线程发生系统调用陷入系统内核中时,操作系统会将这个进程阻塞,运行其他进程,它不知道也不会管该进程中是否有其他线程在运行着。还有就是在用户空间中运行多线程,当某一个多线程一直占用cpu时,在当前进程的运行时刻,由于用户空间中没有时钟机制,所以其他线程只能等待,无能为力。

     第二种就是操作系统支持多线程,那么进程中的线程就交由操作系统来管理,当进程中某一线程发生阻塞时,操作系统会选择此进程中的另一线程继续运行,而且当某一线程占用cpu时间过长时,操作系统也会根据时钟阻塞线程,这也算是相对于用户空间来说的优点。但是毕竟是在内核中由操作系统来管理线程,无论线程的创建还是管理消耗的资源都比在用户空间大,这也是内核管理多线程的缺点。

     前面两种模式都各有优缺点,如果能将其结合到一起,各取其优点,应该会更好。

     一种做法是使用内核级线程,然后将用户级线程与内核级线程多路复用起来。这样,用户就可以决定有多少个用户级线程和多少内核级线程多路复用,这样会更加灵活。

      

posted @ 2014-03-05 23:32  阿正-WEB  阅读(2604)  评论(1编辑  收藏  举报