Profil de Libin绿色家园PhotosBlogListes Outils Aide

Blog


30 janvier

免费软件清单

Updated by Simon, 27th November 2005
Originally compiled by sanctified, thanks to Neowin members.

Items marked in bold are new additions.

3D Graphics:
3Delight Free - http://www.3delight.com/index.htm
Anim8or - http://www.anim8or.com/
Aqsis - http://www.aqsis.com/
Blender - http://www.blender3d.org/
Houdini (Free Edition) - http://www.sidefx.com/apprentice/index.html
Maya Personal Learning Ed. - http://www.alias.com/eng/products-services..._maya_ple.shtml
Now3D - http://digilander.libero.it/giulios/Eng/homepage.htm
OpenFX - http://www.openfx.org
POV-Ray - http://www.povray.org/
SOFTIMAGE|XSI EXP - http://www.softimage.com/products/exp/v3/
Terragen - http://www.planetside.co.uk/terragen/
Toxic - http://www.toxicengine.org/
Wings 3D - http://www.wings3d.com/

Anti-Virus:
a-squared - http://www.emsisoft.com/en/software/free/
AntiVir - http://www.free-av.com/
Avast - http://www.avast.com/i_idt_1018.html
AVG - http://free.grisoft.com/
BitDefender - http://www.bitdefender.com
ClamWin - http://www.clamwin.com/

Anti Spyware:
Ad-aware - http://www.lavasoft.de/software/adaware/
Bazooka - http://www.kephyr.com/spywarescanner/index.html
CWShredder - http://www.intermute.com/spysubtract/cwshr...r_download.html
Hijackthis - http://www.spywareinfo.com/~merijn/downloads.html
Microsoft AntiSpyware - http://www.microsoft.com/athome/security/s...re/default.mspx
SpyBot Search & Destroy - http://spybot.safer-networking.de/
SpywareBlaster - http://www.javacoolsoftware.com/spywareblaster.html
SpywareGuard - http://www.wilderssecurity.net/spywareguard.html
WinPatrol - http://www.winpatrol.com/

Audio Creation
Gungirl Sequencer - http://ggseq.sourceforge.net/pmwiki.php/Main/HomePage
HammerHead - http://www.threechords.com/hammerhead/introduction.shtml
Jesusonic - http://www.jesusonic.com/soft.php
KRISTAL Audio Engine - http://www.kreatives.org/kristal/index.php
orDrumbox - http://ordrumbox.sourceforge.net/
Tu2 - http://www.brambos.com/news.html

Audio Players:
1by1 - http://www.rz.uni-frankfurt.de/~pesch
Billy - http://www.sheepfriends.com/?page=billy
CoolPlayer - http://coolplayer.sourceforge.net/
DeejaySystem MK1 - http://www.deejaysystem.com/prod_mk1.asp
DeliPlayer. http://www.deliplayer.com/
Foobar 2000 - http://www.foobar2000.org/
iTunes - http://www.apple.com/itunes/
Jet Audio Basic - http://www.jetaudio.com/
Mixere - http://mixere.sourceforge.net/
Mixxx - http://mixxx.sourceforge.net/
monoRAVEik - http://www.mono211.com/monoraveik/mr1200.html
MoreAmp - http://sourceforge.net/projects/moreamp/
Musik - http://musik.berlios.de/
musikCube - http://www.musikcube.com/
QCD Player - http://www.quinnware.com/
Sonique - http://sonique.lycos.com/
Winamp - http://www.winamp.com/
XMPlay - http://www.un4seen.com/xmplay.html
Zinf - http://www.zinf.org/

Audio Tools:
Audacity - http://audacity.sourceforge.net/
AudioShell - http://www.softpointer.com/AudioShell.htm
BeSweet - http://dspguru.doom9.net/
CDex - http://cdexos.sourceforge.net/
dBpowerAMP Music Converter - http://www.dbpoweramp.com/dmc.htm
EAC - http://www.exactaudiocopy.de/
Encounter 2003 - http://www.waschbusch.com/
GermaniXEncoder - http://www.germanixsoft.de/
K-MP3 - http://www.katarncorp.com/
KraMixer - http://www.kramware.com/
MP3 Book Helper - http://mp3bookhelper.sourceforge.net/
MP3 Tag - http://www.mp3tag.de/
Mp3 Tag Tools - http://massid3lib.sourceforge.net/
mp3DirectCut - http://www.rz.uni-frankfurt.de/~pesch/
MP3Gain - http://www.geocities.com/mp3gain/
mp3Trim - http://www.logiccell.com/~mp3trim/
MusicBrainz - http://musicbrainz.org/
Rarewares - http://rarewares.hydrogenaudio.org/
SoundEngine Free - http://www.cycleof5th.com/en/index.htm
TagScanner - http://xdev.narod.ru/tagscan_e.htm
The GodFather - http://users.otenet.gr/~jtcliper/tgf/
TigoTago - http://www.tigotago.com/

CD/DVD Burning:
Burn4Free - http://www.burn4free.com/
Burnatonce - http://www.burnatonce.com/
Burrrn - http://www.burrrn.net/
CDBurnerXP - http://www.cdburnerxp.se/
CDRDAO - http://cdrdao.sourceforge.net/
CDR Tools Frontend - http://demosten.com/cdrfe/
Deepburner - http://www.deepburner.com/
DVD Decrypter: http://www.dvddecrypter.com/
Easy Burning, DropCD & Audio CD - http://www.paehl.de/cdr
ImgBurn - http://www.imgburn.com/

Compression / Decompression:
7-zip - http://www.7-zip.org/
bzip2 - http://sources.redhat.com/bzip2/index.html
ExtractNow - http://www.extractnow.com/
FilZip - http://www.filzip.com/
Info-Zip - http://www.info-zip.org/
IZArc - http://www.florida.plus.com/izarc/
QuickZip - http://www.quickzip.org/
TUGZip - http://www.tugzip.com/
UPX - http://upx.sourceforge.net/
Zip&Go - http://www.handybits.com/zipngo.htm
Zipgenius - http://www.zipgenius.it/

Defrag Software:
DIRMS & Buzzsaw - http://www.dirms.com/
OpenVMS - http://www.execsoft.com/freeware/freeware.asp

Desktop Enhancements:
AveDesk - http://www.aqua-soft.org/board/showthread.php?t=17372
CursorXP - http://www.stardock.com/products/cursorxp/download.html
Desktop Sidebar - http://www.desktopsidebar.com/
Filehand - http://www.filehand.com/
Glass2k - http://www.chime.tv/products/glass2k.shtml
Kapsules - http://kapsules.shellscape.org/
Konfabulator - http://www.konfabulator.com/info
Lost Goggles - http://www.lostgoggles.com/
MobyDock - http://www.mobydock.com/
Panorama - http://www.ivory.org/panorama.html
Rainlendar - http://www.ipi.fi/~rainy/index.php?pn=proj...ject=rainlendar
RunFast - http://www.idiogensoftware.com/runfast/index.htm
Samurize - http://www.samurize.com/
SlickRun - http://www.bayden.com/SlickRun/
Snippy - http://www.bhelpuri.net/Snippy/
TaskSwitchXP Pro - http://www.ntwind.com/taskswitchxp/
tclock2 - http://home.inreach.com/2tone/tclock2/tclock2.htm
Trip - http://trip.glenmurphy.com/
Weather Watcher - http://www.singerscreations.com/
WinRoll - http://www.palma.com.au/winroll/

Download managers:
Free Download Manager - http://www.freedownloadmanager.org/
Fresh Download - http://www.freshdevices.com/freshdown.html
LeechGet - http://www.leechget.net/en/
Retriever - http://www.halogenware.com/software/retriever.html
Star Downloader - http://www.stardownloader.com/downloads.php
Sun Download Manager - http://www.sun.com/download/sdm/index.xml
wackget - http://millweed.com/projects/wackget/
wget - http://xoomer.virgilio.it/hherold/
WellGet - http://www.wellget.com/

Encryption and data security:
Axcrypt - http://axcrypt.sourceforge.net/
Blowfish Advanced CS- http://web.bsn.ch/lasse/bfacs.htm
Eraser - http://www.heidi.ie/eraser/default.php
File Shredder - http://www.sys-shield.com/fileshredder.htm
KeePass - http://keepass.sourceforge.net/
GnuPG - http://www.gnupg.org/
PGP Freeware - http://www.pgp.com/products/freeware.html
PicoCrypt - http://picofactory.com/picocrypt.html
TrueCrypt - http://www.truecrypt.org/
WindowsCleaner - http://www.winnowsoft.com/internet-eraser.htm

File Managers:
2xExplorer - http://netez.com/2xExplorer/
A43 - http://www.shawneelink.net/~bgmiller/
ExplorerXP - http://www.explorerxp.com
freeCommander - http://www.freecommander.com/index_en.htm
Gyula's Navigator - http://www.wanari.com/
JExplorer - http://home.megapass.co.kr/~woosjung/
MeeSoft Commander - http://meesoft.logicnet.dk/

File repair and recovery:
PC Inspector File Recovery - http://www.pcinspector.de/file_recovery/UK/welcome.htm

Firewalls:
Jetico Personal Firewall - http://www.jetico.com/index.htm#/jpfirewall.htm
Kerio (Kerio Personal Firewall is FREE for home and personal use) - http://www.kerio.com/kpf_home.html
NetVida Safetynet - http://www.netveda.com/consumer/safetynet.htm
Outpost Firewall (version 1 is free) - http://www.agnitum.com/download/outpost1.html
SoftPerfect Personal Firewall - http://www.softperfect.com/products/firewall/
Sygate - http://smb.sygate.com/products/spf_standard.htm
Wyvern Firewall 2004 - http://www.wyvernworks.com/firewall.html
Zonealarm Basic firewall -

http://www.zonelabs.com/store/content/comp...reeDownload.jsp

FTP Clients:
CoreFTP - http://www.coreftp.com/
Filezilla - http://sourceforge.net/projects/filezilla
miFiles - http://www.simdata.com.au/mifiles.html
SmartFTP - http://www.smartftp.com/

FTP Servers:
Cerberus - http://www.cerberusftp.com/
FileZilla - http://filezilla.sourceforge.net/
Golden FTP Server - http://www.goldenftpserver.com
GuildFTPD - http://www.totalshareware.com/asp/detail_v...pplication=8334
Quick 'n Easy FTP Server - http://www.pablovandermeer.nl/ftp_server.html
SlimFTPd - http://www.whitsoftdev.com/slimftpd
TYPSoft FTP Server - http://en.typsoft.com/
WarFTPD - http://www.jgaa.com/

HTML Editors:
1st page 2000 - http://www.evrsoft.com/download.shtml
AceHTML - http://freeware.acehtml.com/download.html
Aracnophilia - http://www.arachnoid.com/arachnophilia/
FoxEditor - http://foxeditor.sourceforge.net/
HTML-Kit - http://www.chami.com/html-kit/
NVU - http://www.nvu.com/index.html
Selida - http://www.amaryllis.8m.com/
Trellian webPAGE - http://webpage.vendercom.com/
TSW WebCoder - http://www.tsware.net/
WYSIWYG Web Builder -

http://www.pablosoftwaresolutions.com/html...eb_builder.html

Image viewers:
Ahaview - http://www.aha-soft.com/ahaview/ahaviewfree.exe
FastStone Image Viewer - http://www.faststone.org/FSViewerDetail.htm
Irfanview - http://www.irfanview.com/
Picasa - http://www.picasa.com/content/download.php
Shell Extension - http://www.firmtools.com/products/shellextension/
SlowView - http://www.slowview.at/
XNView - http://www.xnview.com/

Instant Messengers:
AMSN - http://amsn.sourceforge.net/index.php
Gaim - http://gaim.sourceforge.net/
Google Talk - http://www.google.com/talk/
IM2: http://www.im2.com
Mercury Messenger - http://www.mercury.to/
Miranda IM - http://www.miranda-im.org/
Pandion - http://www.pandion.be/
PSI - http://psi.affinix.com/
qip - http://www.qip.ru/
Skype - http://www.skype.com/
SIM - http://sim-icq.sourceforge.net/
TerraIM - http://terraim.sourceforge.net/
Trillian Basic - http://trillian.cc/downloads

Internet Explorer Front-Ends:
AM Browser - http://www.ambrowser.com/
AOL Browser - http://beta.aol.com/projects/aolbrowser/
Avantbrowser - http://www.avantbrowser.com/
Maxthon - http://www.maxthon.com/
SlimBrowser - http://www.flashpeak.com/sbrowser/sbrowser.htm

IRC Clients:
BersIRC - http://www.bersirc.com/
BitchX - http://bitchx.org/download.php
HydraIRC - http://www.hydrairc.com/
NodeIRC - http://node.sourceforge.net/
TinyIRC - http://www.tinyirc.net/
XChat - http://www.silverex.org/news/

Mail programs:
Foxmail - http://fox.foxmail.com.cn/english/
i.Scribe - http://www.memecode.com/
Mahogany Mail - http://mahogany.sourceforge.net/
Pegasus Mail - http://www.pmail.com/
PopTray - http://www.poptray.org/
Thunderbird - http://www.mozilla.org/projects/thunderbird/

Anti-spam programs:
K9 - http://www.keir.net/k9.html
MailWasher- http://www.mailwasher.net/
POPFile - http://popfile.sourceforge.net/
SpamBayes - http://spambayes.sourceforge.net/
SpamPal - http://www.spampal.org/

Network Tools:
CMDTime NTP Utility - http://www.softshape.com/download/
Ethereal Protocol Analyzer - http://www.ethereal.com/
Gencontrol - http://www.gensortium.com/products/gencontrol.html
hamachi - http://www.hamachi.cc/
NetMeter - http://readerror.gmxhome.de/
NetProfiles - http://netprofiles.danielmilner.com/
NMap - http://www.insecure.org/nmap/
Ntop - http://www.ntop.org
PingPlotter - http://www.pingplotter.com
PuTTY - http://www.chiark.greenend.org.uk/~sgtatham/putty
RAS Graph & Stats - http://forum.flashfxp.com/showthread.php?s=&threadid=2400
RealVNC - http://www.realvnc.com/
TightVNC - http://www.tightvnc.org/
Ultr@VNC - http://ultravnc.sourceforge.net/
WinSCP - http://www.winscp.com/

Office Suite:
602PC Suite free edition - http://www.software602.com/products/pcs/download.html
AbiWord - http://www.abiword.com/
OpenOffice.org - http://www.openoffice.org/
qjot - http://www.xtort.net/xtort/qjot.php
TreeDBNotes - http://www.softviewer.com/treedbnotes/free_index.htm

Partition Managers:
Partition Resizer - http://zeleps.com/
Ranish Partition Manager - http://www.ranish.com/part/
SwissKnife - http://www.compuapps.com/Download/swissknife/swissknife.htm
TestDisk - http://www.cgsecurity.org/index.html?testdisk.html

PDF Utilities:
CutePDF - http://www.cutepdf.com/Products/CutePDF/writer.asp
Foxit PDF Reader - http://www.foxitsoftware.com/pdf/rd_intro.php
Free PDF - http://www.webxd.com/zipguy/frpdfdl.htm
Ghostscript/GSView - http://www.ghostscript.com/
PDF 995 - http://www.pdf995.com/
PDFCreator - http://sourceforge.net/projects/pdfcreator/
PrimoPDF - http://www.primopdf.com/

Photo manipulation and image design:
ArtRage - http://www.ambientdesign.com/artrage.html
Artweaver - http://www.artweaver.de/index.php?en_version
ColorPic - http://www.iconico.com/colorpic/
Deep Paint - http://www.download.com/Deep-Paint/3000-21...tml?tag=lst-2-7
Delineate - http://delineate.sourceforge.net/
iIco - http://www.mintrasystems.com/world/product...duct.php?p=iico
Inkscape - http://www.inkscape.org/
JPEGCrops - http://ekot.dk/programmer/JPEGCrops/
Paint .NET - http://www.eecs.wsu.edu/paint.net/
Pixia - http://www.ab.wakwak.com/~knight/
Pixie - http://www.nattyware.com/pixie.html
PhotoFiltre - http://www.photofiltre.com/
Sodipodi - http://www.sodipodi.com/
The Gimp - http://www.gimp.org/
Tuxpaint - http://www.newbreedsoftware.com/tuxpaint
Wax - http://www.debugmode.com/wax/
Wink - http://www.debugmode.com/wink/
WinMorph - http://www.debugmode.com/winmorph/

Programming:
ActivePerl - http://www.activestate.com/Products/ActivePerl
BlueJ - http://www.bluej.org/download/download.html
Crimson Editor - http://www.crimsoneditor.com/
Code::Blocks - http://www.codeblocks.org/
Context - http://www.context.cx/
Dev C++ - http://www.bloodshed.net/
Dev Pascal - http://www.bloodshed.net/
Digital Mars C++ - http://www.digitalmars.com/download/freecompiler.html
Eclipse - http://www.eclipse.org/
ExamDiff - http://www.prestosoft.com/ps.asp?page=edp_examdiff
Freepascal - http://www.freepascal.org/
JCreator LE - http://www.jcreator.com/download.htm
jEdit - http://www.jedit.org/
Metapad - http://www.liquidninja.com/metapad/
Notepad++ - http://notepad-plus.sourceforge.net/uk/site.htm
Notepad2 - http://www.flos-freeware.ch/
Open Watcom - http://www.openwatcom.org/
Pelles C - http://www.smorgasbordet.com/pellesc/index.htm
PHP Hypertext Parser - http://www.php.net/
Programmer's Notepad - http://www.pnotepad.org/
PSPad - http://www.pspad.com/
Python - http://www.python.org/
Ruby - http://www.dm4lab.to/~usa/ruby/index_en.html
SharpDevelop - http://www.icsharpcode.net/opensource/sd/
WebMatrix - http://www.asp.net/webmatrix/
WinMerge - http://winmerge.org/

Pop-up Blockers:
Google Toolbar - http://www.google.com/
NoAds - http://www.southbaypc.com/NoAds/
PopUp Stopper - http://www.panicware.com/product_psfree.html
Privoxy - http://www.privoxy.org/
Proxomitron - http://www.proxomitron.info/

RSS Readers:
Ablion - http://www.fileheaven.com/Abilon/download/14059.htm
BlogExpress - http://www.usablelabs.com/productBlogExpress.html
Feedreader - http://www.feedreader.com/
FireANT - http://www.antisnottv.net/
GreatNews - http://www.curiostudio.com/
RssBandit - http://www.rssbandit.org/
RSSOwl - http://www.rssowl.org/
RssReader - http://www.rssreader.com/
RSS Xpress - http://rssxpress.free.fr/en/?page=&idNews=
SharpReader - http://www.sharpreader.net/

System Information and monitoring:
AIDA32 - http://www.sofotex.com/AIDA32-download_L9326.html
ATITool - http://www.techpowerup.com/atitool/
ATI Tray Tools - http://www.guru3d.com/article/atitraytools/189/
CPU-Z - http://www.cpuid.com/cpuz.php
Everest - http://www.lavalys.com/products/overview.p...ang=en&pageid=1
Gkrellm - http://bill.nalens.com/
K1 - http://clanpkm.free.fr/K1/?lng=en
Motherboard monitor - http://www.majorgeeks.com/download.php?det=311
SpeedFan - http://www.almico.com/speedfan.php
Sysmetrix - http://www.xymantix.com
WCPUID - http://hp.vector.co.jp/authors/VA002374/src/download.html
WhatsRunning - http://www.whatsrunning.net/whatsrunning/main.aspx

Video codecs:
DivX Codec - http://www.divx.com/divx/?src=toptab_divx_from_/index.php
FFDSHOW - http://sourceforge.net/projects/ffdshow
XviD - http://www.xvid.org/

Video players :
AC3Filter - http://sourceforge.net/projects/ac3filter
BsPlayer - http://www.bsplayer.org/
Crystal Player - http://www.crystalplayer.com/index.php?page=downloads
Cygwin MPlayer - http://armory.nicewarrior.org/projects/cygmp/
DivX Player - http://www.divx.com/
MaximusDVD - http://www.maximusdvd.com/
Media Player Classic - http://sourceforge.net/project/showfiles.php?group_id=82303
VideoLan - http://www.videolan.org/

Video tools:
DScaler - http://deinterlace.sourceforge.net/
FlasKMPEG - http://www.flaskmpeg.net
GSpot - http://www.headbands.com/gspot/
TMPGEnc - http://www.tmpgenc.net/e_main.html
VCDEasy - http://www.aplusfreeware.com/categories/Au...eo/VCDEasy.html
VirtualDub - http://www.virtualdub.org/
VirtualDubMod - http://sourceforge.net/project/showfiles.php?group_id=65889
Zwei-Stein Video Editor - http://www.thugsatbay.com/software/index.html

Web browsers:
Firefox - http://www.getfirefox.com
K-Meleon - http://kmeleon.sourceforge.net/
Mozilla - http://www.mozilla.org/
Netscape - http://channels.netscape.com/ns/browsers/default.jsp
Opera - http://www.opera.com

Web servers:
Abyss - http://abyss.sourceforge.net/
Apache - http://httpd.apache.org/
Apache2Triad - http://apache2triad.net/
HTTP File Server - http://www.rejetto.com/sw/
Sambar - http://www.sambar.com/
Savant - http://savant.sourceforge.net/
SimpleServer:WWW - http://www.analogx.com/contents/download/network/sswww.htm
Smart Cache - http://scache.sourceforge.net/
TinyWeb - http://www.ritlabs.com/tinyweb/index.html
Xitami - http://www.xitami.com/

Webcam Software:
booruWebCam - http://www.booru.net/
Dorgem - http://dorgem.sourceforge.net/
grabMotion - http://www.grabmotion.com/
Pryme - http://www.hilo.dk/pryme/

Checksum Utilities:
fsum - http://www.slavasoft.com/fsum/
HashCalc - http://www.slavasoft.com/hashcalc/
hksfv - http://www.big-o-software.com/products/hksfv/
ICEECC - http://www.ice-graphics.com/ICEECC/IndexE.html
md5sum - http://www.etree.org/md5com.html
md5summer - http://www.md5summer.org/
MooSFV - http://www.ubercow.com/moosfv/
QuickPar - http://www.quickpar.org.uk/
QuickSFV - http://www.geocities.com/SiliconValley/Mouse/4668/

General Utilities And Other Applications:
AdShield - http://www.lossepladsen.dk/all4you/TheLostWorld/AdShield.php
AnalogX - http://www.analogx.com/
AppRocket - http://www.candylabs.com/approcket/
AutoIt - http://www.hiddensoft.com/autoit3/
BISS - http://www.bluetack.co.uk/index.php
Celestia - http://www.shatters.net/celestia
CCleaner - http://www.ccleaner.com
ClipX - http://bluemars.org/clipx/
Contact - http://www.isaacboy.com/contact.htm
Cygwin - http://www.cygwin.com
Dir2HTML - http://www.pc-tools.net/win32/dir2html/
Dirkey - http://www.protonfx.com/dirkey/
EasyCleaner - http://personal.inet.fi/business/toniarts/ecleane.htm
EditPad Lite - http://www.editpadpro.com/editpadlite.html
EssentialPIM - http://www.essentialpim.com/
Excessive-software - http://www.excessive-software.eu.tt/
File Transfer Expert - http://www.digital-digest.com/dvd/download...re_fte_105.html
Folder Size Extension - http://foldersize.sourceforge.net/
Hamsin Clipboard - http://www.iisr-cnc.com/hamsin/
HTTrack - http://www.httrack.com/
Inno Setup - http://www.jrsoftware.org/isinfo.php
KeyNote - http://www.tranglos.com/free/keynote.html
Language Identifier - http://www.languageidentifier.com/
Link Checker - http://www.relsoftware.com/rlc/
Memtest-86 - http://www.memtest86.com
Money Manager - http://www.thezeal.com/software/manager/default.asp
Multi Install - http://multiinstall.sourceforge.net/
MWSnap - http://www.mirekw.com/winfreeware/mwsnap.html
NetTime - http://nettime.sourceforge.net
Nullsoft Installer - http://www.nullsoft.com/free/nsis
Open Subfolder - http://www.bubblepop.com/opensubfolder/index.html
Peerguardian - http://www.methlabs.org/
Process Explorer (aka ProcessXP) -

