??xml version="1.0" encoding="utf-8" standalone="yes"?>
-> Pages,Locked Pages.
在现代操作系l中Q内存管理会把主?RAM)分成Pages来管理?Paging(或者swappingQ指的是d与外存之间以Page为单位进行数据的交换。Locked Pages指的是被锁定在主存中的内存页Q以保证一些内核组?driver可以讉K到它们。windows一定会保证一定数量的可交换的内存I间Q防止一些非法程序锁定所有的物理内存Q而致使系l崩溃。在windows NT, windows 2000上,可锁定的内存ȝ大小上限大概是物理内存的1/8(当然对于E序的开发h员,不应该对q个D行Q何的假设Q这个值可能会随着操作pȝ本版的变化而变?。在Winsock应用开发过E中Q以overlapped方式dIO操作Q将会导致内存被锁定?br />-> working set
在程序开始运行,q达到其E_的运行状态(主要指的是其对内存的使用Q,在这个状态下Q程序用内存的数量一般小于其需要用内存的总量。这样一个稳定的q行状态,我们可以UCؓworking set: 被该E序频繁讉K的内存页的集合。在windows上,你可以用SetWorkingSetSize Win32 API来增加程序用物理内存的数量?br />-> non-paged pool
不可交换的内存。这主要指以non-paged的方式分配的内存Q这些内存就像locked pages一P是从来不会被交换出去的,用来存放一些由内核lg,driver讉K的信息?在Winsock应用开发过E中Q以下的操作可能D分配non-paged内存?br />1) 调用pȝ一些系l的APIQ例如打开文gQcreate socketQ等Q都会导致从non-paged pool分配内存?br />2) 一些driver可以昑ּC该区域分配内存?br />? Winsock server上Locked Pages使用?br /> 我们提到q,M的overlapped IO操作Q都会导致锁定内存页。这些内存页一旦被locked,׃会被交换出去。我们知道,windows操作pȝҎ(gu)大的可锁定内存页做了一个上限,如果出q个上限Qoverlapped IO调用会DWSAENOBUFS错误?br /> 考虑下面的情况,如果server在每个连接上会发出很多的overlapped receives操作Q那么,随着q接数目的增多,很明显,被锁定的内存数量很有可能辑ֈ上限而导致WSAENOBUFS错误。在q种情况下,如果服务器预期会处理大数量的客户端连接,则需要服务器在每个连接上发出zero-byte buffer的overlapped接收h(q种情况下,因ؓthe size of buffer is zero,所以没有Q何内存被锁定)Q一旦overlapped接收操作完成Qserver可以以non-blocking方式执行receive操作Q以取得所有缓存在so_rcvbuf中的数据Q直到返回WSAEWOULDBLOCK为止?br /> 另外需要注意的是,windows在page的边界上对内存进行锁定,在x86q_上,它是4kb的整数倍。所以,假如你post了一? KB bufferQ而系l真实锁定的? KB 的大,Z避免q样的浪费,量?kb的整数做overlapped IO操作?br />? Winsock server上non-paged pool使用?br /> 同Locked Pages限制一Pwindows对non-paged pool也有一个最大的限制。ƈ且,当你的应用出现这个问题的时候,出它的最大限制数Q情况要q比Locked Pages复杂。这U情况下Q后果是不确定的Q有可能你的Winsock调用q回WSAENOBUFS错误Q也有可能,在系l中Q一个和你的应用毫无兌的driver׃甌不到non-paged内存而致使system crash。而这L(fng)NQ是没法恢复的?br /> 考虑一个具体的例子Q我们假讑֜windows2000?pȝ? GB内存。这L(fng)配置下,windows大概会预?/4的空间用作non-paged pool(同样Q对于程序的开发h员,不应该对q个D行Q何的假设)Q即Q?56MB。这L(fng)配置下,保守估计Q我们的Winsock server能够处理到大?0,000q接Q或者更多?每个accepted socket大概消?.5kbQ每个连接上post一个overlapped操作Q分配一个IRP,大概需?00 byte, 总计Q?1500+500)*50,000 = 100 Mb) ?br /> 无论是对于Locked PagesQ还是对于non-paged pool使用Q一旦超Z上限QW(xu)insock调用仅仅会返回一般的WSAENOBUFS 或者ERROR_INSUFFICIENT_RESOURCES错误。ؓ了处理这些错误,你可以试试以下的Ҏ(gu)Q?br />1Q?需要首先调用SetWorkingSetSize,增加应用的可支配资源敎ͼ看能否解冟?br />2) 信你的应用没有做出太多的overlapped IO操作?br />3Q?关闭一些连接数?br />? SOCKET的缓冲区讄问题?/u>
Winsock在默认的情况下,每个socket都会与一个send和receive buffer相关联。你可以通过调用setsockopt来设|buffer的大?br /> 在缓冲区没有被关闭的情况下,我们看看overlapped send和revc是怎么工作的?br /> 当上层的应用做出了send调用Q而这时如果send bufferq有剩余的空_那么数据会从用h交的buffer复制到send buffer中,然后调用q回成功。否则,假如q时send buffer已满Q用h交的buffer会被锁定,q且调用q回WSA_IO_PENDING。当send buffer的数据被下层的tcp处理完成Qwinsock直接处理用h交的buffer里的数据Q而不需要再复制?br /> 同样Q对于recv操作Q如果数据已l被~存在socket的receive buffer里,当发生recv调用的时候,数据直接从socket的receive buffer复制到用L(fng)buffer里,recv调用q回成功。否则,假如发生调用时receive buffer里没有数据,用户提交的buffer会被锁定,recv调用q回WSA_IO_PENDING。当数据到达当前q接Q将会被直接复制到用h交的buffer里?br /> 一个应用程序通过讑֮send buffer?Q把~冲区关闭,然后发出一个阻塞send()调用。在q样的情况下Q系l内怼把应用程序的~冲区锁定,直到接收方确认收C整个~冲区后send()调用才返回。似乎这是一U判定你的数据是否已lؓҎ(gu)全部收到的简z的Ҏ(gu)Q实际上却ƈ非如此。想想看Q即使远端tcp通知数据已经收到Q其实也Ҏ(gu)不代表数据已l成功送给客户端应用程序,比如Ҏ(gu)可能发生资源不的情况,Dafd.sys不能把数据拷贝给应用E序。另一个更要紧的问题是Q在每个U程中每ơ只能进行一ơ发送调用,效率极其低下?
另外Q希望通过关闭Winsock~冲区,从而避免数据复Ӟ辑ֈ优化性能的目的,也是不可取的。从上面Q我们看刎ͼ只要应用保证适量的,_的send, recv调用Q这L(fng)复制是完全可以避免的?br /> 高性能的服务器应用E序可以关闭发送缓冲区Q同时不会损失性能。不q,q样的应用程序必d分小心,保证它L发出多个重叠发送调用,而不是等待某个重叠发送结束了才发Z一个。如果应用程序是按一个发完再发下一个的序来操作,那浪Ҏ(gu)两次发送中间的I档旉QM是要保证传输驱动E序在发送完一个缓冲区后,立刻可以转向另一个缓冲区?br /> 如果关闭了recv bufferQ在你的应用没有保证_的recv操作前提下,Mq来数据Q必dTCP层进行缓存,最大缓存的数量取决于tcp windows的大?17Kb)。而最Z重的是这些缓存是从non-paged pool分配而来。如上所qͼnon-paged pool是非常珍贵,E~的内存。所以,从这个意义上来讲Q关闭了recv buffer操作是不可取的?img src ="http://www.shnenglu.com/lapcca/aggbug/116873.html" width = "1" height = "1" />
]]>
现在大家在和Java, PHP, .net写应用程序时Q都会用C些成熟的服务框架Q所以开发效率是比较高的。而在用C/C++写服务器E序Ӟ用的׃花八门了Q有些h用ACE, 有些人用ICEQ号U比ACE多)Q等{,q类服务器框架及库比较丰富,但入门门槛比较高Q所以更多的人是自己直接写服务器E序Q初始写时觉得比较简单,可时间久了,便会觉得难以扩展Q性能低,Ҏ(gu)出错。其实,Postfix 作者ؓ我们提供了一个高效、稳定、安全的服务器框架模型,虽然Postfix主要用作邮gpȝ?mtaQ但其框架设计却非常h通用性。ACL(http://acl.sourceforge.net/) 的作者将Postfix的服务器框架模型抽取出来QŞ成了更加通用的服务器E序开发框Ӟ使程序员在编写服务器E序时可以达C半功倍的效果。本文主要介l了ACL中acl_master服务器程序(ZPostifx服务器程序框Ӟ的设计及功能?/p>
二、框架设计图
如下图所C:
?--框架?/p>
master主进Eؓ控制q程Q刚启动时其负责监听所有端口服务,当有新的客户端连接到达时Qmaster便会启动子进E进行服务,而自׃然监控服务端口,同时监控子进E的工作状态;而提供对外服务的子进E在master启动Ӟ若没有请求Q务则不会被启动,只有当有q接或Q务到达时才会被master 启动Q当该服务子q程处理完某个连接服务后q不立即退出,而是ȝ在系l一D|_{待可能的新q接到达Q这样当有新的连接到达时master׃会启动新的子q程Q因为已l有处于I闲的子q程在等待下一个连接请求;当服务子q程I闲旉达一定阀值后Q就会选择退出,资源全部归q操作系l(当然Q也可以配置成服务子q程怸退出的模式Q。因此,可以U这U服务器框架为协作式半驻留式服务器框Ӟ下面会对协作式和半ȝ作进一步介l?/p>
三、协作方?/p>
Postfix服务器框架设计的非常巧妙Q因为master毕竟属于用户I间q程Q不能象操作pȝ那样可以控制每个q程的运行时间片Q所以master主进E必M其服务子q程之间协作好,以处理好以下几个q程Q?/p>
1Q新q接到达Ӟmaster是该启动新的子进E接该q接q是q闲子q程直接接管
2Qmaster何时应该启动新的子进E?/p>
3Q新q接到达Q空闲子q程池中的子q程如何竞争接管该连?/p>
4Q子q程异常退出时Qmaster如何处理新连?/p>
5Q空闲子q程如何选择退出时_I闲旉或服务次数应军_子进E的退出)
6Qmaster如何知道各个子进E的工作状态(是死了还是活着Q)
7Q在不停止服务的前提下,服务子进E程序如果在U更新、如何添加新的服务、如何在U更新子q程配置
8Q如何减所有子q程与master之间的通讯ơ数从而降低master的负?/p>
四、流E图
1) master主进E流E图
?--masterq程程?/p>
Postifx 中的 master 主进E与各个子进E之间的IPC通讯方式为管道,所以管道的个数与子q程数是成正比的。如果管道中断,?master 认ؓ该管道所对应的子q程已经退出,如果是异帔R出,masterq需要标记该服务cdq程池以防止该类子进E异帔R出频J而启动也异常频繁Q如果子q程启动q于频繁则会l操作系l造成巨大负蝲Q;另外Q如果某cL务的子进E在服务W一个连接时异帔R出,则master认ؓ该服务有可能是不可用的,所以当有新的连接再到达时就会gq启动该服务子进E?/p>
当服务子q程池中有空闲子q程Ӟmaster便会把该服务端口的监听权让出Q从而该服务的空闲子q程在该服务端口上接收新的连接。当某个子进E获得新的连接后便会立即通知master其已l处于忙状态,master便立x找该服务的子q程q程池还有无I闲子进E,如果有则master依然不会接管该服务端口的监听dQ如果没有了Q则master立即接管该服务端口的监听dQ当有新的连接到达时Qmaster先检查有没有该服务的I闲q程Q若有便让出该服务端口的监听权,若没有便会启动新的子q程Q然后让出监听权?/p>
2) 服务子进E流E图
?--子进E流E图
在master主进E刚启动Ӟ因ؓ没有M服务hQ所以子q程是不随master一起启动的Q此时所有服务端口的监控工作是由masterl一负责Q当有客L(fng)q接到达Ӟ服务子进E才由master启动Q进而接收该新连接,在进一步处理客L(fng)h前,子进E必让masterq程知道它已l开? ?了,好由master来决定是否再ơ接该服务端口的监控Q务,所以子q程首先向master发送“忙”消息,然后才开始接收ƈ处理该客L(fng)hQ当子进E完成了对该客户端的hd后,需向master发送“空關y消息,以表明自己又可以l箋处理新的客户端连接Q务了。这一“忙”一“闲”两个消息,便体C服务子进E与master主进E的协作特点?/p>
当然Q服务子q程可以选择合适的退出时机:如果自己的服务次数达到配|的阀|或自q闲时间达到阀|或与master主进E之间的IPC道中断(一般是由master停止服务要求所有服务子q程退出时或master要求所有服务子q程重读配置时而引L(fng))Q则服务子进E便应该l束q行了。这个停止过E,一斚w体现了子q程与master主进E之间的协作特点Q另一斚w也体C子进E半ȝ的特点,从而体现子q程q程池的半驻留特性,q一Ҏ(gu)的最大好处就是:按需分配Q当hq接比较多时Q所启动q行的子q程多Q当hq接d较少Ӟ只有数的子q程在运行,其它的都退Z?/p>
3) 结
以上从整体上介绍了Postfix服务器框架模型中master主进E与服务子进E的逻辑程图,当然Q其内部实现要点与细节远比上面介l的要复杂,只是q些复杂层面别h已经帮我们屏蔽了Q我们所要做的是在此基础上写q应用服务来,下面要介l了ZPostfix的服务器框架攚w抽象的ACL库中服务器程序的开发方式,以大家比较Ҏ(gu)上手?/p>
五、五U服务器框架模板?/p>
要想使用ACL服务器框架库开发服务器E序Q首先介l两个个名词:acl_master服务器主q程与服务器模板。acl_master服务器主q程?Postfix中的master主进E功能相|它的主要作用是启动ƈ控制所有的服务子进E,q个E序不用我们写,是ACL服务器框架中已经写好的;所谓服务器模板Q就是我们需要根据自q需要从ACL服务器框架中选择一U服务器模型(x务器模板)Q然后在~写服务器时按该服务器模板的方式写即可。ؓ什么还要选择合适的服务器框架模板?q是因ؓPostfix本n提供了三类服务器应用Ş?单进E单q接q程池、单q程多连接进E池、触发器q程?Q这三类应用形式便分别用了Postfix中的三种服务器模板,此外QACL中又增加了两U服务器应用形式(多线E进E池、单q程非阻塞进E池)。下面分别就q五U服务器框架模板一一做简介:
1) 单进E单q接q程池:?acl_master 主进E启动多个进E组成进E池提供某类服务Q但每个q程每次只能处理一个客L(fng)q接h
2) 单进E多q接q程池:?acl_master 主进E启动多个进E组成进E池提供某类服务Q而每个进E可以同时处理多个客L(fng)q接h
3) 触发器进E池Q由 acl_master 主进E启动多个进E组成进E池提供定时器类服务(cM于UNIX中的cron)Q当某个定时器时间到达时Q便׃个进E开始运行处理Q?/p>
4) 多线E进E池Q由 acl_master 主进E启动多个进E组成进E池提供某类服务Q而每个进E是由多个线E组成,每个U程处理一个客L(fng)q接h
5) 单进E非dq程池:?acl_master 主进E启动多个进E组成进E池提供某类服务Q每个进E可以ƈ发处理多个连?cM于Nginx, Lighttpd, Squid, Ircd)Q由于采用非d技术,该模型服务器的ƈ发处理能力大大提高,同时pȝ资源消耗也最;当然Q该模型与单q程多连接进E池采用的技术都是非d技术,但该模型q行更多的应用封装与高处理Qɾ~写非阻塞程序更加容?/p>
以上五种服务器方式中Q由于可以根据需要配|成多个q程实例Q所以可以充分地利用多核的系l。其中,W?U的q行效率是最高的Q当然其~程的复杂度要比其它的高Q而第1U是执行效率最低的Q其实它也是最安全?在Postfix中的smtpd/smtp {进E就属于q一c?Q而相Ҏ(gu)_W?U在q行效率与编写复杂度斚w是一个比较好的折P所以在写一般性服务器Ӟ该服务器模型是作者推荐的Ҏ(gu)Q此外,W?U方案还有一个好处,可以做到对于I接不必占用线E,q样也大大提供了q发?即线E数可以q小于连接数)?/p>
六、用简?/p>
本节暂不介绍具体的编E过E,只是介绍一些配|用过E。假讑ַl写好了服务器程序:echo_server, 该程序可以采用上面的 1), 2), 4), 5) 中的M服务器模型来写,假设采用了第4)U;另外Q还假设Qacl_master{所有文件安装的根目录ؓ /opt/acl/, 主进E程?acl_master ?echo_server 安装?/opt/acl/libexec/, acl_master ȝ序配|文件安装在 /opt/acl/conf/Qecho_server 配置文g安装?/opt/acl/conf/service/, 日志文g目录?/opt/acl/var/log/, q程h件目录ؓ /opt/acl/var/pid/。比如,你让 echo_server 的服务端口ؓ 6601Q服务地址?127.0.0.1, 它只是提供简单的 echo 服务?/p>
你可以运?/opt/acl/sh/start.sh 脚本来启?acl_master 主进E??ps -ef|grep acl_master 会看?acl_master q程存在 Q但 ps -ef|grep echo_server 却没有发现它的存?Q然后你在一个Unixl端?telnet 127.0.0.1 6601, 在另一个终端上 ps -ef|grep echo_server ׃发现有一?echo_server子进E了Q然后在 telnet 6601 的终端上随便输入一些字W串Q便会立卛_到回复,关闭?telnet q接Q用 ps -ef|grep echo_server 会发现该q程依然存在Q当再次 telnet 127.0.0.1 6601 Ӟ该echo_serverq程又lؓ新连接提供服务了。可以试着多开几个l端同时 telnet 127.0.0.1 6601Q看看运行效果如何?
注意Q服务子q程的配|文件格式需要与其所采用的模板类型一_q程池中最大进E个数、进E中U程池最大线E个数、进E最大服务次数、空闲时间等都是可以以配|文件中指定的?/p>
postfix是Wietse Venema在IBM的GPL协议之下开发的MTAQ邮件传输代理)软g。下面一D话摘自postfix的官方站点(http://www.postfix.orgQ:“postfix是Wietse Venema惌Z用最q泛的sendmail提供替代品的一个尝试。在Internet世界中,大部分的?sh)子邮g都是通过sendmail来投递的Q大U有100万用户用sendmailQ每天投递上亿封邮g。这真实一个让人吃惊的数字。Postfix试图更快、更Ҏ(gu)理、更安全Q同时还与sendmail保持_的兼Ҏ(gu)。?/p>
1.1 postfix的特?/p>
1. postfix是免费的Q?/p>
postfix惌作用的范围是q大的Internet用户Q试囑֪响大多数的Internet上的?sh)子邮gpȝQ因此它是免费的?/p>
2. 更快Q?/p>
postfix在性能上大U比sendmail快三倍。一部运行postfix的台式PC每天可以收发上百万封邮g?/p>
3. 兼容性好:
postfix是sendmail兼容的,从而sendmail用户可以很方便地q移到postfix。Postfix支持/var[/spool]/mail?etc/aliases?NIS、和 ~/.forward 文g?/p>
4. 更健壮:
postfix被设计成在重负荷之下仍然可以正常工作。当pȝq行出了可用的内存或磁盘空间时Qpostfix会自动减运行进E的数目。当处理的邮件数目增长时Qpostfixq行的进E不会跟着增加?/p>
5. 更灵z:
postfix是由过一打的程序组成的Q每个程序完成特定的功能。你可以通过配置文g讄每个E序的运行参数?/p>
6. 安全?/p>
postfixh多层防Ml构Q可以有效地抵M恶意入R者。如大多数的postfixE序可以q行在较低的权限之下Q不可以通过|络讉K安全性相关的本地投递程序等{?/p>
1.2 postfix的Ml构
postfix由十几个h不同功能的半ȝq程l成Qƈ且在q些q程中ƈ无特定的q程间父子关pR某一个特定的q程可以为其他进E提供特定的服务?/p>
大多数的postfixq程׃个进E统一q行理Q该q程负责在需要的时候调用其他进E,q个理q程是masterq程。该q程也是一个后台程序?/p>
q些postfixq程是可以配|的Q我们可以配|每个进E运行的数目Q可重用的次敎ͼ生存的时间等{。通过灉|的配|特性可以整个pȝ的运行成本大大降低?/p>
1.2.1 postfix的邮仉列(mail queuesQ?/p>
postfix有四U不同的邮g队列Qƈ且由队列理q程l一q行理Q?/p>
1Q?maildropQ本地邮件放|在maildrop中,同时也被拯到incoming中?/p>
2Q?incomingQ放|正在到达或队列理q程未发现的邮件?/p>
3Q?activeQ放|队列管理进E已l打开了ƈ正准备投递的邮gQ该队列有长度的限制?/p>
4Q?deferredQ放|不能被投递的邮g?/p>
队列理q程仅仅在内存中保留active队列Qƈ且对该队列的长度q行限制Q这样做的目的是Z避免q程q行内存过pȝ的可用内存?/p>
1.2.2 postfix寚w仉暴的处理
当有新的邮g到达Ӟpostfixq行初始化,初始化时postfix同时只接受两个ƈ发的q接h。当邮g投递成功后Q可以同时接受的q发q接的数目就会缓慢地增长至一个可以配|的倹{当Ӟ如果q时pȝ的消耗已到达pȝ不能承受的负载就会停止增ѝ还有一U情冉|Q如果postfix在处理邮件过E中遇到了问题,则该g开始降低?/p>
当接收到的新邮g的数量超qpostfix的投递能力时Qpostfix会暂时停止投递deferred队列中的邮g而去处理新接收到的邮件。这是因为处理新邮g的gq要于处理deferred队列中的邮g。Postfix会在I闲时处理deferred中的邮g?/p>
1.2.3 postfixҎ(gu)法投递的邮g的处?/p>
当一邮件第一ơ不能成功投递时Qpostfix会给该邮件脓(chung)上一个将来的旉邮票。邮仉列管理程序会忽略贴有来旉邮票的邮件。时间邮到期时Qpostfix会尝试再对该邮gq行一ơ投递,如果q次投递再ơ失败,postfixq该邮件脓(chung)上一个两倍于上次旉邮票的时间邮,{时间邮到期时再次q行投递,依此cL。当Ӟl过一定次数的试之后Qpostfix会放弃对该邮件的投递,q回一个错误信息给该邮件的发g人?/p>
1.2.4 postfix对不可到辄目的地邮件的处理
postfix会在内存中保存一个有长度限制的当前不可到辄地址列表。这样就避免了对那些目的Cؓ当前不可到达地址的邮件的投递尝试。从而大大提高了pȝ的性能?/p>
1.2.5 postfix的安全?/p>
postfix通过一pd的措施来提高pȝ的安全性,q些措施包括Q?/p>
1Q?动态分配内存,从而防止系l缓冲区溢出Q?/p>
2Q?把大邮g分割成几块进行处理,投递时再重l;
3Q?Postfix的各U进E不在其他用戯E的控制之下q行Q而是q行在驻留主q程master的控制之下,与其他用戯E无父子关系Q所有有很好的绝~性?/p>
4Q?Postfix的队列文件有其特D的格式Q只能被postfix本n识别Q?/p>
二?postfix寚w件的处理q程
2.1 接收邮g的过E?br />
当postfix接收C新邮gӞ新邮仉选在incoming队列处停留,然后针对不同的情况进行不同的处理Q?/p>
1Q对于来自于本地的邮Ӟsendmailq程负责接收来自本地的邮件放在maildrop队列中,然后pickupq程对maildrop中的邮gq行完整性检。maildrop目录的权限必设|ؓ某一用户不能删除其他用户的邮件?/p>
2Q对于来自于|络的邮Ӟsmtpdq程负责接收来自于网l的邮gQƈ且进行安全性检。可以通过UCEQunsolicited commercial emailQ控制smtpd的行为?/p>
3Q由postfixq程产生的邮Ӟq是Z不可投递的信息q回l发件h。这些邮件是由bounce后台E序产生的?/p>
5Q?由postfix自己产生的邮Ӟ提示postmasterQ也即postfix理员)postfixq行q程中出现的问题。(如SMTP协议问题Q违反UCE规则的记录等{。)
关于cleanup后台E序的说明:cleanup是对新邮件进行处理的最后一道工序,它对新邮件进行以下的处理Q添加信头中丢失的Form信息Qؓ地址重写成标准的user@fully.qualified.domain格式q行排列Q重信头中抽出收件h的地址Q将邮g投入incoming队列中,q请求邮仉列管理进E处理该邮gQ请求trivial-rewriteq程地址转换成标准的user@fully.qualified.domain格式?/p>
2.2 投递邮件的q程
新邮件一旦到达incoming队列Q下一步就是开始投递邮Ӟpostfix投递邮件时的处理过E如图三所C。相关的说明如下Q?/p>
邮g队列理q程是整个postfix邮gpȝ的心脏。它和local、smtp、pipe{投递代理相联系Q将包含有队列文件\径信息、邮件发件h地址、邮件收件h地址的投递请求发送给投递代理。队列管理进E维护着一个deferred队列Q那些无法投递的邮g被投递到该队列中。除此之外,队列理q程q维护着一个active队列Q该队列中的邮g数目是有限制的,q是Z防止在负载太大时内存溢出。邮仉列管理程序还负责收件h地址在relocated表中列出的邮件返回给发g人,该表包含无效的收件h地址?/p>
如果邮g队列理q程hQrewrite后台E序Ҏ(gu)件h地址q行解析。但是缺省地Qrewrite只对邮g收g人是本地的还是远E的q行区别?/p>
如果邮g对你理q程hQbounce后台E序可以生成一个邮件不可投递的报告?/p>
本地投递代理localq程可以理解cMUNIX风格的邮,sendmail风格的系l别名数据库和sendmail风格?forward文g。可以同时运行多个localq程Q但是对同一个用L(fng)q发投递进E数目是有限制的。你可以配置local邮件投递到用户的宿ȝ录,也可以配|local邮件发送给一个外部命令,如流行的本地投递代理procmail。在行的linux发行版本RedHat中,我们׃用procmail作ؓ最l的本地投递代理?/p>
q程投递代理SMTPq程Ҏ(gu)收g人地址查询一个SMTP服务器列表,按照序q接每一个SMTP服务器,Ҏ(gu)性能对该表进行排序。在pȝ负蝲太大Ӟ可以有数个ƈ发的SMTPq程同时q行?/p>
pipe是用于UUCP协议的投递代理?