QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 5368|回复: 12

Linux下编程为什么多用进程少用线程?编程问题探讨一

[复制链接]
发表于 2005-11-11 23:59:42 | 显示全部楼层 |阅读模式
Linux C/C++ 编程 进程 线程

自己从事Linux/UNIX编程多年,一直用fork在写多进程的程序。当有朋友问起为何Linux下很少用线程编程时才发现自己还真需要研究一下这问题。通过man手册查看fork得到如下提示:
fork  creates  a  child  process that differs from the parent process only in its PID and PPID, and in the fact that resource utilizations are set to 0.  File locks and pending signals are not inherited.
Under Linux, fork is implemented using copy-on-write pages, so the only penalty incurred by fork  is  the  time and memory required to duplicate the parent's page tables, and to create a unique task structure for the child.
也就是说fork做的事只有复制页表(page tables)和创建task structure。另外,父子进程表面看来唯一不同的是PID和PPID而资源消耗为0了。
而查看LinuxThreads的PTHREAD_CREATE(3)只得到是创建一个线程,并没有提到相关详细内容。
实际情况如何呢?我编写了如下两段代码:
/*forktest.c代码如下:*/
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char ** argv)
{
        pid_t        pid;
        char *        mem;
        if(argc < 4)        {printf("\r\nusage: %s time1 time2 memory-size\n", argv[0]); return 1;}
        mem = (char *)malloc(atoi(argv[3])); /*为了测试内存占用情况,故意输入1G或更多内存需求*/
        pid = fork();
        if(pid == 0)    {
                sleep(atoi(argv[1]));
                exit(0);
        }
        sleep(atoi(argv[2]));
        return 0;
}
/*此代码编译生成可执行程序a-fork*/

/*pthread_create.c代码如下:*/
#include <unistd.h>
#include <pthread.h>
void * start_routine(void * a)
{
        sleep(30);
}

int main(int argc, char ** argv)
{
        int ret;
        pthread_t pth;
        ret = pthread_create(&pth, NULL, start_routine, NULL);
        sleep(35);
        return 0;
}
/*此代码编译生成可执行程序a-thread*/

然后分别运行上述两个程序,用ps命令和top命令观察,得到如下表面结论:
1、两者的CPU占有率%CPU都0
2、两者的内存占有率%MEM:a-fork为0.5而a-thread为0.6
3、两个程序运行后ps都能看到两个进程(即a-fork进程有两个,a-thread进程也有两个)

深入理解看到的现象:
有上述结果3的原因是由于LinuxThreads所采用的就是线程和进程"一对一"模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。
有上述结果1应该是程序都在sleep,没有占用CPU是可以理解的吧。
但为什么会出现第2个结果,即a-thread的内存占有率居然比a-fork还要高呢?虽然这个表面现象可以回答为何大家喜欢进程编程而少用线程编程,但究其然到底是为什么呢?还有我这个测试方法到底对了没有呢?(我也是用自己的普通PC机在测啊)看来还得仔细研究内核源代码才知道啊。
查到有资料说:由于Linux特有的优点, Linux的进程是比较高效和轻量级的,所以Linux首选进程编程。
发表于 2005-11-16 12:40:02 | 显示全部楼层
你的线程库是NPTL的吗

原来不是NPTL的话有一个控制线程的
也就是创建一个(或者几个)线程会有一个额外的控制线程

据说grep改用线程后性能提高了几十倍

NPTL是近几年开发的,性能有了很大的提高
而且把控制线程去掉了
redhat最新版(fedora,rhas)都默认用NPTL了
其他版本要自己编译的
回复

使用道具 举报

发表于 2005-11-20 15:39:16 | 显示全部楼层
为什么书上说LINUX下编程应先考虑线程,说线程切换比进程切换容易得多,怎么与你说的不同里???
回复

使用道具 举报

发表于 2005-11-21 16:25:54 | 显示全部楼层

Re: Linux下编程为什么多用进程少用线程?编程问题探讨

[quote:8cb8dc652e="zhoulifa"]Linux C/C++ 编程 进程 线程

自己从事Linux/UNIX编程多年,一直用fork在写多进程的程序。当有朋友问起为何Linux下很少用线程编程时才发现自己还真需要研究一下这问题。通过man手册查看fork得到如下提示:
fork  creates  a  child  process that differs from the parent process only in its PID and PPID, and in the fact that resource utilizations are set to 0.  File locks and pending signals are not inherited.
Under Linux, fork is implemented using copy-on-write pages, so the only penalty incurred by fork  is  the  time and memory required to duplicate the parent's page tables, and to create a unique task structure for the child.
也就是说fork做的事只有复制页表(page tables)和创建task structure。另外,父子进程表面看来唯一不同的是PID和PPID而资源消耗为0了。
而查看LinuxThreads的PTHREAD_CREATE(3)只得到是创建一个线程,并没有提到相关详细内容。
实际情况如何呢?我编写了如下两段代码:
/*forktest.c代码如下:*/
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char ** argv)
{
        pid_t        pid;
        char *        mem;
        if(argc < 4)        {printf("\r\nusage: %s time1 time2 memory-size\n", argv[0]); return 1;}
        mem = (char *)malloc(atoi(argv[3])); /*为了测试内存占用情况,故意输入1G或更多内存需求*/
        pid = fork();
        if(pid == 0)    {
                sleep(atoi(argv[1]));
                exit(0);
        }
        sleep(atoi(argv[2]));
        return 0;
}
/*此代码编译生成可执行程序a-fork*/

/*pthread_create.c代码如下:*/
#include <unistd.h>
#include <pthread.h>
void * start_routine(void * a)
{
        sleep(30);
}