http://www.sysinternals.com/ntw2k/freeware/procexp.shtml
png2ico - http://winterdrache.de/freeware/png2ico
RegSeeker - http://www.hoverdesk.net/freeware.htm
Restoration - http://www3.telus.net/mikebike/RESTORATION.html
Startup Control Panel - http://www.mlin.net/StartupCPL.shtml
Stickies - http://finiteloop.org/~btaylor/software/stickies/
StrokeIt - http://www.tcbmi.com/strokeit/
Syncback - http://www.2brightsparks.com/freeware/freeware-hub.html
Sysinternals - http://www.sysinternals.com/
Toolbox - http://www.sil.org/computing/toolbox/
TreeSize - http://www.jam-software.com/freeware/index.shtml
TuneXP - http://www.driverheaven.net/dforce/showdoc.php?doc=txp_about
Turbo Pad - http://turbopad.sourceforge.net/
Unlocker - http://ccollomb.free.fr/unlocker/
URLSnooper - http://www.donationcoder.com/Software/Mous...oper/index.html
Vim - http://vim.sourceforge.net
WeathAlert - http://www.ic.sunysb.edu/stu/msowul/?page=weathermain
WordWeb - http://wordweb.info/free/
XP AntiSpy - http://www.xpantispy.org/
YourDir - http://www.primeoption.com.au/FreewareYourDir.htm
18 janvier

Obstacle detector using webcam and laser pointer

http://ashishrd.blogspot.com/2006/11/obstacle-detector-using-webcam-and.html

相当有创意!! 这么不起眼的东西都利用上了,你说还有什么做不了吧:)



Hi everyone! I'm back with another webcam project, as usual. :) I've been having a lot of fun making cool things using my webcam for the last couple of months. This time, I've created an obstacle detector which uses a cheap laser pointer and a webcam to determine how far an object is from the camera. I worked with my cousins, Anuj Karpatne and Prateek Raj on this project.

There are all sorts of range finding devices available in the market like ultrasonic and infrared range finders. All these devices work well, but where portability and cost are of primary concern, this technique could be more desirable.

So, here's how it works. A laser pointer is fixed below the camera at a known distance (as you can see in the picture above). The pointer is kept parallel to the optical axis of the camera. Then, a laser-beam is projected on an obstacle which is supposedly in the camera's field of vision. The scene, along with laser dot is captured by the camera. Then, the image is scanned for the brightest pixels (we are assuming that the laser dot is the brightest point in the scene). Then, we calculate how far along the y-axis the laser dot is from the center of the image. The farther from the center, the closer we are to the obstacle. So, the distance from the object (D) is inversely proportional to the number of pixels from the center of the image (y):

D = hY/2y * cot (A/2)
Where the constants used are:
h = the actual distance between the laser pointer and the optical axis of the camera.
Y = total number of pixels along the y-axis
A = angle of view of the camera

 
As these values are constants, we obtain,
D α 1/y
Or, D = k/y (where k is the proportionality constant for all the constants used above)

We used an experimental approach to determine the value of k. We wrote a simple program in C# to do the entire image processing work and for finding the position of the laser dot in the scene. We calibrated the apparatus by placing an obstacle at known distances from the center of the camera and noting down the number of pixels the laser dot is from the center of the image along the y-axis. After taking several readings, we obtained the value of k. By using this value, we were actually able to determine how far an obstacle is, from the camera.
Well, we had a lot of fun working on this project! I hope you find it interesting. I'd love to hear your comments and ideas. :)

floppy stepper's controlling

IBM-PC Parallel Printer Port to Tandon TM100 Disk Drive Logic PCB
IBM-PC Parallel Printer Port to Tandon TM100 Disk Drive Logic PCB


             Cable Connection diagram

              PC    25    34    Line
              ==    ==    ==    ====
          Ground    18 -- 19    Ground
          Data 0     2 -> 20    Step (NSTEP)

          Ground    19 -- 17    Ground
          Data 1     3 -> 18    Direction

          Ground    20 -- 15    Ground
          Data 3     5 -> 16    Drive-Motor Enable (NMOTORON)

          Ground    21 --  9    Ground
          Data 4     6 -> 10    Select 0 (NDS0)
          Ground    21 -- 11    Ground
          Data 5     7 -  12    Select 1 (NDS1)
          Ground    22 -- 13    Ground
          Data 6     8 -> 14    Select 2 (NDS2)
          Ground    22 --  5    Ground
          Data 7     9 ->  6    Select 3 (NDS3)

          Ground    25 -- 25    Ground
        Status 4    13 <- 26    Track 00 (NTRK00)

          Ground    24 -- 27    Ground
        Status 5    12 <- 28    Write Protect (NWRITEPROTECT)

          Ground    23 --  7    Ground
        Status 7    11 <-  8    Index (NINDEX/SECTOR)

This translates to -

             IBM-PC Parallel Printer Port Registers

               7   6   5   4   3   2   1   0   I/O Port
             +---+---+---+---+---+---+---+---+
        Data | x | x | x | x | x | x | x | x | Base = 278/378/3BC Hex
             +---+---+---+---+---+---+---+---+
               N   N   N   N   N       D   N
               D   D   D   D   M       I   S
               S   S   S   S   O       R   T
               3   2   1   0   T       E   E
                               O       C   P
                               R       T
                               O       I
                               N       O
                                       N

             +---+---+---+---+---+---+---+---+
      Status |~x | x | x | x | x | - | - | - | Base+1
             +---+---+---+---+---+---+---+---+
               I       N   N
               N       W   T
               D       R   R
               E       I   K
               X       T   0
               /       E   0
               S       P
               E       R
               C       O
               T       T
               O       E
               R       C
                       T



How to use disk drive stepper-motors

Written by Tomi Engdahl

Why to use components from old disk drives

You might ask why use components from old disk drives. The answer is that
disk-drives have many expensive components and you can get old bad
disk-drives freely or very cheaply. They are good sources for following
components with not much effort:
1. Small stepper motors and controllers
2. Optical sensors or microswitches
3. Accurately speed-regulated DC-motor
4. Useful accurate mechanic parts for small projects

Disk drive interface description

Power connector pinout

Normal floppy disk drives use normally +12V and +5V power supplies. They can
load each power-line with current of less than 100 mA up to even more than 1
A. That depens on the model of the disk drive. Here is the power connector
pinout when you look the CEE-type connector on the drive back:

   _______
  /       \
| 1 2 3 4 |
|_________|

Pin Function

1   +5 V
2   Ground
3   Ground
4   +12 V

Shugart disk-drive interface

The following table shows signals of the most commonly used floppy disk
drive computers. The pinout is somehow de-facto and the use of certain
signals may be different in various systems. This interface is used in both
PC and Amiga computers, but the use and handling of various signals are
different. Anyway those signals are always TTL-level signals.

Pin Function

1   GND
2   High density select
4   Head load / In Use, Eject
6   Drive select 3
8   Index pulse +
10  Drive select 0 / Motor on A
12  Drive select 1 / Drive select B
14  Drive select 2 / Drive select A
16  Motor On / Motor on B
18  Direction
20  Step
22  Write data
24  Write enable
26  Track Zero +
28  Write Protect +
30  Read Data +
32  Select Head
34  Disk Changed + / Ready +
3-33 Odd pins are GND

PC/AT disk drive signals

PC/AT computers use those disk drive signals in the following way. In this
system the drives are jumpered to be type A or B depending the situation.
Usually PC and AT systems use a special cable which changes singal positions
of drive A and B motor on and select signals between disk drive connectors.
This makes it possible to have both drives to be jumpered as drive A and one
drive works in this way as A and another as B.

Pin Function         Direction

1   GND
2   High density sel ???
8   Index pulse      from drive
10  Motor on A       to drive
12  Drive select B   to drive
14  Drive select A   to drive
16  Motor On B       to drive
18  Direction        to drive
20  Step pulse       to drive
22  Write data       to drive
24  Write enable     to drive
26  Track Zero       from drive
28  Write Protect    from drive
30  Read Data        from drive
32  Select Head      to drive
34  Disk Changed     ???
3-33 Odd pins are GND

How to use disk-drive stepper motor

What kind of stepper motors there are in disk drives ?

Floppy disk drives use stepper motors for controlling read/write head
position. Even early hard disk drives also used steppers, but nowadays hard
drives have replaced the teppers with voice-coil servo motors.

The stepper motors used in 5 1/4" floppy disk drives usually move the
read/drite head using wheel and spring mechanism which works quite well.
Motors used in those drives are usually 200 or 400 steps per revolution
models. 200 steps per revolution models are are used in standard density (40
track per disk) drives. High-density floppy-drives have 80 tracks and they
use 400 steps per revolution stepper motors. Both of those motors are very
useful for your own projects. Stepper motors usually use +12V power, but
some new low-power drives use +5V power source for driving steppers.

Small 3 1/2" floppy disk drives usually use screw-type mechanics, where
motor rotates the screw-like axle, which moves the read/write head. This
type of mechanics makes it possible to make smaller drives and depends on th
screw characteristics the motor can have bigger steps. Those motors are
usually driven from +5V source, because many modern 3 1/2" drives use only
+5V power-supply.

How to use those motors in your own projects

Stepper motors are quite useful for robotics, plotter and control projects.
Stepper motors are accurate way of making desired mechanical movements.
Stepper-motors are not very powerful or fast (about 300 steps/sec).

You can build your own controller or use ready-made controllers with them.
You just have to identify the type, wiring and operating voltage of the
stepper motors to be able to use them. Unfortunately stepper motors are not
the easiest motor types to control and ready-made controllers are usually
quite expensive. If you want to make your own controller for a stepper motor
takenfrom a disk drive then take a look at
http://www.doc.ic.ac.uk/~ih/doc/stepper/.

When you have just taken the motor out of the disk drive, you might have
thought that there must be also a stepper controller inside the disk drive
electronics. You are right that there is a controller, which is capable to
drive the motor. It is quite easy to send signals to that controller using
PC parallel port and small program.

How to use controller in disk-drive electronics

The controller in the disk drive electronics can be succesfully used in the
following way:
1. The electronics needs +5V to operate and usually +12V for motors
2. If the disk-drive has function to automatically got to track zero on
powerup, you have to disable that option unless you want to use that option
and the track zero detectio in your project. This option can be found from
modern disk-drives, but old drives does not have it. Sometimes there is
jumper for enabling and disabling this option.
3. Make sure that the electronics does not need to detect the disk in the
drive to be able to move the stepper motor. This sensor can be easily easily
make to give electronics the info that the disk is in drive. You only have
to put some tape or glue to the sensor to make it think that disk is always
in the drive. You can also accomplish this by cutting one wire or adding one
extra wire to bypass the sensor.
4. Use drive select signal to select the drive electronics. Then use
direction and step pulse signals to control the steper motor.

Disk drive interface stepper motor control signals

The following signals are used to control the disk drive stepper motor
controller circuit. First you have to select the drive by connecting the
correct drive select signal to ground. Then you use the direction signal to
select the direction which you want the motor to take step. The stepping is
controlled using step pulse signal, which is normally high. One low going
pulse at step pulse line makes the motor to take the one step. Make sure
that pulse signals are longer than 1 microsecond and you are not sending
then faster than the motor can take steps.

Pin Function         Direction

1   GND
12  Drive select B   to drive
14  Drive select A   to drive
18  Direction        to drive
20  Step pulse       to drive
26  Track Zero       from drive
3-33 Odd pins are GND

PC parallel port to stepper-motor interface

This is a simple example how to control disk drive stepper motor using PC
parallel port. I expect that the drive is jumpered to be as drive A. The
parallel port pin numbers are according the 25 pin connector numbering which
is in the the back of your PC.

Connect parallel port pin 20 (ground) to disk drive connector pins 17 and 19
(ground). Connect disk drive connector pin 14 (drive select A) to disk drive
connector 17 (ground). Connect parallel port pin 2 (D0) to disk drive
connector pin 20 (step pulse). Connect parallel port pin 3 (D1) to disk
drive connector pin 18 (direction).

Parallel port (25 pin)      Disk drive (34 pin)

   2 ------------------------- 20
   3 ------------------------- 18
                          +--- 14
  20 ---------------------+--- 17
                          +----19

In this way you have made a cable with which you can easily control the
stepper motor using parallel port datapins D0 and D1. Those pins can be
easily controlled in your software by directly writing to parallel port
hardware. You can't use the DOS, BIOS or other operating system functions,
because this interface does not generate the handshaking signals those
routines need.

Directly controlling the parallel port is very easy. First you have to read
the I/O address from the BIOS data area. LPT1 I/O address is the 16-bit word
which can be found from memory address 0008h at segment 0040h. Then you
simply write the data you want send to parallel port data pins to this I/O
address. The writing can be easily done using following commands in
different languages: out in assembler, outp in borland c and port in pascal.
You can find more programming details from my Parallel port interfacing made
easy article. You can also try Floppystepper C++ source code fro DOS from
Circuit Cookbook Archive.

Using stepper motor as constantly rotatating motor

Stepper motors can be also used as contantly rataing moros in applications
where a slower speed that is easily avaialble using direct drive DC motor is
needed or the speed has to be very accurately adjusted.

Disk drive stepper motor can be used as freely rotating motr quite easily by
using the stepper motoro controller from the disk drive. You just need to
activate drive select line and then select the rotation direction using
direction pin. Then all you need is to send constant clock signal to the
step pin (or adjustable if you want). Suitable oscillator can be quite
easily made using for example 555 timer IC or from fre TTL logic gates.

Using the stepper motors without the disk drive electronics

Disk drive stepper motors consist two coils which move the motor to the
desired direction when a current is applied to those coils in correct order.
The following signals will make the stpper motor to run to one direction. To
get the motor to run to other direction you must invert the polarity of the
signals of one of the coils (two phase wires).

Coil 1 signals

                ..........           .......
Phase 1   .....|          |.........|

Phase 2   .....            .........
               |..........|         |.......

Coil 2 signals

            ........          .........
Phase 3   .|        |........|         |....
          .          ........           ....
Phase 4    |........|        |.........|

The signals can be presented also in binary format. The common sequence (1
means current flowing, 0 means open circuit):

Phase1  00110011
Phase2  11001100
Phase3  01100110
Phase4  10011001

Douglas W. Jones has put a a project on the web how to control stepper
motors using printer port and some simple electronics under name A Worked
Stepping Motor Example. It a part of Control of Stepping Motors tutorial.

How to use disk drive rotating motor

Disk drive motors are quite nice motors quite accurate speed control system.
Disk drive motors rotate the disk at 300 or 360 rpm speed. Standard drives
use 300 rpm, but high-density drives use 360 rmp or selectable speed 300/360
rpm motors.

Motor control signals in disk drive interface

You can make the disk drive motor to spin when you enable both drive select
and motor on signals by pulling those signals to low logical state. This can
be easily done by connecting both signals to signal ground.

The high-density signal might have something to do with selecting the speed
of the motor in disk drives with two-speed (300/360 rmp) motors. I have not
needed and figured out this yet.

Pin Function         Direction

1   GND
2   High density sel ???
10  Motor on A       to drive
12  Drive select B   to drive
14  Drive select A   to drive
16  Motor On B       to drive
3-33 Odd pins are GND

Other useful components from disk drives

Disk drives are also quite nice source for other components also. When you
take the motors out of the disk drive, you can easy take other components
also. Usually there are optical sensors or microswiches in disk drive to
sense the write protect tab. Those components are useful sensors in you
stepper-motor controlled robotics circuits. Many direct drive rotating
motors use hall-sensors for sensing rotating speed. This can be quite useful
component if you can figure out how it is connected.

How about using hard disk stepper motors ?

Many older hard disk also had stepper motors for controlling read/drite head
motion. Many modern disk drive drive motors use voice-coil control systems,
which makes another story.

In my experiments I have used ST506/412 interface hard disk drives. Those
are the original IBM PC/AT hard disks (usually called MFM disks) which
needed a controlling card. The modern ATA/IDE drives are basically the same
drives, but the controller electronics is integrated to disk drive
electronics, which makes them more complicated and harder to control.

Basically hard disk drive has same basic elements as the floppy disk drive:
read/write-head, head moving motor, disk rotation motor, sensors and
controller electronics. You can use those differents componens at their own
or use the controlling electronics provided by the hard disk drive
electronics. Stepper motors are same types as used in floppy drives, though
the number of steps/revolution might be higher. The drive rotating motor is
same type of drushless DC motor as used in modern floppy drives, but those
run at much higher speed (about 3000-3600 rmp).

ST506/412 hard disk interface

ST506/412 hard disk interface is something like a modified floppy disk drive
interface. The physical interface in ST506/412 consists of two connectors:
34 pin control connector and 20 pin data connector. The control connector
carried all disk drive control information and data connector carries the
data. The controller cable goes to two hard drive control interfaces (up to
four can be supported), but each hard disk drive has it's own data cable.

Control connector pinout

All control connector signals are TLL level signals. They are active when
set to low state (0 V).

Pin Function               Direction

2   Reduced write current  to drive
4   Head select 2^2        to drive
6   Write gate             to drive
8   Seek complete          from drive
10  Track 0                from drive
12  Write fault            from drive
14  Head select 2^0        to drive
16  reserved
18  head select 2^1        to drive
20  Index                  from drive
22  Ready                  from drive
24  Step                   to drive
26  Drive select 1         to drive
28  Drive select 2         to drive
30  Drive select 3         to drive
32  Drive select 4         to drive
34  Direction in           to drive

Pins 1-33 all odd pins are ground

Data connector

Pin Function               Direction

1   Drive selected         from drive
3   reserved
4   reserved
7   reserved
9   reserved
10  reserved
13  +MFM Write data        to drive
14  -MFM Write data        to drive
17  +MFM Read data         from drive
18  -MFM Read data         from drive

Pins 2,4,6,8,11,12,15,16,19 and 20 are ground pins.

Using hard disk drive head actuators

Originally, head positioning was controlled by a stepper motor that rotated
in either direction by reacting to stepper pulses and moving the head
assembly back and forth by means of a "rack and pinion" or by spooling and
unspooling a band attached to the actuator arms. Each pulse moved the
assembly over the surface in predefined steps or detents. Each step
represented a track location and data was expected to be under the head.

Stepper motor controlled head actuators are not suitable for current drive
densities and is prone to alignment problems caused by friction, wear and
tear, heat deformation, and lack of feedback information needed for
correcting positioning error. Nowadays the hard disks use voice coil
actuators which are harder to use by experimenters.

I have made some experiments of using also hard disk drive stepper motor and
controller electronics in my own projects. The stepper motor is easy to
connect same type of step and ditection signals as disk drives. You can get
more information from disk drive chapters. The only difference to disk drive
system is that the disk drive electronics can buffer the movement signals,
so that the controller stores the movement pulses and executes the muvement
after the controller has received the last movement pulse.

Stepper motor control pins in ST506/412 interface

The following signals from control connector are needed for using stepper
motor controller in hard disk drive electronics. Most important signals for
stepper motor control are Direction and Step signals. The direction signals
works so that low logic level moves the read/write head inward (towards the
center of the disk) of the disk and high logic level moves the head outward.
Step signal is active low pulse signal. Drive select signal must be
activated (pulled low) and Write gate must be deactivated (pulled hight) to
make the drive to take the stepper motor control signals. Seek complete,
Ready and Track 0 provide some extra status information.

Pin Function               Direction

6   Write gate             to drive
8   Seek complete          from drive
10  Track 0                from drive
22  Ready                  from drive
24  Step                   to drive
26  Drive select 1         to drive
28  Drive select 2         to drive
30  Drive select 3         to drive
32  Drive select 4         to drive
34  Direction              to drive
1-33 all odd pins are ground

Procedure to control hard disk stepper motor using ST506/412 interface

I have used the following procedure for controlling hard disk drive stepper
motors (taken from DP8466 Design Guide):

1. Deactivate the WRITE GATE line (pin 6)
2. Activate the DRIVE SELECT line (pin 26,28,30 or 32)
3. Wait for READY and SEEK COMPLETE signal to be true.
4. Select the direction with DIRECTION signal. (pin 34)
5. Send the movement pulses using STEP line. (pin 24)

Experimenting with voice coil head actuators

Voice coil actuator controls the movement of a coil toward or away from a
permanent magnet based upon the amount of current flowing through it. Voice
coil actuators used in hard disk systems have an acceleration to current
transfer function.

The armatures are attached to this coil and move in and out over the surface
with it. Servo controlled voicecoil actuator a very precise method, but also
very sensitive. Any variation in the current can cause the head assembly to
change position and there are no pre-defined positions. Inherently this is
an analog system, with the exact amount of movement controlled by the exact
amount of current applied.

The actual position of the coil is typically determined by servo (or
indexing) information, which is written to the drive by the manufacturer.
Precise location of the data track (of which there are 6000 to 10000 per
inch) depends on an "embedded servo" which is a special pattern written on
the disk at the time of manufacture. The result is that the track is divided
into servo fields, id fields , and data fields. Location is adjusted to
different tracks by reading and reacting to these control signals. When the
servo informatioin is read, a Position Error Signal (POS) is generated,
which indicates how far from track center you are. A complex feedback system
converts this error to a current into the voice coil, and the head moves
back toward track center.

Because of this construction the hard disk drive must be in quite well
working condition so that the voice coil head controllin mechanism would
work. Voice coil positioners always need feedback, and carefully designed
loop filters. If the disk drive sort of works and has an interface you know,
then you can try the controlling as you would control the disk drive stepper
motor (easy with ST506/412 interface).

Playing with damaged servo mechanism

If the hard disk is so damaged that the controlling mechanism does not work
anymore, then it is very hard to accurately control the voice coil servo
anymore. But there is one experiment you can make with voice coils.

You can control a voice coil using a normal audio amplifier and a music
source. First find the wires going to the voice coil. The check the
resistance of the voice. If it is 4 ohms or more, then you can try to drive
it directly using an audio amplifier. Replace the speaker with the voice
coil and start to play music. The voice coil will move back and forth and
you might even hear some sound form the music. Be careful with the
experimenting, because the voice coil can be burhed down easily with
excessive power put to voice coil. And remeber also that you can damage your
amplifier if make mistakes with the connections.

You can try to convert the voice coil to sort of current to position
transfer function by adding some mechanism which tries to center the
actuator whn no current is applied (you can try springs or small pices of
soft rubber).

