进程与线程的区别
进程是一个运行中的程序,站在操作系统的角度,进程就是一个运行中的长须的描述-PCB(进程控制块)。
线程是轻量级的进程。在传统的操作系统中,pcb就是进程,而tcb是线程控制块,每个线程有他自己的tcb。但是在Linux下,因为线程是通过进程的pcb描述实现的(task_struct结构体),因此Linux下的pcb实际上是一个线程组,并且因为这些线程共用一个虚拟地址空间,因此也把Linux下的线程成为轻量级进程,相较于传统pcb更加的轻量化。
进程是资源分配的基本单位,线程是CPU调度的基本单位。
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要使用进程间通信方式(IPC)进行。
- 多线程的优点:
因为共用同一块虚拟地址空间,因此线程间通信更加灵活方便(可以使用全局变量、传参等方式)
线程间共用进程的大部分资源,因此线程的创建与销毁成本更低。
也因为线程间共用进程的大部分资源,所以线程的切换调度成本更低。 - 多线程缺点:
线程间缺乏访问控制,某些系统调用以及异常是针对整个进程产生效果的。 - 多进程优点:
进程间相互独立,因此稳定、健壮性更高,适用于对主程序安全稳定性要求更高的场景,例如shell、服务器。
线程
线程共享的资源
线程共用所属进程的同一地址空间,因此代码段、数据段(虚拟地址空间)都是共享的,定义一个函数,则在各线程中都可以调用,定义一个全局变量,在各线程中都可以访问,除此之外,各线程还共享以下进程资源和环境:
- 文件描述符表
- 每种信号的处理方式(SIG_ IGN忽略、SIG_ DFL默认或者自定义的信号处理函数)
- 当前工作目录
- 用户id和组id
线程独有的资源
线程共享进程的数据,但也有一部分自己独有的数据:
- 线程ID,即线程标识符
- 寄存器(存放上下文数据等)
- 栈
- errno
- 信号屏蔽字(阻塞信号集合)
- 调度优先级
线程属性pthread_attr_t结构体
typedef struct{
int detachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置(最低地址)
size_t stacksize; //线程栈的大小
} pthread_attr_t;
属性值不能直接设置,须使用相关函数进行操作,初始化pthread_attr_init,这个函数必须在pthread_create函数之前调用。
之后须用pthread_attr_destroy函数来释放资源。
线程属性主要包括如下属性:作用域(scope)、栈尺寸(stack size)、栈地址(stack address)、优先级(priority)、分离的状态(detached state)、调度策略和参数(scheduling policy and parameters)。
默认属性为非绑定、非分离、缺省的堆栈、与父进程同样级别的优先级。
线程创建:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
pthread_t:无符号长整型
*thread:线程地址空间,用户态县城描述信息/操作句柄。线程地址空间在进程虚拟地址空间中的首地址。
(pcb中的pid为轻量级进程ID,而tgid为进程ID,即线程组ID,值默认等于主线程的pid)
pthread_attr_t *attr:线程的属性参数,通常置空
void *(*start_routine) (void *), void *arg:线程入口函数,*arg为传递给线程的参数
线程终止:
- 线程见自己的入口函数运行完毕return退出,在主函数main函数中退出的是进程。
- void pthread_exit(void *retval); 退出调用线程
- int pthread_cancel(pthread_t thread); 取消一个指定线程,成功返回0,失败返回错误编号
线程等待:
默认情况下,线程退出后也不会完全释放资源,需要被其他线程等待,线程等待即指等待一个指定的线程退出,retval获取这个退出线程的返回值,并且回收资源。
#