看到知乎上有个关于linux多进程、多线程的讨论:http://www.zhihu.com/question/19903801/answer/14842584

自己项目里也对这个问题有过很多探讨和测试,所以正好开贴整理一下,题目有点长,其实就2点:

1. 多进程模型和多线程模型,这两种模型在linux上有什么区别,各有何优缺点?

这里仅限于linux平台,因为linux平台跟win平台关于线程的实现差异很大。

2. 采用intel dpdk做包处理程序,是采用多进程模型好,还是多线程模型好?

 这里仅限于包处理程序(ips,waf,其他网络设备引擎),因为不同应用场景区别也很大。

首先知乎里边的评论,有个miao网友说的跟我的经验比较相符,先将其说法贴一下:

"linux使用的1:1的线程模型,在内核中是不区分线程和进程的,都是可运行的任务而已。fork调用clone(最少的共享),pthread_create也是调用clone(最大共享).fork创建会比pthread_create多消耗一点点,因为要拷贝tables和cow mapping.但是其实差别真的很细微,这些在内核开发者的努力下已经变的很小了。
再来说说contex switch的cost吧。线程的context switch是要比process小一些,因为线程共享了大部分的memory和tables,当switch的时候这些东西已经在缓存中了。
但是其实差别也很细微。但是在multiprocessor的系统中不共享memory其实是会比共享memory要有一点优势的,因为当任务在不同的processor中运行的时候,同步memory带来的损耗是不可忽视的。"

他这里说了两点有价值的信息,1  linux里的线程实现决定,创建、调度、切换线程的开销跟进程相比,好不了多少。

              2 多核CPU下由于缓存命中率的问题,进程这种天生不共享内存的做法,实际上比线程这种天生共享内存

                的做法,从性能上是有好处的。

这两点见解跟我们项目实际测试和研究结果是相符合的。下面从几个方面探讨这些问题:

1 linux 线程创建方式

linux提供的线程实际上是核外线程,即主要的线程机制是通过应用层面的库pthread提供的(线程的id分配、线程创建和管理,据说基本实现是pthread库为每一个进程维护一个管理线程,单调用 pthread_create等posix API时,调用者与该管理线程通过管道传递命令),

核内层面,线程几乎可以等同于进程。  这里贴一段从引用1 拷贝的内容:

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()) 的调用者则通过管道将请求信息传给管理线程。

上述内容基本可以这么表示:

  创建进程= fork ——> do_fork(不使用共享属性)

      创建线程= pthread_create——>__clone ——> do_fork(共享地址空间(代码区、数据区)、页表、文件描述符、信号。。)

这里其实另外一种多进程创建方式,就是脚本直接启动多个进程

下面再贴一段:

“对于一个进程来说必须有的数据段、代码段、堆栈段是不是全盘复制呢?对于多进程来说,代码段是肯定不用复制的,因为父进程和各子进程的代码段是相同的,数据段和堆栈段呢?也不一定,因为在Linux里广泛使用的一个技术叫copy-on-write,即写时拷贝。copy-on-write意味着什么呢?意味着资源节省,假设有一个变量x在父进程里存在,当这个父进程创建一个子进程或多个子进程时这个变量x是否复制到了子进程的内存空间呢?不会的,子进程和父进程使用同一个内存空间的变量,但当子进程或父进程要改变变量x的值时就会复制该变量,从而导致父子进程里的变量值不同。”

这里我的理解是,刚fork完,子进程和父进程代码段、页表等还是共享的,接下去有两种可能发展方向,1是子进程修改了数据,这时候,代码段:仍然是共享的,不需要拷贝;堆和静态数据区: 根据copy-on-wirte机制,不改变值的地方仍然共享,改变值的地方需要重新申请物理页面并修改值,修改页表(可能还要拷贝页表);栈: 不管进程还是线程,都不能共享,都需要创建的时候分配栈区。2是fork之后马上调用 exec 用新的进程替换,这时候会载入新的代码段、数据段,构建新的页表。

对于我们的包处理系统而已,无论怎么启动,创建时的性能开销其实是无所谓的,因为都是在系统初始化的时候创建。

2 调度和切换

由于核内的线程本质就是进程,其调度过程跟进程一样。切换,不论是进程切换还是线程切换,都需要替换运行环境(内核堆栈,运行时寄存器等),对于内存的切换,内核部分内存是一样的,用户空间部分:如果是进程,需要替换页目录基址寄存器,如果是线程,不需要替换;总体而言,linux进程和线程的切换,从内存寄存器、内核堆栈寄存器、其他寄存器等的换值开销应该是差不多的。具体切换代码参考引用2