Spindle motors

Most drives have several platters that are separated by disk spacers and
clamped to a rotating spindle that turns the platters in unison. A direct
drive, brushless DC spindle motor is built into the spindle or mounted
directly below it. The spindle and the platters, are rotated at a constant
speed, usually 3,600 RPM, though newer models have increased that to 4800,
5400, or 7,200

The spindle motor receives control signals through a closed loop feedback
system that stabilizes to a constant rotation speed. Control signals come
from information written onto the surface(s) during manufactur. Older drives
have used magnetic hall or coil type sensors for sensing the rotating speed.

Hard disk motors typically start rotating then the power is applied to the
hard disk. First they accelerate the spindle to the full speed. If the servo
controlling can't work properly or something other goes wrong in the hard
disk they will typically stop rotating the spindle.

Where I got all this information

I have got most of this information in this documents from numerous some
articles usenet newsgroups alt.comp.hardware.homebuilt and sci.electronics.
I have also used some reference books to check some facts and get more
detailed information. The ideas how to use those motors in your own projects
is my ideas. I have used parts from old disk drives in many of my
electronics projects (most have been only some simple experiments).

More

If you are looking for ready made source code for disk drive stepper motor
controlling, it is a good idea to check Interfacing a Junk Disk 5-1/4 inch
Drive with a Parallel Port paper by Towanda L. Malone. That paper is
available at http://www.eng.morgan.edu/~malone/dskdrv/dskdrv.html.

Reference book list

   * Thom Hogan, the Programmer's PC Sourcebook, Microsoft Press, 1988
   * DP8466 Disk Data Controller Design Guide / User's Manual, National
     Semiconductor Corp, 1985
   * Tandon 386 Engineer Quick Reference 5b
   * -----------------------------------------------------------------------
     Tomi Engdahl



Software to control Motors connected via a Disk Drive
Logic PCB to an IBM-PC Parallel Printer Port



program stepit;

uses crt;

const OUTREG    = $378;
       INREG    = OUTREG + 1;

      ALLOFF    = $FF; { 1111 1111 }

      NDS0      = $E0; { 1110 0000 }
      NDS1      = $D0; { 1101 0000 }
      NDS2      = $B0; { 1011 0000 }
      NDS3      = $70; { 0111 0000 }

      NMOTOROFF = $08; { 0000 1000 }
      NMOTORON  = $00; { 0000 0000 }

      CLOCKWISE = $00; { 0000 0000 }
      ANTICLOCK = $02; { 0000 0010 }

      NOSTEP    = $01; { 0000 0001 }
      STEP      = $00; { 0000 0000 }

procedure delay;
var i: integer;
begin
    for i := 1 to 10000 do { nothing }
end;

begin { main }
    ClrScr;

    writeln;
    writeln('***** Ian''s amazing motors *****');
    writeln;

    port[OUTREG] := ALLOFF;

    write('All OFF to start with                 <press Enter to continue> ');
    readln;

    write('Stepper Motor only - Clockwise        <press Enter to continue> ');

    repeat
        port[OUTREG] := NDS1 + NMOTOROFF + CLOCKWISE + NOSTEP;
        delay;
        port[OUTREG] := NDS1 + NMOTOROFF + CLOCKWISE + STEP;
        delay
    until keypressed;
    port[OUTREG] := NDS1 + NMOTOROFF + CLOCKWISE + NOSTEP;
    readln;

    write('Stepper Motor only - AntiClockwise    <press Enter to continue> ');
    repeat
        port[OUTREG] := NDS1 + NMOTOROFF + ANTICLOCK + NOSTEP;
        delay;
        port[OUTREG] := NDS1 + NMOTOROFF + ANTICLOCK + STEP;
        delay
    until keypressed;
    port[OUTREG] := NDS1 + NMOTOROFF + ANTICLOCK + NOSTEP;
    readln;

    write('Servo Motor only                      <press Enter to continue> ');
    port[OUTREG] := NDS1 + NMOTORON + NOSTEP;
    repeat { just sit there } until keypressed; readln;
    port[OUTREG] := NDS1 + NMOTOROFF + NOSTEP;

    write('Stepper Motor and Servo Motor         <press Enter to continue> ');

    repeat
        port[OUTREG] := NDS1 + NMOTORON + CLOCKWISE + NOSTEP;
        delay;
        port[OUTREG] := NDS1 + NMOTORON + CLOCKWISE + STEP;
        delay
    until keypressed;
    port[OUTREG] := NDS1 + NMOTORON + CLOCKWISE + NOSTEP;
    readln;

    port[OUTREG] := ALLOFF;

    write('That''s all, folks !                   <press Enter to continue> ');
    readln
end.

Cross platform command line parsing and file globbing

Learn it!!

A cross-platform command line library which can parse almost any of the standard command line formats in use today. It supports character types of ASCII, MBCS and Unicode. It is designed explicitly to be portable to any platform and has been tested on Windows and Linux. It also includes a cross-platform implementation of glob() so that wildcards in command line arguments are simply expanded for use by the program. Released as open-source and free using the MIT licence.

Features

  • MIT Licence allows free use in all software (including GPL and commercial)
  • multi-platform (Windows 95/98/ME/NT/2K/XP, Linux, Unix)
  • supports all types of options:
    - switch character only (e.g. use stdin for input)
    -o short (single character)
    -long long (multiple character, single switch character)
    --longer long (multiple character, multiple switch characters)
    word special word construed as an option, no switch characters
  • supports all types of arguments for options:
    --option short/long option flag (no argument)
    --option ARG short/long option with separate required argument
    --option=ARG short/long option with combined required argument
    --option[=ARG] short/long option with combined optional argument
    -oARG short option with combined required argument
    -o[ARG] short option with combined optional argument
  • supports clumping of multiple short options (no arguments) in a string, e.g. "foo.exe -abcdef file1" <==> "foo.exe -a -b -c -d -e -f file1"
  • automatic recognition of a single slash as equivalent to a single hyphen on Windows, e.g. "/f FILE" <==> "-f FILE" .
  • file arguments can appear anywhere in the argument list: "foo.exe file1.txt -a ARG file2.txt --flag file3.txt file4.txt"
  • file arguments will be returned to the application in the same order they were supplied on the command line
  • short-circuit option matching: "--man" will match "--mandate"
  • invalid options can be handled while continuing to parse the command line
  • valid options list can be changed dynamically during command line processing, i.e. accept different options depending on an option supplied earlier in the command line.
  • implemented with only a single C++ header file
  • uses no C runtime or OS functions
  • char, wchar_t and Windows TCHAR in the same program
  • complete working examples included
  • compiles cleanly at warning level 4 (Windows/VC.NET 2003), warning level 3 (Windows/VC6) and -Wall (Linux/gcc)

Download

The current version of SimpleOpt is 2.2 (last updated 14 August 2006)
Homepage is http://code.jellycan.com/simpleopt/

Usage

The SimpleOpt class is used by following these steps:

  1. include the SimpleOpt.h header file
    #include "SimpleOpt.h"
    
  2. define an array of valid options for your program
    enum { OPT_HELP, OPT_FLAG, OPT_ARG };
    CSimpleOpt::SOption g_rgOptions[] = {
        { OPT_FLAG, _T("-a"),     SO_NONE    }, // "-a"
        { OPT_FLAG, _T("-b"),     SO_NONE    }, // "-b"
        { OPT_ARG,  _T("-f"),     SO_REQ_SEP }, // "-f ARG"
        { OPT_HELP, _T("-?"),     SO_NONE    }, // "-?"
        { OPT_HELP, _T("--help"), SO_NONE    }, // "--help"
        SO_END_OF_OPTIONS                       // END
    };
    

    Note that all options must start with a hyphen even if the slash will be accepted. This is because the slash character is automatically converted into a hyphen to test against the list of options. For example, the following line matches both "-?" and "/?" (on Windows).

        { OPT_HELP, _T("-?"),     SO_NONE    }, // "-?"
  3. instantiate a CSimpleOpt object supplying argc, argv and the option table
    CSimpleOpt args(argc, argv, g_rgOptions);
    
  4. process the arguments by calling Next() until it returns false. On each call, first check for an error by calling LastError(), then either handle the error or process the argument.
    while (args.Next()) {
        if (args.LastError() == SO_SUCCESS) {
            // handle option, using OptionId(), OptionText() and OptionArg()
        }
        else {
            // handle error, one of: SO_OPT_INVALID, SO_OPT_MULTIPLE, 
            // SO_ARG_INVALID, SO_ARG_INVALID_TYPE, SO_ARG_MISSING
        }
    }
    
  5. process all non-option arguments with File(), Files() and FileCount()
    ShowFiles(args.FileCount(), args.Files());
    

The SimpleGlob class is used by following these steps:

  1. include the SimpleGlob.h header file
  2. instantiate a CSimpleGlob object supplying flags
  3. adding file specs with Add()
  4. extract the file names using FileCount() and Files()

Examples

Basic usage of the SimpleOpt library is illustrated by the following code sample (included as basicSample.cpp). This shows simple command line processing including the use of SimpleGlob to expand wildcard filenames passed on the command line.

Collapse
#include "SimpleOpt.h"
#include "SimpleGlob.h"

// define the ID values to indentify the option
enum { OPT_HELP, OPT_FLAG, OPT_ARG };

// declare a table of CSimpleOpt::SOption structures. See the SimpleOpt.h header
// for details of each entry in this structure. In summary they are:
//  1. ID for this option. This will be returned from OptionId() during processing.
//     It may be anything >= 0 and may contain duplicates.
//  2. Option as it should be written on the command line
//  3. Type of the option. See the header file for details of all possible types.
//     The SO_REQ_SEP type means an argument is required and must be supplied
//     separately, e.g. "-f FILE"
//  4. The last entry must be SO_END_OF_OPTIONS.
//
CSimpleOpt::SOption g_rgOptions[] = {
    { OPT_FLAG, _T("-a"),     SO_NONE    }, // "-a"
    { OPT_FLAG, _T("-b"),     SO_NONE    }, // "-b"
    { OPT_ARG,  _T("-f"),     SO_REQ_SEP }, // "-f ARG"
    { OPT_HELP, _T("-?"),     SO_NONE    }, // "-?"
    { OPT_HELP, _T("--help"), SO_NONE    }, // "--help"
    SO_END_OF_OPTIONS                       // END
};

// show the usage of this program
void ShowUsage() {
    _tprintf(_T("Usage: basicSample [-a] [-b] [-f FILE] [-?] [--help] FILES\n"));
}

int _tmain(int argc, TCHAR * argv[]) {
    // declare our options parser, pass in the arguments from main
    // as well as our array of valid options.
    CSimpleOpt args(argc, argv, g_rgOptions);

    // while there are arguments left to process
    while (args.Next()) {
        if (args.LastError() == SO_SUCCESS) {
            if (args.OptionId() == OPT_HELP) {
                ShowUsage();
                return 0;
            }
            _tprintf(_T("Option, ID: %d, Text: '%s', Argument: '%s'\n"),
                args.OptionId(), args.OptionText(),
                args.OptionArg() ? args.OptionArg() : "");
        }
        else {
            _tprintf(_T("Invalid argument: %s\n"), args.OptionText());
            return 1;
        }
    }

    // process any files that were passed to us on the command line.
    // send them to the globber so that all wildcards are expanded
    // into valid filenames (e.g. *.cpp -> a.cpp, b.cpp, c.cpp, etc)
    // See the SimpleGlob.h header file for details of the flags.
    CSimpleGlob glob(SG_GLOB_NODOT|SG_GLOB_NOCHECK);
    if (SG_SUCCESS != glob.Add(args.FileCount(), args.Files())) {
        _tprintf(_T("Error while globbing files\n"));
        return 1;
    }

    // dump all of the details, the script that was passed on the
    // command line and the expanded file names
    for (int n = 0; n < glob.FileCount(); ++n) {
        _tprintf(_T("file %d: '%s'\n"), n, glob.File(n));
    }

    return 0;
}

Another included example 'fullSample.cpp' implements the following command line. This demonstrates all of the different types of arguments which can be parsed by SimpleOpt.

Collapse
Usage: fullSample [OPTIONS] [FILES]

--exact     Disallow partial matching of option names
--noslash   Disallow use of slash as an option marker on Windows
--shortarg  Permit arguments on single letter options with no equals sign
--clump     Permit single char options to be clumped as long string
--noerr     Do not generate any errors for invalid options

-a  -b  -c  -d  -flag  --flag               Flag (no arg)
-s ARG   -sep ARG  --sep ARG                Separate required arg
-cARG    -c=ARG    -com=ARG    --com=ARG    Combined required arg
-o[ARG]  -o[=ARG]  -opt[=ARG]  --opt[=ARG]  Combined optional arg
-man     -mandy    -mandate                 Shortcut matching tests
--man    --mandy   --mandate                Shortcut matching tests
open read write close zip unzip             Special words

-?  -h  -help  --help                       Output this help.

