进程、线程

在学习的过程中,我们很多人会搞不清楚进程和线程的区别,我们今天就从以下几个方面来讨论进程和线程的区别。

一、概念

1.进程(Process)

进程时处于执行期的程序,但进程并不仅仅局限于一段可执行程序代码。进程还包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间以及一个或多个执行的线程(thread of execution),当然还包括用来存放全局变量的数据段等,实际上,进程就是正在执行的程序代码的实时结果。内核需要有效透明的管理所有细节。

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程时系统进行资源分配和调度的一个独立单位。每个进程都有自己独立的内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存空间,所以上下文进程切换的开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较安全。

总的来讲进程的概念一共有两点:一、进程是一个实体。每一个进程都有自己的地址空间,一般情况下包括文本区域(.text region)、数据区域(.data region)和堆栈区(heap and stack region)。文本区域存储处理器执行的代码,数据区域存储变量(全局变量)、堆区存储进程执行期间通过动态分配的内存、栈区存储活动过程中调用的指令和本地变量(局部变量)。二、进程是一个执行中的程序,程序是一个没有灵魂的实体,只有处理器赋予其生命时,它才能成为一个活动的实体,我们称之为进程。

2.线程(Thread)

线程是操作系统能够进行运算调度的最小单位,它是进程的一个实体,它是比进程更小能够独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行过程中必不可少的资源(如程序计数器、一组寄存器、栈等),但它可以与同属一个进程的其他线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,进程上下文切换很快,资源开销少,但相比于进程不够稳定,容易造成数据丢失。

线程也成执行线程,是在进程中活动的对象。内核调度的对象是线程,而不是进程,传统的Unix系统中,一个进程只包含一个线程,但在现在的系统中,包含多个线程的多线程进程司空见惯,在Linux系统中,线程的实现十分特别,它对线程和进程并不特别区分。对Linux而言,线程就是一种特殊的进程罢了。

二、创建以及销毁

1.进程的创建

Unix的进程创建很特别。许多其他的操作系统都提供了产生(spawn)进程的机制,首先在新的地址空间里创建进程,读入可执行文件,最后开始执行。Unix采用了与众不同的实现方式,他把上述步骤分解到两个函数分解到两个单独的函数中去执行,fork()和exec()。首先,fork()通过拷贝当前进程创建一个进程,子进程与父进程的区别仅仅在于PID(每个进程唯一)、PPID(父进程的进程号,子进程将其设置为被拷贝进程的PID)和某些资源和统计变量(例如,挂起的信号,它没有必要被继承)。exec()函数负责读取可执行文件,并将其载入地址空间开始运行。把这两个函数组合起来使用效果跟其他系统使用的单一函数的效果相似。

//fork()函数原型
pid_t fork(void);

由fork()创建的新进程被称为子进程(child process)。fork()函数被调用一次,返回两次 。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值是新子进程的进程ID。将新子进程的进程ID返回给父进程的理由是:因为一个进程到的子进程可以有多个,并且没有一个函数能获取其所有子进程的进程ID。fork()函数使子进程的返回值为0的理由是:一个进程只会有一个父进程,所以子进程总是可以调用getppid()函数来获取父进程的进程ID(进程ID为0是由内核交换进程使用,所以进程的进程ID不可能为0)。

excx()在这里指所有exec()一族的函数。内核实现了execve()函数,在此基础上,还实现了execlp()、execle()、execv()和execvp()。

//进程替换
int execl(char* pathname,char* argv0,char* argv1,...,(char*)0);
int execv(char* pathanem,char* argv[]);

int execlp(char* pathname,char* argv0,char* argv1,...,(char*)0);
int execvp(char* pathanem,char* argv[]);
int execle(char* pathname,char* argv0,char* argv1,...,(char*)0,char* envp[]);
int execve(char* pathanem,char* argv[],char* envp[]);

2.线程创建

线程时一个轻量级的进程,就想每个进程有一个进程ID一样,每个线程也有一个线程ID。进程ID在整个系统中是唯一的,但线程ID不同,线程ID只在它所属进程环境中有效。

在传统的UNIX进程模型中,每个进程只有一个控制线程。从概念上讲,这与基于线程的模型中每个进程只包含一个线程是相同的,在POSIX线程(pthread)的情况下,程序开始运行时,他也是以单进程中的单个控制线程启动的,在创建对个控制线程以前,程序的行为与传统的进程没有什么区别,新增的线程可以通过调用pthread_create()函数创建。

#include<pthread.h>

int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,
                   void*(*start_rtn)(void),void* restrict arg);

当pthread_create()成功返回时,有tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性。一般设置为NULL,创建默认属性的线程。

新创建的线程从start_rtn()函数的地址开始运行,该函数只有一个无类型指针参数arg,如果需要向start_rtn()函数传递的参数不止一个,那么需要将这些参数放到一个结构体中,然后间这个结构体的地址作为arg参数传入。

线程创建并不能保证那个线程先执行,是新创建的线程还是调用线程,新创建的线程可以访问进程地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程未决信号集被清除。

注意:pthread()函数在调用失败时通常会返回错误码,他们并不像其他的POSIX函数一样设置erron。每个线程都提供errno的副本,这只是为了与使用errno的现有函数兼容。在线程中,从函数中返回错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局状态,因为可以把错误的范围限制在引起出错的函数中。

3.进程的终止

进程终止,也称进程终结,当一个进程终结时,内核必须释放它所占有的资源,并把终结的信号告知父进程。

一般来说,进程的析构是自身引起的。它发生在进程调用exit()系统调用时,既可能是显示的调用这个系统调用,也可能是隐式地从某个程序的主函数返回。当进程接收到它既不能处理也不能忽略的信号或者异常时,它还有可能被动终结,不管进程是怎么终结的,该任务大部分都要靠do_exit()来完成。

值得注意的是,子进程终止会将终止的信号发送给其父进程,父进程清除子进程的进程ID,但若是父进程终止后,其还有子进程正在执行,则需要为其所有的子进程选取新的父进程。若没有合适的父进程则有Init进程接管子进程,这些子进程成为孤儿进程。

4.线程的终止

如果进程中的任一线程调用了exit,_Exit或者_exit,那么整个进程就会终止,与此类似,如果信号的默认动作是终止进程,那么,把该信号发送到线程会终止整个进程。

单个线程可以通过以下方式退出,在不终止整个进程的情况下停止它的控制流。

(1)线程只是从启动历程中返回,返回值是线程的退出码。

(2)线程可以别统一进程中的其他线程取消。

(3)线程调用pthread_exit()。

#include<pthread.h>

void pthread_exit(void* rval_ptr);

rval_ptr是一个无类型指针,与传给启动历程的单个参数类似。进程中的其他线程可以通过调用pthread_join()函数访问到这个指针。

#include<pthread.h>

int pthread_join(pthread_t thread,void **rval_ptr);//成功返回0,失败返回错误码

调用线程将一直阻塞,直到指定的线程调用pthread_exit(),从启动例程中返回或者被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。

可以通过调用pthread_join()自动把线程置于分离状态,这样资源就可以恢复,如果线程已处于分离状态,pthread_join()就会调用失败,返回EINVAL。

如果对线程的返回值并不感兴趣,可以把rval_ptr置为NULL。这种情况下,调用pthread_join()函数将等待指定线程终止,但并不获取线程的终止状态。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值