但是由于多线程共享地址空间,从一个线程切换到同一个进程上另一个线程运行,页表,数据区等很多都已经在内存甚至缓存里,而从一个进程切换到另一个进程,可能由于刚切换进来的进程的页面被虚拟内存管理模块替换出去导致的页面替换开销,另外还有缓存tlb失效导致的缓存更新开销,这里性能有所差别。

对于我们的包处理系统而已,采用多核架构,主体进程/线程是绑定到不同的物理CPU core上并独占的,所以发生调度和切换的情况不多,因而这种影响不是很重要。

3. 地址空间共享相关问题

进程地址空间是独立的,这意味着,不同进程的内存天生就是不共享的,如果要共享,则需要开发者自己构建共享机制,比如使用IPC。

线程地址空间是共享的,这意味着,同一进程不同线程的内存天生是共享的,如果想要不共享,需要开发者自己实施,比如使用线程本地变量。

进程模型和线程模型,地址空间不共享和共享,会引发以下系列问题:

3.1 进程模型更安全、更健壮、更容易开发

由于一般公司成熟产品不是从无到有一个项目就开发完毕,必然有很多历史代码、多项目组合作的代码,这时候采用多进程模型,

可以有效隔离历史代码和当下代码、不同项目组的代码,当然,这需要产品本身是可以这么做的。比如,项目组A开发包处理进程,

项目组B开发包安全检测功能,两个功能是两个进程,这种模型无疑更容易开发和维护

另外,由于天生所以变量都不共享,对开发者要求也比开发多线程要低

3.2 多核下的性能

传统意义上,一般认为多线程比多进程性能要高,这其实是有前提的。比如不同线程之间需要频繁交互大量数据,由于IPC本身的开销,

如果数据交互非常频繁且量大,多线程会比多进程性能要高。

对于基于DPDK的多核数据包处理程序而言,由于3个原因,多进程模型更可预见性能高于多线程:

a DPDK提供了基于hugepage的共享内存机制,使得多进程物理地址相同,其虚拟地址也相同,这事实上就跟多线程之间共享地址空间是

一样的了。即采用DPDK的基础库,多进程之间不需要共享部分使用普通内存(libc malloc,静态区,栈区),相互隔离很安全。需要共享

部分采用dpdk hugepage 内存,通过特殊映射,也能共享虚拟地址。在这片共享内存上交互数据和指针(虚拟地址是一样的),性能

远高于利用内核的IPC机制。

b 多核缓存伪共享问题

这个问题在之前帖子里http://www.cnblogs.com/jiayy/p/3246133.html说过,多核架构一般有3层缓存,缓存命中率是系统整体性能最关键是因素之一。缓存命中率有一个致命杀手就是

伪共享现象,多线程由于天生所有内存全部是共享的,所以更容易发生伪共享现象,其任何变量,只要一个CPU核改了,其余CPU核都产生

一次缓存失效并重新加载。。,而多进程模型,共享部分是有限的且开发者可以精确设计和控制的,其伪共享现象可以得到有效控制。

在项目实际开发中,经常的情况就是多线程性能低于多进程,需要将很大变量改为线程局部变量,才能让性能有所提升。

c 同步互斥

其实,无论是多线程还是多进程,都需要面临同步和互斥,这个不是进程/线程模型决定的,而是业务模型决定的。dpdk 提供了应用层

空间实现的基础互斥同步接口,包括原子操作、自旋锁、读写锁等,主要是配合共享内存的访问,因为从数据包处理系统来说,基本上

没有阻塞的概念,所以这种原子操作和忙等待的锁可以满足大部分需求,对于需要阻塞的系统,比如应用层协议栈,则还是需要使用内核的

机制,比如信号量等

4 最终采用的模型

最终我们采用的模型是:主体框架是多进程,主进程内部有若干线程用于处理诸如命令接收、文件监控、配置同步、统计数据写出、

debug数据写出等功能,包处理的主体流程是多进程的,不同进程之间基础表项、数据包等数据采用dpdk共享内存,在系统启动时

静态映射好,这些关键的基础表项和数据包结构针对缓存做细致优化,比如对齐内存以避免发生伪共享。由于我们的业务同步和互斥方面

的要求不多,所以只使用了有限的忙等待的锁和原子操作函数。这种模型实际上也是intel 推荐的模型。当然,选择多进程模型后,

