现在的位置: 首页 > 综合 > 正文

linux轻量级进程与线程实现

2018年01月25日 ⁄ 综合 ⁄ 共 2614字 ⁄ 字号 评论关闭

一、Linux内核对多进程和多线程的支持方式
Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用__clone()和fork(),最终都用不同的参数调用do_fork()核内API。 do_fork()提供了很多参数,包括CLONE_VM(共享内存空间)、CLONE_FS(共享文件系统信息)、CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程,即0号进程有效)。
当使用fork系统调用产生多进程时,内核调用do_fork()不使用任何共享属性,进程拥有独立的运行环境。
当使用pthread_create()来创建线程时,则最终设置了所有这些属性来调用__clone(),而这些参数又全部传给核内的do_fork(),从而创建的"进程"拥有共享的运行环境,只有栈是独立的,由 __clone()传入。
即:Linux下不管是多线程编程还是多进程编程,最终都是用do_fork实现的多进程编程,只是进程创建时的参数不同,从而导致有不同的共享环境。
Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外pthread库中进行。pthread库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线程相关的信号,而主线程pthread_create())的调用者则通过管道将请求信息传给管理线程。

/************关于本文档********************************************
*filename: Linux内核中线程的实现-Linux下编程为什么多用进程少用线程之二
*purpose: 探讨Linux下多进程和多线程的关系
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言编程
*date time:2006-07-12 21:20:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
*********************************************************************/

二、Linux下轻量级进程
很多朋友都说使用多线程的好处是资源占用少,其隐含之意就是说进程占用资源比线程多,对吧?
但实际上Linux下多进程是否就真的点用很多资源呢?暂且不说进程是否比线程占用资源多,就进程占用资源的多少情况而言,Linux确实是做得相当节省的。产生一个多进程时肯定是要产生的一点内存是要复制进程表项,即一个task_struct结构,但这个结构本身做得相当小巧。其它对于一个进程来说必须有的数据段、代码段、堆栈段是不是全盘复制呢?对于多进程来说,代码段是肯定不用复制的,因为父进程和各子进程的代码段是相同的,数据段和堆栈段呢?也不一定,因为在Linux里广泛使用的一个技术叫copy-on-write,即写时拷贝。copy-on-write意味着什么呢?意味着资源节省,假设有一个变量x在父进程里存在,当这个父进程创建一个子进程或多个子进程时这个变量x是否复制到了子进程的内存空间呢?不会的,子进程和父进程使用同一个内存空间的变量,但当子进程或父进程要改变变量x的值时就会复制该变量,从而导致父子进程里的变量值不同。
下面这段测试代码显示了父子进程变量不能共享:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

#define PARENTSLEEP 5
#define INTERVALSLEEP 3
int main(int argc, char ** argv)
{
int x = 0;
pid_t chldpid;
x = 5;
chldpid = fork();
if(chldpid < 0){
perror("fork");
return 0;
}
else if(chldpid > 0){
printf("here is the parent process\n");
printf("variable x(in parent) is:%d\n", x);
x = 8;
sleep(PARENTSLEEP);
printf("again variable x(in parent) is:%d\n", x);
sleep(INTERVALSLEEP);
printf("last variable x(in parent) is:%d\n", x);
waitpid(0, NULL, 0);
}
else{
printf("\there is the child process\n");
printf("\tvariable x(in child) is:%d\n", x);
sleep(INTERVALSLEEP);
printf("\tagain variable x(in child) is:%d\n", x);
sleep(INTERVALSLEEP);
x = 10;
sleep(INTERVALSLEEP);
printf("\tlast variable x(in child) is:%d\n", x);
}
return 0;
}

其运行结果如下:
/*
here is the child process
variable x(in child) is:5
here is the parent process
variable x(in parent) is:5
again variable x(in child) is:5
again variable x(in parent) is:8
last variable x(in parent) is:8
last variable x(in child) is:10
*/

从上述代码可以看到,父子进程变量是互不影响的。但遗憾的是由于父子进程地址空间是完全隔开的,变量的地址可以是完全相同的,所以我没办法用代码验证变量确实是不同的变量,有谁能想到一段验证的代码呢?

抱歉!评论已关闭.