int main(int argc, char ** argv)
{
        int ret;
        pthread_t pth;
        ret = pthread_create(&pth, NULL, start_routine, NULL);
        sleep(35);
        return 0;
}
/*此代码编译生成可执行程序a-thread*/

然后分别运行上述两个程序,用ps命令和top命令观察,得到如下表面结论:
1、两者的CPU占有率%CPU都0
2、两者的内存占有率%MEM:a-fork为0.5而a-thread为0.6
3、两个程序运行后ps都能看到两个进程(即a-fork进程有两个,a-thread进程也有两个)

深入理解看到的现象:
有上述结果3的原因是由于LinuxThreads所采用的就是线程和进程"一对一"模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。
有上述结果1应该是程序都在sleep,没有占用CPU是可以理解的吧。
但为什么会出现第2个结果,即a-thread的内存占有率居然比a-fork还要高呢?虽然这个表面现象可以回答为何大家喜欢进程编程而少用线程编程,但究其然到底是为什么呢?还有我这个测试方法到底对了没有呢?(我也是用自己的普通PC机在测啊)看来还得仔细研究内核源代码才知道啊。
查到有资料说:由于Linux特有的优点, Linux的进程是比较高效和轻量级的,所以Linux首选进程编程。[/quote]
我觉得是不是你的测试代码太简单呢?线程之间总是共享地址空间的,而父子进程之间靠的是COW,这种共享是暂时的,一旦父子进程至少有一个向地址空间写入就要复制,这就减少了共享。而且,好像书上建议用线程,有的是出于调度开销的考虑以及线程间通讯的便利与低开销。理论上线程间切换似乎是不更换页表的,这能大大提高页表cache的命中率。
回复

使用道具 举报

发表于 2005-11-21 19:11:47 | 显示全部楼层
说都是靠fork()函数实现,根据参数不同,执行情况不同(说只共享代码,就是进程),能说说fork()两次返回的具体情况吗?很迷糊啊!!!
回复

使用道具 举报

发表于 2005-11-30 10:41:36 | 显示全部楼层
说都是靠fork()函数实现,根据参数不同,执行情况不同(说只共享代码,就是进程),能说说fork()两次返回的具体情况吗?很迷糊啊!!!
===========================
内核 线程才是靠fork函数实现的说
回复

使用道具 举报

发表于 2006-4-6 18:53:26 | 显示全部楼层
都是一个函数叫clone()
回复

使用道具 举报

发表于 2006-4-10 19:27:39 | 显示全部楼层
对线程的模拟,只是创建了一个与父进程共享pid,内存和文件描述符的进程而已。照理来说,这样模拟出的线程应该是比传统的用fork产生的进程更轻,因为它少了写时拷贝与单独管理其它数据的开销。

我认为,说编程上用进程比线程多,可能是历史原因。Unix一开始就是没有线程的,不像Windows,Solaris对线程的支持那么好。而且,用pthread_create模拟线程,一开始可能并不那么完善,大家可能不太愿意使用。

至于你的第二个结果,不太清楚。
但不能这样简单地单凭那一点点内存占用率来评判哪个更好。

其实,我觉得两者应该差不多。应该在内核部分两者实质上没有差别。只是用pthread_create写的程序,放到有真正多线程支持的系统上编译,能得到更好的性能,而且适合习惯以线程思考问题的人。
回复

使用道具 举报

发表于 2006-4-27 14:21:38 | 显示全部楼层
长见识了。
回复

使用道具 举报

发表于 2006-5-12 17:59:38 | 显示全部楼层
父进程在fork()的后半部分修改了子进程的系统堆栈,然后主动请求重新调度,这个时候父进程和子进程同样作为就绪进程,被调度到后从相同的地方回到用户空间,只是返回值不同
回复

使用道具 举报

发表于 2006-5-18 19:01:17 | 显示全部楼层
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
2664 root      16   0 11792  420  336 S  0.0  0.2   0:00.00 example1   
上面是我用top看到的!VIRT  代表什么啊?是虚拟内存的意思吗?为什么pthread_create会占用如此多的VIRT  ,请各位大虾指教!
下面是我的代码:
#include <stdio.h>
#include <pthread.h>
void thread(void)
{
        int i;
        char c ;
        for(i=0;i<5;i++){
                printf("This is a pthread.n");
                //sleep(1000);
                c=getchar();
        }
}

int main(void)
{
        pthread_t id;
        int i,ret;
        char c;
        pthread_attr_t attr;

        pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
        ret=pthread_create(&id,NULL,(void *) thread,NULL);
        if(ret!=0){
                printf ("Create pthread error!n");
                exit (1);
        }
        for(i=0;i<3;i++)
                printf("This is the main process.n");
pthread_join(id,NULL);
return (0);
}
回复

使用道具 举报

发表于 2006-5-20 17:21:27 | 显示全部楼层
有点迷糊,等待最后结论
回复

使用道具 举报

发表于 2006-6-20 13:05:06 | 显示全部楼层
具体原因,大家可以参考《Linux内核设计与实现》第二章

我大概说一下。

Linux从内核角度来讲,没有线程的概念,意思就是说没有专门的算法和数据结构来支持线程,所用的东西,本质上就是进程那一套。

Linux进程的创建是非常迅速的。内核设计与实现一书中甚至指出Linux创建进程的速度和其他针对线程优化的操作系统(Windows,Solaris)创建线程的速度相比,测试结果非常的好,也就是说创建速度很快。这也就解决了第一个问题。

第二个问题就是资源共享,这个和进程的近似点更多,就不多说了。

书中说的非常清楚,大家去看看就明白了。关键点是进程调度算法,结构体,初始化过程。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-4-18 19:19 , Processed in 0.114707 second(s), 15 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表