又有很多需要考虑的东西了,比如是流水线的worker1-worker2-worker3的多进程,还是 master-worker-worker-worker的对称多进程,这里头根据业务逻辑、同步互斥、性能、扩展性、可维护性有很多深入的考虑,这里就不详细说了。

http://www.soft-bin.com/html/2010/07/09/%E5%A4%9A%E8%BF%9B%E7%A8%8Bvs%E5%A4%9A%E7%BA%BF%E7%A8%8B%EF%BC%8C%E4%B8%80%E4%B8%AA%E9%95%BF%E6%9C%9F%E7%9A%84%E4%BA%89%E8%AE%BA.html

http://blog.sina.com.cn/s/blog_d9889c5b0101e7x6.html

谈谈dpdk应用层包处理程序的多进程和多线程模型选择时的若干考虑的更多相关文章

  1. php多进程和多线程的比较

    前言 最近在学习php多进程和多线程的编程.说实话,这两样在工作中几乎都没有用到,毕竟php并不以异步处理擅长,对于网络请求同步处理可以解决绝大多数问题.但是既然有这样的机制,也了解一下,对于以后接触 ...

  2. Flask 开启多进程或多线程

    2018-07-15 23:31:20 yang9315 阅读数 7703更多 分类专栏: python   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接 ...

  3. 强大的数据包处理程序scapy

    实验目的 利用scapy工具构造arp.icmp数据包,发送到目标主机,根据应答包推测出目标系统存活情况 实验原理 Scapy是Python写的一个功能强大的交互式数据包处理程序,可用来发送.嗅探.解 ...

  4. Python多进程与多线程编程及GIL详解

    介绍如何使用python的multiprocess和threading模块进行多线程和多进程编程. Python的多进程编程与multiprocess模块 python的多进程编程主要依靠multip ...

  5. 深入浅析python中的多进程、多线程、协程

    深入浅析python中的多进程.多线程.协程 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源 ...

  6. Python 中多进程、多线程、协程

    进程: 一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享.开销大. 线程: 调度执行的最小单位,也叫执行路径,不 ...

  7. Python第十二章-多进程和多线程01-多进程

    多进程和多线程 一.进程 1.1 进程的引入 现实生活中,有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的:试想,如果把唱歌和跳舞这2件事情分开依次 ...

  8. Android中的多进程、多线程

    前面几篇总结了进程.线程相关的知识.这里总结下关于Android中的多进程.多线程及其使用. 这里总结的Android中的多进程.多线程也是一个基础,可扩展的很多. Android中多进程 常见的几种 ...

  9. Python中的多进程、多线程和协程

    本文中的内容来自我的笔记.撰写过程中参考了胡俊峰老师<Python程序设计与数据科学导论>课程的内容. 并发处理:多进程和多线程 前置 概念: 并发:一段时间内同时推进多个任务,但不一定要 ...

随机推荐

  1. 笔记-python-built-in functions-eval,exec,compile

    笔记-python-built-in functions-eval,exec,compile 1.      python代码执行函数 有时需要动态改变代码,也就是说代码需要是字符串格式,然后在按需要 ...

  2. python基础之入门基础

    编程语言分类 机器语言 使用二进制代码直接编程,直接与硬件交互,执行速度非常快,灵活,但是开发难度高,开发效率低下,缺乏移植性. 汇编语言 对机器语言指令进行了英文封装,较机器语言容易记忆,直接与硬件 ...

  3. Hive 压缩技术Data Compression

    Mapreducwe 执行流程 :input > map > shuffle > reduce > output 压缩执行时间,map 之后,压缩,数据存储在本地磁盘,减少磁盘 ...

  4. android 文件下载 超简单

    public void downloadPlug(String downloadUrl,String savePath) { try { URL url = new URL(downloadUrl); ...

  5. SpringMVC集成RSA加密算法

    技术交流群: 233513714 本文介绍的是RSA加密算法+Spring Security在SpringMVC中的集成使用. Spring Security是什么? 引用: Spring Secur ...

  6. python 学习分享-socket编程

    socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意思. 通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟 ...

  7. 使用dib element proliant-tools制作deploy image

    element proliant-tools会在ipa ramdisk中安装一个rpm包hpssacli(HP的RAID管理工具),和一个python module proliantutils(里面P ...

  8. hdu 1203 01背包 I need a offer

    hdu 1203  01背包  I need a offer 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1203 题目大意:给你每个学校得到offe ...

  9. 团队冲刺Alpha(九)

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  10. SPOJ AMR10I Dividing Stones

    Time limit: 7s Source limit: 50000B Memory limit: 256MB The first line contains the number of test c ...