If a FILE is `-', read standard input.

A third included example 'globSample.cpp' implements the a test program to test all of the flags supported by SimpleGlob.

Short-Circuit Option Matching (SimpleOpt)

The short-circuit matching of the long option names is enabled by default. It can be disabled by passing SO_O_EXACT as one of the flags to Init(). When disabled, only exact matches are accepted for options. When enabled, matches are calculated in the following manner.

  1. an exact match takes precedence over a partial match
  2. equal partial matches returns an "SO_OPT_MULTIPLE" error
  3. a better partial match will return the matched option

For example, if the command line has the following valid options:

  • --main
  • --mainline
  • --mainlamp

The following matches would occur:

Commandline Result Why
--mai SO_OPT_MULTIPLE multiple patial matches
--main --main exact match
--mainl SO_OPT_MULTIPLE multiple patial matches
--mainli --mainline single partial match
--mainline --mainline exact match

Alternatives

This library is just one method of processing command line options. If it doesn't suit your requirements, have a look at the following alternative libraries:

MIT Licence

The licence text below is the boilerplate "MIT Licence" used from: http://www.opensource.org/licenses/mit-license.php

Copyright (c) 2006, Brodie Thiesfield

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

About brofield


Must eat, enjoy travel, thus programming since 1995. Donations of canned food gladly accepted.

Click here to view brofield's online profile.

 

A Survey of Pseudo Random Number Generators

Good porject!!

Introduction

Random numbers are a primitive element not only to cryptographers, but Computer Science in general. What is desired is a method which produces a pseudo random stream of numbers fast which are cryptographically secure. However, the goals are diametrically opposed - pseudo random sequences can be produced quickly, or can be produced strongly; but usually not quickly with properties which resist Cryptanalysis.

This article will examine two applications of Pseudo Random Number Generators: those used for Simulation and Sampling, and those used for Cryptography. Topics covered will include:

  • Usefulness of Random Numbers
  • Random Number Generator Classes
  • Types of Generators
  • Random Number Sources
  • Testing Sequences
  • Seeding
  • Key Derivation Function
  • Standard C++ rand( )
  • Non Linear Congruential Generators
  • Win32 API CryptGenRandom( )
  • Compiling and Integrating Crypto++
  • Crypto++ Random Number Generators
    • LC_RNG
    • RandomPool
    • AutoSeededRandomPool
    • AutoSeeded917RNG
  • Application Table
  • Additional CSPRNGs

Downloads

There are seven downloads available with this article. The downloads are presented at the end of the article.

Usefulness of Random Numbers

In his prolific work The Art of Computer Programming, Volume II: Seminumerical Algorithms, Donald Knuth identifies the following common situations in which one may require random numbers:

  • Simulation
  • Sampling
  • Numerical Analysis
  • Computer Programming
  • Decision Making
  • Aesthetics
  • Recreation

Simulation and sampling are self explanatory. Numerical Analysis uses random numbers to solve complicated problems. For example, a Predator/Prey model using differential equations. Four is equally obvious. Taking from Knuth on item five, Decision Making:

There are reports that many executives make their decisions by flipping a coin or by throwing darts, etc. It is also rumored that some college professors prepare their grades on such a basis. Sometimes it is important to make a completely "unbiased" decision. Randomness is also an essential part of optimal strategies in the theory of matrix games.

The human ear can distinguish between synthesized music (generated from a computer) versus that of an artist. The artist will have small deviations in rhythm which makes the music more pleasing. Additionally, a small bit of randomness makes computer generated graphics appear softer. Note the rigid structure below of the left image versus the softness of the right image. Below the right image was produced with a filter which added a small random amount noise. In Signal Processing, this is known as Anti-Aliasing.

     

No Random Noise

     

Random Noise

To further impress aesthetics upon the reader, Adobe Photoshop employs random numbers in many of its filters, including Blur, Distort, and Noise.

Adobe Photoshop Filters

Finally, recreation would include shuffling cards or rolling dice.

Random Number Generator Classes

Taking frm NIST, FIPS 140-2:

Random Number Generators fall into one of two classes: deterministic and nondeterministic. A deterministic RNG consists of an algorithm that produces a sequence of bits from an initial value called a seed. A nondeterministic RNG produces output that is dependent on some unpredictable physical source that is outside human control.

Note that layman generally refer to the nondeterministic generator as a 'true random number generator'.

Types of Random Number Generators

There are two types of deterministic random number generartors from which a programmer may choose:

  • Pseudo Random Number Generators
  • Cryptographically Secure Pseudo Random Number Generators

Pseudo Random Number Generators would include generators such as Linear Congruential Generators and Mersenne Twisters. They are generally good at quickly providing a uniformly distributed stream over the interval [0, 1). They offer little to no cryptographic security.

Cryptographically Secure Pseudo Random Number Generators have additional properties which make them suitable for use in Cryptography. Cryptographic uses would include:

  • Key Generation
  • Nonces
  • Salts
  • One Time Pads

Random Number Sources

Though NIST does not currently recognize any nondeterministic RNGs, one may use a deterministic RNG to seed a cryptographically secure pseudo random number generator. Taking from FIPS 140-2:

Until such time as an Approved nondeterministic RNG standard exists, nondeterministic RNGs approved for use in classified applications may be used for key generation or to seed Approved deterministic RNGs used in key generation

NIST provides tests which allows one to develop heuristics for determining the of the quality of the sequence from the generator inquestion. Included for download are NIST Validation Suite, FIPS 140-2, and and FIPS 186. Additionally, the reader may want to examine ANSI 9.17, Appendix C (Approved Random Number Generators for FIPS 140-2, Security Requirements for Cryptographic Modules).

Background on Tests

In a random sequence, on would expect each of the ten decimal digits to occur approximately 1/10 times. Should the radix be 2 (digits of either 0 or 1), each digit should represent approximately 50% of the sequence.

Testing involves differentiating good sources from poor choices. For example, the binary stream ...1111111110000000000... would perform ideally with a simple Frequency test, but fail at advanced tests such as Runs, Longest Runs in a Block, and Cumulative Sums. A counter intuitive point with the presented binary stream is that it is a valid sequence of random numbers. Each stream is as equally likely to occur as any other in an unbiased generator.

Testing Sequences

Below the reader will find various tests which should be used when evaluating the effectiveness of a generator. The reader should refer to NIST's Guide to the Statistical Tests and Knuth's The Art of Computer Programming for a complete description.

NIST requires a PRNG pass 16 statistical tests. The tests are listed below with a brief description.

  • Frequency - proportion of zeroes and ones for the entire sequence.
  • Frequency within a Block - determine whether the frequency of ones is an M-bit block is approximately M/2.
  • Runs - the total number of zero and one runs in the entire sequence, where a run is an uninterrupted sequence of identical bits.
  • Longest Run of Ones in a Block - determine whether the length of the longest run of ones within the tested sequence is consistent with the length of the longest run of ones that would be expected in a random sequence.
  • Random Binary Matrix Rank - check for linear dependence among fixed length substrings of the original sequence.
  • Discrete Fourier Transform (Spectral) - detect periodic features.
  • Non-overlapping (Aperiodic) Template Matching - number of occurrences of predefined target substrings.
  • Overlapping (Periodic) Template Matching - number of occurrences of predefined target substrings.
  • Maurer's Universal Statistical Test - number of bits between matching patterns. The purpose of the test is to detect whether or not the sequence can be significantly compressed without loss of information.
  • Lempel-Ziv Complexity - how far the tested sequence can be compressed. The sequence is considered to be non-random if it can be significantly compressed.
  • Linear Complexity - length of a generating feedback register.
  • Serial - frequency of each and every overlapping m-bit pattern across the entire sequence.
  • Approximate Entropy - frequency of each and every overlapping m-bit pattern.
  • Cumulative Sum - determine whether the cumulative sum of the partial sequences occurring in the tested sequence is too large or too small relative to the expected behavior of that cumulative sum for random sequences.
  • Random Excursions - determine if the number of visits to a state within a random walk exceeds what one would expect for a random sequence.
  • Random Excursions Variant - detect deviations from the expected number of occurrences of various states in the random walk.

Knuth sates a random sequence should pass 13 statistical tests. Tests which are not included in NIST's requirements are listed below.

  • Chi-Square - Classic Definition
  • Kolmogorov-Smirnov - Extends the Chi-Square test to the set of Real Numbers
  • Gap - detect gaps between a number over a range of numbers in a sequence
  • Poker (Partition) - n groups of five successive integers from the stream, observing the resulting pattern. One pair is aabcd, full house is aaabb, etc.
  • Coupon Collector's - Examines the length of the sequence required to observe all numbers in the set 0 to d-1.
  • Permutation - number of orderings of grouping the sequence into partitions
  • Collision - Used when the Chi-Squared Test exceeds a certain number of collisions
  • Birthday Spacing - Similar to the Birthday Paradox when selecting two integers in the sequence

Seeding

Cryptographically Secure RNGs share an Achilles' heel with the other pseudo random number generators - both require an starting seed as an input. Should the secret seed become compromised, an adversary can generate the entire output sequence instantly by using the same algorithm.

In 1995 David Wagner, then a graduate student at Berkeley, and fellow student Ian Goldberg cracked the random number generator used by the Netscape web browser to secure online transactions. The pair determined that Netscape was constructing its seeds using easily predicted numbers, such as the time of day.

Key Derivation Functions

Key derivation functions are used (among others) to derive keys from secret passwords or passphrases. Key derivation functions are often used in conjunction with non-secret parameters to derive a keys from a common secret value. A KDF may also be used to ensure that derived keys have other desirable properties, such as avoiding weak keys in some specific encryption systems.

Key derivation functions are also used in applications to derive keys from secret passwords or passphrases, which typically do not have the desired properties to be used directly as cryptographic keys. In such applications, it is generally recommended that the key derivation function be made deliberately slow so as to frustrate brute force attack or dictionary attack on the password or passphrase input value.

Such use may be expressed as Dk = KDF(Key, Salt, Iterations) where Dk is the derived key, KDF is the key derivation function, Key is the original key or password, Salt is a random number which acts as cryptographic salt, and Iterations refers to the number of iterations of a sub-function. The derived key is used instead of the original key or password as the key to the system. The values of the salt and the number of iterations (if it isn't fixed) are stored with the hashed password or sent as plaintext with an encrypted message.

Standard C++ rand( ) Function

The Standard C++ rand() function uses a linear congruential generator. It offers a uniformly distributed bit stream quickly when the parameters m, a, c, and X0 are appropriately chosen. The LCG offers no Cryptographic Security.

X0 is colloquially referred to as the seed, often using the system time. The generator obtains numbers by using the following recurrence relation:

Xn+1 = (aXn + c) mod m

The values chosen for the generator in Microsoft's Visual C++ environment are shown below. Note that the & 0x7FFF is performing the modular reduction.


return (((holdrand = holdrand * 214013L + 2531011L) >> 16) & 0x7fff);

Joan Boyar's PhD dissertation, Inferring Sequences Produced by a Linear Congruential Generator Missing low-order Bits is an interesting read. The astute reader should realize there probably exists an online gaming site using an insecure system. Note that this is a different attack than Wonging (named after Stanford Wong, a former IBM researcher and professional gambler), which is basically a card counting system.

Non Linear Congruential Generators

There are many fast random number generators available for use as a replacement to the standard C/C++ library's rand() and srand() funtions. The reader should familiarize themselves with Random Number Generators: Good Ones Are Hard To Find. Steve Park provides the source code to the Lehmer generator at his Random Number Generator webpage. Park's code provides additional PRNGs based on the following distributions (these generators are referred to as Non Linear Congruential Generators):

  • Bernoulli
  • Binomial
  • Equilikely
  • Geometric
  • Pascal
  • Poisson

Win32 API CryptGenRandom( )

CryptGenRandom() generates a requested number of bytes for the user. GenCryptRandom() is an excellent source of entropy on Windows since it is available for all Operating Systems except NT 3.51 and earlier, and Windows 95 (SR1) and earlier.

The latest seed value for CryptGenRandom is stored in the Windows Registry under the key \SOFTWARE\Microsoft\Cryptography\RNG\Seed of the HKEY_LOCAL_MACHINE hive. The seed is developed by the Operating Systems using various system parameters. For example, on a Windows CE device, the following will be used:

  • Thread and kernel switches (CeGetRandomSeed)
  • The current process identifier (GetCurrentProcessId)
  • The current thread identifier (GetCurrentThreadId)
  • Ticks since boot (GetTickCount)
  • Current time (GetLocalTime)
  • Memory information (GlobalMemoryStatus)
  • Object store statistics (GetStoreInformation)

After the seed is developed, it undergoes two cryptographic transforms: an MD4 hash and a RC4 encryption for additional mixing and chopping.

Sample 1 demonstrates using the Windows API to acquire pseudo random values. To remove the burden of an SDK, the program uses LoadLibrary() and GetProcAddress().

Compiling and Integrating Crypto++

Crypto++Please see the related article, Compiling and Integrating Crypto++ into the Microsoft Visual C++ Environment . This article is based upon basic assumptions presented in the previously mentioned article. It also addresses most problems encountered with projects from Command Line to MFC (Errors C1083, C1189, LNK1104, LNK2001, and LNK2005). Additionally, it provides some tips and other niceties for using the Crypto++ Library.

LC_RNG

LC_RNG is a Linear Congruential RNG. Though this generator has no cryptographic value, it does allow one to reproduce results when debugging a program. Additionally, it is generally faster at generating a byte block (or stream). If one seeds the LCG with 0x00, a steady stream of 0x80 is the result. This seed value produces a period of 1. Other seeds perform as desired. Sample 1 may be used to compare the results of various seed values of Linear Congruential Generators.

RandomPool

The RandomPool behaves similar to an LCG in that the same seed produces the same results. However, unlike LC_RNG, the cipher behind the RandomPool is currently MD5<SHA> . randpoool.cpp provides typedef MDC<SHA> RandomPoolCipher. Then RandomPool would be initialized and used as follows (as demonstrated in Sample 3):

// Must be at least 16 for RandomPool
const unsigned int SEEDSIZE = 16;
byte pcbSeed[ SEEDSIZE ];

// Scratch Area
const unsigned int BLOCKSIZE = 16 * 8;
byte pcbScratch[ BLOCKSIZE ];

...

// Random Pool Initialization
CryptoPP::RandomPool rng( SEEDSIZE );
rng.Put( pcbSeed, SEEDSIZE );

// Use
rng.GenerateBlock( pcbScratch, BLOCKSIZE );

AutoSeededRandomPool

An auto seeded random pool was suggested by Leonard Janke, which Wei Dai later incorporated into Crypto++. AutoSeededRandomPool uses the RandPool design from PGP. It is suitable for all cryptographic purposes including generating keys and IVs. Depending on the Operating System, the generator will be seeded using the following:

  • CryptGenRandom() by way of a Cryptographic Service Provider
  • /dev/urand
  • /dev/rand

Sample 4 trivially demonstrates using an AutoSeedeRandomPool.

// Scratch Area
const unsigned int BLOCKSIZE = 16 * 8;
byte pcbScratch[ BLOCKSIZE ];

// Construction
AutoSeededRandomPool rng;

// Random Block
rng.GenerateBlock( pcbScratch, BLOCKSIZE );

AutoSeededX917RNG

Unlike LG_RNG and RandomPool, AutoSeededX917RNG does not require seeding. However, one must specify an approved Block Cipher as a template parameter. The two ANSI 9.17 approved ciphers are 3-key Triple DES and SHA1. Sample 5 demonstrates use with SHA1.

// Scratch Area
const unsigned int BLOCKSIZE = 16 * 8;
byte pcbScratch[ BLOCKSIZE ];

// Construction
AutoSeededX917RNG< DES_EDE3 > rng;

// Random Block
rng.GenerateBlock( pcbScratch, BLOCKSIZE );

Application Table

The following table should be applied when chosing a Random Number Generator in practice.

Additional CSPRNGs

In addition to the previously mentioned, various FIPS standards recognizes other cryptographically secure generators. As the reader should now realize, a Cryptographically Secure Pseudo Random Number Generator wraps a deterministic generator in a difficult problem. FIPS 186-3 approves the Digital Signature Algorithm (DSA) and Elliptic Curve DSA (ECDSA) as CSPRNGs.

Summary

This article presented the reader with with various choices for a pseudo random number generator based on the problem domain. Should the reader require a source for Simulation or Sampling, choose an LCG. If the reader requires strength, choose the Win32 API, AutoSeededRandomPool, or AutoSeededX917RNG. If the reader requires Cryptographic Security, choose an AutoSeededX917. And finally, if the reader requires entropy, use Win32 API or AutoSeededRandomPool.

In addition, other generators exist (some of which are exposed in Crypto++). For example, the Blum Blum Shub CSPRNG produces a bit sequence based on Quadratic Residues. The generator will remain cryptographically secure as long as Integer Factorization remains hard. It has yet to be determined where Integer Factorization lies in Complexity Theory.

Acknowledgements

  • Wei Dai for Crypto++ and his invaluable help on the Crypto++ mailing list
  • Dr. A. Brooke Stephens who laid my Cryptographic foundations

Revisions

  • 01.12.2007 Added non Linear Congruential Generators
  • 01.09.2007 Expanded Usefulness of Random Numbers
  • 01.07.2007 Added Reference to Leonard Janke
  • 01.05.2007 Added Application Table
  • 01.04.2006 Added Key Derivation Function
  • 01.03.2007 Initial Release

Downloads

Checksums

ASRP.zip
  MD5: 773B2C409A7140CBA16EFF7903AA2C11
  SHA-1: 9CD4BC2C3E156157AA10ADF62994D4065980EC80

AutoSeededX917.zip
  MD5: B855380703A24DF9671AE8C7146113A9
  SHA-1: 1B724CD0C28CC4909F7A14F374EE1874CFB49E42

FIPS140-2.zip
  MD5: 7E106DBF757F281C764AD6F09DFEF4DA
  SHA-1: 0ACD845C20B4F8FA5EA58C3C04270A8DFB6DCAC7

GenRandom.zip
  MD5: B99A0CCC8BA862350235EBEBDFC3A0D2
  SHA-1: 33EA241D0E86E8AB6D86F64FB62C7C67678163CC

LCG.zip
  MD5: 0F4A91276B2CE558DB8C8F65218AA32D
  SHA-1: BB598A1A9FC13B519BD5D5CDB37FA52B39E1230C

RNGVS.zip
  MD5: 6E608C5AFFFE4D906E435C39BC2CC687
  SHA-1: ED5E73E39E3ADBEDA008A175E4243D8A59ABD254

RandomPool.zip
  MD5: 345CF4D59BED53B501ED1D58EF0FD892
  SHA-1: E699F73A53A57178E150A1A020DE4B173F4FF1E9

About Jeffrey Walton


In the past, I have worked as an IT consultant for County Government (Anne Arundel County), the Nuclear Energy Institute, the Treasury Department, and Social Security Administration as a Network Engineer and System Administrator. Primary Administration experience includes Microsoft Windows and Novell NetWare, with additional exposure and familiarity with Mac and Linux OS's. An undergraduate degree (BS in Computer Science) was obtained from University of Maryland. Graduate work includes a Masters of Science (Computer Science) from Johns Hopkins University (expected in the near future). Training and Certifications include Microsoft, Checkpoint, and Cisco.

Code Project Article Competition, November 2006

Click here to view Jeffrey Walton's online profile.

Laser Gesture Recognition

Best project!!!!

Sample Image - lasergesture.jpg

Introduction

Wouldn't it be great if we could somehow give visual commands to our computer without touching the keyboard or mouse? In this article, we will put together a simple laser gesture recognition application and use it to control Windows Media Player. This is far more comfortable than using a remote control because you don't have to look for the correct buttons in the dark. All you have to do is make a few simple gestures anywhere in the camera's field of view with a laser pointer, and that's it! This program recognizes simple gestures made on a wall with a laser pointer such as left, right, up, down, two downward and two upward diagonals. This program could be modified to recognize some more gestures; however, it cannot recognize complex gestures since I haven't taken a neural network approach for image recognition.

Video

Our first step is to get a video feed into our application from a webcam. We can use DirectX's component DirectShow for accomplishing this. I have directly used Andrew Kirillov's Motion Detection code[^] (with permission) for image acquisition. I modified the code in MotionDetector1.cs to perform laser gesture recognition.

Recognition

The program searches for the brightest pixel in its field of view (which is a laser dot in our case) with luminance above a certain threshold value. Luminance of a pixel can be calculated using its RGB values with a simple formula:

Luminance = (299 * red + 587 * green + 114 * blue) / 1000

After it finds the pixel, it analyzes how much that point moved along the x and y axes. Based on these parameters, the program tries to recognize the movement. For example, if the laser dot's movement along the x-axis is much more than its movement along the y-axis, the program will determine that it was more or less a horizontal movement. Then, based on the initial and final position of the laser dot, it will determine if the movement was towards the left or towards the right. It uses a similar technique to detect upward, downward and diagonal movements.

Controlling Windows Media Player

For controlling Windows Media Player, the program simply simulates some of the keyboard shortcuts used by Media Player. This code can skip to the next/previous track, or play, pause, stop a track based on the gesture the program recognizes:

Collapse
        // Get a handle to an application window.
        [DllImport("USER32.DLL")]
        private static extern IntPtr FindWindow(string lpClassName,
            string lpWindowName);

        // Activate an application window.
        [DllImport("USER32.DLL")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        

        private void ControlMediaPlayer(string <CODE>gesture)
        {
            IntPtr mediaPlayerHandle = FindWindow("WMPlayerApp", "Windows Media Player");

            // Verify that WMP is a running process.
            if (mediaPlayerHandle == IntPtr.Zero)
            {
                System.Windows.Forms.MessageBox.Show("WMP is not running.");
                return;
            }

            switch (gesture)
            {        
                case "LEFT":
                    SetForegroundWindow(mediaPlayerHandle);
                    SendKeys.SendWait("^b");
                    break;

                case "RIGHT":
                    SetForegroundWindow(mediaPlayerHandle);
                    SendKeys.SendWait("^f");
                    break;

                case "UP":
                    SetForegroundWindow(mediaPlayerHandle);
                    SendKeys.SendWait("^s");
                    break;

                case "DOWN":
                    SetForegroundWindow(mediaPlayerHandle);
                    SendKeys.SendWait("^p");
                    break;
            }
        }

Using the program

Since the program searches for the brightest pixel in the camera's field of view, the lighting conditions of your room can affect its performance. So, adjust the brightness threshold and lighting conditions so that nothing (except the laser), exceeds the brightness threshold.

In the sample program I've provided with this article, the gestures for controlling Windows Media Player are:

Up – Stop
Down – Play/Pause
Left – Previous Track
Right – Next Track

You can also easily modify the code and use diagonal gestures for volume control. :)

Conclusion

We have reached the end of this article. I might update this program to perform more complex gesture recognition later on. However, for now, have fun with it! You can find some videos of this application being used to control Media Player on my blog[ ^]. You can also easily modify the code to make this program do much more than just controlling Windows Media Player. Have fun!

History

  • [13-JAN-2007] - Minor corrections
  • [09-JAN-2007] - Initial publication

About Ashish Derhgawen


View my blog: http://ashishrd.blogspot.com[^]

Click here to view Ashish Derhgawen's online profile

16 janvier

IBM笔记本无法用DOS的U盘启动的原因

问:

一台IBM X60s的笔记本,没有光驱和软驱,我现在想重装系统(香港买的,英文系统,想换成简体中文系统),主板显示是支持USB启动的,但是我用USB-Boot制作的U盘启动盘确怎么都没有办法引导进入DOS,试了好几个U盘都试一样,请问是什么问题?是我用的几个U盘都不支持?还是我的方法有问题?

答:

不知道你做的启动盘是什么系统的?如果是DOS系统确实有可能启动不了。因为现在的电脑C盘都是NTFS分区,这是无法识别DOS系统的,所以还是建议你使用外接光驱用Windows XP的安装盘进行安装和使用……

1。如果您想从U盘安装操作系统的话,首先您要先把它接到机器上,用WIN98启动盘启动后,通过FDISK.EXE分区工具把它先进行分区和分配盘符,这样,我们才能在DOS下找到它,并对它进行操作。否则,我们在DOS下就找不到U盘。这点是和WINDOWS是不一样的,在WINDOWS下的操作不需要此过程。
2。有一些USB硬盘可能存在兼容性或者是通过USB端口供电不足的情况,使用时候请尽可能连接USB硬盘的外置电源。如果还是有问题,请换用其他硬盘再试。

3。有时候可能USB设备没有在CMOS中被识别,我们可以在开机状态下插入USB设备,然后重新启动计算机,再进入CMOS中即可发现USB设备。

DOS启动版U盘制作方法详解

新版启动型U盘制作工具下载地址:  Http://pzz.cn/soft/usbboot.rar

下载MaxDOS压缩包下载地址:  Http://Pzz.cn/soft/usbdos.rar

 

下载后把UBSBOOT程序解压缩出来,再把你的U盘插上,按照下面的步骤就可以把你的U盘制作成DOS启动型的(注意事先备份U盘中有用的数据)
 

USBOOT运行后(见下图):

 

①选中你的U盘;

②点击蓝色的字选择工作模式;

③强烈建议选择ZIP模式!
HDD模式和FDD模式建议在ZIP模式不能正常工作时再试用;

ZIP模式是指把U盘模拟成ZIP驱动器模式,启动后U盘的盘符是A:

HDD模式是指把U盘模拟成硬盘模式;特别注意:如果选择了HDD模式,那么这个启动U盘启动后的盘符是C:,在对启动分区进行操作时就容易产生很多问题,比如:装系统时安装程序会把启动文件写到U盘而不是你硬盘的启动分区!导致系统安装失败。所以请尽量先选择ZIP模式。

FDD模式是指把U盘模拟成软驱模式,启动后U盘的盘符是A:,这个模式的U盘在一些支持USB-FDD启动的机器上启动时会找不到U盘,所以请酌情

使用。
 

④点击《开始》,开始制作。

⑤出现下面这个对话框时,确保你的U盘中数据已没用,再选择《是》。


⑥启动盘制作时出现下面的提示,请按正常程序拔下U盘:

Win9x系统:可直接拔下U盘

Win2000、XP、2003系统:请双击任务栏右侧红色圆圈内的《安全删除硬件》图标(如下图),正常卸载U盘。

⑦请再次插上U盘

⑧稍后就会出现下面这个成功的提示,说明你的U盘目前已经是可启动基本DOS的了,点击右上角的×关闭USBOOT。


 

2、将前面下载的MAXDOS压缩包,把里面的所有文件释放到U盘的根目录下,注意必须是根目录!

3、至此,你已经有了一个功能不错的DOS启动U盘了。

4、用这个U盘启动计算机:将U盘插入主板USB接口(最好将其他无关的USB设备暂时拔掉),重启电脑,在系统自检的界面上按Del键进入BIOS

设置(如果是特殊BIOS,请参考主板手册后确定按哪个键进入BIOS),进入BIOS FEATURES SETUP中,将Boot Sequence(启动顺序)设定为

USB-ZIP(或USB-HDD、USB-FDD,请与你制作的U盘工作模式对应)第一,设定的方法是在该项上按PageUP或PageDown键来转换选项。设定好后

按ESC一下,退回BIOS主界面,选择Save and Exit(保存并退出BIOS设置,直接按F10也可以,但不是所有的BIOS都支持)回车确认退出BIOS设

置。

注意:有些主板(尤其是老主板)的BIOS中不支持U盘启动,所以会找不到相应的选项。

注意事项:

1、制作启动盘之前请备份好U盘上有用的数据,最好能完全格式化一遍U盘。

2、有NTFS分区的硬盘或多硬盘的系统,在DOS下硬盘的盘符排列和在Windows中的顺序可能不一样,请大家自行查找确定,以免误操作。

3、如果启动U盘在使用中发生问题,请试试下面的方法:

①换成其他的工作模式(ZIP、HDD、FDD);
②选择DOS启动菜单中其他的选项;
③更换一个不同品牌的U盘重新制作一次;
④把U盘拿到其他品牌的电脑上试一下能不能正常工作。

4、U盘启动盘出现问题主要原因:

①主板不支持U盘启动(或支持的不完善);
②某些DOS软件(尤其是对磁盘操作类的)对U盘支持的可能不是很好;
③U盘是DOS之后出现的新硬件,种类比较繁杂,而且目前绝大多数的USB设备都没有DOS下的驱动,目前使用的基本都是兼容驱动,所以出现一

些问题也在所难免;
④U盘本身质量有问题;
⑤经常对U盘有不正确的操作,比如2000、XP、2003下直接插拔U盘,而不是通过《安全删除硬件》来卸载。

5、关于USBOOT这个工具的使用注意事项和容易产生的问题,请看其压缩包中的PDF文档,里面说的已经很详细了

15 janvier

通用线程:POSIX 线程详解,第 1 部分------一种支持内存共享的简捷工具

POSIX(可移植操作系统接口)线程是提高代码响应和性能的有力手段。在本系列中,Daniel Robbins 向您精确地展示在编程中如何使用线程。其中还涉及大量幕后细节,读完本系列文章,您完全可以运用 POSIX 线程创建多线程程序。

线程是有趣的

了解如何正确运用线程是每一个优秀程序员必备的素质。线程类似于进程。如同进程,线程由内核按时间分片进行管理。在单处理器系统中,内核使用时间分片来模拟线程的并发执行,这种方式和进程的相同。而在多处理器系统中,如同多个进程,线程实际上一样可以并发执行。

那么为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是因为,线程共享相同的内存空间。不同的线程可以存取内存中的同一个变量。所以,程序中的所有线程都可以读或写声明过的全局变量。如果曾用 fork() 编写过重要代码,就会认识到这个工具的重要性。为什么呢?虽然 fork() 允许创建多个进程,但它还会带来以下通信问题: 如何让多个进程相互通信,这里每个进程都有各自独立的内存空间。对这个问题没有一个简单的答案。虽然有许多不同种类的本地 IPC (进程间通信),但它们都遇到两个重要障碍:

  • 强加了某种形式的额外内核开销,从而降低性能。
  • 对于大多数情形,IPC 不是对于代码的"自然"扩展。通常极大地增加了程序的复杂性。

双重坏事: 开销和复杂性都非好事。如果曾经为了支持 IPC 而对程序大动干戈过,那么您就会真正欣赏线程提供的简单共享内存机制。由于所有的线程都驻留在同一内存空间,POSIX 线程无需进行开销大而复杂的长距离调用。只要利用简单的同步机制,程序中所有的线程都可以读取和修改已有的数据结构。而无需将数据经由文件描述符转储或挤入紧窄的共享内存空间。仅此一个原因,就足以让您考虑应该采用单进程/多线程模式而非多进程/单线程模式。





回页首


线程是快捷的

不仅如此。线程同样还是非常快捷的。与标准 fork() 相比,线程带来的开销很小。内核无需单独复制进程的内存空间或文件描述符等等。这就节省了大量的 CPU 时间,使得线程创建比新进程创建快上十到一百倍。因为这一点,可以大量使用线程而无需太过于担心带来的 CPU 或内存不足。使用 fork() 时导致的大量 CPU 占用也不复存在。这表示只要在程序中有意义,通常就可以创建线程。

当然,和进程一样,线程将利用多 CPU。如果软件是针对多处理器系统设计的,这就真的是一大特性(如果软件是开放源码,则最终可能在不少平台上运行)。特定类型线程程序(尤其是 CPU 密集型程序)的性能将随系统中处理器的数目几乎线性地提高。如果正在编写 CPU 非常密集型的程序,则绝对想设法在代码中使用多线程。一旦掌握了线程编码,无需使用繁琐的 IPC 和其它复杂的通信机制,就能够以全新和创造性的方法解决编码难题。所有这些特性配合在一起使得多线程编程更有趣、快速和灵活。





回页首


线程是可移植的

如果熟悉 Linux 编程,就有可能知道 __clone() 系统调用。__clone() 类似于 fork(),同时也有许多线程的特性。例如,使用 __clone(),新的子进程可以有选择地共享父进程的执行环境(内存空间,文件描述符等)。这是好的一面。但 __clone() 也有不足之处。正如__clone() 在线帮助指出:

"__clone 调用是特定于 Linux 平台的,不适用于实现可移植的程序。欲编写线程化应用程序(多线程控制同一内存空间),最好使用实现 POSIX 1003.1c 线程 API 的库,例如 Linux-Threads 库。参阅 pthread_create(3thr)。"

虽然 __clone() 有线程的许多特性,但它是不可移植的。当然这并不意味着代码中不能使用它。但在软件中考虑使用 __clone() 时应当权衡这一事实。值得庆幸的是,正如 __clone() 在线帮助指出,有一种更好的替代方案:POSIX 线程。如果想编写 可移植的 多线程代码,代码可运行于 Solaris、FreeBSD、Linux 和其它平台,POSIX 线程是一种当然之选。





回页首


第一个线程

下面是一个 POSIX 线程的简单示例程序:


thread1.c

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

 void *thread_function(void *arg) {
  int i;
  for ( i=0; i<20; i++) {
    printf("Thread says hi!\n");
    sleep(1);
  }
  return NULL;
}

int main(void) {

  pthread_t mythread;
  
  if ( pthread_create( &mythread, NULL, thread_function, NULL) ) {
    printf("error creating thread.");
    abort();
  }

  if ( pthread_join ( mythread, NULL ) ) {
    printf("error joining thread.");
    abort();
  }

  exit(0);

}

要编译这个程序,只需先将程序存为 thread1.c,然后输入:


$ gcc thread1.c -o thread1 -lpthread

运行则输入:


$ ./thread1





回页首


理解 thread1.c

thread1.c 是一个非常简单的线程程序。虽然它没有实现什么有用的功能,但可以帮助理解线程的运行机制。下面,我们一步一步地了解这个程序是干什么的。main() 中声明了变量 mythread,类型是 pthread_t。pthread_t 类型在 pthread.h 中定义,通常称为"线程 id"(缩写为 "tid")。可以认为它是一种线程句柄。

mythread 声明后(记住 mythread 只是一个 "tid",或是将要创建的线程的句柄),调用 pthread_create 函数创建一个真实活动的线程。不要因为 pthread_create() 在 "if" 语句内而受其迷惑。由于 pthread_create() 执行成功时返回零而失败时则返回非零值,将 pthread_create() 函数调用放在 if() 语句中只是为了方便地检测失败的调用。让我们查看一下 pthread_create 参数。第一个参数 &mythread 是指向 mythread 的指针。第二个参数当前为 NULL,可用来定义线程的某些属性。由于缺省的线程属性是适用的,只需将该参数设为 NULL。

第三个参数是新线程启动时调用的函数名。本例中,函数名为 thread_function()。当 thread_function() 返回时,新线程将终止。本例中,线程函数没有实现大的功能。它仅将 "Thread says hi!" 输出 20 次然后退出。注意 thread_function() 接受 void * 作为参数,同时返回值的类型也是 void *。这表明可以用 void * 向新线程传递任意类型的数据,新线程完成时也可返回任意类型的数据。那如何向线程传递一个任意参数?很简单。只要利用 pthread_create() 中的第四个参数。本例中,因为没有必要将任何数据传给微不足道的 thread_function(),所以将第四个参数设为 NULL。

您也许已推测到,在 pthread_create() 成功返回之后,程序将包含两个线程。等一等, 两个 线程?我们不是只创建了一个线程吗?不错,我们只创建了一个进程。但是主程序同样也是一个线程。可以这样理解:如果编写的程序根本没有使用 POSIX 线程,则该程序是单线程的(这个单线程称为"主"线程)。创建一个新线程之后程序总共就有两个线程了。

我想此时您至少有两个重要问题。第一个问题,新线程创建之后主线程如何运行。答案,主线程按顺序继续执行下一行程序(本例中执行 "if (pthread_join(...))")。第二个问题,新线程结束时如何处理。答案,新线程先停止,然后作为其清理过程的一部分,等待与另一个线程合并或"连接"。

现在,来看一下 pthread_join()。正如 pthread_create() 将一个线程拆分为两个, pthread_join() 将两个线程合并为一个线程。pthread_join() 的第一个参数是 tid mythread。第二个参数是指向 void 指针的指针。如果 void 指针不为 NULL,pthread_join 将线程的 void * 返回值放置在指定的位置上。由于我们不必理会 thread_function() 的返回值,所以将其设为 NULL.

您会注意到 thread_function() 花了 20 秒才完成。在 thread_function() 结束很久之前,主线程就已经调用了 pthread_join()。如果发生这种情况,主线程将中断(转向睡眠)然后等待 thread_function() 完成。当 thread_function() 完成后, pthread_join() 将返回。这时程序又只有一个主线程。当程序退出时,所有新线程已经使用 pthread_join() 合并了。这就是应该如何处理在程序中创建的每个新线程的过程。如果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。这意味着如果未对线程做正确的清理,最终会导致 pthread_create() 调用失败。





回页首


无父,无子

如果使用过 fork() 系统调用,可能熟悉父进程和子进程的概念。当用 fork() 创建另一个新进程时,新进程是子进程,原始进程是父进程。这创建了可能非常有用的层次关系,尤其是等待子进程终止时。例如,waitpid() 函数让当前进程等待所有子进程终止。waitpid() 用来在父进程中实现简单的清理过程。

而 POSIX 线程就更有意思。您可能已经注意到我一直有意避免使用"父线程"和"子线程"的说法。这是因为 POSIX 线程中不存在这种层次关系。虽然主线程可以创建一个新线程,新线程可以创建另一个新线程,POSIX 线程标准将它们视为等同的层次。所以等待子线程退出的概念在这里没有意义。POSIX 线程标准不记录任何"家族"信息。缺少家族信息有一个主要含意:如果要等待一个线程终止,就必须将线程的 tid 传递给 pthread_join()。线程库无法为您断定 tid。

对大多数开发者来说这不是个好消息,因为这会使有多个线程的程序复杂化。不过不要为此担忧。POSIX 线程标准提供了有效地管理多个线程所需要的所有工具。实际上,没有父/子关系这一事实却为在程序中使用线程开辟了更创造性的方法。例如,如果有一个线程称为线程 1,线程 1 创建了称为线程 2 的线程,则线程 1 自己没有必要调用 pthread_join() 来合并线程 2,程序中其它任一线程都可以做到。当编写大量使用线程的代码时,这就可能允许发生有趣的事情。例如,可以创建一个包含所有已停止线程的全局"死线程列表",然后让一个专门的清理线程专等停止的线程加到列表中。这个清理线程调用 pthread_join() 将刚停止的线程与自己合并。现在,仅用一个线程就巧妙和有效地处理了全部清理。





回页首


同步漫游

现在我们来看一些代码,这些代码做了一些意想不到的事情。thread2.c 的代码如下:


thread2.c

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int myglobal;

 void *thread_function(void *arg) {
  int i,j;
  for ( i=0; i<20; i++) {
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);
    myglobal=j;
  }
  return NULL;
}

int main(void) {

  pthread_t mythread;
  int i;

  if ( pthread_create( &mythread, NULL, thread_function, NULL) ) {
    printf("error creating thread.");
    abort();
  }

  for ( i=0; i<20; i++) {
    myglobal=myglobal+1;
    printf("o");
    fflush(stdout);
    sleep(1);
  }

  if ( pthread_join ( mythread, NULL ) ) {
    printf("error joining thread.");
    abort();
  }

  printf("\nmyglobal equals %d\n",myglobal);

  exit(0);

}





回页首


理解 thread2.c

如同第一个程序,这个程序创建一个新线程。主线程和新线程都将全局变量 myglobal 加一 20 次。但是程序本身产生了某些意想不到的结果。编译代码请输入:


$ gcc thread2.c -o thread2 -lpthread

运行请输入:


$ ./thread2

输出:


$ ./thread2
..o.o.o.o.oo.o.o.o.o.o.o.o.o.o..o.o.o.o.o
myglobal equals 21

非常意外吧!因为 myglobal 从零开始,主线程和新线程各自对其进行了 20 次加一, 程序结束时 myglobal 值应当等于 40。由于 myglobal 输出结果为 21,这其中肯定有问题。但是究竟是什么呢?

放弃吗?好,让我来解释是怎么一回事。首先查看函数 thread_function()。注意如何将 myglobal 复制到局部变量 "j" 了吗? 接着将 j 加一, 再睡眠一秒,然后到这时才将新的 j 值复制到 myglobal?这就是关键所在。设想一下,如果主线程就在新线程将 myglobal 值复制给 j 立即将 myglobal 加一,会发生什么?当 thread_function() 将 j 的值写回 myglobal 时,就覆盖了主线程所做的修改。

当编写线程程序时,应避免产生这种无用的副作用,否则只会浪费时间(当然,除了编写关于 POSIX 线程的文章时有用)。那么,如何才能排除这种问题呢?

由于是将 myglobal 复制给 j 并且等了一秒之后才写回时产生问题,可以尝试避免使用临时局部变量并直接将 myglobal 加一。虽然这种解决方案对这个特定例子适用,但它还是不正确。如果我们对 myglobal 进行相对复杂的数学运算,而不是简单的加一,这种方法就会失效。但是为什么呢?

要理解这个问题,必须记住线程是并发运行的。即使在单处理器系统上运行(内核利用时间分片模拟多任务)也是可以的,从程序员的角度,想像两个线程是同时执行的。thread2.c 出现问题是因为 thread_function() 依赖以下论据:在 myglobal 加一之前的大约一秒钟期间不会修改 myglobal。需要有些途径让一个线程在对 myglobal 做更改时通知其它线程"不要靠近"。我将在下一篇文章中讲解如何做到这一点。到时候见。

通用线程:POSIX 线程详解,第 2部分------称作互斥对象的小玩意

POSIX 线程是提高代码响应和性能的有力手段。在此三部分系列文章的第二篇中,Daniel Robbins 将说明,如何使用被称为互斥对象的灵巧小玩意,来保护线程代码中共享数据结构的完整性。

互斥我吧!

前一篇文章中 ,谈到了会导致异常结果的线程代码。两个线程分别对同一个全局变量进行了二十次加一。变量的值最后应该是 40,但最终值却是 21。这是怎么回事呢?因为一个线程不停地"取消"了另一个线程执行的加一操作,所以产生这个问题。现在让我们来查看改正后的代码,它使用 互斥对象(mutex)来解决该问题:


thread3.c

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int myglobal;
pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;

 void *thread_function(void *arg) {
  int i,j;
  for ( i=0; i<20; i++) {
    pthread_mutex_lock(&mymutex);
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);
    myglobal=j;
    pthread_mutex_unlock(&mymutex);
  }
  return NULL;
}

int main(void) {

  pthread_t mythread;
  int i;

  if ( pthread_create( &mythread, NULL, thread_function, NULL) ) {
    printf("error creating thread.");
    abort();
  }

  for ( i=0; i<20; i++) {
    pthread_mutex_lock(&mymutex);
    myglobal=myglobal+1;
    pthread_mutex_unlock(&mymutex);
    printf("o");
    fflush(stdout);
    sleep(1);
  }

  if ( pthread_join ( mythread, NULL ) ) {
    printf("error joining thread.");
    abort();
  }

  printf("\nmyglobal equals %d\n",myglobal);

  exit(0);

}





回页首


解读一下

如果将这段代码与 前一篇文章 中给出的版本作一个比较,就会注意到增加了 pthread_mutex_lock() 和 pthread_mutex_unlock() 函数调用。在线程程序中这些调用执行了不可或缺的功能。他们提供了一种 相互排斥的方法(互斥对象即由此得名)。两个线程不能同时对同一个互斥对象加锁。

互斥对象是这样工作的。如果线程 a 试图锁定一个互斥对象,而此时线程 b 已锁定了同一个互斥对象时,线程 a 就将进入睡眠状态。一旦线程 b 释放了互斥对象(通过 pthread_mutex_unlock() 调用),线程 a 就能够锁定这个互斥对象(换句话说,线程 a 就将从 pthread_mutex_lock() 函数调用中返回,同时互斥对象被锁定)。同样地,当线程 a 正锁定互斥对象时,如果线程 c 试图锁定互斥对象的话,线程 c 也将临时进入睡眠状态。对已锁定的互斥对象上调用 pthread_mutex_lock() 的所有线程都将进入睡眠状态,这些睡眠的线程将"排队"访问这个互斥对象。

通常使用 pthread_mutex_lock() 和 pthread_mutex_unlock() 来保护数据结构。这就是说,通过线程的锁定和解锁,对于某一数据结构,确保某一时刻只能有一个线程能够访问它。可以推测到,当线程试图锁定一个未加锁的互斥对象时,POSIX 线程库将同意锁定,而不会使线程进入睡眠状态。


请看这幅轻松的漫画,四个小精灵重现了最近一次 pthread_mutex_lock() 调用的一个场面。

图中,锁定了互斥对象的线程能够存取复杂的数据结构,而不必担心同时会有其它线程干扰。那个数据结构实际上是"冻结"了,直到互斥对象被解锁为止。pthread_mutex_lock() 和 pthread_mutex_unlock() 函数调用,如同"在施工中"标志一样,将正在修改和读取的某一特定共享数据包围起来。这两个函数调用的作用就是警告其它线程,要它们继续睡眠并等待轮到它们对互斥对象加锁。当然,除非在 每个 对特定数据结构进行读写操作的语句前后,都分别放上 pthread_mutex_lock() 和 pthread_mutext_unlock() 调用,才会出现这种情况。





回页首


为什么要用互斥对象?

听上去很有趣,但究竟为什么要让线程睡眠呢?要知道,线程的主要优点不就是其具有独立工作、更多的时候是同时工作的能力吗?是的,确实是这样。然而,每个重要的线程程序都需要使用某些互斥对象。让我们再看一下示例程序以便理解原因所在。

请看 thread_function(),循环中一开始就锁定了互斥对象,最后才将它解锁。在这个示例程序中,mymutex 用来保护 myglobal 的值。仔细查看 thread_function(),加一代码把 myglobal 复制到一个局部变量,对局部变量加一,睡眠一秒钟,在这之后才把局部变量的值传回给 myglobal。不使用互斥对象时,即使主线程在 thread_function() 线程睡眠一秒钟期间内对 myglobal 加一,thread_function() 苏醒后也会覆盖主线程所加的值。使用互斥对象能够保证这种情形不会发生。(您也许会想到,我增加了一秒钟延迟以触发不正确的结果。把局部变量的值赋给 myglobal 之前,实际上没有什么真正理由要求 thread_function() 睡眠一秒钟。)使用互斥对象的新程序产生了期望的结果:


$ ./thread3
o..o..o.o..o..o.o.o.o.o..o..o..o.ooooooo
myglobal equals 40

为了进一步探索这个极为重要的概念,让我们看一看程序中进行加一操作的代码:


thread_function() 加一代码:
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);
    myglobal=j;

主线程加一代码:
    myglobal=myglobal+1;

如果代码是位于单线程程序中,可以预期 thread_function() 代码将完整执行。接下来才会执行主线程代码(或者是以相反的顺序执行)。在不使用互斥对象的线程程序中,代码可能(几乎是,由于调用了 sleep() 的缘故)以如下的顺序执行:


    thread_function() 线程        主线程

    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);                     myglobal=myglobal+1;
    myglobal=j;

当代码以此特定顺序执行时,将覆盖主线程对 myglobal 的修改。程序结束后,就将得到不正确的值。如果是在操纵指针的话,就可能产生段错误。注意到 thread_function() 线程按顺序执行了它的所有指令。看来不象是 thread_function() 有什么次序颠倒。问题是,同一时间内,另一个线程对同一数据结构进行了另一个修改。





回页首


线程内幕 1

在解释如何确定在何处使用互斥对象之前,先来深入了解一下线程的内部工作机制。请看第一个例子:

假设主线程将创建三个新线程:线程 a、线程 b 和线程 c。假定首先创建线程 a,然后是线程 b,最后创建线程 c。


    pthread_create( &thread_a, NULL, thread_function, NULL);
    pthread_create( &thread_b, NULL, thread_function, NULL);
    pthread_create( &thread_c, NULL, thread_function, NULL);

在第一个 pthread_create() 调用完成后,可以假定线程 a 不是已存在就是已结束并停止。第二个 pthread_create() 调用后,主线程和线程 b 都可以假定线程 a 存在(或已停止)。

然而,就在第二个 create() 调用返回后,主线程无法假定是哪一个线程(a 或 b)会首先开始运行。虽然两个线程都已存在,线程 CPU 时间片的分配取决于内核和线程库。至于谁将首先运行,并没有严格的规则。尽管线程 a 更有可能在线程 b 之前开始执行,但这并无保证。对于多处理器系统,情况更是如此。如果编写的代码假定在线程 b 开始执行之前实际上执行线程 a 的代码,那么,程序最终正确运行的概率是 99%。或者更糟糕,程序在您的机器上 100% 地正确运行,而在您客户的四处理器服务器上正确运行的概率却是零。

从这个例子还可以得知,线程库保留了每个单独线程的代码执行顺序。换句话说,实际上那三个 pthread_create() 调用将按它们出现的顺序执行。从主线程上来看,所有代码都是依次执行的。有时,可以利用这一点来优化部分线程程序。例如,在上例中,线程 c 就可以假定线程 a 和线程 b 不是正在运行就是已经终止。它不必担心存在还没有创建线程 a 和线程 b 的可能性。可以使用这一逻辑来优化线程程序。





回页首


线程内幕 2

现在来看另一个假想的例子。假设有许多线程,他们都正在执行下列代码:


    myglobal=myglobal+1;

那么,是否需要在加一操作语句前后分别锁定和解锁互斥对象呢?也许有人会说"不"。编译器极有可能把上述赋值语句编译成一条机器指令。大家都知道,不可能"半途"中断一条机器指令。即使是硬件中断也不会破坏机器指令的完整性。基于以上考虑,很可能倾向于完全省略 pthread_mutex_lock() 和 pthread_mutex_unlock() 调用。不要这样做。

我在说废话吗?不完全是这样。首先,不应该假定上述赋值语句一定会被编译成一条机器指令,除非亲自验证了机器代码。即使插入某些内嵌汇编语句以确保加一操作的完整执行――甚至,即使是自己动手写编译器!-- 仍然可能有问题。

答案在这里。使用单条内嵌汇编操作码在单处理器系统上可能不会有什么问题。每个加一操作都将完整地进行,并且多半会得到期望的结果。但是多处理器系统则截然不同。在多 CPU 机器上,两个单独的处理器可能会在几乎同一时刻(或者,就在同一时刻)执行上述赋值语句。不要忘了,这时对内存的修改需要先从 L1 写入 L2 高速缓存、然后才写入主存。(SMP 机器并不只是增加了处理器而已;它还有用来仲裁对 RAM 存取的特殊硬件。)最终,根本无法搞清在写入主存的竞争中,哪个 CPU 将会"胜出"。要产生可预测的代码,应使用互斥对象。互斥对象将插入一道"内存关卡",由它来确保对主存的写入按照线程锁定互斥对象的顺序进行。

考虑一种以 32 位块为单位更新主存的 SMP 体系结构。如果未使用互斥对象就对一个 64 位整数进行加一操作,整数的最高 4 位字节可能来自一个 CPU,而其它 4 个字节却来自另一 CPU。糟糕吧!最糟糕的是,使用差劲的技术,您的程序在重要客户的系统上有可能不是很长时间才崩溃一次,就是早上三点钟就崩溃。David R. Butenhof 在他的《POSIX 线程编程》(请参阅本文末尾的 参考资料部分)一书中,讨论了由于未使用互斥对象而将产生的种种情况。





回页首


许多互斥对象

如果放置了过多的互斥对象,代码就没有什么并发性可言,运行起来也比单线程解决方案慢。如果放置了过少的互斥对象,代码将出现奇怪和令人尴尬的错误。幸运的是,有一个中间立场。首先,互斥对象是用于串行化存取*共享数据*。不要对非共享数据使用互斥对象,并且,如果程序逻辑确保任何时候都只有一个线程能存取特定数据结构,那么也不要使用互斥对象。

其次,如果要使用共享数据,那么在读、写共享数据时都应使用互斥对象。用 pthread_mutex_lock() 和 pthread_mutex_unlock() 把读写部分保护起来,或者在程序中不固定的地方随机使用它们。学会从一个线程的角度来审视代码,并确保程序中每一个线程对内存的观点都是一致和合适的。为了熟悉互斥对象的用法,最初可能要花好几个小时来编写代码,但是很快就会习惯并且*也*不必多想就能够正确使用它们。





回页首


使用调用:初始化

现在该来看看使用互斥对象的各种不同方法了。让我们从初始化开始。在 thread3.c 示例 中,我们使用了静态初始化方法。这需要声明一个 pthread_mutex_t 变量,并赋给它常数 PTHREAD_MUTEX_INITIALIZER:


pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;

很简单吧。但是还可以动态地创建互斥对象。当代码使用 malloc() 分配一个新的互斥对象时,使用这种动态方法。此时,静态初始化方法是行不通的,并且应当使用例程 pthread_mutex_init():


int pthread_mutex_init( pthread_mutex_t *mymutex, const pthread_mutexattr_t *attr)

正如所示,pthread_mutex_init 接受一个指针作为参数以初始化为互斥对象,该指针指向一块已分配好的内存区。第二个参数,可以接受一个可选的 pthread_mutexattr_t 指针。这个结构可用来设置各种互斥对象属性。但是通常并不需要这些属性,所以正常做法是指定 NULL。

一旦使用 pthread_mutex_init() 初始化了互斥对象,就应使用 pthread_mutex_destroy() 消除它。pthread_mutex_destroy() 接受一个指向 pthread_mutext_t 的指针作为参数,并释放创建互斥对象时分配给它的任何资源。请注意, pthread_mutex_destroy() 不会 释放用来存储 pthread_mutex_t 的内存。释放自己的内存完全取决于您。还必须注意一点,pthread_mutex_init() 和 pthread_mutex_destroy() 成功时都返回零。





回页首


使用调用:锁定


pthread_mutex_lock(pthread_mutex_t *mutex)

pthread_mutex_lock() 接受一个指向互斥对象的指针作为参数以将其锁定。如果碰巧已经锁定了互斥对象,调用者将进入睡眠状态。函数返回时,将唤醒调用者(显然)并且调用者还将保留该锁。函数调用成功时返回零,失败时返回非零的错误代码。


pthread_mutex_unlock(pthread_mutex_t *mutex)

pthread_mutex_unlock() 与 pthread_mutex_lock() 相配合,它把线程已经加锁的互斥对象解锁。始终应该尽快对已加锁的互斥对象进行解锁(以提高性能)。并且绝对不要对您未保持锁的互斥对象进行解锁操作(否则,pthread_mutex_unlock() 调用将失败并带一个非零的 EPERM 返回值)。


pthread_mutex_trylock(pthread_mutex_t *mutex)

当线程正在做其它事情的时候(由于互斥对象当前是锁定的),如果希望锁定互斥对象,这个调用就相当方便。调用 pthread_mutex_trylock() 时将尝试锁定互斥对象。如果互斥对象当前处于解锁状态,那么您将获得该锁并且函数将返回零。然而,如果互斥对象已锁定,这个调用也不会阻塞。当然,它会返回非零的 EBUSY 错误值。然后可以继续做其它事情,稍后再尝试锁定。





回页首


等待条件发生

互斥对象是线程程序必需的工具,但它们并非万能的。例如,如果线程正在等待共享数据内某个条件出现,那会发生什么呢?代码可以反复对互斥对象锁定和解锁,以检查值的任何变化。同时,还要快速将互斥对象解锁,以便其它线程能够进行任何必需的更改。这是一种非常可怕的方法,因为线程需要在合理的时间范围内频繁地循环检测变化。

在每次检查之间,可以让调用线程短暂地进入睡眠,比如睡眠三秒钟,但是因此线程代码就无法最快作出响应。真正需要的是这样一种方法,当线程在等待满足某些条件时使线程进入睡眠状态。一旦条件满足,还需要一种方法以唤醒因等待满足特定条件而睡眠的线程。如果能够做到这一点,线程代码将是非常高效的,并且不会占用宝贵的互斥对象锁。这正是 POSIX 条件变量能做的事!

而 POSIX 条件变量将是我下一篇文章的主题,其中将说明如何正确使用条件变量。到那时,您将拥有了创建复杂线程程序所需的全部资源,那些线程程序可以模拟工作人员、装配线等等。既然您已经越来越熟悉线程,我将在下一篇文章中加快进度。这样,在下一篇文章的结尾就能放上一个相对复杂的线程程序。说到等到条件产生,下次再见!

通用线程:POSIX 线程详解,第 3 部分-----使用条件变量提高效率

本文是 POSIX 线程三部曲系列的最后一部分,Daniel 将详细讨论如何使用条件变量。条件变量是 POSIX 线程结构,可以让您在遇到某些条件时"唤醒"线程。可以将它们看作是一种线程安全的信号发送。Daniel 使用目前您所学到的知识实现了一个多线程工作组应用程序,本文将围绕着这一示例而进行讨论。

条件变量详解

上一篇文章结束时,我描述了一个比较特殊的难题:如果线程正在等待某个特定条件发生,它应该如何处理这种情况?它可以重复对互斥对象锁定和解锁,每次都会检查共享数据结构,以查找某个值。但这是在浪费时间和资源,而且这种繁忙查询的效率非常低。解决这个问题的最佳方法是使用 pthread_cond_wait() 调用来等待特殊条件发生。

了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。

首先,让我们考虑以下情况:线程为查看已链接列表而锁定了互斥对象,然而该列表恰巧是空的。这一特定线程什么也干不了 -- 其设计意图是从列表中除去节点,但是现在却没有节点。因此,它只能:

锁定互斥对象时,线程将调用 pthread_cond_wait(&mycond,&mymutex)。pthread_cond_wait() 调用相当复杂,因此我们每次只执行它的一个操作。

pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改已链接列表),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的"信号"时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改已链接列表,可能还会添加项。

此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。

现在继续说明,假设另一个线程(称作"2 号线程")锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,2 号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒。

现在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&mymutex) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 1 号线程继续执行。那时,它可以马上检查列表,查看它所感兴趣的更改。





回页首


停止并回顾!

那个过程非常复杂,因此让我们先来回顾一下。第一个线程首先调用:



    pthread_mutex_lock(&mymutex);

然后,它检查了列表。没有找到感兴趣的东西,于是它调用:



    pthread_cond_wait(&mycond, &mymutex);

然后,pthread_cond_wait() 调用在返回前执行许多操作:


       
    pthread_mutex_unlock(&mymutex);

它对 mymutex 解锁,然后进入睡眠状态,等待 mycond 以接收 POSIX 线程"信号"。一旦接收到"信号"(加引号是因为我们并不是在讨论传统的 UNIX 信号,而是来自 pthread_cond_signal() 或 pthread_cond_broadcast() 调用的信号),它就会苏醒。但 pthread_cond_wait() 没有立即返回 -- 它还要做一件事:重新锁定 mutex:


     
    pthread_mutex_lock(&mymutex);

pthread_cond_wait() 知道我们在查找 mymutex "背后"的变化,因此它继续操作,为我们锁定互斥对象,然后才返回。





回页首


pthread_cond_wait() 小测验

现在已回顾了 pthread_cond_wait() 调用,您应该了解了它的工作方式。应该能够叙述 pthread_cond_wait() 依次执行的所有操作。尝试一下。如果理解了 pthread_cond_wait(),其余部分就相当容易,因此请重新阅读以上部分,直到记住为止。好,读完之后,能否告诉我在调用 pthread_cond_wait() 之 ,互斥对象必须处于什么状态?pthread_cond_wait() 调用返回之后,互斥对象处于什么状态?这两个问题的答案都是"锁定"。既然已经完全理解了 pthread_cond_wait() 调用,现在来继续研究更简单的东西 -- 初始化和真正的发送信号和广播进程。到那时,我们将会对包含了多线程工作队列的 C 代码了如指掌。





回页首


初始化和清除

条件变量是一个需要初始化的真实数据结构。以下就初始化的方法。首先,定义或分配一个条件变量,如下所示:



    pthread_cond_t mycond;

然后,调用以下函数进行初始化:



    pthread_cond_init(&mycond,NULL);

瞧,初始化完成了!在释放或废弃条件变量之前,需要毁坏它,如下所示:



    pthread_cond_destroy(&mycond);

很简单吧。接着讨论 pthread_cond_wait() 调用。





回页首


等待

一旦初始化了互斥对象和条件变量,就可以等待某个条件,如下所示:



    pthread_cond_wait(&mycond, &mymutex);

请注意,代码在逻辑上应该包含 mycond 和 mymutex。一个特定条件只能有一个互斥对象,而且条件变量应该表示互斥数据"内部"的一种特殊的条件更改。一个互斥对象可以用许多条件变量(例如,cond_empty、cond_full、cond_cleanup),但每个条件变量只能有一个互斥对象。





回页首


发送信号和广播

对于发送信号和广播,需要注意一点。如果线程更改某些共享数据,而且它想要唤醒所有正在等待的线程,则应使用 pthread_cond_broadcast 调用,如下所示:



    pthread_cond_broadcast(&mycond);

在某些情况下,活动线程只需要唤醒第一个正在睡眠的线程。假设您只对队列添加了一个工作作业。那么只需要唤醒一个工作程序线程(再唤醒其它线程是不礼貌的!):



    pthread_cond_signal(&mycond);

此函数只唤醒一个线程。如果 POSIX 线程标准允许指定一个整数,可以让您唤醒一定数量的正在睡眠的线程,那就更完美了。但是很可惜,我没有被邀请参加会议。





回页首


工作组

我将演示如何创建多线程工作组。在这个方案中,我们创建了许多工作程序线程。每个线程都会检查 wq("工作队列"),查看是否有需要完成的工作。如果有需要完成的工作,那么线程将从队列中除去一个节点,执行这些特定工作,然后等待新的工作到达。

与此同时,主线程负责创建这些工作程序线程、将工作添加到队列,然后在它退出时收集所有工作程序线程。您将会遇到许多 C 代码,好好准备吧!





回页首


队列

需要队列是出于两个原因。首先,需要队列来保存工作作业。还需要可用于跟踪已终止线程的数据结构。还记得前几篇文章(请参阅本文结尾处的 参考资料)中,我曾提到过需要使用带有特定进程标识的 pthread_join 吗?使用"清除队列"(称作 "cq")可以解决无法等待 任何已终止线程的问题(稍后将详细讨论这个问题)。以下是标准队列代码。将此代码保存到文件 queue.h 和 queue.c:


queue.h

/* queue.h
** Copyright 2000 Daniel Robbins, Gentoo Technologies, Inc.
** Author: Daniel Robbins
** Date: 16 Jun 2000
*/

typedef struct node {
  struct node *next;
} node;

typedef struct queue {
  node *head, *tail; 
} queue;

void queue_init(queue *myroot);
void queue_put(queue *myroot, node *mynode);
node *queue_get(queue *myroot);



queue.c

/* queue.c
** Copyright 2000 Daniel Robbins, Gentoo Technologies, Inc.
** Author: Daniel Robbins
** Date: 16 Jun 2000
**
** This set of queue functions was originally thread-aware.  I
** redesigned the code to make this set of queue routines
** thread-ignorant (just a generic, boring yet very fast set of queue
** routines).  Why the change?  Because it makes more sense to have
** the thread support as an optional add-on.  Consider a situation
** where you want to add 5 nodes to the queue.  With the
** thread-enabled version, each call to queue_put() would
** automatically lock and unlock the queue mutex 5 times -- that's a
** lot of unnecessary overhead.  However, by moving the thread stuff
** out of the queue routines, the caller can lock the mutex once at
** the beginning, then insert 5 items, and then unlock at the end.
** Moving the lock/unlock code out of the queue functions allows for
** optimizations that aren't possible otherwise.  It also makes this
** code useful for non-threaded applications.
**
** We can easily thread-enable this data structure by using the
** data_control type defined in control.c and control.h.  */

#include <stdio.h>
#include "queue.h"

void queue_init(queue *myroot) {
  myroot->head=NULL;
  myroot->tail=NULL;
}

void queue_put(queue *myroot,node *mynode) {
  mynode->next=NULL;
  if (myroot->tail!=NULL)
    myroot->tail->next=mynode;
  myroot->tail=mynode;
  if (myroot->:head==NULL)
    myroot->head=mynode;
}

node *queue_get(queue *myroot) {
  //get from root
  node *mynode;
  mynode=myroot->head;
  if (myroot->head!=NULL)
    myroot->head=myroot->head->next;
  return mynode;
}





回页首


data_control 代码

我编写的并不是线程安全的队列例程,事实上我创建了一个"数据包装"或"控制"结构,它可以是任何线程支持的数据结构。看一下 control.h:


control.h

#include 

typedef struct data_control {
  pthread_mutex_t mutex;
  pthread_cond_t cond;
  int active;
} data_control;

现在您看到了 data_control 结构定义,以下是它的视觉表示:


所使用的 data_control 结构

图像中的锁代表互斥对象,它允许对数据结构进行互斥访问。黄色的星代表条件变量,它可以睡眠,直到所讨论的数据结构改变为止。on/off 开关表示整数 "active",它告诉线程此数据是否是活动的。在代码中,我使用整数 active 作为标志,告诉工作队列何时应该关闭。以下是 control.c:


control.c

/* control.c
** Copyright 2000 Daniel Robbins, Gentoo Technologies, Inc.
** Author: Daniel Robbins
** Date: 16 Jun 2000
**
** These routines provide an easy way to make any type of
** data-structure thread-aware.  Simply associate a data_control
** structure with the data structure (by creating a new struct, for
** example).  Then, simply lock and unlock the mutex, or
** wait/signal/broadcast on the condition variable in the data_control
** structure as needed.
**
** data_control structs contain an int called "active".  This int is
** intended to be used for a specific kind of multithreaded design,
** where each thread checks the state of "active" every time it locks
** the mutex.  If active is 0, the thread knows that instead of doing
** its normal routine, it should stop itself.  If active is 1, it
** should continue as normal.  So, by setting active to 0, a
** controlling thread can easily inform a thread work crew to shut
** down instead of processing new jobs.  Use the control_activate()
** and control_deactivate() functions, which will also broadcast on
** the data_control struct's condition variable, so that all threads
** stuck in pthread_cond_wait() will wake up, have an opportunity to
** notice the change, and then terminate.
*/

#include "control.h"

int control_init(data_control *mycontrol) {
  int mystatus;
  if (pthread_mutex_init(&(mycontrol->mutex),NULL))
    return 1;
  if (pthread_cond_init(&(mycontrol->cond),NULL))
    return 1;
  mycontrol->active=0;
  return 0;
}

int control_destroy(data_control *mycontrol) {
  int mystatus;
  if (pthread_cond_destroy(&(mycontrol->cond)))
    return 1;
  if (pthread_cond_destroy(&(mycontrol->cond)))
    return 1;
  mycontrol->active=0;
  return 0;
}
int control_activate(data_control *mycontrol) {
  int mystatus;
  if (pthread_mutex_lock(&(mycontrol->mutex)))
    return 0;
  mycontrol->active=1;
  pthread_mutex_unlock(&(mycontrol->mutex));
  pthread_cond_broadcast(&(mycontrol->cond));
  return 1;
}

int control_deactivate(data_control *mycontrol) {
  int mystatus;
  if (pthread_mutex_lock(&(mycontrol->mutex)))
    return 0;
  mycontrol->active=0;
  pthread_mutex_unlock(&(mycontrol->mutex));
  pthread_cond_broadcast(&(mycontrol->cond));
  return 1;
}






回页首


调试时间

在开始调试之前,还需要一个文件。以下是 dbug.h:


dbug.h

#define dabort() \
 {  printf("Aborting at line %d in source file %s\n",__LINE__,__FILE__); abort(); }

此代码用于处理工作组代码中的不可纠正错误。





回页首


工作组代码

说到工作组代码,以下就是:


workcrew.c

#include <stdio.h>
#include <stdlib.h>
#include "control.h"
#include "queue.h"
#include "dbug.h"

/* the work_queue holds tasks for the various threads to complete. */

struct work_queue {
  data_control control;
  queue work;
} wq;


/* I added a job number to the work node.  Normally, the work node
   would contain additional data that needed to be processed. */

typedef struct work_node {
  struct node *next;
  int jobnum;
} wnode;

/* the cleanup queue holds stopped threads.  Before a thread
   terminates, it adds itself to this list.  Since the main thread is
   waiting for changes in this list, it will then wake up and clean up
   the newly terminated thread. */

struct cleanup_queue {
  data_control control;
  queue cleanup;
} cq;

/* I added a thread number (for debugging/instructional purposes) and
   a thread id to the cleanup node.  The cleanup node gets passed to
   the new thread on startup, and just before the thread stops, it
   attaches the cleanup node to the cleanup queue.  The main thread
   monitors the cleanup queue and is the one that performs the
   necessary cleanup. */

typedef struct cleanup_node {
  struct node *next;
  int threadnum;
  pthread_t tid;
} cnode;

void *threadfunc(void *myarg) {

  wnode *mywork;
  cnode *mynode;

  mynode=(cnode *) myarg;

  pthread_mutex_lock(&wq.control.mutex);

  while (wq.control.active) {
    while (wq.work.head==NULL && wq.control.active) {
      pthread_cond_wait(&wq.control.cond, &wq.control.mutex);
    }
    if (!wq.control.active) 
      break;
    //we got something!
    mywork=(wnode *) queue_get(&wq.work);
    pthread_mutex_unlock(&wq.control.mutex);
    //perform processing...
    printf("Thread number %d processing job %d\n",mynode->threadnum,mywork->jobnum);
    free(mywork);
    pthread_mutex_lock(&wq.control.mutex);
  }

  pthread_mutex_unlock(&wq.control.mutex);

  pthread_mutex_lock(&cq.control.mutex);
  queue_put(&cq.cleanup,(node *) mynode);
  pthread_mutex_unlock(&cq.control.mutex);
  pthread_cond_signal(&cq.control.cond);
  printf("thread %d shutting down...\n",mynode->threadnum);
  return NULL;
  
}

#define NUM_WORKERS 4

int numthreads;

void join_threads(void) {
  cnode *curnode;

  printf("joining threads...\n");

  while (numthreads) {
    pthread_mutex_lock(&cq.control.mutex);

    /* below, we sleep until there really is a new cleanup node.  This
       takes care of any false wakeups... even if we break out of
       pthread_cond_wait(), we don't make any assumptions that the
       condition we were waiting for is true.  */

    while (cq.cleanup.head==NULL) {
      pthread_cond_wait(&cq.control.cond,&cq.control.mutex);
    }

    /* at this point, we hold the mutex and there is an item in the
       list that we need to process.  First, we remove the node from
       the queue.  Then, we call pthread_join() on the tid stored in
       the node.  When pthread_join() returns, we have cleaned up
       after a thread.  Only then do we free() the node, decrement the
       number of additional threads we need to wait for and repeat the
       entire process, if necessary */

      curnode = (cnode *) queue_get(&cq.cleanup);
      pthread_mutex_unlock(&cq.control.mutex);
      pthread_join(curnode->tid,NULL);
      printf("joined with thread %d\n",curnode->threadnum);
      free(curnode);
      numthreads--;
  }
}


int create_threads(void) {
  int x;
  cnode *curnode;

  for (x=0; x<NUM_WORKERS; x++) {
    curnode=malloc(sizeof(cnode));
    if (!curnode)
      return 1;
    curnode->threadnum=x;
    if (pthread_create(&curnode->tid, NULL, threadfunc, (void *) curnode))
      return 1;
    printf("created thread %d\n",x);
    numthreads++;
  }
  return 0;
}

void initialize_structs(void) {
  numthreads=0;
  if (control_init(&wq.control))
    dabort();
  queue_init(&wq.work);
  if (control_init(&cq.control)) {
    control_destroy(&wq.control);
    dabort();
  }
  queue_init(&wq.work);
  control_activate(&wq.control);
}

void cleanup_structs(void) {
  control_destroy(&cq.control);
  control_destroy(&wq.control);
}


int main(void) {

  int x;
  wnode *mywork;

  initialize_structs();

  /* CREATION */
  
  if (create_threads()) {
    printf("Error starting threads... cleaning up.\n");
    join_threads();
    dabort();
  }

  pthread_mutex_lock(&wq.control.mutex);
  for (x=0; x<16000; x++) {
    mywork=malloc(sizeof(wnode));
    if (!mywork) {
      printf("ouch! can't malloc!\n");
      break;
    }
    mywork->jobnum=x;
    queue_put(&wq.work,(node *) mywork);
  }
  pthread_mutex_unlock(&wq.control.mutex);
  pthread_cond_broadcast(&wq.control.cond);

  printf("sleeping...\n");
  sleep(2);
  printf("deactivating work queue...\n");
  control_deactivate(&wq.control);
  /* CLEANUP  */

  join_threads();
  cleanup_structs();

}





回页首


代码初排

现在来快速初排代码。定义的第一个结构称作 "wq",它包含了 data_control 和队列头。data_control 结构用于仲裁对整个队列的访问,包括队列中的节点。下一步工作是定义实际的工作节点。要使代码符合本文中的示例,此处所包含的都是作业号。

接着,创建清除队列。注释说明了它的工作方式。好,现在让我们跳过 threadfunc()、join_threads()、create_threads() 和 initialize_structs() 调用,直接跳到 main()。所做的第一件事就是初始化结构 -- 这包括初始化 data_controls 和队列,以及激活工作队列。





回页首


有关清除的注意事项

现在初始化线程。如果看一下 create_threads() 调用,似乎一切正常 -- 除了一件事。请注意,我们正在分配清除节点,以及初始化它的线程号和 TID 组件。我们还将清除节点作为初始自变量传递给每一个新的工作程序线程。为什么这样做?

因为当某个工作程序线程退出时,它会将其清除节点连接到清除队列,然后终止。那时,主线程会在清除队列中检测到这个节点(利用条件变量),并将这个节点移出队列。因为 TID(线程标识)存储在清除节点中,所以主线程可以确切知道哪个线程已终止了。然后,主线程将调用 pthread_join(tid),并联接适当的工作程序线程。如果没有做记录,那么主线程就需要按任意顺序联接工作程序线程,可能是按它们的创建顺序。由于线程不一定按此顺序终止,那么主线程可能会在已经联接了十个线程时,等待联接另一个线程。您能理解这种设计决策是如何使关闭代码加速的吗(尤其在使用几百个工作程序线程的情况下)?





回页首


创建工作

我们已启动了工作程序线程(它们已经完成了执行 threadfunc(),稍后将讨论此函数),现在主线程开始将工作节点插入工作队列。首先,它锁定 wq 的控制互斥对象,然后分配 16000 个工作包,将它们逐个插入队列。完成之后,将调用 pthread_cond_broadcast(),于是所有正在睡眠的线程会被唤醒,并开始执行工作。此时,主线程将睡眠两秒钟,然后释放工作队列,并通知工作程序线程终止活动。接着,主线程会调用 join_threads() 函数来清除所有工作程序线程。





回页首


threadfunc()

现在来讨论 threadfunc(),这是所有工作程序线程都要执行的代码。当工作程序线程启动时,它会立即锁定工作队列互斥对象,获取一个工作节点(如果有的话),然后对它进行处理。如果没有工作,则调用 pthread_cond_wait()。您会注意到这个调用在一个非常紧凑的 while() 循环中,这是非常重要的。当从 pthread_cond_wait() 调用中苏醒时,决不能认为条件肯定发生了 -- 它 可能发生了,也可能没有发生。如果发生了这种情况,即错误地唤醒了线程,而列表是空的,那么 while 循环将再次调用 pthread_cond_wait()。

如果有一个工作节点,那么我们只打印它的作业号,释放它并退出。然而,实际代码会执行一些更实质性的操作。在 while() 循环结尾,我们锁定了互斥对象,以便检查 active 变量,以及在循环顶部检查新的工作节点。如果执行完此代码,就会发现如果 wq.control.active 是 0,while 循环就会终止,并会执行 threadfunc() 结尾处的清除代码。

工作程序线程的清除代码部件非常有趣。首先,由于 pthread_cond_wait() 返回了锁定的互斥对象,它会对 work_queue 解锁。然后,它锁定清除队列,添加清除代码(包含了 TID,主线程将使用此 TID 来调用 pthread_join()),然后再对清除队列解锁。此后,它发信号给所有 cq 等待者 (pthread_cond_signal(&cq.control.cond)),于是主线程就知道有一个待处理的新节点。我们不使用 pthread_cond_broadcast(),因为没有这个必要 -- 只有一个线程(主线程)在等待清除队列中的新节点。当它调用 join_threads() 时,工作程序线程将打印关闭消息,然后终止,等待主线程发出的 pthread_join() 调用。





回页首


join_threads()

如果要查看关于如何使用条件变量的简单示例,请参考 join_threads() 函数。如果还有工作程序线程,join_threads() 会一直执行,等待清除队列中新的清除节点。如果有新节点,我们会将此节点移出队列、对清除队列解锁(从而使工作程序可以添加清除节点)、联接新的工作程序线程(使用存储在清除节点中的 TID)、释放清除节点、减少"现有"线程的数量,然后继续。





回页首


结束语

现在已经到了"POSIX 线程详解"系列的尾声,希望您已经准备好开始将多线程代码添加到您自己的应用程序中。有关详细信息,请参阅 参考资料部分,这部分内容还包含了本文中使用的所有源码的 tar 文件。下一个系列中再见!





回页首


参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 本文中使用的 源码的 tar 文件

  • 友好的 Linux pthread 在线帮助 ("man -k pthread") 是极好的参考资料。

  • 如果要彻底了解 POSIX 线程,我推荐此书: Programming with POSIX Threads ,David R. Butenhof (Addison-Wesley, 1997)。据证实,此书是现有最好的讨论 POSIX 线程的书籍。

  • W. Richard Stevens 撰写的 UNIX Network Programming - Networking APIs: Sockets and XTI ,(Prentice Hall, 1997) 一书还涵盖了 POSIX 线程。这是一本经典著作,但它讨论线程不如上述的 Programming with POSIX Threads那样详细。

  • 请参考 Daniel 在 developerWorks上发表的 POSIX 线程系列中的前几篇文章:
  • 请参阅 Sean Walton 撰写的有关 Linux 线程的文档,KB7rfa

  • 请学习亚里桑那大学的 Mark Hays 编写的 POSIX 线程 教程

  • 请在 Pthreads-Tcl 介绍中查看对 Tcl 的更改,此更改使 Tcl 能够与 POSIX 线程一起使用。

  • 请学习另一个教程 POSIX 线程入门, 此教程由位于阿姆赫斯特镇的马萨诸塞大学计算机科学系的 Tom Wagner 和 Don Towsley 编写。

  • FSU PThreads是实现 POSIX 线程 (基于 SunOS 4.1.x, Solaris 2.x, SCO UNIX, FreeBSD, Linux, 和 DOS)的 C 类库。

  • 请访问 LINUX POSIX 和 DCE 线程主页。

  • 请参阅 LinuxThreads 资料库

  • Proolix是一种简单的遵从 POSIX 标准的基于 i8086+ 的操作系统。




回页首


关于作者

Daniel Robbins 居住在新墨西哥州的 Albuquerque。他是 Gentoo Technologies, Inc. 的总裁兼 CEO,Gentoo 项目的总设计师,MacMillan 出版书籍的撰稿作者,他的著作有: Caldera OpenLinux Unleashed, SuSE Linux Unleashed, 和 Samba Unleashed。Daniel 自二年级起就与计算机某些领域结下不解之缘,那时他首先接触的是 Logo 程序语言,并沉溺于 Pac-Man 游戏中。这也许就是他至今仍担任 SONY Electronic Publishing/Psygnosis 的首席图形设计师的原因所在。Daniel 喜欢与妻子 Mary 和新出生的女儿 Hadassah 一起共度时光。可通过 drobbins@gentoo.org与 Daniel 联系。

Posix线程编程指南(1)

这是一个关于Posix线程编程的专栏。作者在阐明概念的基础上,将向您详细讲述Posix线程库API。本文是第一篇将向您讲述线程的创建与取消。

线程创建

1.1 线程与进程

相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

1.2 创建线程

POSIX通过pthread_create()函数创建线程,API定义如下:

int  pthread_create(pthread_t  *  thread, pthread_attr_t * attr, 
void * (*start_routine)(void *), void * arg)

与fork()调用创建一个进程的方法不同,pthread_create()创建的线程并不具备与主线程(即调用pthread_create()的线程)同样的执行序列,而是使其运行start_routine(arg)函数。thread返回创建的线程ID,而attr是创建线程时设置的线程属性(见下)。pthread_create()的返回值表示线程创建是否成功。尽管arg是void *类型的变量,但它同样可以作为任意类型的参数传给start_routine()函数;同时,start_routine()可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由pthread_join()获取。

1.3 线程创建属性

pthread_create()中的attr参数是一个结构指针,结构中的元素分别对应着新线程的运行属性,主要包括以下几项:

__detachstate,表示新线程是否与进程中其他线程脱离同步,如果置位则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。

__schedpolicy,表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过pthread_setschedparam()来改变。

__schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。

__inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。

__scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。

pthread_attr_t结构中还有一些值,但不使用pthread_create()来设置。

为了设置这些属性,POSIX定义了一系列属性设置函数,包括pthread_attr_init()、pthread_attr_destroy()和与各个属性相关的pthread_attr_get---/pthread_attr_set---函数。

1.4 线程创建的Linux实现

我们知道,Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用__clone()和fork(),最终都用不同的参数调用do_fork()核内API。当然,要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行的,因此,do_fork()提供了很多参数,包括CLONE_VM(共享内存空间)、CLONE_FS(共享文件系统信息)、CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程,即0号进程有效)。当使用fork系统调用时,内核调用do_fork()不使用任何共享属性,进程拥有独立的运行环境,而使用pthread_create()来创建线程时,则最终设置了所有这些属性来调用__clone(),而这些参数又全部传给核内的do_fork(),从而创建的"进程"拥有共享的运行环境,只有栈是独立的,由__clone()传入。

Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外pthread库中进行。pthread库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线程相关的信号(比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。





回页首


线程取消

2.1 线程取消的定义

一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。

2.2 线程取消的语义

线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。

线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。

2.3 取消点

根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及read()、write()等会引起阻塞的系统调用都是Cancelation-point,而其他pthread函数都不会引起Cancelation动作。但是pthread_cancel的手册页声称,由于LinuxThread库与C库结合得不好,因而目前C库函数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调用中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用pthread_testcancel(),从而达到POSIX标准所要求的目标,即如下代码段:

pthread_testcancel();
    retcode = read(fd, buffer, length);
    pthread_testcancel();

2.4 程序设计方面的考虑

如果线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止。因此在这样的循环体的必经路径上应该加入pthread_testcancel()调用。

2.5 与线程取消相关的pthread函数

int pthread_cancel(pthread_t thread)
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。

int pthread_setcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。

int pthread_setcanceltype(int type, int *oldtype)
设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。

void pthread_testcancel(void)
检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回。

Posix线程编程指南(2)

这是一个关于Posix线程编程的专栏。作者在阐明概念的基础上,将向您详细讲述Posix线程库API。本文是第二篇将向您讲述线程的私有数据。

概念及作用

在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问,比如程序可能需要每个线程维护一个链表,而使用相同的函数操作,最简单的办法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由Posix线程库维护,称为线程私有数据(Thread-specific Data,或TSD)。





回页首


创建和注销

Posix定义了两个API分别用来创建和注销TSD:


int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *))

该函数从TSD池中分配一项,将其值赋给key供以后访问使用。如果destr_function不为空,在线程退出(pthread_exit())时将以key所关联的数据为参数调用destr_function(),以释放分配的缓冲区。

不论哪个线程调用pthread_key_create(),所创建的key都是所有线程可访问的,但各个线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量。在LinuxThreads的实现中,TSD池用一个结构数组表示:


static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = { { 0, NULL } };

创建一个TSD就相当于将结构数组中的某一项设置为"in_use",并将其索引返回给*key,然后设置destructor函数为destr_function。

注销一个TSD采用如下API:


int pthread_key_delete(pthread_key_t key)

这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function),而只是将TSD释放以供下一次调用pthread_key_create()使用。在LinuxThreads中,它还会将与之相关的线程数据项设为NULL(见"访问")。





回页首


访问

TSD的读写都通过专门的Posix Thread函数进行,其API定义如下:

int  pthread_setspecific(pthread_key_t  key,  const   void  *pointer)
void * pthread_getspecific(pthread_key_t key)

写入(pthread_setspecific())时,将pointer的值(不是所指的内容)与key相关联,而相应的读出函数则将与key相关联的数据读出来。数据类型都设为void *,因此可以指向任何类型的数据。

在LinuxThreads中,使用了一个位于线程描述结构(_pthread_descr_struct)中的二维void *指针数组来存放与key关联的数据,数组大小由以下几个宏来说明:

#define PTHREAD_KEY_2NDLEVEL_SIZE       32
#define PTHREAD_KEY_1STLEVEL_SIZE   \
((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1)
/ PTHREAD_KEY_2NDLEVEL_SIZE)
    其中在/usr/include/bits/local_lim.h中定义了PTHREAD_KEYS_MAX为1024,因此一维数组大小为32。而具体存放的位置由key值经过以下计算得到:
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE
idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE

也就是说,数据存放与一个32×32的稀疏矩阵中。同样,访问的时候也由key值经过类似计算得到数据所在位置索引,再取出其中内容返回。





回页首


使用范例

以下这个例子没有什么实际意义,只是说明如何使用,以及能够使用这一机制达到存储线程私有数据的目的。

#include <stdio.h>
#include <pthread.h>

pthread_key_t   key;

void echomsg(int t)
{
        printf("destructor excuted in thread %d,param=%d\n",pthread_self(),t);
}

void * child1(void *arg)
{
        int tid=pthread_self();
        printf("thread %d enter\n",tid);
        pthread_setspecific(key,(void *)tid);
        sleep(2);
        printf("thread %d returns %d\n",tid,pthread_getspecific(key));
        sleep(5);
}

void * child2(void *arg)
{
        int tid=pthread_self();
        printf("thread %d enter\n",tid);
        pthread_setspecific(key,(void *)tid);
        sleep(1);
        printf("thread %d returns %d\n",tid,pthread_getspecific(key));
        sleep(5);
}

int main(void)
{
        int tid1,tid2;

        printf("hello\n");
        pthread_key_create(&key,echomsg);
        pthread_create(&tid1,NULL,child1,NULL);
        pthread_create(&tid2,NULL,child2,NULL);
        sleep(10);
        pthread_key_delete(key);
        printf("main thread exit\n");
        return 0;
}

给例程创建两个线程分别设置同一个线程私有数据为自己的线程ID,为了检验其私有性,程序错开了两个线程私有数据的写入和读出的时间,从程序运行结果可以看出,两个线程对TSD的修改互不干扰。同时,当线程退出时,清理函数会自动执行,参数为tid。

Posix线程编程指南(3)

这是一个关于Posix线程编程的专栏。作者在阐明概念的基础上,将向您详细讲述Posix线程库API。本文是第三篇将向您讲述线程同步。

互斥锁

尽管在Posix Thread中同样可以使用IPC的信号量机制来实现互斥锁mutex功能,但显然semphore的功能过于强大了,在Posix Thread中定义了另外一套专门用于线程同步的mutex函数。

1. 创建和销毁

有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下: pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; 在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

pthread_mutex_destroy()用于注销一个互斥锁,API定义如下: int pthread_mutex_destroy(pthread_mutex_t *mutex) 销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

2. 互斥锁属性

互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

  • PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
  • PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
  • PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

3. 锁操作

锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

4. 其他

POSIX线程锁机制的Linux实现都不是取消点,因此,延迟取消类型的线程不会因收到取消信号而离开加锁等待。值得注意的是,如果线程在加锁后解锁前被取消,锁将永远保持锁定状态,因此如果在关键区段内有取消点存在,或者设置了异步取消类型,则必须在退出回调函数中解锁。

这个锁机制同时也不是异步信号安全的,也就是说,不应该在信号处理过程中使用互斥锁,否则容易造成死锁。





回页首


条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

1. 创建和注销

条件变量和互斥锁一样,都有静态动态两种创建方式,静态方式使用PTHREAD_COND_INITIALIZER常量,如下:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER

动态方式调用pthread_cond_init()函数,API定义如下:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。

注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下:
int pthread_cond_destroy(pthread_cond_t *cond)

2. 等待和激发

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)

等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。

无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。

激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。

3. 其他

pthread_cond_wait()和pthread_cond_timedwait()都被实现为取消点,因此,在该处等待的线程将立即重新运行,在重新锁定mutex后离开pthread_cond_wait(),然后执行取消动作。也就是说如果pthread_cond_wait()被取消,mutex是保持锁定状态的,因而需要定义退出回调函数来为其解锁。

以下示例集中演示了互斥锁和条件变量的结合使用,以及取消对于条件等待动作的影响。在例子中,有两个线程被启动,并等待同一个条件变量,如果不使用退出回调函数(见范例中的注释部分),则tid2将在pthread_mutex_lock()处永久等待。如果使用回调函数,则tid2的条件等待及主线程的条件激发都能正常工作。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t mutex;
pthread_cond_t  cond;

void * child1(void *arg)
{
        pthread_cleanup_push(pthread_mutex_unlock,&mutex);  /* comment 1 */
        while(1){
                printf("thread 1 get running \n");
        printf("thread 1 pthread_mutex_lock returns %d\n",
pthread_mutex_lock(&mutex));
        pthread_cond_wait(&cond,&mutex);
                    printf("thread 1 condition applied\n");
        pthread_mutex_unlock(&mutex);
                    sleep(5);
    }
        pthread_cleanup_pop(0);     /* comment 2 */
}

void *child2(void *arg)
{
        while(1){
                sleep(3);               /* comment 3 */
                printf("thread 2 get running.\n");
        printf("thread 2 pthread_mutex_lock returns %d\n",
pthread_mutex_lock(&mutex));
        pthread_cond_wait(&cond,&mutex);
        printf("thread 2 condition applied\n");
        pthread_mutex_unlock(&mutex);
        sleep(1);
        }
}

int main(void)
{
        int tid1,tid2;

        printf("hello, condition variable test\n");
        pthread_mutex_init(&mutex,NULL);
        pthread_cond_init(&cond,NULL);
        pthread_create(&tid1,NULL,child1,NULL);
        pthread_create(&tid2,NULL,child2,NULL);
        do{
        sleep(2);                   /* comment 4 */
                pthread_cancel(tid1);       /* comment 5 */
                sleep(2);                   /* comment 6 */
        pthread_cond_signal(&cond);
    }while(1);  
        sleep(100);
        pthread_exit(0);
}

如果不做注释5的pthread_cancel()动作,即使没有那些sleep()延时操作,child1和child2都能正常工作。注释3和注释4的延迟使得child1有时间完成取消动作,从而使child2能在child1退出之后进入请求锁操作。如果没有注释1和注释2的回调函数定义,系统将挂起在child2请求锁的地方;而如果同时也不做注释3和注释4的延时,child2能在child1完成取消动作以前得到控制,从而顺利执行申请锁的操作,但却可能挂起在pthread_cond_wait()中,因为其中也有申请mutex的操作。child1函数给出的是标准的条件变量的使用方式:回调函数保护,等待条件前锁定,pthread_cond_wait()返回后解锁。

条件变量机制不是异步信号安全的,也就是说,在信号处理函数中调用pthread_cond_signal()或者pthread_cond_broadcast()很可能引起死锁。





回页首


信号灯

信号灯与互斥锁和条件变量的主要不同在于"灯"的概念,灯亮则意味着资源可用,灯灭则意味着不可用。如果说后两中同步方式侧重于"等待"操作,即资源不可用的话,信号灯机制则侧重于点灯,即告知资源可用;没有等待线程的解锁或激发条件都是没有意义的,而没有等待灯亮的线程的点灯操作则有效,且能保持灯亮状态。当然,这样的操作原语也意味着更多的开销。

信号灯的应用除了灯亮/灯灭这种二元灯以外,也可以采用大于1的灯数,以表示资源数大于1,这时可以称之为多元灯。

1. 创建和注销

POSIX信号灯标准定义了有名信号灯和无名信号灯两种,但LinuxThreads的实现仅有无名灯,同时有名灯除了总是可用于多进程之间以外,在使用上与无名灯并没有很大的区别,因此下面仅就无名灯进行讨论。

int sem_init(sem_t *sem, int pshared, unsigned int value)
这是创建信号灯的API,其中value为信号灯的初值,pshared表示是否为多进程共享而不仅仅是用于一个进程。LinuxThreads没有实现多进程共享信号灯,因此所有非0值的pshared输入都将使sem_init()返回-1,且置errno为ENOSYS。初始化好的信号灯由sem变量表征,用于以下点灯、灭灯操作。

int sem_destroy(sem_t * sem)
被注销的信号灯sem要求已没有线程在等待该信号灯,否则返回-1,且置errno为EBUSY。除此之外,LinuxThreads的信号灯注销函数不做其他动作。

2. 点灯和灭灯

int sem_post(sem_t * sem)

点灯操作将信号灯值原子地加1,表示增加一个可访问的资源。

int sem_wait(sem_t * sem)
int sem_trywait(sem_t * sem)

sem_wait()为等待灯亮操作,等待灯亮(信号灯值大于0),然后将信号灯原子地减1,并返回。sem_trywait()为sem_wait()的非阻塞版,如果信号灯计数大于0,则原子地减1并返回0,否则立即返回-1,errno置为EAGAIN。

3. 获取灯值

int sem_getvalue(sem_t * sem, int * sval)

读取sem中的灯计数,存于*sval中,并返回0。

4. 其他

sem_wait()被实现为取消点,而且在支持原子"比较且交换"指令的体系结构上,sem_post()是唯一能用于异步信号处理函数的POSIX异步信号安全的API。





回页首


异步信号

由于LinuxThreads是在核外使用核内轻量级进程实现的线程,所以基于内核的异步信号操作对于线程也是有效的。但同时,由于异步信号总是实际发往某个进程,所以无法实现POSIX标准所要求的"信号到达某个进程,然后再由该进程将信号分发到所有没有阻塞该信号的线程中"原语,而是只能影响到其中一个线程。

POSIX异步信号同时也是一个标准C库提供的功能,主要包括信号集管理(sigemptyset()、sigfillset()、sigaddset()、sigdelset()、sigismember()等)、信号处理函数安装(sigaction())、信号阻塞控制(sigprocmask())、被阻塞信号查询(sigpending())、信号等待(sigsuspend())等,它们与发送信号的kill()等函数配合就能实现进程间异步信号功能。LinuxThreads围绕线程封装了sigaction()何raise(),本节集中讨论LinuxThreads中扩展的异步信号函数,包括pthread_sigmask()、pthread_kill()和sigwait()三个函数。毫无疑问,所有POSIX异步信号函数对于线程都是可用的。

int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask)
设置线程的信号屏蔽码,语义与sigprocmask()相同,但对不允许屏蔽的Cancel信号和不允许响应的Restart信号进行了保护。被屏蔽的信号保存在信号队列中,可由sigpending()函数取出。

int pthread_kill(pthread_t thread, int signo)
向thread号线程发送signo信号。实现中在通过thread线程号定位到对应进程号以后使用kill()系统调用完成发送。

int sigwait(const sigset_t *set, int *sig)
挂起线程,等待set中指定的信号之一到达,并将到达的信号存入*sig中。POSIX标准建议在调用sigwait()等待信号以前,进程中所有线程都应屏蔽该信号,以保证仅有sigwait()的调用者获得该信号,因此,对于需要等待同步的异步信号,总是应该在创建任何线程以前调用pthread_sigmask()屏蔽该信号的处理。而且,调用sigwait()期间,原来附接在该信号上的信号处理函数不会被调用。

如果在等待期间接收到Cancel信号,则立即退出等待,也就是说sigwait()被实现为取消点。





回页首


其他同步方式

除了上述讨论的同步方式以外,其他很多进程间通信手段对于LinuxThreads也是可用的,比如基于文件系统的IPC(管道、Unix域Socket等)、消息队列(Sys.V或者Posix的)、System V的信号灯等。只有一点需要注意,LinuxThreads在核内是作为共享存储区、共享文件系统属性、共享信号处理、共享文件描述符的独立进程看待的。

Posix线程编程指南(4

这是一个关于Posix线程编程的专栏。作者在阐明概念的基础上,将向您详细讲述Posix线程库API。本文是第四篇将向您讲述线程中止。

线程终止方式

一般来说,Posix的线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。





回页首


线程终止时的清理

不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。

最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,或者在打开独占锁以前的运行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。

在POSIX线程API中提供了一个pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源--从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。API定义如下:

void pthread_cleanup_push(void (*routine) (void  *),  void *arg)
void pthread_cleanup_pop(int execute)

pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。

pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

#define pthread_cleanup_push(routine,arg)                                     \
  { struct _pthread_cleanup_buffer _buffer;                                   \
    _pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute)                                          \
    _pthread_cleanup_pop (&_buffer, (execute)); }

可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。在下面的例子里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。

pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_mutex_unlock(&mut);
pthread_cleanup_pop(0);

必须要注意的是,如果线程处于PTHREAD_CANCEL_ASYNCHRONOUS状态,上述代码段就有可能出错,因为CANCEL事件有可能在pthread_cleanup_push()和pthread_mutex_lock()之间发生,或者在pthread_mutex_unlock()和pthread_cleanup_pop()之间发生,从而导致清理函数unlock一个并没有加锁的mutex变量,造成错误。因此,在使用清理函数的时候,都应该暂时设置成PTHREAD_CANCEL_DEFERRED模式。为此,POSIX的Linux实现中还提供了一对不保证可移植的pthread_cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()扩展函数,功能与以下代码段相当:

{ int oldtype;
 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
 pthread_cleanup_push(routine, arg);
 ...
 pthread_cleanup_pop(execute);
 pthread_setcanceltype(oldtype, NULL);
 }





回页首


线程终止的同步及其返回值

一般情况下,进程中各个线程的运行都是相互独立的,线程的终止并不会通知,也不会影响其他线程,终止的线程所占用的资源也并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。

void pthread_exit(void *retval) 
int pthread_join(pthread_t th, void **thread_return)
int pthread_detach(pthread_t th)

pthread_join()的调用者将挂起并等待th线程终止,retval是pthread_exit()调用者线程(线程ID为th)的返回值,如果thread_return不为NULL,则*thread_return=retval。需要注意的是一个线程仅允许唯一的一个线程使用pthread_join()等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态。

如果进程中的某个线程执行了pthread_detach(th),则th线程将处于DETACHED状态,这使得th线程在结束运行时自行释放所占用的内存资源,同时也无法由pthread_join()同步,pthread_detach()执行之后,对th请求pthread_join()将返回错误。

一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止,要么已设为DETACHED,要么就需要使用pthread_join()来回收。





回页首


关于pthread_exit()和return

理论上说,pthread_exit()和线程宿体函数退出的功能是相同的,函数结束时会在内部自动调用pthread_exit()来清理线程相关的资源。但实际上二者由于编译器的处理有很大的不同。

在进程主函数(main())中调用pthread_exit(),只会使主函数所在的线程(可以说是进程的主线程)退出;而如果是return,编译器将使其调用进程退出的代码(如_exit()),从而导致进程及其所有线程结束运行。

其次,在线程宿主函数中主动调用return,如果return语句包含在pthread_cleanup_push()/pthread_cleanup_pop()对中,则不会引起清理函数的执行,反而会导致segment fault。

Posix线程编程指南(5)

 
这是一个关于Posix线程编程的专栏。作者在阐明概念的基础上,将向您详细讲述Posix线程库API。本文是第五篇将向您讲述pthread_self()、pthread_equal()和pthread_once()等杂项函数。
 
在Posix线程规范中还有几个辅助函数难以归类,暂且称其为杂项函数,主要包括pthread_self()、pthread_equal()和pthread_once()三个,另外还有一个LinuxThreads非可移植性扩展函数pthread_kill_other_threads_np()。本文就介绍这几个函数的定义和使用。

获得本线程ID

pthread_t pthread_self(void)

本函数返回本线程的标识符。

在LinuxThreads中,每个线程都用一个pthread_descr结构来描述,其中包含了线程状态、线程ID等所有需要的数据结构,此函数的实现就是在线程栈帧中找到本线程的pthread_descr结构,然后返回其中的p_tid项。

pthread_t类型在LinuxThreads中定义为无符号长整型。





回页首


判断两个线程是否为同一线程

int pthread_equal(pthread_t thread1, pthread_t thread2)

判断两个线程描述符是否指向同一线程。在LinuxThreads中,线程ID相同的线程必然是同一个线程,因此,这个函数的实现仅仅判断thread1和thread2是否相等。





回页首


仅执行一次的操作

int pthread_once(pthread_once_t *once_control, void (*init_routine) (void))

本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。

#include <stdio.h>
#include <pthread.h>

pthread_once_t  once=PTHREAD_ONCE_INIT;

void    once_run(void)
{
        printf("once_run in thread %d\n",pthread_self());
}

void * child1(void *arg)
{
        int tid=pthread_self();
        printf("thread %d enter\n",tid);
        pthread_once(&once,once_run);
        printf("thread %d returns\n",tid);
}

void * child2(void *arg)
{
        int tid=pthread_self();
        printf("thread %d enter\n",tid);
        pthread_once(&once,once_run);
        printf("thread %d returns\n",tid);
}

int main(void)
{
        int tid1,tid2;

        printf("hello\n");
        pthread_create(&tid1,NULL,child1,NULL);
        pthread_create(&tid2,NULL,child2,NULL);
        sleep(10);
        printf("main thread exit\n");
        return 0;
}

once_run()函数仅执行一次,且究竟在哪个线程中执行是不定的,尽管pthread_once(&once,once_run)出现在两个线程中。

LinuxThreads使用互斥锁和条件变量保证由pthread_once()指定的函数执行且仅执行一次,而once_control则表征是否执行过。如果once_control的初值不是PTHREAD_ONCE_INIT(LinuxThreads定义为0),pthread_once()的行为就会不正常。在LinuxThreads中,实际"一次性函数"的执行状态有三种:NEVER(0)、IN_PROGRESS(1)、DONE(2),如果once初值设为1,则由于所有pthread_once()都必须等待其中一个激发"已执行一次"信号,因此所有pthread_once()都会陷入永久的等待中;如果设为2,则表示该函数已执行过一次,从而所有pthread_once()都会立即返回0。





回页首


pthread_kill_other_threads_np()

void pthread_kill_other_threads_np(void)

这个函数是LinuxThreads针对本身无法实现的POSIX约定而做的扩展。POSIX要求当进程的某一个线程执行exec*系统调用在进程空间中加载另一个程序时,当前进程的所有线程都应终止。由于LinuxThreads的局限性,该机制无法在exec中实现,因此要求线程执行exec前手工终止其他所有线程。pthread_kill_other_threads_np()的作用就是这个。

需要注意的是,pthread_kill_other_threads_np()并没有通过pthread_cancel()来终止线程,而是直接向管理线程发"进程退出"信号,使所有其他线程都结束运行,而不经过Cancel动作,当然也不会执行退出回调函数。尽管LinuxThreads的实验结果与文档说明相同,但代码实现中却是用的__pthread_sig_cancel信号来kill线程,应该效果与执行pthread_cancel()是一样的,其中原因目前还不清楚。





回页首






回页首


关于作者

杨沙洲,男,现攻读国防科大计算机学院计算机软件方向博士学位。您可以通过电子邮件 pubb@163.net跟他联系。

10 janvier

ThinkPad电池保养细节

在IBM笔记本电脑电池使用,激活等操作中,由于种种原因,往往在操作的细节上出现错误,不知不觉无意中伤害了你的电池。

  下面我们就举两个例子来详细分析,以说明"操作细节"的重要性。

  例一:当笔记本处交流供电状态,关机后,应何时拔出交流插头?工作完毕关机后,过一会儿屏幕关闭,此时我们很容易以为已经关机成功,便马上随手拔掉交流插头,殊不知此时屏幕虽然关闭,但主板加电指示灯(圆圈中带Z字模样的指示灯)依然燃亮,此时笔记本实际上还在工作,也就是说机器此时还需电力供应;若此时拔下交流插头,笔记本会马上从交流供电状态转为电池供电状态,无端地使用几秒钟电池,这样虽然好象用不了多少电量,但却加速了电池更快地跌落至设置的起始充电电量值,从而缩短了电池的充用周期,影响了电池的使用寿命。交流插头早拔了不行,那晚拔又会如何呢?当主板加电指示灯熄灭后(电源指示灯还燃亮),若不拔掉交流插头,系统的电源管理程序和电池里的由电池管理芯片所组成的接口电路约5秒后会自动转入"关机充电状态",所以关机后迟迟不拔出交流插头会无端地对电量>96%的电池进行了一小段时间的充电,而伤害了你的电池。[电量没有完全耗尽前(即电量在5~100%),不要对电池进行充电,否则会缩短电池的寿命。]

  综上述,正确的操作方法是:除了电源指示灯以外,屏幕下方的所有指示灯熄灭后,即随拔掉交流插头(不要迟于5秒),交流插头拔出约5秒后,电源指示灯自动熄灭。

  据上述分析可知,本来使用交流供电方式,其原意是尽量少使用电池,从而延长电池的充用周期;而由于在操作细节上欠妥,"爱卿未竟反害了卿";"操作细节"的重要性由此可见一斑。

  例二:当使用交流供电时,交流适配器的交流输入插头和直流输出插头插(卸)先后顺序的操作细节上是有讲究的。正确的操作方法是:先把交流适配器的直流输出插头插到笔记本的电源插孔,然后把交流输入插头插到220V交流电源插座上;而卸下时则应先把交流输入插头拔下,然后再把直流输出插头拔出。如果在插(卸)时颠倒了先后顺序,交流适配器则会处空载状态,交流适配器是不允许处空载状态的,因其工作于脉冲状态,空载时其内的开关变压器绕组会产生极高的反峰电压而击穿诸如IC,电容等电压敏感元件。虽然现时的交流适配器其内已设有空载保护电路,但由于设计上要兼顾到其它种种问题,这种保护电路的保护作用是很有限的;故此当交流适配器处空载状态时有可能被损坏。

  从上面的分析我们可了解到,在操作中的任何细节都是那么的重要,来不得半点含糊和马虎;说的大一点,这就是我们平时所讲的科学技术的慎密性。为此,我觉得有必要将我以前写下的一些关于电池操作细则的文字作适当的修改,整理;供大家参考,以提请对操作细节的注意;同时也请各位老师批评指正和补充。因编幅所限,下文中不可能再如上述举例中那样进行详细分析,这点还请大家见谅。

  激活处理:

  以下情况的电池需要进行激活处理:

  1.刚启用的新电池。

  2.因长时间不使用电池,电池离开电脑处储存状态,现再启用此电池时。

  3.较长时间不使用笔记本电脑或电池从笔记本电脑移出存放备用(时间超过两三个月),现再启用此电池时。

  对电池进行激活处理的方法:

  激活电池内的化学物质,最大化电池的性能,需要通过对电池重复充放电(完全充电,然后再完全放电)三次。所谓完全充放电是指:正常开机令电池耗电至3%电池电量(最好一次耗尽,也可分几次来耗电,但不宜间隔太长时间),然后必须马上对电池进行关机充电十二个小时。

  这里强调必须马上对电池进行充电,是因为电池即使不使用也会进行自我放电,当电池耗电至3%电量,此时若不及时对电池进行充电,自放电现象极易造成电池的过放电而损害电池,过放电正是锂电池的一大禁忌。

  对电池进行激活处理充电时,机子必须处关机状态。如果此时机子处等待,挂起等状态,当充电至3~4小时后绿色电源状态指示灯不再跳动时,机器会自动停止对电池的充电,令无法对电池进行完全充电。当放电至3%电量时,机器会马上自动进入休眠状态,此时应再启动机器,在机器启动的过程中单击屏幕左下方关机按钮,令机器关机。

  如果上述操作的各个细节都掌握得好,且你的电池质量又没有问题,就能很好地激活电池内的化学物质,最大化电池的性能,基本上电池在以后的正常使用中每次都能充到100%。
使用中应注意的问题:

  电池是一种"易耗品",电池的损耗程度取决于你使用电池时所进行的操作的正确性,操作方式,操作频率,工作环境温度,库存时间(对未使用过的电池)等因数。

  1. 电池的充放电次数直接关系到电池寿命,一般锂电池的充放电次数只有400~600次左右,改进型的产品也不过800多次,每充一次电池就向使用的终点前进了一步。(当对85%以下电量的电池进行充电,将被记录并增加一次充电次数。)

  为此,当电池电压大于电池管理程序中所设定的充电起始值96%,而且你当前所处场所有220V交流电源时,应尽量使用交流电源,尽量减少电池的充电次数,以延长电池的寿命。

  此时电池不必从机器上移开,因为开机时不会对电量大于96%的电池进行充电。不过即使这样,如果你是长期使用交流电源而极少使用电池时,建议还是将电池从机器上移开来比较好,因为笔记本长时间运行时产生的热量会对电池造成一定的伤害。但应该指出,在此种情况下一旦交流供电断电,你尚未保存的数据会由于没有电池的支持而丢失,这一得一失,用户必须好好权衡。

  2. 当电量为3~5%时,应及时给电池充电,否则电池的自放电现象会造成过放电而损害电池,充电时机器可以处关机,挂起等任何状态,也可以边充边用。

  充电必须一次充满,否则会损害电池。这是基于避免因缩短充用周期增加充电次数而缩短电池的寿命;而并非是由于"记忆特性"问题,锂电池不象镍镉电池和镍氢电池具有"记忆特性",锂电池不具有"记忆特性"。

  如充电时机器处关机或休眠状态,当充电至3~4小时后绿色电源状态指示灯不再跳动时,此时电量已达100%,应及时拔除交流电源插头以防对电池造成过充电而伤害电池。

  如充电时机器处等待,挂起或边充边用状态,则当充电至3~4小时后绿色电源状态指示灯不再跳动时,电池会自动退出充电状态。

  充电过程中会先进行快速充电,这时电源状态指示灯呈黄色(常亮),充至某一定值后转入涓细电流慢充,这时电源状态指示灯的颜色变得闪动的绿色;一直充满100%之后,就变成绿色(常亮)了。锂电池的充电过程是恒压方式,开始充电不久即电压即会稳定下来,而电流则越来越小。

  3. 电量没有完全耗尽前(即电量在5~100%),不要对电池进行充电,否则会因缩短充用周期增加充电次数而缩短电池的寿命。

  当电量为5~95%时应使用电池工作,如此时使用AC电源适配器会对电池进行充电(边充边用状态)。

  4. 当电量为96~100%或电池已从机器上移出,可使用AC电源适配器开机运行,使用AC电源适配器时请先把AC电源适配器的直流输出插头插到机器上,然后再把交流电源插头插到220V~插座上;卸下AC电源适配器时则应先拔下交流电源插头然后才从机器上拔除AC电源适配器的直流输出插头。否则可能会对你的设备造成损害。

  使用AC电源适配器时,当把交流电源插头插到220V~插座上,而机器上的电源状态指示灯尚未燃亮前(注:其间约5秒时间)请及时按下电源按钮开机;关机或进入休眠时,除了绿色电源状态指示灯外其他所有指示灯都已熄灭后,请及时从220V~插座上拔下交流电源插头(不要迟于5秒),交流电源插头拔出后约5秒,绿色电源状态指示灯熄灭;否则都会对电池造成过充电而损害电池。因关机状态下AC电源适配器仍处非卸下状态,系统的电源管理程序和电池里的由电源管理芯片所组成的接口电路约5秒后会自动转入"关机充电状态",对电池进行充电。(如果需要,可不从机器上拔下AC电源适配器的直流输出插头,此时电池不会通过AC电源适配器逆向放电。)

  如果暂停工作令机器进入等待或挂起状态,则不要从220V~插座上拔下交流电源插头;因此时机器仍须电源维持。(建议不要长时间令机器进入等待或挂起状态,以爱护你的机器。)

  5. IBM在ThinkPad笔记本电脑的相关资料的技术指标中所标出的"电池使用时间",是厂商在特指条件下所测出,该指标在我们一般正常使用笔记本的条件下是无法达到的,例如:相关资料的技术指标中所标出的"电池使用时间"为4小时,我们在一般正常使用中只能得到约3.5个小时的电池电力供应 。如果相差很远,而且尚在保修期内,请速联系IBM。

下一篇:节前选购轻薄笔记本应注意事项上篇

保养和储存

  1. 若电池较长时间(数个月)不用,应从机器上取下保存备用。建议每一个半月左右对电池进行一次放电和充电整理,以改善你的电池状态来增加电池容量。

  2. 若电池长时间不用,应将其电量设置至30~50%后储存,建议大约每年给电池充电一次,并将其电量设置至30~50%,以防过放电而损害电池。将电池电量设置至30~50%的具体操作方法为:当电池的电量大于50%时,可开机耗电至30~50%。若电量少于30%,则先开机耗电至3%,再充电至100%然后开机耗电至30~50%即可。

  3. 不要将电池放在高温和寒冷的环境中,电池性能会暂时降低。应将处保存备用状态和储存状态的电池放置于温度较低的地方,对于电池,最佳的温度范围约为10~20摄氏度。
关于充电起始值的调整

  设置充电起始值,请单击系统托盘中的电源插头图标(当你使用交流电开机时)或白色大电池图标(当你使用电池开机时)/选择Improve Battery Health/修改Start charging when below的数值。

  不要为延长充用周期而刻意大幅度地改动电池管理程序中所设定的充电起始值;因为当你把充电起始值设低(例如80%)后,虽然带电池使用AC电源适配器的"充用周期"可能长达三四个月,但若在使用该电池时势必又要对其重新激活处理,否则该电池的状态和容量都欠佳。这反而增加了充电次数,缩短了电池寿命。再者电池每一个半月左右需进行一次放电和充电整理,那因充电起始值设低(例如80%)后所延长的充用周期便更显得毫无意义。

  电池充满电后因自放电现象而电量下滑至96%一般历时约50天左右,而电池刚好每一个半月左右需进行一次放电和充电整理,由此看来,IBM为我们所预设的充电起始值为96%,是不无道理的。然而随着电池的衰老,电池充满电后因自放电现象而电量下滑至96%所需的时间可能会缩短,此时我们就有必要将充电起始值稍微下调,令该时间刚好在一个半月左右。

  如果你经常需在办公室和设备现场之间"两地"奔忙,在办公室用交流适配器供电操作,而在设备现场则用电池供电操作,建议你把充电起始值设置为6%;这样便可避免因缩短充用周期增加充电次数而缩短电池的寿命;而且不会因爱护电池而影响了笔记本的移动优势。

  请注意,文中所讨论的问题仅限于IBM ThinkPad笔记本,其他品牌的机器本文仅作参考,因为不同品牌机器,其系统的电源管理与控制程序以及电池的电池管理,监控程序也不尽相同之故。

笔记本电池的秘密

目前笔记本电脑均使用了锂离子电池来作为其动力来源,对于一台没有配备电池的笔记本来说,笔记本已经完全失去了其自身的意义,与一台移动PC并无二样,所以说笔记本
笔记本电池如何高效使用
虽然很多笔记本电脑厂商都声称自己的笔记本电脑的电池的使用时间可以长达多少多少小时。一台笔记本电脑在使用电池供电时,假设电池已经充满电,实际能用到3个小时也已经很不错了。所以正确地使用笔记本电脑,会延长笔记本的使用时间。
1、尽量不要使用外接设备
对于一些外接的设备(比如C卡设备、USB设备等),在使用电池的时候应该尽可能将它们拔掉,以节省电力。因为有些设备的耗电量很大,比如:USB接口或PC卡接口的外置硬盘盒、外置光驱等设备的耗电量在5V 500mA左右,会大大缩短电池的使用时间。
2、禁用暂不使用的设备和接口
一般来说,笔记本电脑当中的一些内接设备和接口并不经常使用,如果你使用笔记本的时候是用电池供电的,那么可以暂时将这些设备和接口禁用。你可以在BIOS里禁用它们,如果你是使用Windows 2000/XP操作系统,那么也可以在"设备管理器"中双击需禁用的设备条目。
3、关闭屏幕保护程序
很多人以为屏幕保护程序可能耗电并不大,其实,一些庞大复杂的屏幕保护程序可能会比你正常运行时更加耗电,所以建议不要开启屏幕保护功能。
4、选择合适的关机方式
想关闭笔记本电脑时,就会出现"待机"、"睡眠"、"关闭"3个选项,分别该如何选择使用呢?这需要根据对耗电量及恢复时间的要求而定。由于笔记本电脑的电池驱动时间有限,因此最好尽量控制耗电量。因此,不用的时候,必须尽量避免耗电。话又说回来,如果完全关机、再从头启动Windows,花费的时间就太长。而且,重启时对硬盘的访问量增加,也会相应地增加耗电量。因此,必须充分考虑耗电量和恢复时间的平衡,选择适当的关机方法。
5.禁用电源警报功能
在桌面上右击鼠标,在弹出的菜单选择"属性",然后依次选择"屏幕保护程序→电源→警报",禁用"电池不足警报"和"电池严重短缺警报"功能。当电池电量显示为0%的时候,仍然能强行使用一段时间,这时候你应该立即保存文档。因为电量完全耗尽时,笔记本电脑是不会给出任何提示就强行关机的。
6.活用Intel SpeedStep程序自动降频
如果笔记本电脑采用支持SpeedStep技术的CPU,那么就可以让处理器在2种工作模式之间随意地切换,即通电状态时的最高性能模式和电池状态时的电池优化模式。
笔记本电脑电池的保养
1.在使用笔记本电脑之前认真阅读使用手册的电池保养部分;
2.第一次充电时,你应该连续地把电池充电到12个小时,并且循环地完全充、放电3次以完全唤醒新电池;
3.如果长时间使用外接电源,最好取下电池。很多朋友都没注意到这个问题,用外接电源时不取下电池不但影响电池寿命,还让笔记本电脑的散热负担变得更重,缩短笔记本电脑的寿命;
4.定期充放电。即使没有记忆效应的锂离子电池存在一定的惰性效应,长时间不使用会使锂离子失去活性,需要重新激活。因此,如果长时间(3个星期或更长)不使用电脑或发现电池充放电时间变短,应使电池完全放电后再充电,一般每个月至少完整地充放电1次。
5.充电时最好关掉笔记本电脑,使电池能够完全充满电,不要在充电中途拔掉电源。关机充电会比开机充电缩短30%以上的充电时间,而且能延长电池的使用寿命。最好在充电完毕30分钟后再使用。
6.防止暴晒、防止受潮、防止化学液体侵蚀、避免电池触点与金属物接触发生短路等情况的发生。
锂电池的充放电次数一般不超过800次,每充一次电,它就缩短一次的寿命。建议大家尽量使用外接电源,如果使用中途需要多次插拔电源,且笔记本电脑内置电池没有拔出,会让电池折寿很大,因为每次外接电源接入就相当于给电池充电一次

IBM T43 T60 X60 滋滋响的解释

现象描述:

当我们不插电源,用电池供电时,T43在电源位置(好多人感觉是USB口的声音)会发出滋滋的噪音,而且声音很大!若将电源设置成always on,声音尤为明显!后来本人用intel的测试工具,


结合windows自带的perfmon工具测试发现,当CPU进入C3状态是,电源处就会发出声音,在运用SE.exe,在windous下根据intel的南桥规范,将CPU C3状态关闭,声音立即消失!


ACPI高级电源管理规范中规定CPU电源管理工作状态可分为 :

C0: CPU 正常执行指令.实际就是CPU全速运行时的状态;
C1: 有最低的唤醒时间. 在该状态下的硬件唤醒时间必须足够小,这样操作软件在决定是否使用该设备时可以完全忽略掉该状态下的硬件唤醒时间。除了将处理器置于一个非执行指令电源状态外且该状态下软件完全不受影响。
C2: 较 C1 更节省功耗,该状态下有比C1稍长的唤醒时间,这是由ACPI系统固件所决定的,操作软件可以依据这个信息来决定CPU该在什么时候由C2状态进入C1状态。除了将处理器置于一个非执行指令电源状态外且该状态下软件完全不受影响。
C3: 较 C1和C2节省更多功耗,该状态下的唤醒时间最长,. 这是由ACPI系统固件所决定的,操作软件可以依据这个信息来决定CPU该在什么时候由C3状态进入C2状态,在该状态下,处理器的缓存内容仍然保持,但是忽略任何侦听。操作软件负责保持缓存内容的一致性。
www.bjbhlzx.com
所以说关闭C3不会导致系统性能的下降,但可能会导致机器进入待机状态时消耗更多的电能(对电池续航时间有影响)

不知道Lenovo最新BIOS对于T43、R51这类机型主板电源滋滋声的解决方案是不是通过关闭C3状态解决的,如果是,那可能待机时电池电量会比以前下降的更快

摘录一段关于ACPI的文章:
www.bjbhlzx.com
许用过笔记本的人都有用过这样的功能:当你想离开笔记本电脑去做一些比较耗时的工作时,你通常都会让笔记本电脑进入待机模式(Standby)或休眠模式(Hibernation),但是你知道这时候你的笔记本电脑什么元件正在工作?什么元件已经停止工作?不同状态的功耗是多少了吗?了解这些对于现在我们保护我们自己的爱机的元件寿命和都在提倡的节电和环保都有很多积极的意义,好了,废话少说进入正题:
ACPI 是E文"Advanced Configuration and Power interface "的缩写,是由 INTEL,MICROSOFT,TOSHIBA所共同制定的. 是为了在操作系统和硬件之间有一个共同的电源管理接口. 以改进以前在电源管理上由各别的厂商所制定的不统一接口.

ACPI改善了原有的通过BIOS来进行电源管理的模式(APM),提供了一个比较优秀的电源管理模式和配置管理的接口规范.ACPI为从原有的硬件到ACPI兼容硬件之间进行有序的过渡提供了一种有效的方式 ,且它还允许在一台机器当数码之家存 ACPI和APM管理机制,已备需要时使用.

另外,新的系统架构还突破了当前即插即用接口介面的局限性,对其进行了扩展.ACPI为原来的母板配置接口进行了改善,使其能够支持这些高级的系统架构并以更有效的状态运行.

ACPI 由 Win98 及 WNT5.0 开始支持. 把电源管理的功能整合到操作系统中. 藉由统一的接口来控制所有硬件的电源操作. 从 Notebook 到桌上型和服务器均包含在此规格内,是操作系统直接进行电源管理(OSPM)中的关键.
www.bjbhlzx.com
所有的状态可分为 G ( Global) , D ( Device ) , S ( Sleeping ) , C ( CPU ).

Global 是指所有系统. 又可分为:
www.bjbhlzx.com
G0 - Working 工作状态. 使用者程序可正常的执行. 但是设备可以动态分配它们自己的状态. 在没有用到此设备时. 此设备可进入其它非工作状态。该状态下,系统实时响应外部事件(该状态下,不能拆装机)
G1 - Sleeping 此状态下系统销耗较小的电源. 没有任何使用者的程序在执行.系统看起来就像在关机状态.因为此时显示屏幕是被关闭的 . 只要有任何唤醒激活的事件传达进入系统即很快会回复到工作状态. (该状态下,不能拆装机)
G2/S5 - Soft Off 此状态下系统只保留非常少的电源. 没有任何使用者和操作系统的程序在执行. 这个状态下需要较长的时间来回复到工作状态. (该状态下,不能拆装机)
G3 - Mechanical Off 整个系统的电源均关闭. 没有任何电流通过系统. 系统只能重新打开电源供应器的开关来激活. 此状态下电源的消耗为零.

Device 是指一些设备. 例如调制解调器 , 硬盘, 光驱等. 又可分为:
D0 - Fully-On 正常工作下.
D1 可节省较少的功耗,仍然保持ACTIVE的设备功能较D2要多的多,该状态由设备本身所决定,有些设备不能进入D1 STATE。
D2 某些功能被关闭. 可省较多的电源. 该状态由设备本身所决定,有些设备不能进入D2 STATE。
D3 - Off 此状态下设备的电源完全被移出, 所以下次电源再一次被供应时需要操作系统重新再对这个设备作一次设定(此状态下设备不对地址线进行译码)该状态需要最长的唤醒时间,所有的设备都可以进入该状态。

Sleeping 是指在 G1 下系统进入睡眠状态. 又可分为:
S0 - Full on 正常工作下,所有设备全开,功耗一般会超过80W
S1 – Sleeping(POS)Power on Suspend,浅休眠状态,在此状态下可很快的回复系统的运作, 系统(CPU OR CHIPSET)的内容均没有遗失.,但是CPU已经停止工作,其他的部件仍然正常工作,这时的功耗一般在30W以下。(其实有些CPU降温软件就是利用这种工作原理)
S2 - Sleeping 类似 S1 但是 CPU 和 Cache 的内容巳遗失. 系统回复后操作系统需要维护 CPU 和 Cache 的内容。这时CPU处于关闭状态,总线时钟也被关闭,但其余的设备仍然运转,唤醒事件发生后,首先由CPU 的reset信号开始动作。www.bjbhlzx.com
S3 – Sleeping(STR) Suspend to RAM,除了内存的资料外其余 CPU , Cache , Chipset 的内容均遗失. 内存的内容由硬件维护,唤醒事件发生后,首先由CPU 的reset信号开始动作。这时的功耗不超过10W。
S4 - Sleeping(STD) Suspend to DISK,此状态有最低的功耗, 最长的唤醒时间,所有的设备均被关闭。系统主电源关闭,但是系统信息会存入硬盘,硬盘仍然带电并可以被唤醒。
S5 - Soft Off 即是G2 的状态,和 S4 类似。连电源在内的所有设备全部关闭,但操作系统不维护任何内容,该状态下需要一个完整彻底的启动过程来重新唤醒系统,BIOS使用一个不同的状态值来区分S4和S5两种状态唤醒时是否将需要从保存的内存镜像来启动。这时的功耗为0。