进程及线程基本定义
进程(process)
处于执行期的程序及其所包含资源的总称
程序:可执行程序代码
资源:打开文件、挂起信号、地址空间、数据段等
线程(thread)
进程中活动的对象
有独立的程序计数器、进程栈及一组进程寄存器
节省主存、减少管理开销、快速切换
进程
资源分配单位
进程的上下文组成
进程控制块PCB:包括进程的编号、状态、优先级以及正文段和数据段中数据分布的大概情况
正文段(text segment):存放该进程的可执行代码
数据段(data segment):存放进程静态产生的数据结构
用户堆栈(stack)
线程
CPU调度基本单位
进程执行状态相关信息
进程 ID、进程组 ID、用户 ID和组 ID
工作环境(Environment)
工作目录(Working directory)
程序指令(Program instructions)
寄存器(Registers)
栈(Stack)
堆(Heap)
文件描述符(File descriptors)
信号(Signal actions )
共享库(Shared libraries)
进程间通信手段(Inter-process communication tools)
线程的基本特点
是进程的一个实体,可作为系统独立调度和分派的基本单位
有不同的状态,有控制线程的各种原语,包括创建和撤销线程等
不拥有系统资源(只拥有从属进程的全部资源,资源是分配给进程)
一个进程中的多个线程可并发执行
进程中创建的线程可执行同一程序的不同部分
也可以执行相同代码
系统开销小、切换快
进程的多个线程都在进程的地址空间活动,共享全局变量
同一进程中的多个线程共享该进程的虚拟空间
进程代码段
进程的公有数据
利用这些共享的数据,线程很容易的实现相互通讯
进程打开的文件描述符
信号的处理器
进程的当前目录和进程用户ID与进程组ID
说明
对不同进程来说,具有独立的数据空间,数据传递只能通过通信的方式进行,这种方式费时而不方便
线程是实现并发的必要条件
线程ID
每个线程都有自己唯一的线程ID
寄存器组的值
创建线程时,须将原有线程的寄存器集合的状态保存
线程的堆栈
线程必须拥有自己的函数堆栈,使得函数调用可以正常执行,不受其他线程的影响
错误返回码
不同线程应该拥有自己的错误返回码变量
线程的信号屏蔽码
线程的信号屏蔽码应由线程自己管理,但所有线程都共享同样的信号处理器
线程的优先级
用户线程
用户线程存在于用户空间,通过线程库来实现
线程库提供对线程的创建、调度和管理的支持,而无须内核支持
内核并不知道用户级线程,所有线程的创建和调度都在用户空间内进行,无须内核干预
用户级线程的调度以进程为单位
优点
同一进程内的线程切换不需要转换到内核,调度算法是进程专用的
缺点
系统调度阻塞问题,不能充分利用多处理器
由操作系统直接支持,内核在其空间内执行线程的创建、调度和管理
由于线程管理由操作系统完成,因此内核线程的创建和管理要慢于用户线程的创建和管理
优点
支持多处理器,支持用户进程中的多线程、内核线程切换的速度快
缺点
对用户的线程切换来说,系统开销大
线程-多线程模型
多对一模型
将许多用户级线程映射到一个内核线程
线程管理在用户空间进行,效率较高
处理机调度的单位仍然是进程
缺点
如果一个线程执行了阻塞系统调用,那么整个进程就会阻塞
因为任何时刻只有一个线程访问内核,多个线程不能并行运行在多处理器上
说明
在不支持内核级线程的操作系统上所实现的用户级线程库也使用多对一模型
一对一模型
将每个用户线程映射到一个内核线程
在一个线程执行阻塞时,允许另一个线程继续执行
允许多个线程运行在多处理机系统上
提供比多对一模型更好的并发功能
缺点
创建一个用户线程就需要创建一个相应内核线程
创建内核线程的开销会影响应用程序的性能,这种模型的绝大多数实现限制系统所支持的线程数量
多对多模型
多路复用许多用户级线程到同样数量或更小数量的内核线程上
内核线程的数量可能与特定应用程序或特定机器有关
克服前两种模型的缺点
开发人员可以创建任意多的必要的线程,并且相应内核线程能在多处理器系统上并行运行
当一个线程执行阻塞系统调用时,内核能调度另一个线程来执行
pthread背景
早期各硬件厂商主要使用私有版本线程库,实现差异非常大,开发者难于开发可移植的线程应用
为能够最大限度的提高线程的性能,需要一个标准的编程接口
对于UNIX系统,IEEE POSIX 1003.1c标准 (1995)定义了这样的接口
遵从该标准实现的线程被称做POSIX线程,或pthreads
pthreads定义了一套C语言编程接口和函数调用
包括一个pthread.h头文件和一个线程库
基于POSIX标准的线程编程接口
包括一个pthread.h头文件和一个线程库
编译方法
gcc –g **.c -o *** –lpthread
功能
线程管理
支持线程创建/删除、分离/联合,设置/查询线程属性
互斥
处理同步,称为“mutex”
创建/销毁、加锁/解锁互斥量,设置/修改互斥量属性
条件变量
支持基于共享互斥量的线程间通信
建立/销毁、等待/触发特定条件变量,设置/查询条件变量属性
线程管理
支持线程创建、分离、联合等,还包括线程属性的设置/查询
互斥
处理同步,称为“mutex”
提供创建、销毁、加锁和解锁互斥量
也包括补充的修改互斥量属性功能,并用它去设置或者修改与互斥相关的属性
条件变量
支持基于共享互斥量的线程间通信,以开发者的特定条件变量为基础。
包括基于特定条件变量的建立、销毁、等待和信号触发
设置/查询条件变量属性的功能也包括在内
线程管理
线程创建
函数原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void*),void * arg);
参数说明
thread:要创建的线程id指针
attr:创建线程时的线程属性
v(*start_routine)(void*):返回值是void*类型的指针函数
arg:start_routine的参数
返回值
成功返回0
失败返回错误编号
EAGAIN:表示系统限制创建新的线程,如线程数目过多
EINVAL:代表线程属性值非法
#include <pthread.h>
#include <stdio.h>
void *create(void *arg) {
printf("new thread created ..... ");
}
int main(int argc, char *argv[]){
pthread_t tidp;
int error;
error=pthread_create(&tidp, NULL, create, NULL);
if(error != 0)
{
printf("pthread_create is not created ... ");
return -1;
}
printf("prthread_create is created... ");
return 0;
}
基本问题
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void*),void * arg);
仅允许传递一个参数给线程待执行的函数
如何传递多个参数
解决途径
构造一个包含所有参数的结构,将结构指针作为参数传递给pthread_create()
所有参数必须利用(void *)来传递
获取线程自身的id
函数原型
pthread_t pthread_self(void);
返回值
调用线程的线程id
比较线程ID
函数原型
int pthread_equal(pthread_t tid1, pthread_t tid2);
参数
tid1:线程1的id
tid2:线程2的id
返回值
相等返回非0值
否则返回0
线程终止
正常终止
方法1:线程自己调用pthread_exit()
void pthread_exit(void *rval_ptr);
rval_ptr:线程退出返回的指针,进程中其他线程可调用pthread_join()访问到该指针
方法2:在线程函数执行return
非正常终止
其它线程的干预
自身运行出错
同步方式(非分离状态)
等待新创建线程结束
只有当pthread_join()函数返回时,创建的线程才算终止,才可释放自己占用的系统资源
异步方式(分离状态)
未被其他线程等待,自己运行结束即可终止线程并释放系统资源
函数原型
int pthread_join( pthread_t thread, void ** rval_ptr);
功能
调用者将挂起并等待新进程终止
当新线程调用pthread_exit()退出或者return时,进程中的其他线程可通过pthread_join()获得进程的退出状态
使用约束
一个新线程仅仅允许一个线程使用该函数等待它终止
被等待线程应处于可join状态,即非DETACHED状态
返回值
成功结束返回值为0,否则为错误编码
说明
类似于waitpid()
函数原型
int pthread_detach(pthread_t thread)
功能
执行该函数后线程处于DETACHED状态
处于该状态的线程结束后自动释放内存资源,不能被pthread_join()同步
说明
当线程被分离时,不能用pthread_join()等待其终止状态
为避免内存泄漏,线程终止要么处于分离状态,要么处于同步状态
功能描述
客户端
使用线程向服务器发送从标准输入得到的字符
在主线程中将从服务器端返回的字符显示到标准输出
服务器端
将客户端发来的数据原样返回给客户端,每一个客户在服务器上对应一个线程