[Linux]——多进程与多线程对比

多进程还是多线程

进程和线程是伴随我们学习操作系统时经常会提到的概念。那句"进程是分配系统资源基本实体,线程是CPU调度的基本单位"也成了经典的俩者关系的概括。但是随着深入的学习,我们发现只理解这句话是不够的,我们需要对比两者,找到两者各自的优点,具体结合场景讨论到底使用谁更好。

我们经常会看到一些一劳永逸的问题,“多进程和多线程哪个更好”、“Linux下用多进程好还是多线程好”。其实这种问法太过于绝对。仔细思考,如果真的某一者比另一者好,那么差的那一者应该早就被淘汰了吧。

多进程和多线程没有绝对的谁比谁好或者谁比谁差,还是需要结合具体的场景来进行选择。接下来我们就从不同的角度对多进程和多线程进行对比。

多进程与多线程对比

对比要素多进程多线程总结
数据共享、同步数据共享复杂,需要使用IPC;数据私有,同步简单共享进程数据;同步复杂,需要各种同步机制各有优势
内存、CPU占用内存多,CPU使用率低占用内存少,CPU使用率高线程较优
创建销毁、CPU创建销毁代价高,切换速度慢创建销毁代价低,切换速度快线程较优
可靠性进程间独立运行可靠性高在进程内运行,一个异常导致整个进程异常进程较优
分布式适应于多核、多机分布式多核分布式进程较优

上表中概括性的总结了进程和线程不同维度的优缺点比较,除了以上这些有的同学还会提到编程和调试的难度。其实这个笔者倒觉得他们俩五五开,因为你对哪个更熟悉也就觉得哪个更简单。

如何选择两者?

具体如何选择我们就一定要结合场景了,下面就给大家列举出几种不同的场景:

需要频繁创建销毁的优先用线程

需要频繁创建和销毁的应用绝对要提到web服务器啦,每接受一个请求就建立线程,而断开就销毁线程。从上表中我们对比进程和线程可以知道,进程的创建销毁不管从使用资源还是速度上来说都是没有线程优的。所以此场景下大力推荐使用线程

需要进行大量计算的优先使用线程

所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的

强相关的处理用线程,弱相关的处理用进程

如何理解强相关和弱相关呢?这俩个关键词非要下一个定义反而让人不好理解。举一个生活中的栗子,你刷牙和涂牙膏动作就属于强相关,而你涂牙膏和洗脸的动作就属于若相关。

之所以弱相关选择进程还是因为考虑到了可靠性的问题,俩个模块能分开就不要放在一起。学过软件设计的同学也就一定听过一句话"高内聚,低耦合"。线程异常往往会导致整个进程出问题。

可能要扩展到多机分布的用进程,多核分布的用线程

这个不难理解,多线程也仅仅能实现多内核的分布式。

未必一定非此即彼

其实大多数情况下,在多线程和多进程没有特别大差异的情况下你还是需要选择你最熟悉的那个进行编程。并且鱼与熊掌未必不可兼得,编程中我们还是要灵活的分析各种情况,有时候选择"进程+线程"才能让我们的程序到达最优的情况。

其他因素

资源的消耗

一个进程的开销大约是一个线程开销的30倍左右,所以某种情况下从资源使用程度来看线程确实是比进程更好的选择。

通讯方式

我们之前说过进程间通信是有难度的,实际上进程间通信是为了让他们看到同一份资源。所以进程在通信时还要使用其他额外的通信机制。而线程本身就是共享同一份资源,只要确保数据安全的情况下做好同步工作,线程间的通信就会比进程简单很多。

总结

