程序:程序是源代码编译后生成的可执行程序文档,是为了完成特定任务而准备好的指令序列与数据的集合。
进程(process)则是程序执行的具体实例,包含该程序在执行时候的所有状态与所使用的资源。在程序执行的过程中,它享有系统的资源,至少包括进程的运行环境、CPU、外设、存储器、进程ID等资源与信息。
程序并不能单独执行,只有将程序加载到存储器中,系统为他分配资源后才能够执行,这种执行的程序称之为进程,也就是说进程是系统进行资源分配和调度的一个独立单位。操作系统会为每个进程分配单独的地址空间。
同样的一个程序,可以实例化为多个进程;
程序只是个静态的可执行文档,而进程是一个动态的实体。
程序到进程转换的过程,整个转换过程主要包含以下3个步骤:
查找到对应程序代码存放的位置。
使用fork()函数为启动一个新进程。
在新进程中调用exec族函数(请参考第五章内核部分)装载程序档,并执行程序档中的main()函数。
程序与进程有以下的关系:
1)程序只是一系列指令序列与数据的集合,只是一个静态的实体。进程则不同,它是程序在某个数据集上的执行过程,它是一个动态运行的实体,有自己的生命周期
2)进程和程序并不是一一对应的,一个程序执行在不同的数据集上运行就会成为不同的进程。
3)进程具有并发性,而程序没有。
4)进程是竞争计算机资源的基本单位,而程序不是。
每个进程都拥有自己的数据段、代码段和堆栈段,以进程为单位进行调度时,需要有比较复杂的上下文切换等动作,系统开销大。
为了降低任务切换时的系统开销,将进程分解成几个小模块——线程,这些线程共享进程的数据空间,以这些线程作为单位进行调度,减少系统调度开销。
线程是进程的子集,进程是资源管理的最小单位,线程是程序执行的最小单位。
线程本质是一个进程内部的一个控制序列,类似于队列,一个进程可以拥有一个线程或者多个线程。
当进程执行fork()函数创建一个新进程时,将创建出该进程的一份新副本。新进程拥有自己的变量和PID,执行几乎完全独立于父进程,进程开销大;在进程中创建一个新线程时,新的执行线程将拥有自己的栈空间,但与它的创建者共享全局变量、文件描述符、信号处理函数和当前目录状态,不是产生当前进程的副本。
Linux系统每个进程都有独立的地址空间,一个进程崩溃后,在系统的保护模式下并不会对系统中其他进程产生影响;而线程只是一个进程内部的一个控制序列,当线程崩溃后,进程也随之崩溃。
2、线程管理
在Linux系统下的多线程遵循POSIX标准,而其中的一套常用的线程库是pthread,它是一套通用的线程库。
使用时必须包含以下头文件:
#include <pthread.h>
除此之外在链接时需要使用库libpthread.a。因为pthread的库不是Linux系统的文件,所以在编译时要加上-lpthread(或-pthread)选项。
(1)线程属性
Linux中线程属性结构如下:
typedef struct
{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
}pthread_attr_t;
1)初始化一个线程对象的属性
函数原型:
int pthread_attr_init(pthread_attr_t *attr);
若函数调用成功返回0,否则返回对应的错误代码。
attr:指向一个线程属性的指针
2)销毁一个线程属性对象
pthread_attr_destroy()函数销毁。
函数原型:
int pthread_attr_destroy(pthread_attr_t *attr);
若函数调用成功返回0,否则返回对应的错误代码。
attr:指向一个线程属性的指针
(2)线程创建
pthread_create()函数是用于创建一个线程的,创建线程实际上就是确定调用该线程函数的入口点。
函数原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数说明:
1) pthread_t *thread:pthread_t是一种用于表示线程的数据类型,每一个pthread_t类型的变量都可以表示一个线程。
2) const pthread_attr_t *attr:用于手动设置新建线程的属性,例如线程的调用策略、线程所能使用的栈内存的大小等。
3) void *(*start_routine) (void *):以函数指针的方式指明新建线程需要执行的函数。
4)void *arg:指定传递给start_routine()函数的实参,当不需要传递任何数据时,将arg赋值为NULL即可。
线程的调度策略选择:
POSIX标准指定了三种调度策略:
分时调度策略 (SCHED_OTHER)。
实时调度策略,先进先出方式调度(SCHED_FIFO)。
实时调度策略 ,时间片轮转方式调度(SCHED_RR)。如图所示。
与调度相关的API如下:
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
attr:指向一个线程属性的指针。
inheritsched:线程是否继承调度属性,可选值分别为:
PTHREAD_INHERIT_SCHED:调度属性将继承于创建的线程,attr中设置的调度属性将被忽略。
PTHREAD_EXPLICIT_SCHED:调度属性将被设置为attr中指定的属性值。
policy:可选值为线程的三种调度策略,SCHED_OTHER、SCHED_FIFO、SCHED_RR。
(3)线程的优先级设置
在Linux系统中,优先级数值越小,线程优先级越高.
获取、设置线程静态优先级可以使用以下函数:
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
参数:
attr:指向一个线程属性的指针。
param:静态优先级数值。
仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时,可以在运行时通过pthread_setschedparam()函数来改变优先级,默认为0。
(4)线程栈设置
线程共享使用的是进程的存储器空间,那么一个进程有n个线程,默认的线程栈大小是1M,那么就有可能导致进程的存储器空间是不够的。可以根据具体需要调小和调大线程栈空间。
获取、设置线程栈大小可以使用以下函数:
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
参数:
attr:指向一个线程属性的指针。
stacksize:线程栈的大小。
(5)线程退出
在该函数运行完之后,该线程也就隐式退出了,这与进程的退出差不多。而另一种退出线程的方法是使用pthread_exit()函数。
不能随意使用exit()退出函数来进行出错处理,将会导致整个进程退出,线程退出只能使用pthread_exit()函数。
函数原型:
void pthread_exit(void *retval);
参数:
retval:如果retval不为空,则会将线程的退出值保存到retval中,如果不关心线程的退出值,形参为NULL即可。
如果某个线程想要等待另一个线程退出,并且获取它的退出值,可以使用pthread_join()函数以阻塞的方式等待thread指定的线程结束。
(6)线程创建实例
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *test_thread(void *arg) /* 线程执行代码 */
{
int num = (unsigned long long)arg;
printf("arg is %d\n", num);
pthread_exit(NULL);
}
进入system_programing/thread目录下执行make编译源码,然后运行,实验现象如下:
thread gitmaster) ./targets
start create thread
create treads success
waiting for threads to finish...
arg is 520
thread exit ok!