Chinaunix首页 | 论坛 | 博客
  • 博客访问: 716057
  • 博文数量: 67
  • 博客积分: 994
  • 博客等级: 准尉
  • 技术积分: 1749
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-03 14:10
文章分类
文章存档

2014年(11)

2013年(14)

2012年(14)

2011年(28)

分类: LINUX

2011-10-15 18:41:45

关于线程的引入:

前面讲了进程,大家知道一个进程在一个时刻只能干一件事,不能同时干两件以上的事:还有进程发生阻塞后,进程里的其他操作就不能继续执行;每个进程的创建都要消耗一定的内存空间,这对于寸土寸金的内存来说是一件很糟的事;且进程间的切换比起线程切换要花费很多时间,所以就引入了线程。

线程的管理:

在介绍线程如何管理之前首先要介绍如何创建线程,就像进程的创建一样(进程的创建通过两种方式,一是由操作系统创建。比如INIT进程,还有一种就是通过父进程创建,比如fork())线程pthread的创建函数为pthread_create.其函数声明为

int pthread_create(pthread_t *thread,pthread_attr_t *attr,void * (*start_routine) (void *),void *arg);

各参数含义如下:

thread:当函数创建成功时,用来返回创建的线程ID

attr:为线程属性,默认情况为NULL

start_routine:为一个函数指针,指向线程创建后要调用的函数。

arg:指向传递给线程函数的参数,默认为NULL


进程通过PCB(进程控制块)或进程表来进行管理,PCB的信息定义在task_struct结构体中,线程和进程类似,她通过TCB(线程控制块)或线程控制表来进行管理。大家知道线程是共享进程的资源,共享的东西不会放到TCB里面,应该放线程私有的资源。

线程的共享资源包括:全局变量、地址空间、子进程、信号和信号服务程序等。

线程的私有资源:线程号:每个线程都有唯一的线程号、寄存器(程序寄存器和堆栈寄存器)、堆栈、信号掩码、优先级等。

线程的实现:

线程的管理有两种方式:一种是有操作系统管理线程,另一种是进程自己来管理线程。这就出现了线程的两种实现方式,一种是用户态线程,另一种是内核态线程。

内核态线程的实现:既然操作系统管理线程,那么通过什么来管理?聪明的你可能猜到了操作系统通过共同管理PCBTCB来实现。那么操作系统管理有何好处?首先就是方便了编程人员,我们不必知道复杂的线程调度,这些操作系统都为我们做好了。还有就是一个线程阻塞后,操作系统就会运行其他线程。当然内核态管理线程也是有不足的,线程陷入内核会增加用户态与内核态切换的开销,造成效率降低。

用户态线程的实现:线程在用户 态就是要用户自己进行线程切换,自己管理线程调度,操作系统无需知道线程的存在。(早期线程的提出,操作系统不愿为线程的引入冒险)。

一般情况线程不是单独行动的,他们是团队合作的,那么他们的关系如何?他们通过同步和通信就可以很好的实现协调合作,提高办事效率。

线程的同步:首先你可能会问如何理解同步?同步就是按照一定的规则有序的执行。

先看下面一个例子:假如我们有两个线程同时运行,第一个线程在执行了一些操作后想检查当前的错误状态erron,但是在作出检查之前

,线程2获得了CPU,这时线程2修改了erron。这样,当线程一再次获得控制权后,得到的却是线程2修改的结果,这当然是不对的。以上列子中erron是全局变量,且线程之间的执行是不确定的。要解决问题就要从全局变量和线程执行顺序找问题,让erron私有化,但是这样违背了线程共享资源的初衷,只有在线程的执行顺序上找解决方法,这样就引入了互斥锁。互斥锁通过锁的机制来实现同步,在同一时刻只允许一个线程执行一个段代码,这样就有效的实现了同步问题,真的锁机制就完美的解决了问题?当一个线程独享某个资源时,其他线程是默默等待锁的释放吗?这样不就又降低了程序的执行效率?我们可以让其他线程睡觉或干其他的事,待锁释放之后发送信号进行通知,如果睡觉还好,直接通过信号叫醒,但是你在做其他事时不就又打断了当前所做的事吗????

实现同步的另一个方式是就是使用调件变量,他是利用线程间共享的全局变量进行同步,也就是在满足条件后就会执行某段程序。

线程的通信:估计大家都知道IPC是什么意思,其意思就是线程通行。怎么又扯到进程了?进程的通信其实质就是进程里面的线程的通信。那么我们之前已经知道进程间的几种主要的通信手段;

1、管道:从根本上说,管道是一个线性字节数组,类似文件,使用文件读写方式进行访问,read(),write()。但却不是文件,通过文件系统看不到管道的存在。管道是一种半双工的通信方式,只能在只有亲缘关系的线程之间使用,shell里面通过“|”来表示管道,表示一个进程的输出做为另一个进程的输入。如:ls -l | grep root.

下面是一段实现父子通信的管道代码

int pp[2];

pipe(pp);

if(fork()==0) {

read(pp[0]);

.....

} else{

write(pp[1]);

.....

}



2、有名管道:和管道比起来有名管道就是多了个名字,单就是这点改变不知是两个字的不同,有名管道可以把没有任何关系的线程连接起来。还有就是有名管道使用时必须调用open将其打开,而管道不需要这样的操作。为什么会这样?因为有名管道存在硬盘上的文件。管道文件在shell下可通过mknod或mkfifo创建。

***@wuyaalan:~$ mknod mypipe p
***@wuyaalan:~$ ls -l mypipe
prw-r--r-- 1 ***  *** 0 2011-10-16 11:42 mypipe
当然,除此之外,管道与有名管道区别:管道是一种进程之间的通信机制,而有名管道则是一种特殊文件。有名管道用于缓存接受到的数据,同时供从有名管道中读取数据的进程按照FIFO的方式读取数据。尽管有名管道具有文件名和信息节点,但是这个特殊文件并不拥有任何数据
***@wuyaalan:~$ ls -l mypipe
prw-r--r-- 1 ***  *** 0 2011-10-16 11:42 mypipe

3、信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。她更是一种同步机制。信号量的值的改变通过PV操作来改变。具体P操作就是申请一个资源,V操作释放一个资源。其通过sembuf结构体和semop实现。Semop原型:

int semop (int semid,struct sembuf *sops,size_t nsops)

semid为信号集的标识符;参数sops指向进行操作的结构体数组首地址;参数nsops指出将要进行操作的信号的个数。

4、信号:为什么要引入信号?当然引入信号是有他的道理,前面我们了解了管道了什么的,是不是觉得有点繁琐,拿管道来说,使用之前要先创建管道,这需要消耗系统资源。还有就是发送的消息不一定实时接收。这样我们就有了信号,她要求发送的消息要立刻作出反映,且通信不必事先连接好,临时需要即通信,而且我们的消息要尽量的小。在计算机里,信号就是一个内核对象,或内核数据结构。

5、消息队列:消息队列就是一列有头有尾的消息排列。

6、共享内存:就是开辟一块其他进程都可以访问的内存。共享内存是最快的IPC方式,它往往与其他机制配合使用。

7、套接字:用于不同机器间的进程通信。



阅读(2720) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

WuYaalan2011-10-18 09:15:58

shenfeng128: 口水话太多,而且深度一点点,根本上不了主页。.....
要试着接纳新手

shenfeng1282011-10-18 09:01:54

口水话太多,而且深度一点点,根本上不了主页。