所有的东西都没有绝对的优秀,关键还是在于不同场景下的选择。进程和线程是如此,人生亦是如此。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Tcl_TK编程权威指南pdf 内容简介回到顶部↑Tcl/Tk是第一种能通过Windows、Macintosh和Solaris等主要平台处理企业级任务的脚本语言。本书共分为55章,依次详细讲述了Tcl基础、Tcl高级特性、TK基础、TK组件、TK详解、C语言编程、各版本之间的差异等方面的知识,并通过大量实例,生动翔实地向读者介绍了Tcl/Tk编程,是读者掌握Tcl/Tt的必备参考书。 本书适合各个层次的读者阅读。 目录回到顶部↑第1部分 tcl基础 第1章 tcl的基本知识 tcl命令 hello,world! 变量 命令替换 数学表达式 反斜杠替换 使用花括号和双引号进行分组 过程 一个阶乘的例子 更多有关变量的知识 更多有关数学表达式的内容 注释 有关替换与分组的总结 要点 参考 第2章 开始使用 source命令 unix上的tcl脚本程序 .windows 95的开始菜单 macintosh与resedit console命令 命令行变元 预定义变量 第3章 cgi应用程序--顾客留言簿 html简介 使用cgi创建动态页面 guestbook.cgi脚本程序 定义表单以及处理表单数据 cgi.tcl软件包 接下去的几步 第4章 tcl中的字符串处理 string命令 append命令 format命令 scan命令 binary命令 相关章节 第5章 tcl列表 tcl列表 构建列表 获取列表元素 修改列表 搜索列表 对列表进行排序 split命令 join命令 相关章节 第6章 控制结构命令 if then else switch while foreach for break与continue catch error return 第7章 过程与作用域 proc命令 使用rename来改变命令名 作用域 global命令 通过upvar以名字进行调用 使用upvar来处理变量别名 第8章 tcl数组 数组的语法 array命令 使用数组来构建数据结构 第9章 对文件和程序的操作 使用exec运行程序 file命令 跨平台的文件命名方式 操作文件和目录 文件属性 对i/o命令的总结 打开文件用于i/o操作 读写操作 当前目录-cd和pwd 使用glob来匹配文件名 exit和pid命令 环境变量 registry命令 第2部分tcl高级特性 第10章 引用问题与eval 使用list命令来构建代码 在eval内部利用concat uplevel命令 subst命令 第11章 正则表达式 何时使用正则表达式 正则表达式的语法 高级正则表达式(are) 语法总结 regexp命令 rgsub命令 使用regsub将数据转换为程序 其他使用正则表达式的命令 第12章 脚本库及软件包 确定软件包的位置:auto-path变量 使用软件包 对软件包加载的总结 package命令 基于文件tclindex的库 unknown命令 方便交互 tclshell的库环境 编码风格 第13章 反射与调试 clock命令 info命令 跨平台支持 跟踪变量的值 交互式命令历史记录 调试 scriptics的tclpro 其他工具 性能调校 第14章 名字空间 使用名字空间 名字空间变量 命令查找 嵌套名字空间 过程的进口与输出 回调与名字空间 内省(introspection) namespace命令 转换现有的软件包以使用名字空间 [incrtcl]对象系统 注意事项 第15章 国际化(internationalization) 字符集与编码 消息目录 第16章 事件驱动的编程 tcl事件循环 after命令 fileevent命令 vwait命令 fconfigure命令 第17章 套接字编程 客户端套接字 服务器端套接字 回送(echo)服务 使用http获取一个url http软件包 基本认证 第18章 tclhttpd web服务器 将 tclhttpd与你的应用程序集成 域处理程序 应用执导的url 文档类型 html+tcl模板 表单处理程序 编程参考 标准应用执导(application-dirct)的url tclhttpd发行版 服务器配置 第19章 多解释器与 safe-tcl interp命令 创建解释器 安全解释器 命令别名 隐藏命令 替换 从安全解释器中执行i/o操作 安全基础 安全策略 第20章 safe-tk与浏览器插件 子解释器中的tk 浏览器插件 安全策略与浏览器插件 配置安全策略 第3部分 tk基础 第21章 tk的基本知识 th中的hello,world! tk组件的命名 配置tk组件 tk组件属性与资源数据库 tk命令概要 第22章 tk实例解析 execlog example browser tcl shell 第23章 打包摆放布局管理器(pack) 朝一侧摆放 水平与垂直难叠 空腔模型( cavity model) 打包摆放空间(packing space)与显w空间(display space) 尺寸调整与一expand 挂靠 摆放顺序 选择用于摆放的父组件 取消一个组件的摆放 打包器总结 窗口的堆叠顺序 第24章 栅格摆放布局管理器( grid) 一种基本栅格 跨行列摆放 行列约束 grid命令 第25 章定位摆放布局管理器( place) place的基础知识 面板管理器 place命令 第26章 将命令与事件编联 bind命令 bindtags命令 事件的语法 修饰符 事件序列 虚拟事件 事件关键词 第4部分 tk组件 第27章 按钮与菜单 按钮命令与作用域问题 与tcl变量关联的按钮 按钮属性 按钮操作 菜单和菜单按钮 键盘遍历 操纵菜单和菜单条目 菜单属性 通过名字来指定菜单的软件包 第28章 资源数据库 有关资源的介绍 加载选项数据库 添加单一的数据库条目 存取数据库 用户定义的按钮 用户定义的菜单 第29章 简单的tk组件 框架组件与顶层窗口 标签组件 消息组件 标尺组件 bell命令 第30章 滚动条 使用滚动条 滚动条协议 滚动条组件 第31章 输入条组件 使用输入条组件 输入条组件 第32章 列表框组件 使用列表框组件 列表框组件的编联 列表框组件的属性 第33章 文本组件 文本索引 文本标记 文本标签 文本信息的选择( selection) 标签的编联 文本搜索 嵌入组件 图片的嵌入 查看文本组件的内部信息 文本组件的编联 文本组件的操作 文本组件的属性 第34章 画布组件 画布坐标 hello, world! 最小和最大标尺的例子 画布对象 画布组件的操作 产生postscript输出 画布组件的属性 建议 第5部分 tk详解 第35章 选择和剪贴板 选择模型 selection命令 clipboard命令 选择处理程序 第36章 焦点、焦点的捕获和对话框 标准对话框 定制对话框 使用update命令实现动画 第37章 tk组件的属性 配置属性 尺寸 边界与浮雕效果 焦点的高亮显示 补自(padding)与挂靠(anchor) 第38章 颜色、图片和鼠标指针 颜色 色彩映射与视频种类 位图和图片 文本插入光标 鼠标指针 第39章 字体与文本属性 字体命名 x字体名 字模 font命令 文本属性 栅格化、尺寸调整和布局 一个字体选择应用程序 第40章 send send命令 发送者脚本 通信进程 通过套接字来实现远程eval 第41章 窗口管理器与窗口信息 win命令 winfo命令 tk命令 第42章 管理用户首选项 应用默认设置文件 定义首选项 首选项的用户界面 管理首选项文件 跟踪对首选项变量的修改 对该软件包的改进 第43章 一种操作编联的用户界面 一对协调工作的列表框 编辑界面 保存与加载编联 第6部分 c语言编程 第44章 c语言编程与tcl 基本概念 创建可加载软件包 一个用c语言实现的命令过程 blob命令的例于 字符串与国际化 tolmain和tcl-applnit tk_main 事件循环 从c中调用脚本 第45章 编译tci及扩展模块 标准目录结构 从源代码建立tci 使用占位函数库(stub library) 使用autoconf 扩展模块范例 makefile.in 第46章 使用c语言编写tk组件 初始化扩展模块 组件的数据结构 组件的类命令 组件实例命令 配置和重新配置属性 指定组件属性 时钟的显示 窗口事件过程 最后的清除工作 第47章 c函数库概览 tclc函数库概览 tk c函数库概览 第7部分 各版本之间的差异 第48章 tcl 7.4/tk 4.0 wish 过时废弃的功能 cgct操作 输入焦点的高亮显示 编联 滚动条接日 pack info 焦点 send命令 按钮的内部补白 单选按钮的值 输入条组件 菜单 列表框 没有了geometry属性 文本组件 颜色属性 颜色分配与tk colormodel 画布组件的scrollincrement 选择 bell命令 第49章 tcl 7.5/tk 4.1 跨平台脚本 clock命令 load命令 package命令 多个foreach循环变量 事件循环从tk转移到了tcl 网络套接字 多解释器与safe-tcl grid布局管理器 文本组件 输入条组件 第50章 tcl7.6/tk 4.2 更多的file操作 虚拟事件 标准对话框 新的grid布局管理器 macintosh的unsupportedl命令 第51章 tcl/tk 8.0 tcl编译器 名字空间 safe-tcl 新的lsort tcl_precision变量 2000年约定 http软件包 串行线i/o 独立于平台的字体 tk scaling命令 应用程序的嵌入 本地化菜单与菜单条 cde的边界宽度 本地化的按钮和滚动条 文本组件中的图片 destroy不再产生错误 grid rowconfigure 补丁版本 第52章 tcl/tk 8.1 unicode与国际化 线程安全 高级正则表达式 新字符串命令 dde扩展模块 杂类 第53章 tcl/tk 8.2 trf补丁 更快的字符串操作 空数组名 浏览器插件的兼容性 第54章 tcl/tk 8.3 关于tcl的修改建议 关于tk的改动建议 第55章 有关本书的cd-rom ↓展开全部内容 序言回到顶部↑Tcl为工具命令语言(Tool Command Language)的缩写。它其实是指两样东西:一种脚本语言,以及该脚本语言的解释器。该解释器可以很容易地嵌入到你的应用程序中。Tcl和与之关联的图形用户界面工具包(Tk)是由加州大学的John Ousterhout教授设计并编写的。尽管它是个商用软件包,但你也可以在Internet上找到它(见第VII页),而且可以在自己的应用程序中自由使用这个软件包。Tcl解释器已经从Unix平台移植到了DOS、Windows、OS/2、NT以及Macintosh环境中,而TK工具包也从X window系统移植到了Windows和Macintosh环境中。 1988年,当我在Berkeley做ousterhout教授的博士生时,第一次听说了Tcl。我们当时正在设计一种名为Sprite的网络操作系统。同学们在努力编制一个新式的内核程序,而John编写了一个新的编辑器和终端仿真程序。他使用Tcl作为这两种工具的命令语言,这样用户就可以定义菜单或者对那些程序进行定制。那时还处在使用X10的时代,他计划编写一个基于Tcl的X工具包,以使程序之间通过Tcl命令进行通信,彼此相互协作。对我来说,这种工具之间的相互协作就是Tcl的实质。 这种早期的设想就是让应用程序由包含编译代码的大块实体和一小部分用于进行配置和编写高级命令的Tcl代码组成。John的编辑器皿,还有终端仿真程序tx就遵循了这种模式。虽然这种模式仍然是有效的,但结果表明用Tcl来编写整个应用程序也是可能的。这是因为Tcl/Tk的shell程序wish提供了对其他程序、文件系统和网络套接字的存取功能,同时还能够创建图形用户界面。不管怎样,现在发现包含几千行Tcl脚本的应用程序并不稀奇。 我编写这本书的原因就是,虽然自己觉得使用Tcl与Tk既有乐趣又高效,但是也有令人头痛的时候。此外,在Xerox PARC工作,那里有许多语言和系统上的专家,我不得不强迫自己去理解Tcl/Tk的长处和弱点。我的许多同事都在他们的项目中采用了Tcl和Tk,但是他们也很快指出了它的缺点。因此,我就总结了一套编程技巧以充分利用Tcl/Tk的强大功能,同时回避一些棘手的问题。这本书就是一本帮助你最大限度地利用Tcl/Tk并回避一些我所经历过的令人头痛的问题的实用编程指南。 我接触Tcl语言大概已经有10年的时间了,而本书的第一版也已经出版5年了。在过去的几年中,我一直在John Ousterhout的手下工作,最初是在Sun微系统公司,而现在是在Scriptics公司。我一直使自己在很大程度上保持着一个Tcl程序员的角色,而我们工作组中的其他人员则埋头于Tcl本身的C语言实现。我创建的应用程序有HTML编辑器、EMAIL比用户接口程序、Web服务器以及用户数据库,我们的商务应用就建立在它们的基础上。这些经历在本书中有所反映。本书的大部分内容是有关Tcl脚本编程的,而有关使用C语言来创建Tcl扩展模块的内容没有着重讲述。我有幸一直参与Tcl核心技术的开发活动,希望通过本书能够将自己使用Tcl时获得的切身体会表达出来。 为什么要使用Tcl 作为一种脚本语言,Tcl与其他的Unix shell语言,如Bourne Shell(sh)、C Shell(csh)、Korn Shell以及Perl类似。Shell程序可以让你执行其他的程序。它们提供了足够的可编程特性(变量、流程控制和过程),使你可以将现有程序组装成符合自己需要的复杂的脚本程序。Shell程序非常适用于一些日常任务的自动化处理工作。 Tcl解释器可以很容易地添加到你的应用程序中,这种能力将它与其他的shell语言区分开来。Tcl扮演了一种扩展语言的角色,用来配置和定制应用程序。你没有必要再去为自己的新应用程序发明一种命令语言,或是费力为自己的工具提供某种用户可编程特性。其实,你可以通过添加一个Tcl解释器,来将自己的应用程序组织成一组操作原语,并使用这些原语来构造最符合用户需求的脚本程序。这样还可以允许其他的程序通过编程来控制你的应用程序,以使套装应用程序能够很好地在一起工作。 Tcl的C函数库拥有清晰的接口而且便于使用。该函数库实现了基本的解释器,它有一套实现变量、流程控制和过程的核心脚本命令,而且还有一组用来存取操作系统服务以运行其他程序、存取文件系统和使用网络套接字的命令。Tcl和Tk提供了一台可以在UNIX、Windows和Macintosh环境中可移植的"虚拟机"。 因为你的应用程序可以定义新的Tcl命令,所以Tcl虚拟机是可扩展的。这些命令与你的应用程序所提供的C或C++过程关联。结果应用程序就分割成一组用编译语言编写的原语,并输出成为相应的Tcl命令。使用Tcl脚本程序可以将这些原语组装成完整的应用程序。脚本语言层可以存取与shell类似的功能以运行其他的程序,可以存取文件系统,还可以直接通过自己定义的Tcl命令来调用应用程序中编译的代码部分。此外,从C编程的层面上来说,你还可以调用Tcl脚本程序、设置和询问Tcl变量,甚至跟踪Tcl解释器的执行。 在Internet上有许多可自由使用的Tcl扩展模块。许多扩展模块都包含了一个提供某种新功能的C函数库,以及该函数库的Tcl接口。这样的例子包括数据库存取、电话控制、MIDI控制器存取,还有expect,它为控制交互式程序增加了一组Tcl命令。 最为著名的扩展模块就是Tk,这是一种图形用户界面工具包。Tk定义了用来创建和操作用户界面组件的Tcl命令。这种基于脚本的用户界面编程方法有三个好处: . 由于快速的响应周期,所以开发迅速,不存在漫长的编译等待过程。 . Tcl命令提供了一种比绝大多数由标准C函数库实现的用户界面工具包更为高级的接口。它只需一小组命令就可以定义简单的用户界面,同时又可以对用户界面进行细化以恰当地实现每一个细节。快速的响应周期又为这种细化过程提供了帮助。 用户界面处理可以从你的应用程序的其余部分分离出来。因而开发人员能够专心致志地实现应用程序的核心部分,然后再颇为轻松地构建出用户界面。Tk组件的核心功能通常能够满足你所有的用户界面需求。不过,你还可以用C语言来编写定制的Tk组件,而且网上还有许多大家提供的Tk组件可以使用。 还有其他可以用做扩展语言的选择,这包括VisualBasic、Scheme、Elisp、Perl;Python和Javascript等,你可以依照个人喜好从中进行选择。Tcl拥有简单的结构,而且还有些地方类似于C语言,可以通过编制C过程来增添新的Tcl原语。Tcl非常易学,许多有关用户使用Tcl在很短的时间内(例如几个星期)就完成了相当难度的项目,并且他们以前压根就没有接触过Tcl。 当本书第一次出版时,Java轰动了计算机界。Java是一种极为优秀的系统编程语言,长远来看还有可能代替C和C什语言。这对Tcl来说挺好,它在设计时就被用来将由任意系统编程语言编写的构件粘连起来。Tcl过去被设计与C语言一起工作,但是现在已经被改造成能够与Java虚拟机一起工作。在我们提到"C或C++"的地方,现在也可以说"C、C++或Java"了,但是对于Java来说,其细节上还多少存在些差异。这本书并没有描述TcVJava接口,但是你可以在CD-ROM上找到TclBlend。TclBlend将Java虚拟机加载到你的Tc3应用程序中并允许你调用Java方法,它还可以让你使用Java而不是C或C十十来实现Tcl命令。 Javascript是一种来自于Netscape的语言,它被设计用来编写与w曲页面进行交互的脚本程序。由于Netscape的广泛使用,Javascript就显得很重要,然而Tcl提供了一种更为通用的脚本方案,可以在更为广泛的范围中使用。Tcl/Tk的Web浏览器插件提供了一种在浏览器中运行Tcl的方式,结果使得Tcl更像是一种Java的替代品而不是Javascript的替代品。该插件可以让你在浏览器中执行Tcl应用程序,而Javascript则为你提供了对浏览器和HTML显示的精细控制。这种插件将在第20章有所描述。TcI与Tk的版本 Tcl与Tk仍在继续演变。请参看http://www.beedub.com/book/来了解有关最新的Tcl版本的更新和消息。由于历史原因,Tcl与Tk曾各有各的版本号,但是它们成对发行,并一起工作。这本书的原始版本基于Tcl7.4和Tk 4.0并有几处引用了Tk 3.6的功能。第三版已经进行了更新,它反映了直到Tcl/Tk8.2以来所增添的各种新特性: . Tcl7.5和Tk 4.1的最终发布在1996年5月。这些版本的特点是将Tk移植到了Windows和Macintosh环境。它引入了Safe-Tcl安全机制,以支持网络小应用程序(Applet)的 .安全执行。它还提供了对网络套接字的支持以及一种新的输入输出(I/O)子系统,以支持高性 能的事件驱动I/O。 . Tcl7.6和Tk4.2的最终发布是在1996年的10月。这些版本包含了对S池-Tcl的改进,以及对在Tk 4.1中引进的grid布局管理器的改进。跨平台的支持包括虚拟事件(例如,以<<Copy>>宋代表<Control-c>=、标准对话框,还有更多的文件操作命令。 . Tcl 7.7和Tk 4.3是内部版本,用于开发NetscapeNavigator和MicrosoftInternetExplorer Web浏览器的Tcl/Tk插件。它们的开发工作实际上与Tcl7.6和Tk 4.2并行进行。Tcl/Tt插件已经发布了许多各种平台上的版本,其中包括Solaris/SPARC、Solaris/INTEL、SunOS、Linux、Digital UNIX、IRIX、HP/UX、Windows95、Windows NT以及Macintosh。该浏览器插件支持Web页面中的Tcl小应用程序(Applet),同时使用Safe-Tcl复杂的安全机制来提供安全保证。 . Tcl8.0为Tcl新增了一个运行时用的编译器,这个编译器提供了数倍于Tcl脚本的执行速度。Tcl8.0支持内嵌空字符的字符串。编译器对脚本来说是透明的,但是扩展模块编写入员需要学习一些新的C API才能发挥它的潜力。由于John Ousterhout从Sun微系统公司到了Scriptics公司,发布8.0版的时间推迟了几年。广泛使用的版本8.0p2是在1997年完成的,但是最终的补丁版本8.0.5直到1999年春才发布。 . 在8.0时,Tk更改了版本号以与Tcl相匹配。Tk 8.0包含了一种新的独立于平台的字体机制,它还包含了本地化菜单和菜单条,以及更多的本地化组件,它们在Windows和Macintosh上拥有更好的本地化外观。 Tcl/Tk8.1新特性主要包括对Unicode的完整支持,以及线程安全,这样你就可以将Tcl嵌入到多线程的应用程序中。Unicode是一种新的正则表达式引擎,它提供了在Perl5中所能找到的所有功能。Tk为找到正确的用于显示Unicode字符的字体完成了卓越的工作,它还增加了一种信息目录设施,这样你就可以编写国际化的应用程序。Tcylk 8.1的发布史中还包括了Sun到Scriptics的过渡。第一个alpha版本完成于1997年秋,而最终的补丁版本完成于1999年5月。 Tcl/Tk 8.2主要是一个进行bug修正和稳固化的版本。它对TclC函数库API进行了几处微小增补,这样无须核心补丁程序也能支持更多的扩展模块。Tcl/Tk 8.2很快在1999年夏进入最终版本。 谁应当阅读本书 本书不仅适用于熟练的编程人员,同样也适用于初学者。对于初学者和熟练编程人员来说,我建议大家仔细学习一下策1章"Tcl的基本知识"。Tcl的编程模型被设计成一种简单的模式,它与许多编程语言存在差异。该模型基于字符串替换,你对这一点的正确理解很重要,这样才能避免在复杂情况下遇到麻烦。这本书的其余部分则包含了演示如何高效地使用Tcl与Tt的例子。每一章中都有对其中所描述的Tcl命令和Tk组件进行总结的表格,以供参考。 本书假定你有一些编程经验,但是你如果是个彻头彻尾的新手也能够读下去。对Unix shell的了解将会对你有所帮助,但这并不是必须的。在那些涉及Windows系统的地方,我会提供一些背景信息。第2章详细描述了在UNIX、Windows和Macintosh上使用Tcl与Tk的内容。 如何阅读本书 本书最好能在上机实习中使用,可以在计算机上尝试一下书中的例子。Tcl与Tk的命令手册尽管完整但却缺少上下文的的相关信息和例子,本书就试图填补在简明手册与现有的文档化或没有很好文档化的Tcl程序之间的空隙。 我推荐使用联机手册来查阅有关的Tcl/Tk命令。它为每个命令都提供了详细的参考指南,但是它没能提供完整的细节,这在每一次发布的版本中都有所不同。HTML版本的联机手册可以在随书的CD-ROM中找到。
一个进程池的服务器程序 下面做了非常简单的http服务器,该服务器只能接收Get请求。 流程大概如下: 1,父进程listen,创建pipe(下面所有父子进程之间的通信都用该pipe) 2,父进程预fork n个子进程 3,各个子进程accept(listenfd),即所有子进程竞争accept请求。由于listenfd是在fork之前就有的,所以所有子进程都可以访问到,不需用到“进程间文件描述符传递”问题; 4,子进程每accept到一个请求都告诉父进程,父进程把请求数加1;子进程没完成一个请求,父进程把请求数减1;当父进程发现请求数 >= 子进程数时,父进程创建新的子进程,并把子进程数加1(当然子进程数有个预先上限);当父进程发现子进程数大于请求数加1时,父进程杀死多余的子进程。 总的来说,思想是让子进程accept并处理请求,父进程通过子进程发来的信息控制请求数与子进程数之间的关系。 代码如下: 代码如下: #include <time.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <dirent.h> #include <sys/stat.h> #include <signal.h> #include <sys/wait.h> #include <pthread.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #define PRECHILD 5 #define MAXCHILD 50 #define BUFSIZE 4096 #define PIDPATH "pid" #define head503 "HTTP/1.1 503 Service unavailable\r\n" #define head404 "HTTP/1.1 404 Not Found\r\n" #define head200 "HTTP/1.1 200 0K\n\rContent—Type: text/html\n\rContent—Length: " int len503, len404, len200; int fd1[2], fd2[2]; typedef struct { pid_t pid; char status; // 'n' means new request; 'f' means finish the request } REPORT; void answer(int listenfd) { int connfd; char buf[BUFSIZE]; int count; int pid = getpid(); struct sockaddr_in cliaddr; int size = sizeof(cliaddr); char comm; REPORT rep; rep.pid = pid; while (1) { connfd = accept(listenfd, (struct sockaddr *)&cliaddr,(socklen_t *)&size ); //子进程accept请求 rep.status = 'n'; if (write(fd1[1], &rep, sizeof(rep)) < 0) { //通知父进程已经accept了请求 perror("write pipe new failed"); exit(-1); } count = read(connfd, buf, BUFSIZE); char req[10]; char filepath[256]; sscanf(buf, "%s%s", req, filepath + 1); filepath[0] = '.'; if (strcmp("GET", req) != 0) {//503 write(connfd, head503, len503); //goto err_out; close(connfd); exit(-1); } char content[BUFSIZE]; struct stat stbuf; if (lstat(filepath, &stbuf) != 0) { int err = errno; if (err == ENOENT) {//404 write(connfd, head404, len404); } close(connfd); exit(-1); } count = write(connfd, head200, len200); u_int filesize = stbuf.st_size; sprintf(content, "%u\n\r\n\r", filesize); count = write(connfd, content, strlen(content)); FILE *fp = fopen(filepath, "r"); if (fp == NULL) { printf("open file %s failed\n", filepath); close(connfd); exit(-1); } while((count = fread(content, 1, sizeof(content), fp)) > 0) { //printf("%s", content); if (write(connfd, content, count) != count) { printf("write failed\n"); } } fclose(fp); close(connfd); rep.status = 'f'; if (write(fd1[1], &rep, sizeof(rep)) < 0) {//告诉父进程自己处理完了请求 perror("write pipe finish failed"); exit(-1); } if (read(fd2[0], &comm, 1) < 1) {//等待来自父进程的命令 perror("read pipe failed"); exit(-1); } //printf("[%d] reve %c from pa\n", pid, comm); if (comm == 'e') { //收到exit命令 printf("[%d] exit\n", pid); exit(-1); } else if (comm == 'c') { //收到继续accept的命令 printf("[%d] continue\n", pid); } else { printf("[%d] comm : %c illeagle\n", pid, comm); } } } void usage() { printf("Usage: http-serv port\n"); } int write_pid() { int fd; if ((fd = open(PIDPATH, O_WRONLY | O_TRUNC | O_CREAT, S_IWUSR)) < 0){ perror("open pidfile faild"); return -1; } struct flock lock; lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) == -1) { int err = errno; perror("fcntl faild"); if (err == EAGAIN) { printf("Another http-serv process is running now!\n"); } return -1; } return 0; } void daemon_init() { //clear file creation mask; umask(0); //become a session leader if (fork() != 0) exit(-1); if (setsid() < 0) exit(-1); //make sure can be never get the TTY control if (fork() != 0) exit(-1); //may chdir here int i; for (i = 0; i < 1024; i++) close(i); /* * Attach file descriptors 0, 1, and 2 to /dev/null. */ int fd0, fd1, fd2; fd0 = open("/dev/null", O_RDWR); fd1 = dup(0); fd2 = dup(0); if (fd0 != 0 || fd1 != 1 || fd2 != 2) { printf("init failed\n"); exit(-1); } } int main(int argc, char **argv) { int listenfd; struct sockaddr_in servaddr; pid_t pid; if (argc != 2) { usage(); return -1; } signal(SIGCHLD, SIG_IGN); len200 = strlen(head200); len404 = strlen(head404); len503 = strlen(head503); daemon_init(); //转为后台程序,如需打印调试,把这行注释掉 if (write_pid() < 0) //避免同时有多个该程序在运行 return -1; if (pipe(fd1) < 0) { perror("pipe failed"); exit(-1); } if (s_pipe(fd2) < 0) { perror("pipe failed"); exit(-1); } int port = atoi(argv[1]); //initialize servaddr and listenfd... bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(port); listenfd = socket(AF_INET, SOCK_STREAM, 0); bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); listen(listenfd, 1000); int i; for (i = 0; i < PRECHILD ; i++) { //父进程预fork 子进程 if ((pid = fork()) < 0) { perror("fork faild"); exit(3); } else if (pid == 0) { answer(listenfd); } else { printf("have create child %d\n", pid); } } char e = 'e'; char c = 'c'; int req_num = 0; int child_num = PRECHILD; REPORT rep; while (1) { //printf("req_num = %d, child_num = %d\n", req_num, child_num); if (read(fd1[0], &rep, sizeof(rep)) < sizeof(rep)) {//等待子进程发来消息 perror("parent read pipe failed"); exit(-1); } //printf("parent: receive from %d\n", pid); if (rep.status == 'n') {//子进程刚accept了新的请求 req_num ++; printf("parent: %d have receive new request\n", rep.pid); if (req_num >= child_num && child_num <= MAXCHILD) { //请求数过多,创建更多子进程 if ((pid = fork()) < 0) { perror("fork faild"); exit(3); } else if (pid == 0) { answer(listenfd); } else { printf("have create child %d\n", pid); child_num ++; } } } else if (rep.status == 'f') {//子进程刚处理完了一个请求 req_num --; //printf("parent: %d have finish a request\n", rep.pid); if (child_num > (req_num + 1) && child_num > PRECHILD) {//子进程数过多,删除多余的子进程 if (write(fd2[1], &e, sizeof(e)) < sizeof(e)) { perror("pa write pipe failed"); exit(-2); } //printf("tell child exit\n"); child_num --; } else { if (write(fd2[1], &c, sizeof(c)) < sizeof(c)) {//让子进程继续等待accept perror("pa write pipe failed"); exit(-2); } //printf("tell child continue\n"); } } } return 0; } 利用fork()创建多个子进程 11:09 pm on Oct 23rd 2010 greenMay 之间我学习了创建一个子进程,也大致理解了子进程与父进程的关系。今天无意间遇到一个创建多个子进程的问题,结果还发现了点小bug,现在写下来和大家分享。 我需要实现的目标如下:编写一段源程序,使系统调用fork()创建两个子进程,当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”;子进程分别显示字符“b”和字符“c”。 一开始我的主要代码如下: view source print? 01 int main() 02 { 03 pid_t child1; 04 pid_t child2; 05 child1 = fork(); 06 child2 = fork(); 07 if(child1 == 0) 08 { 09 printf("Child1:a\n"); 10 return 0; 11 } 12 if(child2 == 0) 13 { 14 printf("Child2:b\n"); 15 return 0; 16 } 17 else 18 { 19 waitpid(child1,NULL,0); 20 waitpid(child2,NULL,0); 21 printf("Parent:c\n"); 22 } 23 return 0; 24 } 奇怪的是,我得到的是这样一个结果: Child1:a Child1:a Child2:b Parent:c 竟然有两个Child1。可是我的代码里明明只是让Chidl1打印一次啊。搜索到一篇好的博文。文章仔细分析了和我几乎相同的情况。事实上,是我的粗心和对fork()的理解不深刻导致了上述的奇怪问题。 我们知道,fork()之后,我们还是首先执行的是父进程,也就是如下代码段: view source print? 1 waitpid(child1,NULL,0); 2 waitpid(child2,NULL,0); 3 printf("Parent:c\n"); 然后waitpid(child1,NULL,0),进入child1的执行。child1将要执行的是如下的的代码段: view source print? 1 child2 = fork(); 2 if(child1 == 0) 3 { 4 printf("Child1:a\n"); 5 return 0; 6 } 注意,第一行的那个child2 = fork()!这就意味着对于child1来说,它自己又要创建一个子进程,这时候他成为了父亲。这时候,它有一个儿子child2,但是这个child2不同与我们刚才定义的那个child2,这个child2其实是parent的孙子。之所以又打印了一边Child1。如果加上如下代码就明白了: view source print? 01 child2 = fork(); 02 if(child1 == 0) 03 { 04 if(child2 == 0) 05 { 06 printf("GrandChild!\n"); 07 } 08 printf("Child1:a\n"); 09 return 0; 10 } 这时候将出现: Child1:a GrandChild! Child1:a Child2:b Parent:c 恩,这就很明白了!我无意间多调用了一次child2=fork(); 所以,如果要达到我最初的目的,需要改变child2的fork()的位置: view source print? 01 #include <stdio.h> 02 #include <unistd.h> 03 #include <sys/types.h> 04 #include <sys/wait.h> 05 int main() 06 { 07 pid_t child1; 08 pid_t child2; 09 child1 = fork(); 10 child2 = fork(); 11 if(child1 == 0) 12 { 13 printf("Child1:a\n"); 14 return 0; 15 } 16 if(child2 == 0) 17 { 18 printf("Child2:b\n"); 19 return 0; 20 } 21 else 22 { 23 waitpid(child1,NULL,0); 24 waitpid(child2,NULL,0); 25 printf("Parent:c\n"); 26 } 27 return 0; 28 } 我参照的那个博文最后给出了一个更为普遍的fork()创建多进程的程序框架: view source print? 01 pid_t create_child() 02 { 03 pid_t p = fork(); 04 if( p == 0 ) 05 { 06 printf("in child %d\n", getpid()); 07 //do something 08 return 0; 09 } 10 return p; 11 } 12 int main(void) 13 { 14 pid_t p1 = create_child(); 15 pid_t p2 = create_child(); 16 17 int st1, st2; 18 waitpid( p1, &st1, 0); 19 waitpid( p2, &st2, 0); 20 printf("in parent, pid = %d\n", getpid()); 21 printf("in parent, child 1 exited with %d\n", st1); 22 printf("in parent, child 2 exited with %d\n", st2); 23 return 0; 24 } 注意到,期中的create_child()函数最后有一个return p。这个return p将pid返回给了父进程,其实也是将子进程对于CPU的控制权交还给了父进程,这样就避免了多个子进程在创建之时互相影响了。 可以说,今天的这个问题真是一个有趣的事情。代码有的时候就是这么奇怪~ 最后,向我引用的那篇文章致敬! Linux内核对多进程多线程的支持方式: 线程机制支持并发程序设计技术,在多处理器上能真正保证并行处理。而在linux实现线程很特别,linux把所有的线程都当作进程实现。linux下线程看起来就像普通进程(只是该进程和其他进程共享资源,如地址空间)。上述机制与Microsoft windows或是Sun Solaris实现差异很大。 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()) 的调用者则通过管道将请求信息传给管理线程。 很多朋友都说使用多线程的好处是资源占用少,其隐含之意就是说进程占用资源比线程多,对吧?但实际上Linux多进程是否就真的点用很多资源呢?暂且不说进程是否比线程占用资源多,就进程占用资源的多少情况而言,Linux确实是做得相当节省的。产生一个多进程时肯定是要产生的一点内存是要复制进程表项,即一个task_struct结构,但这个结构本身做得相当小巧。其它对于一个进程来说必须有的数据段、代码段、堆栈段是不是全盘复制呢?对于多进程来说,代码段是肯定不用复制的,因为父进程和各子进程的代码段是相同的,数据段和堆栈段呢?也不一定,因为在Linux里广泛使用的一个技术叫copy-on-write,即写时拷贝。copy-on-write意味着什么呢?意味着资源节省,假设有一个变量x在父进程里存在,当这个父进程创建一个子进程或多个子进程时这个变量x是否复制到了子进程的内存空间呢?不会的,子进程和父进程使用同一个内存空间的变量,但当子进程或父进程要改变变量x的值时就会复制该变量,从而导致父子进程里的变量值不同。父子进程变量是互不影响的,由于父子进程地址空间是完全隔开的,变量的地址可以是完全相同的。 Linux的”线程”和”进程”实际上处于一个调度层次,共享一个进程标识符空间,这种限制使得不可能在Linux上实现完全意义上的POSIX线程机制,因此众多的Linux线程库实现尝试都只能尽可能实现POSIX的绝大部分语义,并在功能上尽可能逼近。Linux进程的创建是非常迅速的。内核设计与实现一书中甚至指出Linux创建进程的速度和其他针对线程优化的操作系统(Windows,Solaris)创建线程的速度相比,测试结果非常的好,也就是说创建速度很快。由于异步信号是内核以进程为单位分发的,而LinuxThreads的每个线程对内核来说都是一个进程,且没有实现”线程组”,因此,某些语义不符合POSIX标准,比如没有实现向进程中所有线程发送信号,README对此作了说明。LinuxThreads中的线程同步很大程度上是建立在信号基础上的,这种通过内核复杂的信号处理机制的同步方式,效率一直是个问题。LinuxThreads 的问题,特别是兼容性上的问题,严重阻碍了Linux上的跨平台应用(如Apache)采用多线程设计,从而使得Linux上的线程应用一直保持在比较低的水平。在Linux社区中,已经有很多人在为改进线程性能而努力,其中既包括用户级线程库,也包括核心级和用户级配合改进的线程库。目前最为人看好的有两个项目,一个是RedHat公司牵头研发的NPTL(Native Posix Thread Library),另一个则是IBM投资开发的NGPT(Next Generation Posix Threading),二者都是围绕完全兼容POSIX 1003.1c,同时在核内和核外做工作以而实现多对多线程模型。这两种模型都在一定程度上弥补了LinuxThreads的缺点,且都是重起炉灶全新设计的。 综上所述的结论是在Linux下编程多用多进程编程少用多线程编程。 IBM有个家伙做了个测试,发现切换线程context的时候,windows比linux快一倍多。进出最快的锁(windows2k的 critical section和linux的pthread_mutex),windows比linux的要快五倍左右。当然这并不是说linux不好,而且在经过实际编程之后,综合来看我觉得linux更适合做high performance server,不过在多线程这个具体的领域内,linux还是稍逊windows一点。这应该是情有可原的,毕竟unix家族都是从多进程过来的,而 windows从头就是多线程的。 如果是UNIX/linux环境,采用多线程没必要。 多线程多进程性能高?误导! 应该说,多线程多进程成本低,但性能更低。 在UNIX环境,多进程调度开销比多线程调度开销,没有显著区别,就是说,UNIX进程调度效率是很高的。内存消耗方面,二者只差全局数据区,现在内存都很便宜,服务器内存动辄若干G,根本不是问题。 多进程是立体交通系统,虽然造价高,上坡下坡多耗点油,但是不堵车。 多线程是平面交通系统,造价低,但红绿灯太多,老堵车。 我们现在都开跑车,油(主频)有的是,不怕上坡下坡,就怕堵车。 高性能交易服务器中间件,如TUXEDO,都是主张多进程的。实际测试表明,TUXEDO性能和并发效率是非常高的。TUXEDO是贝尔实验室的,与UNIX同宗,应该是对UNIX理解最为深刻的,他们的意见应该具有很大的参考意义 1. 散沙 2010年7月10日08:43 回复 | 引用 | #1 文章很有深度,我们把握一个尺度就可以了,在windows下使用线程,unix下则使用进程就可以了 2. rjoo 2010年9月9日13:49 回复 | 引用 | #2 错的太多了,博主,应该看看新资料了。 现在都2010年了,NPTL早就取代了老的Linux thread。而且通常多线程有性能优势,但是多进程更稳定,并且通常性能瓶颈不在于是进程模型还是线程模型而在于IO。 3. rjoo 2010年9月9日13:56 回复 | 引用 | #3 关于那个critical section和pthread_mutex_t,critical section本质上是一个自旋锁,短期锁当然快,不知道你说的那个IBM的哥们怎么比的,要比也该是和pthread_spinlock_t比。 4. admin 2010年9月9日17:28 回复 | 引用 | #4 rjoo挺热心的,呵呵,这篇文章不是我写的,但有几个地方我可以解答一下: 1. Linux下没有线程的概念,pthread线程实质是通过轻量级进程实现的。你说瓶颈在IO,这一点我很赞同你的意见,作者如果能再写个IO操作的文章来的话就会更好了。 2. mutex和critical section的确是不能比的。一个涉及到内核,一个没有涉及到内核。呵呵,很佩服你对这些东西的掌握程度,有机会多交流。 ^_^ 5. 定时 2010年9月9日17:40 回复 | 引用 | #5 我们组的最近项目的经验告诉我们能用多进程不用多线程多线程安全编程难,而且锁会早成效率很低,甚至不如单线程,你说的NPTL我知道,他只是多线程优化了并不能改变多线程安全编程的问题,锁的问题。谢谢指教,实践出真知。 @rjoo 6. 定时 2010年9月9日17:44 回复 | 引用 | #6 你说的锁,我确实不太了解,但是我们leader对它很了解,就是最近的一个项目,锁搞得他很郁闷,他也终于同意我的关键,尽可能不用多线程。 @rjoo 7. rjoo 2010年9月29日13:41 回复 | 引用 | #7 @admin Linux下没有线程的概念,pthread线程实质是通过轻量级进程实现的—这是2.4内核以前的情况(实际上是2.0时引入的,那可实在是太久了),2.4内核引入NGPL,2.6内核线程支持改为NPTL。NPTL实现的是1:1的线程模型(有资料说Win也是这种实现,虽然不太确定,但我觉得可能性很大),而NGPT虽然是理论上最先进的m:n线程模型,但最后实现出来的性能差NPTL一大截,最后被抛弃。看看文中说法就知道要么文章写的很早,要么作者看了一堆十年前的资料。 给个链接: http://www.kegel.com/c10k.html#threads.linuxthreads 8. finalday 2010年10月15日17:26 回复 | 引用 | #8 忍不住跳出来说,作者对并发编程的理解还不行。 比如说锁的问题,说得好像是多线程才需要的东西一样。如果一个应用多进程时完全不用锁,多线程也就多一个轻量级锁——锁一下,各回各家,每个线程用自己的专有存储,之后不就和多进程一样了?这样会被搞得很郁闷?当然不会。所以说明那个应用对于数据共享的需求不是这么简单,既然不是这么简单,多进程程序一样要加锁。多进程的加解锁代价可比多线程大得多了,共享数据和协作也麻烦多了。 多线程编程难不难?难,但这是由于并发本身的难度引起的。“锁”,“安全编程”不管是多线程还是多进程都一样会遇到。 多线程的最大优点是数据共享和协作方便。 多进程的最大优点是挂了一个进程不会影响其他进程,资源也不会泄露,故比较能容忍程序员犯错。 至于两者裸奔比性能,真的没啥意义。
同步概念 所谓同步,即同时起步,协调一致。不同的对象,对“同步”的理解方式略有不同。如,设备同步,是指在两个设备之间规定一个共同的时间参考;数据库同步,是指让两个或多个数据库内容保持一致,或者按需要部分保持一致;文件同步,是指让两个或多个文件夹里的文件保持一致。等等 而,编程中、通信中所说的同步与生活中大家印象中的同步概念略有差异。“同”字应是指协同、协助、互相配合。主旨在协同步调,按预定的先后次序运行。 线程同步 同步即协同步调,按预定的先后次序运行。 线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据一致性,不能调用该功能。 举例1: 银行存款 5000。柜台,折:取3000;提款机,卡:取 3000。剩余:2000 举例2: 内存中100字节,线程T1欲填入全1, 线程T2欲填入全0。但如果T1执行了50个字节失去cpu,T2执行,会将T1写过的内容覆盖。当T1再次获得cpu继续 从失去cpu的位置向后写入1,当执行结束,内存中的100字节,既不是全1,也不是全0。 产生的现象叫做“与时间有关的错误”(time related)。为了避免这种数据混乱,线程需要同步。 “同步”的目的,是为了避免数据混乱,解决与时间有关的错误。实际上,不仅线程间需要同步,进程间、信号间等等都需要同步机制。 因此,所有“多个控制流,共同操作一个共享资源”的情况,都需要同步。 数据混乱原因: 1. 资源共享(独享资源则不会) 2. 调度随机(意味着数据访问会出现竞争) 3. 线程间缺乏必要的同步机制。 以上3点中,前两点不能改变,欲提高效率,传递数据,资源必须共享。只要共享资源,就一定会出现竞争。只要存在竞争关系,数据就很容易出现混乱。 所以只能从第三点着手解决。使多个线程在访问共享资源的时候,出现互斥。 互斥量mutex Linux中提供一把互斥锁mutex(也称之为互斥量)。 每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。 资源还是共享的,线程间也还是竞争的, 但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。 但,应注意:同一时刻,只能有一个线程持有该锁。 当A线程对某个全局变量加锁访问,B在访问前尝试加锁,拿不到锁,B阻塞。C线程不去加锁,而直接访问该全局变量,依然能够访问,但会出现数据混乱。 所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源的时候使用该机制。但,并没有强制限定。 因此,即使有了mutex,如果有线程不按规则来访问数据,依然会造成数据混乱。 主要应用函数: pthread_mutex_init函数 pthread_mutex_destroy函数 pthread_mutex_lock函数 pthread_mutex_trylock函数 pthread_mutex_unlock函数 以上5个函数的返回值都是:成功返回0, 失败返回错误号。 pthread_mutex_t 类型,其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。 pthread_mutex_t mutex; 变量mutex只有两种取值1、0。 pthread_mutex_init函数 初始化一个互斥锁(互斥量) ---> 初值可看作1 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 参1:传出参数,调用时应传 &mutex restrict关键字:只用于限制指针,告诉编译器,所有修改该指针指向内存中内容的操作,只能通过本指针完成。不能通过除本指针以外的其他变量或指针修改 参2:互斥量属性。是一个传入参数,通常传NULL,选用默认属性(线程间共享)。 参APUE.12.4同步属性 1. 静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。e.g. pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER; 2. 动态初始化:局部变量应采用动态初始化。e.g. pthread_mutex_init(&mutex, NULL) pthread_mutex_destroy函数 销毁一个互斥锁 int pthread_mutex_destroy(pthread_mutex_t *mutex); pthread_mutex_lock函数 加锁。可理解为将mutex--(或-1) int pthread_mutex_lock(pthread_mutex_t *mutex); pthread_mutex_unlock函数 解锁。可理解为将mutex ++(或+1) int pthread_mutex_unlock(pthread_mutex_t *mutex); pthread_mutex_trylock函数 尝试加锁 int pthread_mutex_trylock(pthread_mutex_t *mutex); 加锁与解锁 lock与unlock: lock尝试加锁,如果加锁不成功,线程阻塞,阻塞到持有该互斥量的其他线程解锁为止。 unlock主动解锁函数,同时将阻塞在该锁上的所有线程全部唤醒,至于哪个线程先被唤醒,取决于优先级、调度。默认:先阻塞、先唤醒。 例如:T1 T2 T3 T4 使用一把mutex锁。T1加锁成功,其他线程均阻塞,直至T1解锁。T1解锁后,T2 T3 T4均被唤醒,并自动再次尝试加锁。 可假想mutex锁 init成功初值为1。 lock 功能是将mutex--。 unlock将mutex++ lock与trylock: lock加锁失败会阻塞,等待锁释放。 trylock加锁失败直接返回错误号(如:EBUSY),不阻塞。 加锁步骤测试: 看如下程序:该程序是非常典型的,由于共享、竞争而没有加任何同步机制,导致产生于时间有关的错误,造成数据混乱: #include #include #include void *tfn(void *arg) { srand(time(NULL)); while (1) { printf("hello "); sleep(rand() % 3); /*模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误*/ printf("world\n"); sleep(rand() % 3); } return NULL; } int main(void) { pthread_t tid; srand(time(NULL)); pthread_create(&tid, NULL, tfn, NULL); while (1) { printf("HELLO "); sleep(rand() % 3); printf("WORLD\n"); sleep(rand() % 3); } pthread_join(tid, NULL); return 0; } 【mutex.c】 【练习】:修改该程序,使用mutex互斥锁进行同步。 1. 定义全局互斥量,初始化init(&m, NULL)互斥量,添加对应的destry 2. 两个线程while中,两次printf前后,分别加lock和unlock 3. 将unlock挪至第二个sleep后,发现交替现象很难出现。 线程在操作完共享资源后本应该立即解锁,但修改后,线程抱着锁睡眠。睡醒解锁后又立即加锁,这两个库函数本身不会阻塞。 所以在这两行代码之间失去cpu的概率很小。因此,另外一个线程很难得到加锁的机会。 4. main 中加flag = 5 将flg在while中-- 这时,主线程输出5次后试图销毁锁,但子线程未将锁释放,无法完成。 5. main 中加pthread_cancel()将子线程取消。 【pthrd_mutex.c】 结论: 在访问共享资源前加锁,访问结束后立即解锁。锁的“粒度”应越小越好。 死锁 1. 线程试图对同一个互斥量A加锁两次。 2. 线程1拥有A锁,请求获得B锁;线程2拥有B锁,请求获得A锁 【作业】:编写程序,实现上述两种死锁现象。 读写锁 与互斥量类似,但读写锁允许更高的并行性。其特性为:写独占,读共享。 读写锁状态: 一把读写锁具备三种状态: 1. 读模式下加锁状态 (读锁) 2. 写模式下加锁状态 (写锁) 3. 不加锁状态 读写锁特性: 1. 读写锁是“写模式加锁”时, 解锁前,所有对该锁加锁的线程都会被阻塞。 2. 读写锁是“读模式加锁”时, 如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。 3. 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高 读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。 读写锁非常适合于对数据结构读的次数远大于写的情况。 主要应用函数: pthread_rwlock_init函数 pthread_rwlock_destroy函数 pthread_rwlock_rdlock函数 pthread_rwlock_wrlock函数 pthread_rwlock_tryrdlock函数 pthread_rwlock_trywrlock函数 pthread_rwlock_unlock函数 以上7 个函数的返回值都是:成功返回0, 失败直接返回错误号。 pthread_rwlock_t类型 用于定义一个读写锁变量。 pthread_rwlock_t rwlock; pthread_rwlock_init函数 初始化一把读写锁 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); 参2:attr表读写锁属性,通常使用默认属性,传NULL即可。 pthread_rwlock_destroy函数 销毁一把读写锁 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); pthread_rwlock_rdlock函数 以读方式请求读写锁。(常简称为:请求读锁) int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); pthread_rwlock_wrlock函数 以写方式请求读写锁。(常简称为:请求写锁) int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); pthread_rwlock_unlock函数 解锁 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); pthread_rwlock_tryrdlock函数 非阻塞以读方式请求读写锁(非阻塞请求读锁) int pthread_
第1 章Linux操作系统概述................... .......................................................................... 2 1.1 Linux发展历史........................................................ 2 1.1.1 Linux的诞生和发展.............................................. 2 1.1.2 Linux名称的由来........................................ ........ 3 1.2 Linux的发展要素...................................................... 3 1.2.1 U N I X操作系统.................................................. 4 1.2.2 Minix操作系统.................................................. 4 1.2.3 POSIX 标准.....................................................4 1.3 Linux 与 U N I X 的异同.................................................. 5 1 . 4 操作系统类型选择和内核版本的选择..................................... 5 1.4.1常见的不同公司发行的Linux异同................................. 6 1.4.2 内核版木的选择..................................................6 1.5 Linux的系统架构...................................................... 7 1.5.1 Linux内核的主要模块............................................ 7 1.5.2 Linux的文件结构................................................ 9 1.6 G N U 通用公共许可证..................................................10 1.6.1 G P L许可证的历史.............................................. 10 1.6.2 G P L 的白由理念................................................ 10 1.6.3 G P L 的基本条款................................................ 11 1.6.4关于G P L 许町证的争议......................................... 12 1.7 Linux软件开发的可借鉴之处........................:.................. 12 1-8 .................................................................13 第2 章Linux编程环境....................................................................................................14 2.1 Linux环境下的编辑器................................................. 14 2.1.1 v i m使用简介...................................................14 2 . 1 . 2使用v i m建立文件.............................................. 15 2 . 1 . 3使用v i m编辑文本.............................................. 16 2.1.4 v i m的格式设置.................................................18 目录 2.1.5 vim 配置文件....................................................................................................... .. 2 . 1 . 6 使用其他编辑器................................................................................................... 2.2 Linux下的G C C 编译器工具集.......................................... 19 2.2.1 G C C 简介......................................................19 2 . 2 . 2 编译程序的基本知识......................................................................................... 21 2.2.3 .单个文件编译成执行文件........................................ 22 2 . 2 . 4编译生成目标文件.............................................. 22 2 . 2 . 5多文件编译............ ........................................ 23 2.2.6 预处理........................................................ 24 2 . 2 . 7编译成汇编语言.................................................24 2 . 2 . 8生成和使用静态链接库.......................................... 25 2 . 2 . 9生成动态链接库.................................................26 2.2.10动态加载库................................................... 29 2.2.11 G C C 常用选项................................................ 31 2 . 2 . 1 2编译环境的搭建................................................33 2.3 Makefile 文件简介.....................................................34 2.3.1 一个多文件的工程例子............................. ............. 34 2 . 3 . 2多文件工程的编译.............................................. 36 2.3.3 Makefile 的规则.................................................37 2.3.4 Makefile 中使用变量............................................ 39 2 . 3 . 5搜索路径...................................................... 43 2 . 3 . 6自动推导规则.................................................. 44 2.3.7 递归 make......................................................44 2.3.8 Makefile 中的函数.............................................. 46 2 . 4 用G D B 调试程序..................................................... 47 2 . 4 . 1编译可调试程序.................................................48 2.4.2 使用G D B 调试程序............................................. 49 2.4.3 G D B 常用命令..................................................52 2.4.4 其他的 G D B ....................................................59 2.5 顿.................................................................60 第3 章文件系统简介........................................................ 61 3.1 Linux下的文件系统.................................................. 61 3.1.1 Linux下文件的内涵............................................. 61 3.1.2 文件系统的创建................................................. 62 3 . 1 . 3挂接文件系统.................................................. 64 3.1.4 索弓丨节点 inode.......... •'...................................... 65 3.1.5 普通文件...................................................... 66 3 . 1 . 6设备文件...................................................... 66 3 . 1 . 7虚拟文件系统V F S .............................................. 68 目录 3.2 文件的通用操作方法................................................. 72 3 . 2 . 1文件描述符.................................................... 72 3 . 2 . 2打开创建文件open()、create()函数................................ 72 3 . 2 . 3关闭文件closeO函数............................................ 76 3 . 2 . 4读取文件read()函数............................................. 77 3.2.5 写文件write()函数.............................................. 79 3 . 2 . 6文件偏移lseekO函数............................................ 80 3 . 2 . 7获得文件状态fstat()函数.....................•'................... 83 3 . 2 . 8文件空间映射m m a p ()函数....................................... 85 3 . 2 . 9文件属性fcntl()函数............................................. 88 3. 2 . 1 0文件输入输出控制ioctl()函数....................................92 3.3 socket文件类型....................................................... 93 3.4 /J、@ .................................................................93 第4 章程序、进程和线程.................................................... 94 4 . 1 程序、进程和线程的概念.............................................. 94 4 . 1 . 1程序和进程的差别............................................ ’."94 4.1.2 Linux环境下的进程............................................. 95 4 . 1 . 3进程和线程.................................................... 96 4 . 2 进程产生的方式...................................................... 96 4.2.1 进程号........................................................ 96 4.2.2 进蟬复制 fork()................................................. 97 4.2.3 system()方式....................................................98 4.2.4 进程执行exec()函数系列......................................... 99 4 . 2 . 5所有用户态进程的产生进程init................................. 100 4 . 3 进程间通信和同步................................................... 101 4.3.1 半双工管道................................................... 101 4.3.2 命名管道..................................................... 107 4.3.3 消息队列..................................................... 108 4 . 3 . 4消息队列的一个例子........................................... 114 4.3.5 信号量......................... .............................. 116 4 . 3 . 6共享内存..................................................... 121 4.3.7 信号.......................................................... 124 4.4 Linux下的线程...................................................... 127 4 . 4 . 1多线程编程实例................................................127 4.4.2 Linux 下线程创建函数 pthread_create()............................ 129 4.4.3 线程的结束函数 pthread_join()和 pthread_exit().....................129 4.4.4 线程的属性................................................... 130 4 . 4 . 5线程间的互斥................................................. 132 4 . 4 . 6线程中使用信号量..............................................133 • VII • n 录 4-5 純............................................................... 136 第2 篇Linux用户层网络编程 第5 章T C P / I P协议族简介.................................................. 138 5.1 O S I网络分层介绍....................................................138 5.1.1 O S I网络分层结构............................................. 138 5.1.2 O S I的7 层网络结构........................................... 139 5.1.3 O S I参考模塑中的数据传输..................................... 140 5.2 TCP/IP 协议找....................................................... 141 5.2.1 TCP/IP协议栈参考模型......................................... 141 5 . 2 . 2主机到网络层协议............................................. 143 5.2.3 IP 协议....................................................... 144 5.2.4 网际控制报文协议(ICMP) .................................... 146 5.2.5 传输控制协议(TCP) ......................................... 150 5 . 2 . 6用户数据报文协议(U D P ) ..................................... 154 5 . 2 . 7地址解析协议(A R P) ......................................... 156 5.3 IP地址分类与T C P / U D P端U .......................................... 158 5 . 3 . 1因特网中IP地址的分类........................................ 159 5.3.2 子网掩码(subnet mask address) ................................ 161 5.3.3 IP地址的配置................................................. 162 5.3.4 端口......................................................... 163 5 . 4 主机字节序和网络字节序............................................. 163 5 . 4 . 1字节序的含义................................................. 164 5 . 4 . 2网络字节序的转换............................................. 164 5.5 /J、@ ........................................................... .....166 第6 章应用层网络服务程序简介...................... ;..................... 167 6.1 H T T P协议和服务....................................................167 6.1.1 H T T P 协议概述.................................................167 6.1.2 H T T P协议的基本过程.......................................... 168 6.2 F T P协议和服务......................................................170 6.2.1 F T P协议概述..................................................170 6.2.2 F T P协议的工作模式........................................... 172 6.2.3 F T P协议的传输方式........................................... 172 6.2.4 —个简单的F T P过程........................................... 173 6.2.5 常用的F T P工具............................................... 173 6.3 T E L N E T协议和服务................................................. 174 6 . 3 . 1远程登录的基本概念........................................... 174 • VIII • 目录 6 . 3 . 2使用T E L N E T协议进行远程登录的工作过程...................... 174 6.3.3 T E L N E T 协议................................................. 174 6.4 N F S 协议和服务......................................................176 6.4.1 安装N F S服务器和客户端...................................... 176 6 . 4 . 2服务器端的设定................................................176 6 . 4 . 3客户端的操作................................................. 177 6.4.4 showmount 命令................................................177 6 . 5 自定义网络服务..................................................... 177 6.5.1 xinetd/inetd....................................................178 6.5.2 xinetd月艮务配置................................................178 6 . 5 . 3自定义网络服务............................................... 179 6.6 小结................................................................180 第7 章T C P 网络编程基础...............................................181 7.1 套接字编程基础知识................................................. 181 7.1.1 套接字地址结构................................................181 7 . 1 . 2用户层和内核层交互过程....................................... 183 7.2 T C P 网络编程流程................................................... 184 7.2.1 T C P网络编程架构............................................. 184 7 . 2 . 2创建网络插口函数socket()...................................... 186 7.2.3 绑定一个地址端口对bind()......................................189 7.2.4 监听本地端口 listen............................................ 192 7.2.5 接受一个网络请求acceptO...................................... 194 7 . 2 . 6连接H 标网络服务器connect()................................... 199 7.2.7 写入数据函数writeO........................................... 200 7 . 2 . 8读取数据函数readO............................................ 201 7 . 2 . 9关闭套接字函数close()......................................... 201 7 . 3 服务器/客户端的简单例子............................................ 202 7.3.1 例子功能描述........................................... ......202 7 . 3 . 2服务器网络程序................................................203 7 . 3 . 3服务器读取和显示字符串....................................... 205 7 . 3 . 4客户端的网络程序............................................. 205 7 . 3 . 5客户端读取和显示字符串....................................... 206 7 . 3 . 6编译运行程序................................................. 206 7 . 4 截取信号的例子..................................................... 207 7.4.1 信号处理..................................................... 207 7.4.2 信号 SIGPIPE..................................................208 7.4.3 信号 SI G INT.................................... •.............. 208 7.5 /J、g ................................................................208 • IX • 第8 章服务器和客户端信息的获取...........................................210 8 . 1 字节序............................................................................................................................210 8 . 1 . 1 大端字节序和小端字节序...............................................................................210 8 . 1 . 2 字节序转换函数................................................................................................212 8.1.3 一个字节序转换的例子......................................... 214 8 . 2 字符串I P地址和二进制IP地址的转换................................. 217 8.2.1 inet_xxx()函数................................................. 217 8.2.2 inet_pton()和 inet_ntop()函数.....................................219 8 . 2 . 3使用8.2.1节地址转换函数的例子............................ .•••••••220 8.2.4 使用函数 inet_pton()和函数 inet—ntop()的例子......................223 8 . 3 套接字描述符判定函数issockettype()................................... 223 8 . 3 . 1进行文件描述符判定的函数issockettypeO.........................224 8.3.2 main()g| 数.................................................... 224 8.4 IP地址与域名之间的相互转换................................................................................. 225 8.4.1 D N S 原理.....................................................225 8 . 4 . 2获取主机信息的函数........................................... 226 8 . 4 . 3使用主机名获取主机信息的例子................................. 228 8 . 4 . 4函数gethostbyname()不可重入的例子............................. 230 8 . 5 协议名称处理函数................................................... 232 8.5.1 xxxprotoxxx()函数............................................. 232 8 . 5 . 2使用协议族函数的例子......................................... 233 8.6 小结................................................................236 第9 章数据的I O和复用....................................................237 9.1 IO 函数.......................................................................................................................... 237 9 . 1 . 1使用recv()函数接收数据........................................237 9 . 1 . 2 使用sendO函数发送数据................................................................................239 9 . 1 . 3 使用readvO函数接收数据..............................................................................240 9 . 1 . 4 使用writev()闲数发送数据.............................................................................240 9 . 1 . 5使用recvmsgO函数接收数据.................................... 242 9 . 1 . 6 使用sendmsgO函数发送数据.........................................................................244 9.1.7 I O函数的比较.................................................246 9.2 使用I O函数的例子.................................................. 246 9 . 2 . 1客户端处理框架的例子......................................... 246 9 . 2 . 2服务器端程序框架............................................. 248 9.2.3 使用 recv()和 send()函数........................................ 249 9.2.4 使用 readv()和 write()函数.......................................251 9.2.5 使用 recvmsgO和 sendmsg()函数..................................253 9.3 10 模型............................................................. 256 9 . 3 . 1阻塞I O模型.................................................. 256 • X • 目录 9 . 3 . 2非阻塞1 0模型................................................ 257 9.3.3 10 复用....................................................... 257 9 . 3 . 4信号驱动I O模型.............................................. 258 9 . 3 . 5异步I O模型.................................................. 258 9.4 select()函数和 pselect()函数............................................ 259 9.4.1 select()闲数.................................................... 259 9.4.2 pselect()函数...................................................261 9.5 poll()函数和 ppoll()函数............................................... 262 9.5.1 poll()函数..................................................... 263 9.5.2 p p o l l O ® ^ .................................................... 264 9 . 6 非阻塞编程.......:.................................................. 264 9 . 6 . 1非阻塞方式程序设计介绍....................................... 264 9 . 6 . 2非阻塞程序设计的例子......................................... 264 9.7 小结................................................................266 第1 0章基于U D P 协议的接收和发送...................................... ,...267 10.1 U D P 编程框架......................................................267 10.1.1 U D P 编程框图................................................ 267 10.1.2 U D P 服务器编程框架.......................................... 269 10.1.3 U D P 客户端编程框架.......................................... 269 10.2 U D P 协议程序设计的常用函数....... :................................270 10.2.1建立套接字socket()和绑定套接字bind()......................... 270 10.2.2 接收数据 recvfrom()/recv()......................................270 10.2.3 发送数据 sendtoO/sendO...............................................................................275 10.3 U D P 接收和发送数据的例子.......................................... 279 10.3.1 U D P 服务器端................................................ 279 10.3.2 U D P 服务器端数据处理........................................280 10.3.3 U D P 客户端..................................................281 10.3.4 U D P 客户端数据处理.......................................... 281 10.3.5 测试 U D P 程序............................................... 282 10.4 U D P 协议程序设计中的几个问题......................................282 10.4.1 U D P 报文丢失数据............................................ 282 10.4.2 U D P 数据发送中的乱序........................................284 10.4.3 U D P 协议中的 connect()函数....................................287 10.4.4 U D P 缺乏流量控制............................................ 287 10.4.5 U D P 协议中的外出网络接口....................................289 10.4.6 U D P 协议中的数据报文截断....................................290 10.5 小结.............................................................. 291 第1 1章高级套接字........................................................ 292 11.1 U N I X 域函数.......................................................292 • XI • 目录 11.1.1 UNIX域函数的地址结构..............................................................................292 1 1 .1 .2套接字函数.................................................................................................... 293 11 . 1 . 3 使用UNIX域函数进行套接字编程............................................................ 293 11.1.4传递文件描述符.............................................. 296 11.1.5 socketpair()闲数............................................................................................. 296 11.1.6传递文件描述符的例子.................................................................................297 11.2 广播............................................................................................................................. 302 11.2.1 广播的IP地址............................................................................................... 302 11.2.2广播与单播的比较......................................................................................... 303 11.2.3 广播的示例.....................................................................................................304 1 1 . 3多播................................................ 11.3.1多播的概念....................... 1 1 .3 .2广域网的多播................... 11.3.3多播的编程....................... 11.3.4 内核中的多播................... 11.3.5 一个多播例子的服务器端 11.3.6 —个多播例子的客户端•••• 1 1 . 4数据链路层访问........................... 11.4.1 SOCK_PACKET 类型…… 11.4.2设置套接U 以捕获链路帧的编程方法............................ 320 11.4.3从套接口读取链路帧的编程方法................................ 321 11.4.4定位IP包头的编程方法....................................... 322 11.4.5定位T C P报头的编程方法..................................... 323 11.4.6定位U D P 报头的编程方法..................................... 325 11.4.7定位应用层报文数据的编程方法................................ 326 11.4.8使用S O C K _ P A C K E T编写A R P 请求程序的例子................. 326 11.5 329 第1 2章套接字选项........................................................330 12.1 获取和设置套接字选项 getsocketopt()/setsocketopt()......................330 12.1.1 getsockopt()函数和 setsocketopt()函数的介绍......................330 12.1.2套接字选项................................ ..................331 12.1.3套接字选项简单示例.......................................... 332 12.2 S O L ^ S O C K E T 协议族选项........................................... 336 12.2.1 S O—B R O A D C A S T 广播选项....................................336 12.2.2 S O—D E B U G 调试选项......................................... 337 12.2.3 S O _ D O N T R O U T E 不经过路由选项............................. 337 12.2.4 S O—E R R O R 错误选项......................................... 338 12.2.5 S O _ K E E P A L I V E 保持连接选项................................. 338 12.2.6 S O L I N G E R缓冲区处理方式选项...............................339 • XII • U录 12.2.7 S O _ O O B I N L I N E带外数据处理方式选项.........................342 12.2.8 S O _ R C V B U F 和 S O一S N D B U F 缓冲区大小选项................... 342 12.2.9 S O _ R C V L O W A T 和 S O _ S N D L O W A T 缓冲区下限选项............ 343 12.2.10 S O _ R C V T I M E O 和 S O _ S N D T I M E O 收发超时选项............... 343 12.2.11 S O _ R E U S E R A D D R 地址重用选项............................. 344 12.2.12 S O—E X C L U S I V E A D D R U S E 端 U 独占选项...................... 344 12.2.13 S 0 _ T Y P E套接字类型选项.................................... 345 12.2.14 SO_BSDCOMPAT 与 BSD 銮接字兼容选项............................................345 12.2.15 S O _ B I N D T O D E V I C E套接字网络接口绑定选项................. 345 12.2.16 S O _ P R I O R I T Y套接字优先级选项............................. 346 12.3 I P P R O T O J P 选项...................................................347 12.3.1 IP-HD RT NCL 选项............................................ 347 12.3.2 IP O P T N I O S 选项............................................ 347 12.3.3 IP_TOS 选项................................................. 347 12.3.4 IP_TTL 选项..................................................347 12.4 IPP RO TO_TCP 选项................................................. 348 12.4.1 T C P _ K E E P A L I V E 选项................... ..................... 348 12.4.2 T C P _ M A X R T 选项............................................ 348 12.4.3 T C P—M A X S E G 选项.......................................... 349 12.4.4 T C P _ N O D E L A Y 和 T C P _ C O R K 选项............................ 349 1 2 . 5使用套接字选项.................................................... 351 12.5.1设置和获取缓冲区大小........................................ 351 12.5.2获取套接字类型的例子........................................ 355 12.5.3使用套接字选项的综合例子.................................... 356 12.6 ioctl()函数.......................................................... 361 12.6.1 ioctl()函数的命令选项......................................... 361 12.6.2 ioctl()函数的 IO 请求.......................................... 363 12.6.3 ioctl()函数的文件请求......................................... 365 12.6.4 ioctl()函数的网络接U 请求:.....................................365 12.6.5使用ioctl()函数对A R P 高速缓存操作........................... 372 12.6.6使用ioct〖()函数发送路由表请求.................................374 12.7 fcntl()函数......................................................... 374 12.7.1 fcntl()函数的选项............................................. 375 12.7.2使用fcntl()函数修改套接字非阻塞属性.......................... 375 12.7.3使用fcntlO函数设置信号属主...................................376 12.8 小结...............................................................376 第13章原始套接字......................................................................................................377 13.1 概述...............................................................377 1 3 . 2原始套接字的创建.................................................. 379 • XIII • 目录 13.2.1 S O C K J I A W 选项............................................. 379 13.2.2 IPJHDR 1NCL 套接字选项......................................379 13.2.3 不需要bindO函数............................................. 380 1 3 . 3原始套接字发送报文................................................ 380 13.4 原始套接字接收报文................................................ 380 1 3 . 5原始套接字报文处理时的结构........................................ 381 13.5.1 1 P头部的结构........................................... ..... 381 13.5.2 I C M P 头部结构............................................... 382 13.5.3 U D P 头部结构................................................ 384 13.5.4 T C P 头部结构................................................ 386 13.6 ping 的例子........................................................ 387 13.6.1 协议格式............................................. •••..... 388 13.6.2 校验和函数.................................................. 389 13.6.3 设置I C M P发送报文的头部.................................... 390 13.6.4剥离I C M P接受报文的头部.................................... 391 13.6.5 计算时间差.................................................. 392 13.6.6发送报文.................................................... 393 13.6.7接收报文.................................................... 394 13.6.8主函数过程.................................................. 395 13.6.9 主函数 main()................................................ 397 13.6.10 编译测试................... ................................ 400 1 3 . 7洪水攻击.......................................................... 400 13.8 I C M P洪水攻击..................................................... 401 13.8.1 I C M P洪水攻击的原理.........................................401 13.8.2 I C M P洪水攻击的例子.........................................401 13.9 U D P 洪水攻市......................................................405 13.10 S Y N 洪水攻击.....................................................409 13.10.1 S Y N 洪水攻击的原理.........................................409 13.10.2 S Y N 洪水攻击的例子.........................................409 13.11 小结............................................................. 413 第1 4章服务器模型选择....................................................414 1 4 . 1循环服务器:........................................................ 414 14.1.1 U D P 循环服务器.............................................. 414 14.1.2 T C P循环服务器.............................................. 417 1 4 . 2简单并发服务器.................................................... 420 14.2.1并发服务器的模型............................................ 420 14.2.2 U D P 并发服务器.............................................. 420 14.2.3 T C P并发服务器.............................................. 423 14.3 T C P的高级并发服务器模型.......................................... 426 . XIV • 目录 14.3.1 单客户端单进程,统一 accept()................................. 426 14.3.2 单客户端单线程,统一accept()................................. 429 1 4.3.3单客户端单线程,各线程独自acceptO,使用互斥锁.............. 431 14.4 I O复用循环服务器..................................................435 14.4.1 I O复用循环服务器模型介绍....................................435 14.4.2 I O复用循环服务器模型的例子..................................436 14.5 440 第1 5章IPv6简介.......................... ...............................441 15.1 IPv4 的缺陷........................................................ 441 15.2 IPv6 的特点........................................................ 442 15.3 IPv6 的地址......................:.................................. 443 15.3.1 IPv6的单播地址.............................................. 443 15.3.2可聚集全球单播地址.......................................... 443 15.3.3 本地使用单播地址............................................ 444 15.3.4 兼容性地址.................................................. 445 15.3.5 IPv6 多播地址................................................ 446 15.3.6 IPv6 任播地址................................................ 446 15.3.7主机的多个IPv6地址......................................... 447 15.4 IPv6 的头部........................................................ 447 15.4.1 IPv6 头部格式........................................ ........ 447 15.4.2 与IPv4头部的对比........................................... 448 15.4.3 IPv6 的 T C P 头部............................................. 449 15.4.4 IPv6 的 U D P 头部............................................. 449 15.4.5 IPv6 的 I C M P 头部............................................ 449 15.5 IPv6运行环境...................................................... 451 15.5.1 加载 IPv6 模块............................................... 451 15.5.2查看是否支持IPv6............................................ 452 15.6 IPv6的结构定义.................................................... 453 15.6.1 IPv6的地址族和协议族........................................453 15.6.2套接字地址结构...............................................453 15.6.3 地址兼容考虑................................................ 455 15.6.4 IPv6 通用地址................................................ 455 15.7 IPv6的套接字函数.................................................. 456 15.7.1 socketO 函数.................................................. 456 15.7.2没有发生改变的函数.......................................... 456 15.7.3 发生改变的函数...............................................457 15.8 IPv6的套接字选项.................................................. 457 15.8.1 IPv6的套接字选项............................................ 457 15.8.2 单播跳限 I P V 6 _ U N I C A S T _ H O P S............................... 459 • XV • 目录 15.8.3发送和接收多播包............................................ 459 15.8.4 IPv6中获得时间戳的ioctl命令................................. 460 15.9 IPv6的库函数...................................................... 460 15.9.1地址转换函数的差异.......................................... 460 15.9.2域名解析函数的差异.......................................... 461 15.9.3 测试宏...................................................... 463 15.10 IPv6的编程的一个简单例子......................................... 463 15.10.1 服务器程序................................................. 464 15.10.2 客户端程序................................................. 465 15.10.3 编译调试................................................... 467 15.11 小结............................................................. 467 第3 篇Linux内核网络编程 第16章Linux内核中网络部分结构以及分布...............................................................470 16.1 概述...............................................................470 16.1.1代码目录分布................................................ 470 16.1.2内核中网络部分流程简介...................................... 472 16.1.3系统提供修改网络流程点...................................... 474 16.1.4 sk_buff 结构..................................................475 16.1.5 网络协议数据结构inet_protosw................................. 478 1 6 . 2软中断C P U 报文队列及其处理....................................... 479 16.2.1 Linux内核网络协议层的层间传递手段— 软中断................ 479 1 6 . 2 . 2网络收发处理软中断的实现机制................................ 481 16.3 socket数据如何在内核中接收和发送...................................482 16.3.1 socket()的初始化.............................................. 482 16.3.2 接收网络数据recv()........................................... 482 16.3.3发送网络数据sendO........................................... 483 16.4 小结...............................................................484 第17章neffilter框架内报文处理.................................................................................485 17.1 netfilter............................................................ 485 17.1.1 netfilter 简介..................................................485 17.1.2 netfilter 框架..................................................486 17.1.3 netfilter 检査时的表格.........;................................487 17.1.4 netfilter 的规则................................................487 17.2 iptables 和 netfilter...................................................488 17.2.1 iptables 简介..................................................488 17.2.2 iptables 的表和链............................................. 488 • XVI • 目录 17.2.3 使用iptables设置过滤规则.....................................489 1 7 . 3内核模块编程...................................................... 492 17.3.1 内核 “Hello, World! ”程序................................... 492 17 . 3 . 2内核模块的基本架构.......................................... 494 1 7 . 3 . 3内核模块加载和卸载过程...................................... 496 1 7 . 3 . 4内核模块初始化和清理函数.................................... 497 1 7 . 3 . 5内核模块初始化和淸理过程的容错处理.......................... 497 1 7 . 3 . 6内核模块编译所需的Makefile.................................. 498 17.4 5 个钩子点......................................................... 499 17.4.1 netfilter 的 5 个钩子,点......................................... 499 17.4.2 N F H O O K 'k .................................................500 17.4.3钩了.的处理规则.............................................. 501 1 7 . 5注册/注销钩子...................................................... 502 17.5.1 结构 nf_hook_ops............................................. 502 17.5.2注册钩子.................................................... 503 17.5.3 注销钩子.................................................... 504 17.5.4注册注销函数................................................ 504 1 7 . 6钩子的简单处理例子................................................ 505 17.6.1 功能描述.................................................... 505 17.6.2 需求分析.................................................... 506 17.6.3 ping回显屏蔽实现............................................ 506 17.6.4禁止向目的IP地址发送数据的实现............................. 506 17.6.5 端口关闭实现................................................ 506 17.6.6动态配置实现................................................ 508 17.6.7可加载内核实现代码.......................................... 509 17.6.8应用层测试代码实现.......................................... 516 17.6.9 编泽运行.................................................... 516 17.7 •点多个钩子的优先级...............................................517 1 7 . 8校验和问题........................................................ 518 17.9 小结.... ...........................................................518 第4篇综合案例 第1 8章一个简单W e b 服务器的例子S H T T P D ............................... 522 18.1 S H T T P D的需求分析................................................ 522 18.1.1 S H T T P D启动参数可动态配置的需求............................ 523 18.1.2 S H T T P D的多客户端支持的需求................................ 524 18.1.3 S H T T P D支持方法的需求...................................... 525 18.1.4 S H T T P D支持的H T T P协议版本的需求.......................... 526 • XVII • 0 录 18.1.5 S H T T P D支持头部的需求...................................... 527 18.1.6 S H T T P D 定位 URI 的需求......................................527 18.1.7 S H T T P D 支持 CGI 的需求......................................528 18.1.8 S H T T P D错误代码的需求............... ....................... 529 18.2 S H T T P D的模块分析和设计.......................................... 530 18.2.1 S H T T P D 的主函数............................................ 530 18.2.2 S H T T P D命令行解析的分析设计................................ 531 18.2.3 S H T T P D配置文件解析的分析设计.............................. 532 18.2.4 S H T T P D的多客户端支持的分析设计............................ 534 18.2.5 S H T T P D头部解析的分析设计.................................. 536 18.2.6 S H T T P D 对 URI 的分析设计....................................537 18.2.7 S H T T P D支持方法的分析设计.................................. 537 18.2.8 S H T T P D支持C G I的分析设计................................. 538 18.2.9 S H T T P D错误处理的分析设计................................. <540 18.3 S H T T P D各模块的实现.................................... :......... 542 18.3.1 S H T T P D命令行解析的实现.................................... 543 18.3.2 S H T T P D文件配置解析的实现.................................. 545 18.3.3 S H T T P D的多客户端支持的实现................................ 547 18.3.4 S H T T P D所请求U R I解析的实现............................... 551 18.3.5 S H T T P D方法解析的实现...................................... 552 18.3.6 S H T T P D响应方法的实现...................................... 552 18.3.7 S H T T P D 支持 CGI 的实现......................................556 18.3.8 S H T T P D支持H T T P协议版本的实现............................ 559 18.3.9 S H T T P D内容类型的实现...................................... 559 18.3.10 S H T T P D错误处理的实现..................................... 561 18.3.11 S H T T P D生成目录下文件列表文件的实现.......................563 18.3.12 S H T T P D主函数的实现....................................... 565 18.4 S H T T P D的编译、调试和测试........................................ 566 18.4.1 建立源文件...................................................566 18.4.2 制作 Makefile.................................................566 18.4.3 制作执行文件........................................:....... 567 18.4.4使用不同的浏览器测试服务器程序.............................. 567 18.5 小结...............................................................568 第1 9章一个简单网络协议栈的例子S I P ......................................569 19.1 S I P网络协议找的功能描述........................................... 569 19.1.1 SIP网络协议栈的基本功能描述................................ 570 19.1.2 S I P网络协议栈的分层功能描述................................ 570 19.1.3 S I P网络协议栈的用户接U 功能描述............................ 571 19.2 S I P网络协议找的架构............................................... 571 • XVIII • S 录 19.3 S I P网络协议找的存储区缓存......................................... 572 19.3.1 SIP存储缓冲的结构定义....................................... 573 19.3.2 SIP存储缓冲的处理函数....................................... 577 19.4 S I P网络协议找的网络接U 层......................................... 579 19.4.1 SI P网络接U 层的架构......................................... 579 19.4.2 S I P网络接U 层的数据结构...................................•.••■580 19.4.3 S I P网络接口层的初始化函数...................................581 19.4.4 S I P网络接口层的输入函数.....................................583 19.4.5 S I P网络接口层的输出函数.....................................586 19.5 S I P网络协议栈的A R P 层............................................ 588 19.5.1 SIP地址解析层的架构......................................... 588 19.5.2 SIP地址解析层的数据结构.....................................588 19.5.3 SIP地址解析层的映射表.......................................590 19.5.4 SIP地址解析层的A R P 映射表维护函数......................... 591 19.5.5 SIP地址解析层的A R P 网络报文构建函数.... :..................593 19.5.6 S1P地址解析层的A R P 网络报文收发处理函数................... 595 19.6 S I P网络协议栈的IP层.............................................. 598 19.6.1 S I P网际协议层的架构......................................... 598 19.6.2 S I P网际协议层的数据结构.....................................599 19.6.3 S I P网际协议层的输入函数.....................................601 19.6.4 S I P网际协议层的输出函数.....................................605 19.6.5 S I P网际协议层的分片函数.....................................606 19.6.6 S I P网际协议层的分片组装函数................................ 607 19.7 S I P网络协议栈的I C M P层........................................... 611 19.7.1 SIP控制报文协议的数据结构.................................. 611 19.7.2 SIP控制报文协议的协议支持...................................612 19.7.3 SIP控制报文协议的输入函数.................................. 613 19.7.4 SIP控制报文协议的回显应答函数.............................. 614 19.8 S I P网络协议栈的U D P 层............................................ 615 19.8.1 SIP数据报文层的数据结构.....................................615 19.8.2 SIP数据报文层的控制单元.....................................615 19.8.3 SIP数据报文层的输入函数.....................................617 19.8.4 SIP数据报文层的输出函数.....................................618 19.8.5 SIP数据报文层的建立函数.....................................618 19.8.6 SIP数据报文层的释放函数.....................................619 19.8.7 SIP数据报文层的绑定函数.....................................620 19.8.8 SIP数据报文层的发送数据闲数.................................621 19.8.9 SIP数据报文层的校验和计算...................................622 19.9 S I P网络协议栈的协议无关层......................................... 623 19.9.1 SIP协议无关层的系统架构.....................................623 • XIX • 目录 19.9.2 SIP协议无关层的函数形式.....................................624 19.9.3 S1P协议无关层的接收数据函数................................ 624 19.10 S I P网络协议栈的B S D 接U 层....................................... 625 19.10.1 S IP用户接口层的架构........................................ 625 19.10.2 SIP用户接n 层的套接字建立函数............................. 626 19.10.3 SIP用户接M 层的套接字关闭函数............................. 627 19.10.4 SIP用户接U 层的套接字绑定函数............................. 627 19.10.5 SIP用户接丨」层的套接字连接函数............................. 628 19.10.6 SIP用户接U 层的套接字接收数据函数......................... 628 19.10.7 SIP用户接口层的发送数据函数................................629 19.11 S I P网络协议找的编译.............................................. 630 19.11.1 SI P的文件结构.............................................. 630 19.11.2 SIP 的 Makefile.............................................. 631 19.11.3 SIP的编译运行.............................................. 631 19.12 小结..............................................................631 第2 0章一个简单防火墙的例子S I P F W .......................................633 20.1 S I P F W防火墙的功能描述............................................ 633 20.1.1 S I P F W防火墙对主机进行网络数据过滤的功能描述............... 633 20.1.2 S I P F W防火墙用户设置防火墙规则的功能描述................... 634 20.1.3 S I P F W防火墙配K 文件等附加功能的功能描述................... 634 20.2 SIPFW 需求分析.................................................... 634 20.2.1 S I P F W防火墙条件和动作......................................635 20.2.2 S I P F W防火墙支持过滤的类型和内容........................... 635 20.2.3 S I P F W防火墙过滤的方式和动作............................... 638 20.2.4 S I P F W防火墙的配置文件...................................... 640 20.2.5 S I P F W防火墙命令行配置格式..................................640 20.2.6 S I P F W防火墙的规则文件格式..................................642 20.2.7 S I P F W防火墙的日志文件数据格式............................. 643 20.2.8 S I P F W防火墙构建所采用的技术方案........................... 644 2 0 . 3使用netlink进行用户空间和内核空间数据交S ......................... 645 20.3.1 netlink的用户空间程序设计.................................... 645 20.3.2 netlink 的内核空间 A P I........................................ 648 2 0 . 4使用proc进行内存数据用户空间映射..................................650 20.4.1 proc虚拟文件系统的结构...................................... 650 20.4.2 创建proc虚拟文件........................................... 651 2 0 . 4 . 3删除proc虚拟文件........................................... 652 20.4.4 proc文件的写函数............................................ 652 20.4.5 proc文件的读函数............................................ 653 2 0 . 5内核空间的文件操作函数............................................ 654 • XX • 20.5.1 内核空间的文件结构.......................................... 654 20.5.2 内核空间的文件建立操作...................................... 655 20.5.3 内核空间的文件读写操作...................................... 656 20.5.4 内核空间的文件关闭操作...................................... 657 20.6 S I P F W防火墙的模块分析和设计......................................657 20.6.1 S I P F W防火墙的总体架构...................................... 657 20.6.2 S I P F W防火墙的用户命令解析................................. 660 20.6.3 S I P F W用户空间与内核空间的交互............................. 663 20.6.4 S I P F W防火墙内核链h 的规则处理............................. 666 20.6.5 S I P F W防火墙的P R O C 虑拟文件系统........................... 668 20.6.6 S I P F W防火墙的配置文件和口志文件处理....................... 669 20.6.7 S I P F W防火墙的过滤模块设计................................. 671 20.7 S I P F W防火墙各功能模块的实现......................................673 20.7.〗 S I P F W防火墙的命令解析代码................................. 674 20.7.2 S I P F W防火墙的过滤规则解析模块代码......................... 678 20.7.3 S I P F W防火墙的网络数据拦截模块代码......................... 680 20.7.4 S I P F W防火墙的P R O C 虚拟文件系统........................... 681 20.7.5 S I P F W防火墙对配置文件的解析............................... 683 20.7.6 S I P F W防火墙内核模块初始化和退出........................... 684 2 0 . 7 . 7用户空间处理主函数.......................................... 685 20.8 编译、调试和测试.......................... ....................... 686 2 0 . 8 . 1用户程序和内核程序的Makefile................................ 686 20.8.2 编译及运行.................................................. 687 2 0 . 8 . 3下发过滤规则,测试过滤结果.................................. 688 20.9 小结.............................................................. 690
### 回答1: Linux是一种支持多进程多线程的操作系统。多进程指一个程序可以被分成多个进程同时运行,每个进程拥有自己的内存空间和运行环境,它们之间通过进程间通信进行通信和数据共享;而多线程指一个进程中可以拥有多个线程并行执行,共享同一份内存空间和运行环境,具有更高的并发性和效率。Linux支持通过Thread模块、PThread库和POSIX等方式来实现多线程多进程多线程的应用广泛,可以使系统更加高效稳定和具有更好的用户体验。 ### 回答2: 首先,Linux多进程是指一个程序同时运行多个进程,每个进程有自己的空间和资源,互相独立。每个进程都可以独立执行,有自己的数据空间和代码空间,能够完成不同的任务。在Linux系统中,通过fork()系统调用可以创建新进程,通过exec()可以加载新程序到进程中。 相比之下,Linux多线程是在一个进程内部同时运行多个线程,每个线程共享同一个地址空间和资源多线程可以提高程序的并发性,加快多个任务的执行速度。通过pthread_create()函数可以在程序中创建新线程。 在使用多进程时,每个进程独立运行,内存空间独立,因此进程之间的数据交换比较麻烦,需要通过网络、管道等手段进行通信。而多线程共享同一个地址空间,数据交换更加方便快捷,只需要在不同线程之间直接传递数据即可。 另外,多线程也可以避免上下文切换所带来的开销,因为线程切换比进程切换要快,所以多线程可以提高系统性能,但是多线程编程需要考虑线程之间的同步和互斥,以及死锁等问题,需要更加谨慎。 总的来说,Linux多进程多线程都有其适用的场景和优缺点,需要根据具体的需求来选择合适的编程方式。 ### 回答3: Linux是一种基于unix的操作系统,具有很强的稳定性和可靠性。Linux同时支持多进程多线程,这使得它在并发性方面具有优势。 多进程是指在同一时间内运行多个进程,每个进程之间相互独立,拥有自己的地址空间和资源。每个进程都有一个唯一的进程标识符pid,可以通过pid来查找和管理进程。多进程的好处是可以提高系统的并行效率,同时还能保证进程间数据的隔离性,但是进程间通信需要使用IPC(inter-process communication)机制,会导致一些额外的开销。 多线程是指在同一时间内运行多个线程,线程是轻量级的进程。同一个进程内的不同线程共享相同的地址空间和资源,更加高效。多线程的好处是可以提高系统的并发性,同时也可以共享进程内部的资源,减少进程间的通信开销。但是,多线程会涉及到线程同步和互斥问题,需要使用锁等机制来保证线程的安全性。 在应用程序的设计中,通常需要选择使用多进程还是多线程,这需要根据具体的应用场景来决定。如果应用程序需要执行不同的任务,或者每个任务需要使用不同的资源,则可以考虑使用多进程;如果需要执行相同的任务,但是需要对任务进行分割和分配,或者需要共享资源,则可以考虑使用多线程。 总之,Linux多进程多线程都可以提高系统的并发性和响应速度,但需要在具体场景下选择合适的方式来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值