2007年11月11日星期日

Standard MIDI File Format


                                                        Standard   MIDI   File   Format  
                                                                  Dustin   Caldwell  
   
              The   standard   MIDI   file   format   is   a   very   strange   beast.   When   viewed   as   a  
    whole,   it   can   be   quite   overwhelming.   Of   course,   no   matter   how   you   look   at   it,  
  describing   a   piece   of   music   in   enough   detail   to   be   able   to   reproduce   it  
  accurately   is   no   small   task.   So,   while   complicated,   the   structure   of   the   midi  
  file   format   is   fairly   intuitive   when   understood.    
              I   must   insert   a   disclaimer   here   that   I   am   by   no   means   an   expert   with  
    midi   nor   midi   files.   I   recently   obtained   a   Gravis   UltraSound   board   for   my   PC,  
  and   upon   hearing   a   few   midi   files   (.MID)   thought,   "Gee,   I'd   like   to   be   able   to  
  make   my   own   .MID   files."   Well,   many   aggravating   hours   later,   I   discovered   that  
  this   was   no   trivial   task.   But,   I   couldn't   let   a   stupid   file   format   stop   me.  
  (besides,   I   once   told   my   wife   that   computers   aren't   really   that   hard   to   use,  
  and   I'd   hate   to   be   a   hypocrite)   So   if   any   errors   are   found   in   this  
  information,   please   let   me   know   and   I   will   fix   it.   Also,   this   document's   scope  
  does   not   extend   to   EVERY   type   of   midi   command   and   EVERY   possible   file  
  configuration.   It   is   a   basic   guide   that   should   enable   the   reader   (with   a  
  moderate   investment   in   time)   to   generate   a   quality   midi   file.  
   
  1.   Overview  
   
              A   midi   (.MID)   file   contains   basically   2   things,   Header   chunks   and   Track  
  chunks.   Section   2   explains   the   header   chunks,   and   Section   3   explains   the   track  
  chunks.   A   midi   file   contains   ONE   header   chunk   describing   the   file   format,  
  etc.,   and   any   number   of   track   chunks.   A   track   may   be   thought   of   in   the   same  
  way   as   a   track   on   a   multi-track   tape   deck.   You   may   assign   one   track   to   each  
  voice,   each   staff,   each   instrument   or   whatever   you   want.    
   
  2.   Header   Chunk  
   
              The   header   chunk   appears   at   the   beginning   of   the   file,   and   describes   the  
  file   in   three   ways.   The   header   chunk   always   looks   like:  
   
  4D   54   68   64   00   00   00   06   ff   ff   nn   nn   dd   dd  
   
  The   ascii   equivalent   of   the   first   4   bytes   is   MThd.   After   MThd   comes   the   4-byte  
  size   of   the   header.   This   will   always   be   00   00   00   06,   because   the   actual   header  
  information   will   always   be   6   bytes.    
   
  ff   ff   is   the   file   format.   There   are   3   formats:  
   
  0   -   single-track    
  1   -   multiple   tracks,   synchronous  
  2   -   multiple   tracks,   asynchronous  
   
  Single   track   is   fairly   self-explanatory   -   one   track   only.   Synchronous   multiple  
  tracks   means   that   the   tracks   will   all   be   vertically   synchronous,   or   in   other  
  words,   they   all   start   at   the   same   time,   and   so   can   represent   different   parts  
  in   one   song.   Asynchronous   multiple   tracks   do   not   necessarily   start   at   the   same  
  time,   and   can   be   completely   asynchronous.    
   
  nn   nn   is   the   number   of   tracks   in   the   midi   file.  
   
  dd   dd   is   the   number   of   delta-time   ticks   per   quarter   note.   (More   about   this  
  later)  
   
   
  3.   Track   Chunks  
   
  The   remainder   of   the   file   after   the   header   chunk   consists   of   track   chunks.  
  Each   track   has   one   header   and   may   contain   as   many   midi   commands   as   you   like.  
  The   header   for   a   track   is   very   similar   to   the   one   for   the   file:  
   
  4D   54   72   6B   xx   xx   xx   xx  
   
  As   with   the   header,   the   first   4   bytes   has   an   ascii   equivalent.   This   one   is  
  MTrk.   The   4   bytes   after   MTrk   give   the   length   of   the   track   (not   including   the  
  track   header)   in   bytes.    
              Following   the   header   are   midi   events.   These   events   are   identical   to   the  
  actual   data   sent   and   received   by   MIDI   ports   on   a   synth   with   one   addition.   A  
  midi   event   is   preceded   by   a   delta-time.   A   delta   time   is   the   number   of   ticks  
  after   which   the   midi   event   is   to   be   executed.   The   number   of   ticks   per   quarter  
  note   was   defined   previously   in   the   file   header   chunk.   This   delta-time   is   a  
  variable-length   encoded   value.   This   format,   while   confusing,   allows   large  
  numbers   to   use   as   many   bytes   as   they   need,   without   requiring   small   numbers   to  
  waste   bytes   by   filling   with   zeros.   The   number   is   converted   into   7-bit   bytes,  
  and   the   most-significant   bit   of   each   byte   is   1   except   for   the   last   byte   of   the  
  number,   which   has   a   msb   of   0.   This   allows   the   number   to   be   read   one   byte   at   a  
  time,   and   when   you   see   a   msb   of   0,   you   know   that   it   was   the   last   (least  
  significant)   byte   of   the   number.   According   to   the   MIDI   spec,   the   entire   delta-  
  time   should   be   at   most   4   bytes   long.    
              Following   the   delta-time   is   a   midi   event.   Each   midi   event   (except   a  
  running   midi   event)   has   a   command   byte   which   will   always   have   a   msb   of   1   (the  
  value   will   be   >=   128).   A   list   of   most   of   these   commands   is   in   appendix   A.   Each  
  command   has   different   parameters   and   lengths,   but   the   data   that   follows   the  
  command   will   have   a   msb   of   0   (less   than   128).   The   exception   to   this   is   a   meta-  
  event,   which   may   contain   data   with   a   msb   of   1.   However,   meta-events   require   a  
  length   parameter   which   alleviates   confusion.    
              One   subtlety   which   can   cause   confusion   is   running   mode.   This   is   where  
  the   actual   midi   command   is   omitted,   and   the   last   midi   command   issued   is  
  assumed.   This   means   that   the   midi   event   will   consist   of   a   delta-time   and   the  
  parameters   that   would   go   to   the   command   if   it   were   included.    
   
  4.   Conclusion  
   
              If   this   explanation   has   only   served   to   confuse   the   issue   more,   the  
  appendices   contain   examples   which   may   help   clarify   the   issue.   Also,   2  
  utilities   and   a   graphic   file   should   have   been   included   with   this   document:    
   
  DEC.EXE   -   This   utility   converts   a   binary   file   (like   .MID)   to   a   tab-delimited  
  text   file   containing   the   decimal   equivalents   of   each   byte.  
   
  REC.EXE   -   This   utility   converts   a   tab-delimited   text   file   of   decimal   values  
  into   a   binary   file   in   which   each   byte   corresponds   to   one   of   the   decimal  
  values.  
   
  MIDINOTE.PS   -   This   is   the   postscript   form   of   a   page   showing   note   numbers   with  
  a   keyboard   and   with   the   standard   grand   staff.  
                                                                                                                                                    Appendix   A  
   
  1.   MIDI   Event   Commands  
   
  Each   command   byte   has   2   parts.   The   left   nybble   (4   bits)   contains   the   actual  
  command,   and   the   right   nybble   contains   the   midi   channel   number   on   which   the  
  command   will   be   executed.   There   are   16   midi   channels,   and   8   midi   commands   (the  
  command   nybble   must   have   a   msb   of   1).  
  In   the   following   table,   x   indicates   the   midi   channel   number.   Note   that   all  
  data   bytes   will   be   <128   (msb   set   to   0).  
   
  Hex             Binary               Data                     Description  
  8x               1000xxxx           nn   vv                   Note   off   (key   is   released)  
                                                                          nn=note   number  
                                                                          vv=velocity  
   
  9x               1001xxxx           nn   vv                   Note   on   (key   is   pressed)  
                                                                          nn=note   number  
                                                                          vv=velocity  
   
  Ax               1010xxxx           nn   vv                   Key   after-touch  
                                                                          nn=note   number  
                                                                          vv=velocity  
   
  Bx               1011xxxx           cc   vv                   Control   Change  
                                                                          cc=controller   number  
                                                                          vv=new   value  
   
  Cx               1100xxxx           pp                         Program   (patch)   change  
                                                                          pp=new   program   number  
   
  Dx               1101xxxx           cc                         Channel   after-touch  
                                                                          cc=channel   number  
   
  Ex               1110xxxx           bb   tt                   Pitch   wheel   change   (2000H   is   normal   or   no  
                                                                          change)  
                                                                          bb=bottom   (least   sig)   7   bits   of   value  
                                                                          tt=top   (most   sig)   7   bits   of   value  
  The   following   table   lists   meta-events   which   have   no   midi   channel   number.   They  
  are   of   the   format:  
   
  FF   xx   nn   dd  
   
  All   meta-events   start   with   FF   followed   by   the   command   (xx),   the   length,   or  
  number   of   bytes   that   will   contain   data   (nn),   and   the   actual   data   (dd).  
   
  Hex             Binary               Data                     Description  
  00               00000000           nn   ssss               Sets   the   track's   sequence   number.  
                                                                          nn=02   (length   of   2-byte   sequence   number)  
                                                                          ssss=sequence   number  
   
  01               00000001           nn   tt   ..             Text   event-   any   text   you   want.  
                                                                          nn=length   in   bytes   of   text  
                                                                          tt=text   characters  
   
  02               00000010           nn   tt   ..             Same   as   text   event,   but   used   for  
                                                                          copyright   info.  
                                                                          nn   tt=same   as   text   event  
   
  03               00000011           nn   tt   ..             Sequence   or   Track   name  
                                                                          nn   tt=same   as   text   event  
   
  04               00000100           nn   tt   ..             Track   instrument   name  
                                                                          nn   tt=same   as   text   event  
   
  05               00000101           nn   tt   ..             Lyric  
                                                                          nn   tt=same   as   text   event  
   
  06               00000110           nn   tt   ..             Marker  
                                                                          nn   tt=same   as   text   event  
   
  07               00000111           nn   tt   ..             Cue   point  
                                                                          nn   tt=same   as   text   event  
   
  2F               00101111           00                         This   event   must   come   at   the   end   of   each  
                                                                          track  
   
  51               01010001           03   tttttt           Set   tempo  
                                                                          tttttt=microseconds/quarter   note  
   
  58               01011000           04   nn   dd   ccbb   Time   Signature  
                                                                          nn=numerator   of   time   sig.  
                                                                          dd=denominator   of   time   sig.   2=quarter  
                                                                          3=eighth,   etc.  
                                                                          cc=number   of   ticks   in   metronome   click  
                                                                          bb=number   of   32nd   notes   to   the   quarter  
                                                                          note  
   
  59               01011001           02   sf   mi             Key   signature  
                                                                          sf=sharps/flats   (-7=7   flats,   0=key   of   C,  
                                                                          7=7   sharps)  
                                                                          mi=major/minor   (0=major,   1=minor)  
   
  7F               01111111           xx   dd   ..             Sequencer   specific   information  
                                                                          xx=number   of   bytes   to   be   sent  
                                                                          dd=data  
  The   following   table   lists   system   messages   which   control   the   entire   system.  
  These   have   no   midi   channel   number.   (these   will   generally   only   apply   to  
  controlling   a   midi   keyboard,   etc.)  
   
  Hex             Binary               Data                     Description  
  F8               11111000                                       Timing   clock   used   when   synchronization   is  
                                                                          required.  
   
  FA               11111010                                       Start   current   sequence  
   
  FB               11111011                                       Continue   a   stopped   sequence   where   left  
                                                                          off  
   
  FC               11111100                                       Stop   a   sequence  
   
   
  The   following   table   lists   the   numbers   corresponding   to   notes   for   use   in   note    
  on   and   note   off   commands.  
   
   
  Octave||                                           Note   Numbers  
        #     ||  
              ||   C       |   C#     |   D       |   D#     |   E       |   F       |   F#     |   G       |   G#     |   A       |   A#     |   B  
  -----------------------------------------------------------------------------  
        0     ||       0   |       1   |       2   |       3   |       4   |       5   |       6   |       7   |       8   |       9   |     10   |   11  
        1     ||     12   |     13   |     14   |     15   |     16   |     17   |     18   |     19   |     20   |     21   |     22   |   23  
        2     ||     24   |     25   |     26   |     27   |     28   |     29   |     30   |     31   |     32   |     33   |     34   |   35  
        3     ||     36   |     37   |     38   |     39   |     40   |     41   |     42   |     43   |     44   |     45   |     46   |   47  
        4     ||     48   |     49   |     50   |     51   |     52   |     53   |     54   |     55   |     56   |     57   |     58   |   59  
        5     ||     60   |     61   |     62   |     63   |     64   |     65   |     66   |     67   |     68   |     69   |     70   |   71  
        6     ||     72   |     73   |     74   |     75   |     76   |     77   |     78   |     79   |     80   |     81   |     82   |   83  
        7     ||     84   |     85   |     86   |     87   |     88   |     89   |     90   |     91   |     92   |     93   |     94   |   95  
        8     ||     96   |     97   |     98   |     99   |   100   |   101   |   102   |   103   |   104   |   105   |   106   |   107  
        9     ||   108   |   109   |   110   |   111   |   112   |   113   |   114   |   115   |   116   |   117   |   118   |   119  
      10     ||   120   |   121   |   122   |   123   |   124   |   125   |   126   |   127   |  
   
   
                                                                  BIBLIOGRAPHY  
   
      "MIDI   Systems   and   Control"   Francis   Rumsey     1990   Focal   Press  
   
      "MIDI   and   Sound   Book   for   the   Atari   ST"   Bernd   Enders   and   Wolfgang   Klemme  
                      1989   M&T   Publishing,   Inc.  
   
      MIDI   file   specs   and   general   MIDI   specs   were   also   obtained   by   sending   e-mail  
                    to   LISTSERV@AUVM.AMERICAN.EDU   with   the   phrase   GET   MIDISPEC   PACKAGE  
                    in   the   message.  
   
   
  -------------------------------   DEC.CPP   ------------------------------------  
   
  /*     file     dec.cpp  
   
  by     Dustin   Caldwell         (dustin@gse.utah.edu)  
   
  */  
   
   
  #include   <dos.h>  
  #include   <stdio.h>  
  #include   <stdlib.h>  
   
  void   helpdoc();  
   
  main()  
  {  
                  FILE   *fp;  
   
                  unsigned   char   ch,   c;  
   
                  if((fp=fopen(_argv[1],   "rb"))==NULL)                         /*   open   file   to   read   */  
                  {  
                                  printf("cannot   open   file   %s\n",_argv[1]);  
                                  helpdoc();  
                                  exit(-1);  
                  }  
   
                  c=0;  
                  ch=fgetc(fp);  
   
                  while(!feof(fp))                                                 /*   loop   for   whole   file   */  
                  {  
                                  printf("%u\t",   ch);                           /*   print   every   byte's   decimal   equiv.   */  
                                  c++;  
                                  if(c>8)                                                                   /*   print   8   numbers   to   a   line   */  
                                  {  
                                                  c=0;  
                                                  printf("\n");  
                                  }  
   
                                  ch=fgetc(fp);  
                  }  
   
                  fclose(fp);                                           /*   close   up   */  
  }  
   
  void   helpdoc()                                     /*   print   help   message   */  
  {  
                  printf("\n       Binary   File   Decoder\n\n");  
   
                  printf("\n   Syntax:     dec   binary_file_name\n\n");  
   
                  printf("by   Dustin   Caldwell     (dustin@gse.utah.edu)\n\n");  
                  printf("This   is   a   filter   program   that   reads   a   binary   file\n");  
                  printf("and   prints   the   decimal   equivalent   of   each   byte\n");  
                  printf("tab-separated.   This   is   mostly   useful   when   piped   \n");  
                  printf("into   another   file   to   be   edited   manually.     eg:\n\n");  
                  printf("c:\>dec   sonata3.mid   >   son3.txt\n\n");  
                  printf("This   will   create   a   file   called   son3.txt   which   can\n");  
                  printf("be   edited   with   any   ascii   editor.   \n\n");  
                  printf("(rec.exe   may   also   be   useful,   as   it   reencodes   the   \n");  
                  printf("ascii   text   file).\n\n");  
                  printf("Have   Fun!!\n");  
  }  
   
  ----------------------------   REC.CPP   ----------------------------------  
   
  /*     File     rec.cpp  
                  by   Dustin   Caldwell       (dustin@gse.utah.edu)  
  */  
   
  #include   <dos.h>  
  #include   <stdio.h>  
  #include   <ctype.h>  
  #include   <stdlib.h>  
   
  void   helpdoc();  
   
  main()  
  {  
                  FILE   *rfp,   *wfp;  
   
                  unsigned   char   ch,   c;  
                  char   s[20];  
   
                  if((rfp=fopen(_argv[1],   "r"))==NULL)                                         /*   open   the   read   file   */  
                  {  
                                  printf("cannot   open   file   %s   \n",_argv[1]);  
                                  helpdoc();  
                                  exit(-1);  
                  }  
   
                  if((wfp=fopen(_argv[2],   "wb"))==NULL)                                       /*   open   the   write   file   */  
                  {  
                                  printf("cannot   open   file   %s   \n",_argv[1]);  
                                  helpdoc();  
                                  exit(-1);  
                  }  
   
                  c=0;  
   
                  ch=fgetc(rfp);  
   
                  while(!feof(rfp))                                               /*   loop   for   whole   file   */  
                  {  
   
                                  if(isalnum(ch))                                   /*   only   'see'   valid   ascii   chars   */  
                                  {  
                                                  c=0;  
                                                  while(isdigit(ch))             /*   only   use   decimal   digits   (0-9)   */  
                                                  {  
                                                                  s[c]=ch;                 /*   build   a   string   containing   the   number   */  
                                                                  c++;  
                                                                  ch=fgetc(rfp);  
                                                  }  
                                                  s[c]=NULL;                                             /*   must   have   NULL   terminator   */  
   
                                                  fputc(atoi(s),   wfp);/*   write   the   binary   equivalent   to   file   */  
   
                                  }  
   
                                  ch=fgetc(rfp);                                     /*   loop   until   next   number   starts   */  
   
   
                  }  
   
                  fclose(rfp);                                         /*   close   up   */  
                  fclose(wfp);  
  }  
   
   
  void   helpdoc()                     /*   print   help   message   */  
  {  
                  printf("\n       Text   File   Encoder\n\n");  
   
                  printf("\n   Syntax:     rec   text_file_name   binary_file_name\n\n");  
   
                  printf("by   Dustin   Caldwell     (dustin@gse.utah.edu)\n\n");  
                  printf("This   is   a   program   that   reads   an   ascii   tab-\n");  
                  printf("delimited   file   and   builds   a   binary   file   where\n");  
                  printf("each   byte   of   the   binary   file   is   one   of   the   decimal\n");  
                  printf("digits   in   the   text   file.\n");  
                  printf("   eg:\n\n");  
                  printf("c:\>rec   son3.txt   son3.mid\n\n");  
                  printf("(This   will   create   a   file   called   son3.mid   which   is\n");  
                  printf("a   valid   binary   file)\n\n");  
                  printf("(dec.exe   may   also   be   useful,   as   it   decodes   binary   files)\n\n");  
                  printf("Have   Fun!!\n");  
  }  

WinMM.dll 函数汇总

auxGetDevCaps                  查询指定的辅助输出设备以确定其性能
auxGetNumDevs                  检取系统中存在的辅助输出设备的数量
auxGetVolume                   返回指定的辅助输出设备的当前卷设备
auxOutMessage                  向指定的辅助输出设备发送一条消息
auxSetVolume                   在指定的辅助输出设备中设置卷
CloseDirver                    关闭指定的可安装驱动器
DefDriverProc                  为任何不由可安装驱动器处理的消息提供的缺省处理
Drivercallback                 调用一个回调函数,发送一条消息给窗口或将一个线程的阻塞解除
DrvGetModuleHandle             返回包含指定可安装驱动器模块的实例句柄
DrvsendMessage                 把指定的消息发送给可安装驱动器
GetDriverModuleHandle          返回包含指定可安装驱动器模块的实例句柄
joyGetDevCaps                  查询指定的游戏杆设备以确定其性能
joyGetNumDevs                  返回系统支持的游戏杆设备的数量
joyGetPos                      查询指定的游戏杆设备的位置和活动性
joyGetPosEx                    查询一个游戏杆设备的位置和它的按扭状态
joyGetThreshold                查询指定的游戏杆设备的当前移动阈值
joyReleaseCapture              释放由JoySetCapture函数设置的在指定游戏杆设备上的捕获
joySetCapture                  发送一个游戏杆消息到指定的窗口
joySetThreshold                设置指定的游戏杆设备的移动阈值
mciGetCreatorTask              为指定的MCI设备检取其创建的任务
mciGetDeviceID                 返回和打开设备名相匹配的设备标识符
mciGetErrorString              检取描述指定媒介控制接口错误代码的字符串
mciGetYieldProc                返回和媒介控制接口的WAIT标志相关的回调函数的地址
mciSendCommand                 向指定的媒介控制接口设备发送一条命令
mciSendString                  向指定的媒介控制接口设备发送一个字符串
mciSetYieldProc                设置一个过程地址,在MCI设备因指定了WAIT标志而等待一个命令完成时,该过程被周期性调用
midiConnect                    将指定的MIDI输入设备连接到输出设备
midiDisconnect                 断开MIDI输入设备和输出设备的连接
midiInAddBuffer                向指定的音乐仪器数字接口的输入设备增加一个缓冲区
midiInClose                    关闭指定的音乐仪器数字接口的输入设备
midiInGetDveCaps               查询指定的音乐仪器数字接口的输入设备,以确定其性能
midiInGetErrorText             检取有关音乐仪器数字接口的输入设备指定错误的文本说明
midiInGetID                    获得一个音乐一起数字接口的输入设备的标识符
midiInGetNumDevs               检取系统中音乐仪器数字接口的输入设备的数量
midiInMessage                  向指定的音乐仪器数字接口的输入设备驱动器发送一条消息
midiInOpen                     打开指定的音乐仪器数字接口的输入设备
midiInPrepareHeader            为音乐仪器数字接口的输入设备准备一个缓冲区
midiInReset                    在给定的MIDI输入设备上输入,并将所有挂起的输入缓冲区标记为已执行的
midiInStart                    启动在指定的音乐仪器数字接口的输入设备上的输入
midiInStop                     停止在给定的音乐仪器数字接口的输入设备上的输入
midiInUnprepareHeader          消除由midiInPrepareHeader函数完成的准备
midiOutCacheDrumPatches        请求内部的一个MIDI合成设备预装指定的基于键的击打音色集
midiOutCachePatches            请求内部的音乐仪器数字接口的合成设备预装指定的音色集
midiOutClose                   关闭指定的音乐仪器数字接口的输出设备
midiOutGetDevCaps              查询指定的音乐仪器数字接口的输出设备,以确定其性能
midiOutGetErrorText            检取有关MIDI输出设备指定采取的文本说明
midiOutGetID                   检取指定的MIDI输出设备的标识符
midiOutGetNumDevs              检取系统中存在的MIDI输出设备的数量
midiOutGetVolume               返回一个MIDI输出设备的当前卷设置
midiOutLongMsg                 向指定的MIDI输出设备发送一条系统专用的MIDI消息
midiOutMessage                 向一MIDI输出设备驱动器发送一条消息
midiOutOpen                    打开指定的MIDI输出设备进行回放
midiOutPrepareHeader           为MIDI输出设备准备一个缓冲区
midiOutReset                   为指定的MIDI输出设备关闭所有MIDI通道上的所有标志
midiOutSetVolume               设置一个MIDI输出设备的卷
midiOutShortMsg                向指定的MIDI输出设备发送一条短MIDI消息
midiOutUnprepareHeader         清除由midiOutPrepareHeader函数完成的准备
midiStreamClose                关闭一个打开的MIDI流
midiStreamOpen                 为输出,打开一个MIDI流
midiStreamOut                  在MIDI输出设备上播放或排队一个MIDI数据流
midiStreamPause                暂停一个MIDI流的播放
midiStreamPosition             在一个MIDI流中检取当前位置
midiStreamProperty             设置或检取与MIDI输出设备相关MIDI数据流的特性
midiStreamRestart              重新启动一个暂停的MIDI流
midiStreamStop                 关掉指定MIDI输出设备的所有MIDI通道
mixerClose                     关闭指定的混频器
mixerGetControlDetails         检取和一个声频指线路相关的单一控件的细节
mixerGetDevCaps                查询指定的混频器以确定其性能
mixerGetID                     获取指定混频器的标识符
mixerGetLineContrils           检取和一个声频线路相关的一个或多个控件
mixerGetLineInfo               检取混频器有关特有线路的信息
mixerGetNumDevs                返回系统中存在的混频器的数量
mixerMessage                   把一个定制混频器驱动器消息直接发送给混频器驱动器
mixerOpen                      打开指定的混频器,在应用程序关闭该句柄前保证该设备不被移走
mixerSetControlDetails         设置和一个声频指线路相关的单一控件的细节
mmioAsvance                    填充一个文件的IO缓冲区
mmioAscend                     取出一个RIFF文件块
mmioClose                      关闭有mmioOpen打开的文件
mmioCreateChunk                创建由mmioOpen函数打开的RIFF文件中的一个块
mmioDescend                    进入由mmioOpen函数打开的RIFF文件的块中,并查找一个块
mmioFlush                      把文件缓冲区的数据写入磁盘中
mmioGetInfo                    检取有关由mmioOpen函数创建的RIFF文件的信息
mmioInstallIOProcA             装入或删除一个自定义的IO过程
mmioOpen                       为输入输出打开一个文件
mmioRead                       从由mmioOpen函数打开的文件中读取指定字节数的数据
mmioRename                     重新命名指定的文件
mmioSeek                       改变由mmioOpen函数打开的文件中的当前指针位置
mmioSendMessage                向与指定文件相联系的IO过程发送一条消息
mmioSetBuffer                  允许或禁止文件缓冲区的IO,或改变这个缓冲区,或改变这个缓冲区的大小
mmioSetInfo                    更新从被打开文件中检取的信息
mmioStringToFOURCC             把一个以NULL结束的字符串转换成一个4字符代码
mmioWrite                      向由mmioOpen函数打开的文件中写入指定字节数的数据
mmsystemGetVersion             返回多媒体扩展系统软件的当前版本号
OpenDriver                     打开一个可安装驱动器实例,并用缺省设置或指定值初始化该实例
PlaySound                      播放一个波形声音
SendDriveMessage               向指定的可安装驱动器发送一条消息
SndPlaySound                   播放一个由文件名或由登记的[sound]段的入口指定的波形声音
timeBeginPeriod                设置应用程序或驱动程序使用的最小定时器分辨率
timeEndPeriod                  清除应用程序或驱动程序使用的最小定时器分辨率
timeGetDevCaps                 查询定时器设备以确定其性能
timeGetSystemTime              检取从WINDOWS开始已逝去的毫秒数
timeGetTime                    检取从WINDOWS开始已逝去的毫秒数,此函数比上一条函数开销小
timeKillEvent                  毁掉指定的定时器回调事件
timeSetEvent                   设置一个定时器回调事件
waveInAddBuffer                向波形输入设备添加一个输入缓冲区
WaveInClose                    关闭指定的波形输入设置
waveInGetDevCaps               查询指定的波形输入设备以确定其性能
waveInGetErrorText             检取由指定的错误代码标识的文本说明
waveInGetID                    获取指定的波形输入设备的标识符
waveInGetNumDevs               返回系统中存在的波形输入设备的数量
waveInGetPosition              检取指定波形输入设备的当前位置
waveInMessage                  发送一条消息给波形输入设备的驱动器
waveInOpen                     为录音而打开一个波形输入设备
waveInPrepareHeader            为波形输入准备一个输入缓冲区
waveInReset                    停止给定的波形输入设备的输入,且将当前位置清零
waveInStart                    启动在指定的波形输入设备的输入
waveInStop                     停止在指定的波形输入设备上的输入
waveInUnprepareHeader          清除由waveInPrepareHeader函数实现的准备
waveOutBreakLoop               中断给定的波形输出设备上一个循环,并允许播放驱动器列表中的下一个块
waveOutClose                   关闭指定的波形输出设备
waveOutGetDevCaps              查询一个指定的波形输出设备以确定其性能
waveOutGetErrorText            检取由指定的错误代码标识的文本说明
waveOutGetID                   检取指定的波形输出设备的标识符
waveOutGetNumDevs              检取系统中存在的波形输出设备的数量
waveOutGetPitch                查询一个波形输出设备的当前音调设置
waveOutGetPlaybackRate         查询一个波形输出设备当前播放的速度
waveOutGetPosition             检取指定波形输出设备的当前播放位置
waveOutGetVolume               查询指定波形输出设备的当前音量设置
waveOutMessage                 发送一条消息给一个波形输出设备的驱动器
waveOutOpen                    为播放打开一个波形输出设备
waveOutPause                   暂停指定波形输出设备上的播放
waveOutPrepareHeader           为播放准备一个波形缓冲区
waveOutRestart                 重新启动一个被暂停的波形输出设备
waveOutSetPitch                设置一个波形输出设备的音调
waveOutSetPlaybackRate         设置指定波形输出设备的速度
waveOutSetVolume               设置指定的波形输出设备的音量
waveOutUnprepareHeader         清除由waveOutPrepareHeader函数实现的准备
waveOutWrite                   向指定的波形输出设备发送一个数据块

2007年9月10日星期一

二分法查找&二分法排序

二分查找1、二分查找(Binary Search)
二分查找又称折半查找,它是一种效率较高的查找方法。
二分查找要求:线性表是有序表,即表中结点按关键字有序,并且要用向量作为表的存储结构。不妨设有序表是递增有序的。
2、二分查找的基本思想
二分查找的基本思想是:(设R[low..high]是当前的查找区间)
(1)首先确定该区间的中点位置:
(2)然后将待查的K值与R[mid].key比较:若相等,则查找成功并返回此位置,否则须确定新的查找区间,继续二分查找,具体方法如下:
①若R[mid].key>K,则由表的有序性可知R[mid..n].keys均大于K,因此若表中存在关键字等于K的结点,
则该结点必定是在位置mid左边的子表R[1..mid-1]中,故新的查找区间是左子表R[1..mid-1]。
②类似地,若R[mid].key<K,则要查找的K必在mid的右子表R[mid+1..n]中,即新的查找区间是右子表R[mid+1..n]。下一次查找是针对新的查找区间进行的。
因此,从初始的查找区间R[1..n]开始,每经过一次与当前查找区间的中点位置上的结点关键字的比较,就可确定查找是否成功,
不成功则当前的查找区间就缩小一半。这一过程重复直至找到关键字为K的结点,或者直至当前的查找区间为空(即查找失败)时为止。

3、二分查找算法
  int BinSearch(SeqList R,KeyType K)
{ //在有序表R[1..n]中进行二分查找,成功时返回结点的位置,失败时返回零
int low=1,high=n,mid; //置当前查找区间上、下界的初值
while(low<=high){ //当前查找区间R[low..high]非空
mid=(low+high)/2;
if(R[mid].key==K) return mid; //查找成功返回
if(R[mid].kdy>K)
high=mid-1; //继续在R[low..mid-1]中查找
else
low=mid+1; //继续在R[mid+1..high]中查找
}
return 0; //当low>high时表示查找区间为空,查找失败
} //BinSeareh

二分查找算法亦很容易给出其递归程序【参见练习
6、二分查找的优点和缺点
虽然二分查找的效率高,但是要将表按关键字排序。而排序本身是一种很费时的运算。既使采用高效率的排序方法也要花费O(nlgn)的时间。
二分查找只适用顺序存储结构。为保持表的有序性,在顺序结构里插入和删除都必须移动大量的结点。因此,二分查找特别适用于那种一经建立就很少改动、而又经常需要查找的线性表。
对那些查找少而又经常需要改动的线性表,可采用链表作存储结构,进行顺序查找。链表上无法实现二分查找。

二分法排序

#include <stdlib.h>
#include <stdio.h>
void TwoInsertSort(int array[],int n)
{
int left,right,num;
int middle,j,i;
for(i = 1;i < n;i++)
{
left = 0;// 准备
right = i-1;
num = array[i];
while( right >= left)// 二分法查找插入位置
{
middle = ( left + right ) / 2; // 指向已排序好的中间位置
if( num < array[middle] )// 即将插入的元素应当在在左区间
right = middle-1;
else // 即将插入的元素应当在右区间
left = middle+1;
}
for( j = i-1;j >= left;j-- )// 后移排序码大于R[i]的记录
array[j+1] = array[j];
array[left] = num;// 插入
}
}

int rcmp( const int *a, const int *b)
{
return (*a-*b);
}
void main()
{
int array[50];
int i;
printf("The original array is :\n");
for( i=0; i<50; i++ )//数组初始化并显示
{
array[i] = 50-i;
printf("array[%d]:%d\n", i, array[i]);
}
TwoInsertSort(array,sizeof(array)/sizeof(int));//二分法排序
printf("\nAfter sorted :\n");
for( i=0; i<50; i++ )
printf("array[%d]:%d\n", i, array[i]);

//库函数bsearch用二分法查找一个有序数组中的一个特定数,并返回该数的地址

a = (int *)bsearch(&b, numarray, sizeof(numarray)/sizeof(numarray[0]), sizeof(int),rcmp);

}


七种场景下的软件工作量估算步骤

七种场景下的软件工作量估算步骤

场景一:合同前的工作量估算
场景描述
(1) 没有实施过CMMI2
(2) 合同未签,需要给客户报价
(3) 有客户的概要需求,有类似的项目数据可供参考
(4) 需要估计整个项目的总工作量,以便于估算总成本,给客户报价
估算步骤
1)寻找类似的历史项目,进行项目的类比分析,根据历史项目的工作量凭经验估计本项目的总工作量;
2)进行WBS分解,力所能及地将整个项目的任务进行分解;
3)参考类似项目的数据,采用经验法估计WBS中每类活动的工作量;
4)汇总得到项目的总工作量;
5)与第(1)步的结果进行印证分析,根据分析结果,确定估计结果。
场景二:基于详细需求的经验估计
场景描述
(1) 只有详细需求,没有历史数据
估算步骤:
1WBS分解,将任务分解到一个人或者一个小团队可以执行的颗粒度;WBS分解时要识别出所有的交付物、项目管理活动、工程活动等。
2)采用经验法估计每个活动的工作量;
3)汇总得到:每个阶段的工作量、项目的总工作量。
其他说明:
在该场景下,只使用了经验法,无法对结果进行印证,难以判断结果的合理性。
场景三:由编码估算整体
场景描述
1)有类似项目的历史数据
2)有编码活动的生产率数据
3)有详细需求
4)实施了CMMI2级,但是没有积累历史项目的工作量分布数据
估算步骤
1)产品分解,将系统分为子系统,子系统分解为模块;
2WBS分解,将任务分解到一个人或者一个小团队可以执行的颗粒度;WBS分解时要识别出所有的交付物、项目管理活动、工程活动等。
3)建立WBS分解中的活动与产品元素的映射关系,识别出WBS中哪些活动可以采用模型法估算;
4)估计产品元素的规模,可以采用代码行法或功能点法,并估计每个产品元素的复杂度、复用率等;
5)根据历史的编码阶段的生产率数据和产品元素的规模估计、复杂度、复用率等采用模型法计算每个产品元素的编码工作量;
6)根据历史的类似项目的数据及估算人的经验估计其他活动的工作量,可以采用经验法。
7)汇总得到:每个阶段的工作量、项目的总工作量。
其他说明:
在该场景下,混合使用了经验法与模型法,这2种方法互相补充,而不是互相印证。
场景四:由总体印证基于WBS的估计
场景描述
1)有类似项目的历史数据
(2) 有类似项目的全生命周期的生产率数据(含管理工作量)
3)有详细需求
4)实施了CMMI2级,但是没有积累历史项目的工作量分布数据
估算步骤
1)产品分解,将系统分为子系统,子系统分解为模块;
2)估计产品元素的规模,可以采用代码行法或功能点法;
3)累计出整个产品的总规模,并估计产品总体的复杂度、复用率等;
4)根据类似项目的全生命周期的生产率数据和产品的总规模、复杂度、复用率等采用模型法计算总的开发工作量;
5WBS分解,将任务分解到一个人或者一个小团队可以执行的颗粒度;WBS分解时要识别出所有的交付物、项目管理活动、工程活动等。
6)根据历史的类似项目的数据及估算人的经验估计所有活动的工作量,可以采用经验法。
7)汇总得到:每个阶段的工作量、项目的总工作量。
8)与第(4)步得出的工作量进行比较印证,如果偏差不大,则以第(7)步的结果为准,如果偏差比较大,要仔细分析原因,可能的原因举例如下:
Ø 类似项目的生产率数据不适合本项目;
Ø WBS分解的颗粒度不够详细;
Ø 估算专家的经验不适合本项目;
Ø 具体任务的估计不合理;
针对原因,对估算的结果进行调整,使其趋向合理。
其他说明
在该场景下,对于项目的总工作量有2个结果或者多个结果,这些结果可以互相印证,以发现估算过程中的不合理之处,是估计更加合理。
场景五:三维印证基于WBS的估计
场景描述
1)有类似项目的历史数据
(2) 有类似项目的全生命周期的生产率数据(含管理工作量)
3)有详细需求
4)实施了CMMI3级,有历史项目的工作量分布数据(阶段分布、工种分布)
估算步骤
1)产品分解,将系统分为子系统,子系统分解为模块;
2)估计产品元素的规模,可以采用代码行法或功能点法;
3)累计出整个产品的总规模,并估计产品总体的复杂度、复用率等;
4)根据类似项目的全生命周期的生产率数据和产品的总规模、复杂度、复用率等采用模型法计算总的开发工作量;
5)根据历史项目的工作量分布数据及第(4)步估算的项目总工作量,计算:
Ø 每个阶段的工作量
Ø 每个工种的工作量
6WBS分解,将任务分解到一个人或者一个小团队可以执行的颗粒度;WBS分解时要识别出所有的交付物、项目管理活动、工程活动等。
7)根据历史的类似项目的数据及估算人的经验估计所有活动的工作量,可以采用经验法。
8)汇总得到:每个阶段的工作量、每个工种的工作量、项目的总工作量。
9)与第(4)、(5)步得出的工作量进行比较印证,如果偏差不大,则以第(7)步的结果为准,如果偏差比较大,要仔细分析原因,可能的原因举例如下:
Ø 类似项目的生产率数据不适合本项目;
Ø WBS分解的颗粒度不够详细;
Ø 估算专家的经验不适合本项目;
Ø 具体任务的估计不合理;
针对原因,对估算的结果进行调整,使其趋向合理。
其他说明
在该场景下,对于项目的总工作量有2个结果或者多个结果,并且采用2种方法都得到了每个阶段、每个工种的工作量、项目的总工作量,可以从上述的3个维度对这些结果进行互相印证,以发现估算过程中的不合理之处,是估计更加合理。
场景六:四维印证基于WBS的估计
场景描述
1)有类似项目的历史数据
(2) 有类似项目的编码活动的生产率数据(不含管理工作量)
3)有详细需求
4)实施了CMMI3级,有历史项目的工作量分布数据(阶段分布、工种分布、阶段工种分布)
5)项目采用了瀑布模型
估算步骤
1)产品分解,将系统分为子系统,子系统分解为模块;
2)估计产品元素的规模,可以采用代码行法或功能点法,并估计每个产品元素的复杂度、复用率等;
3)根据类似项目的编码活动的生产率数据和产品元素的规模、复杂度、复用率等采用模型法计算每个产品元素的编码工作量;
4)根据历史项目的按工种的工作量分布数据及第(3)步的估算的编码工作量依次计算:
i)根据历史项目的编码的工作量占编码阶段的工作量的百分比与第(3)部计算出的编码工作量计算编码阶段的总工作量;
ii)根据历史项目的编码阶段各工种的工作量分布百分比计算编码阶段每个工种的工作量;
iii)根据历史项目的其他阶段的工作量与编码阶段的工作量比例计算其他阶段的总工作量;
iv)根据历史项目的其他阶段的每个工种的工作量分布百分比及第iii)步的结果计算其他阶段的每个工种的工作量;
5WBS分解,将任务分解到一个人或者一个小团队可以执行的颗粒度;WBS分解时要识别出所有的交付物、项目管理活动、工程活动等。
6)根据历史的类似项目的数据及估算人的经验估计所有活动的工作量,可以采用经验法。
7)汇总得到:每个阶段每个工种的工作量、每个阶段的工作量、每个工种的工作量、项目的总工作量。
8)与第(4)步得出的工作量进行比较印证,如果偏差不大,则以第(6)步的结果为准,如果偏差比较大,要仔细分析原因,可能的原因举例如下:
Ø 类似项目的生产率数据不适合本项目;
Ø WBS分解的颗粒度不够详细;
Ø 估算专家的经验不适合本项目;
Ø 具体任务的估计不合理;
针对原因,对估算的结果进行调整,使其趋向合理。
其他说明
在该场景下,对于项目的总工作量有2个结果或者多个结果,并且采用2种方法都得到了每个阶段的工作量、每个工种的工作量、每个阶段每个工种的工作量、项目的总工作量,可以从上述的4个维度对这些结果进行互相印证,以发现估算过程中的不合理之处,是估计更加合理。

场景七:需求变更的工作量估计
场景描述
(1) 有变更的需求描述
(2) 项目进行到了编码阶段
(3) 有本项目的编码的生产率
估算步骤:
1)进行需求变更的波及范围分析
2)进行本次变更的的WBS分解
3)对于变更引起的代码变化进行规模、复杂度等其他属性的估计
4)根据本项目的编码的生产率及估计的规模采用模型法估计工作量
5)对于WBS分解中其他活动进行经验估计
6)汇总所有的工作量得到本次变更的工作量估计

线程的使用

线程的使用
1.创建简单的线程;
在视图类的鼠标的消息映射函数中加入:
    AfxBeginThread((AFX_THREADPROC)TestThread,this);
然后在视图类中加入线程的入口函数:
UINT TestThread(LPVOID pParam)
{
    AfxMessageBox("这是一个测试工作线程的对话框");
    return 0;
}
函数说明:
CWinThread*
AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int
nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD
dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
CWinThread*
AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority =
THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
PfnThreadProc:
    指向工作者线程的控制函数,不能是NULL,函数的定义必须如下:
UINT MyControllingFunction( LPVOID pParam );
PThreadClass:CwinThread的RUNTIME_CLASS对象.
PParam:传递给控制函数的参数.
Npriority:线程的优先级,可以用函数
BOOL SetThreadPriority(
  HANDLE hThread, // handle to the thread
  int nPriority   // thread priority level
);来设定
nStackSize:堆栈的大小,如果为0,就和创建的主线程一样的大小.
DwCreateFlags:创建线程时的附加标志,可以为CREATE_SUSPENDED,代表挂起线程,直到调用DWORD ResumeThread( );函数时才执行,为0,表示立即执行.
LpSecurityAttrs:指向一个SECURITY_ATTRIBUTES描述线程的安全性,如果与主线程一样的安全属性,可以为NULL.
另外可以用CwinThread类的成员函数
BOOL
CreateThread( DWORD dwCreateFlags = 0, UINT nStackSize = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );来创建一个线程,具体的参数和上面一样.
还可以用API来创建线程:
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // pointer to security attributes
  DWORD dwStackSize,                         // initial thread stack size
  LPTHREAD_START_ROUTINE lpStartAddress,     // pointer to thread function
  LPVOID lpParameter,                        // argument for new thread
  DWORD dwCreationFlags,                     // creation flags
  LPDWORD lpThreadId                         // pointer to receive thread ID
);
DWORD WINAPI YourThreadFunc(LPVOIDlpvThreadParm);
lpThreadId:32位标示符,用来接收线程的标示
函数返回的都是创建线程的句柄.
2.如何结束一个线程:
void AfxEndThread( UINT nExitCode );//结束当前正在运行的线程.
NExitCode:为线程退出代码.
BOOL TerminateThread(
  HANDLE hThread,    // handle to the thread
  DWORD dwExitCode   // exit code for the thread
);
如果线程调用了VOID ExitThread(UINTfuExitCode);也可以终止

3.如何取得线程的退出代码;
使用函数
BOOL GetExitCodeThread(
  HANDLE hThread,      // handle to the thread
  LPDWORD lpExitCode   // address to receive termination status
);
4.设置线程的优先级;
BOOL SetThreadPriority(
  HANDLE hThread, // handle to the thread
  int nPriority   // thread priority level
);
nPriority;
线程的优先级,有:THREAD_PRIORITY_ABOVE_NORMAL,THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_HIGHEST,THREAD_PRIORITY_IDLE,THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_NORMAL,THREAD_PRIORITY_TIME_CRITICAL具体的函数参见MSDN。
在上面创建的工作者线程中我们可以这样设置优先级:
    SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST);//设置比通常高两个点
5.如何创建多线程多任务。
可以调用利用上面的线程创建函数,传递给同一个线程函数不同的参数,也可以开始另外一个线程。
6.挂起和恢复线程:

前我提到过可以创建挂起状态的线程(通过传递CREATE_SUSPENDED标志给函数CreateThread来实现)。当你这样做时,系统创建指定
线程的核心对象,创建线程的栈,在CONTEXT结构中初始化线程CPU注册成员。然而,线程对象被分配了一个初始挂起计数值1,这表明了系统将不再分配
CPU去执行线程。要开始执行一个线程,一个线程必须调用ResumeThread并传递给它调用CreateThread时返回的线程句柄。
DWORD ResumeThread(HANDLE hThread);
一个线程可以被挂起多次。如果一个线程被挂起3次,则该线程在它被分配CPU之前必须被恢复3次。
除了在创建线程时使用CREATE_SUSPENDED标志,你还可以用SuspendThread函数挂起线程。
DWORDSuspendThread(HANDLE hThread);
7.线程同步对象的使用;

步对象有:Critical_section(临界区),Event(事件),Mutex(互斥对象),Semaphores(信号量)。都有相应的
API创建函数和MFC类,同步对象可以处于两种状态:信号状态(signal
state)或非信号状态,当一个线程与某个对象相关联时,若该对象处于非信号状态,则要等到其变成信号状态线程才能继续执行。
WIN32 API提供了等待命令WaitForSingleObject和WaitForMutipleObjects:
  DWORD WaitForSingleObject(
                HANDLE hObject,                         // 要等待的对象句柄
                DWORD dwMilliseconds);  // 最大等待时间(毫秒)
        DWORD WaitforMultipleObjects(
                DWORD dwNumObjects,             // 要等待的对象数
                LPHANDLE lpHandles,             // 对象句柄数组
                BOOL bWaitAll,               // 是否等待所有对象都有信号才返回
                DWORD dwMilliseconds);  // 最大等待时间

果在指定时间内对象达到 信号状态则返回WAIT_OBJECT_0,超时返回WAIT_TIMEOUT,出错返回
WAIT_FAILED。对于互斥量、信号量和自动重置(auto-reset)事件对象,等待成功
时将它们改成非信号状态(信号量计数器减1),以实现对象的互斥访问。
件:jianuangzhiganhue
8.事件:
"事件"――一个允许一个事件发生时线程通知另一个线程的同步对象。在一个线程需要了解何时执行任务时,事件是十分有用的。
CEvent对象有两种类型:自动和手工。一个手工CEvent对象存在于由ResetEvent 或SetEvent设置的状态中,直到另一个函数被调用。
一个自动CEvent对象在至少一个线程被释放后自动返回一个无标记(无用的)状态。
CEvent(
BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName
= NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );
BInitiallyOwn;如果为TRUE, CMultilock or CsingleLock对象就可以使用,为FALSE,线程就必须等到直到所需的资源被释放.
BManualReset:为TRUE,设置为手动复位事件对象,为FALSE,设置为自动复位事件对象.
LpszName:事件对象的名称,不能指定一个已经存在的对象
LpsaAttribute:事件对象的安全属性
与同步对象相关的多个同步访问对象CMultilock 和单个同步访问对象 CsingleLock对象.
CSingleLock( CSyncObject* pObject, BOOL bInitialLock = FALSE );
PObject:指向一个要处理的同步对象
BinitialLock:指定是否初始的时候就尝试访问对象。
CMultiLock( CSyncObject* ppObjects[ ], DWORD dwCount, BOOL bInitialLock = FALSE );
DwCount:指定同步对象的个数,必须大于0。
下面简单介绍他们的应用:

用此成员函数构造一个已命名或未命名的CEvent对象。要访问或释放一个CEvent对象,可建立一个CSingleLock或CMultiLock对
象并调用其Lock和Unlock成员函数。要将CEvent对象的状态改为已标记(无须等待的线程),可调用SetEvent或PulseEvent。
要设置一个CEvent对象为无标记(必须等待的线程),可调用ResetEvent。
可以在你的线程开始设置CEvent::SetEvent,其他线程使用CSingleLock::Lock来等待你线程处理结束。当你的线程处理完毕,
使用CEvent::ResetEvent清除事件,其他程序就可以继续执行了。
API函数:
    HANDLE CreateEvent(
                LPSECURITY_ATTRIBUTES lpsa,     // 安全属性指针,Win98忽略
                BOOL bManualRest,              // 是否手动重置(manual-reset)
                BOOL bInitialState,            // 初始状态是否为信号状态
                LPCTSTR lpName);              // 对象名字符串指针

动重置(auto-reset)事件和手动重置(manual-reset)事件,这由CreateEvent()的第二个参数指定。对于自动重置事件,
WaitForSingleObject()和
WaitForMultipleObjects()会等待事件到信号状态,随后又自动将其重置为非信号状
态,这样保证了等待此事件的线程中只有一个会被唤醒。而手动重置事件需要用户调用ResetEvent()才会重置事件。可能有若干个线程在等待同一事
件,这样当事件变 为信号状态时,所有等待线程都可以运行了。
SetEvent()函数用来把事件对象设置成信号状态,ResetEvent()把事件对象重置成非信号状态,两者均需事件对象句柄作参数。
9.临界区

一时刻只允许一个线程存取资源或代码区。临界区在控制一次只有一个线程修改数据或其它的控制资源时非常有用。在运行性能比较重要而且资源不会跨进程使用
时,建议采用临界区代替信号灯。有关在MFC中使用信号灯的详细信息,请参阅CMutex。使用CCriticalSection对象之前,需要构造它。
在构造函数返回后,就可以使用临界区了。在使用完之后要调用UnLock函数。存取由CCriticalSection控制的资源时,要在资源的存取函数
中定义一个CSingleLock型的变量。然后调用加锁对象的Lock成员函数(如CSingleLock::Lock)。此时,调用的线程要么获得对
资源的存取权,要么等待他人释放资源等待加锁,或者等待他人释放资源,但又因为超时而加锁失败。这样就保证了一次只有一个线程在存取临界资源。释放资源只
需调用成员函数UnLock(例如CSingleLock:Unlock),或让锁对象在作用范围之外。
此外,可以单独地建立一个CCriticalSection对象,并在存取临界资源之前显式地存取它。这种方式有助于保持代码的清晰,
但是更容易出错,因为要记住在存取临界资源前加锁,存取之后开锁。
构造函数:CCriticalSection ( );
成员函数:BOOL Lock( ); BOOL Lock( DWORD dwTimeout ); 本函数用于取得对临界区对象的存取权。Lock是一个成块性的操作,直到取得临界区对象的存取权才返回。
virtual
BOOL UnLock( );
如果临界区是被本线程占用的并且开锁成功,则返回非零值。否则为0。本函数释放线程占用的CCriticalSection对象。如果
CCriticalSection是单独使用的,在独占使用完临界资源以后,应尽快调用UnLock开锁。如果使用了CSingleLock对象,
CCriticalSection::Unlock将会在它的成员函数Unlock中调用。
API函数:
CRITICAL_SECTION cs; 然后初始化,调用下面函数(参数为空对象的指针):
     VOID InitializeCriticalSection(LPCRITICAL_SECTION lpcs);
     VOID EnterCriticalSection(LPCRITICAL_SECTION lpcs); 进入和离开临界区分别调用函数:
     VOID LeaveCriticalSection(LPCRITICAL_SECTION lpcs);
     VOID DeleteCriticalSection(LPCRITICAL_SECTION lpcs); 删除临界区对象用:
10. Mutex(互斥对象)
它为一个同步对象,允许某线程共同访问同一资源。在仅仅一个线程被允许用于修改数据或其它被控制的资源时。

种使用CMutex
对象的方法就是一个CMutex类型的变量,将其作为你希望控制类的数据成员。在被控制对象的构造过程中,若互斥对象最初拥有了互斥对象的名称或期待的安
全属性,那么就调用CMutex数据成员指定的构造函数,以这种方式访问由CMutex
对象控制的资源,首先要在资源访问的成员函数中创建CSingleLock类型或CMultiLock类型的变量。然后调用封锁对象的Lock成员函数
(例如,
CSingleLock::Lock)。这样,你的线程要么就获得资源的访问权,以等待将要释放的资源,并获取访问权,要么就等待将要释放的资源,当超时
后,返回失败。在任何一种情况下,都可以在线程安全的模式下访问资源。若要释放这些资源,使用封锁对象的Unlock成员函数(例如,
CSingleLock::Unlock),或允许封锁对象越界。
CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL,LPSECUR-ITY_ATTRIBUTES lpsaAttribute = NULL );
BInitiallyOwn:指定是否创建了CMutex 对象的线程最初拥有由互斥对象控制资源的访问权。
lpszName:
CMutex
对象的名称。若其它的互斥对象由同样的名称,那么必须提供lpszName,以便对象能够通过进程边界。若该值为NULL,那么互斥对象将没有命名。若该
名称匹配已经存在的互斥对象,那么构造函数将创建一个参考互斥对象名称的新CMutex
对象。若该名称匹配已存在的不是互斥对象的同步对象,那么构造过程将失败。
LpsaAttribute:对象的安全性
API函数:

HANDLE CreateMutex(
                LPSECURITY_ATTRIBUTES lpsa,     // 安全属性指针
                BOOL bInitialOwner,             // TR UE表示线程将拥有该信号量
                LPCTSTR lpName);                // 对象名

别的进程中可以用相同的互斥量名调用CreateMutex()和
OpenMutex()来打开该对象,这样互斥量就可用于多个进程中,线程通过WaitForSingleObject()和
WaitForMultipleObject()来等待一个互斥量,用ReleaseMutex()来释放一个互斥量。
11.Semaphores(信号量)
    一个CSemaphore类对象代表一个"信号"一个同步对象,它允许有限数目的线程在一个或多个进程中访问同一个资源。
一个Semaphore对象保持了对当前访问某一指定资源的线程的计数。
对于一个只能支持有限数目用户的共享资源来说,CSemaphore是很有用的。
CSemaphore
对象的当前计数是还可以允许的其它用户的数目。当这个计数达到零的时候,所有对这个由CSemaphore对象控制的资源的访问尝试都将被插入到一个系统
队列中等待,直到它们的时间用完或计数值不再为零。这个被控制的资源可以同时接受访问的最大用户数目是在CSemaphore对象的构造期间被指定的。
使
用CSemaphore对象的另一种方法是,将一个CSemaphore类型的变量添加到你想要控制的类中作为一个数据成员。在被控制对象的构造期间,调
用CSemaphore数据成员的构造函数来指定访问计数的初始值,访问计数的最大值,信号的名字(如果它要在整个进程中使用),以及需要的安全标志。要
访问由CSemaphore对象用这种方式控制的资源,首先要在你的资源的访问成员函数中创建一个CSingleLock类型或CMultiLock类型
的变量。然后调用加锁对象的Lock成员函数(例如,CSingleLock::Lock)。这时,你的线程将达到对资源的访问,等待资源被释放并访问
它,或者是在等待资源被释放的过程中超过了时间,对资源的访问失败。不管是哪一种情况,你的资源都是以一种线程安全(thread-safe)方式被访问
的。要释放资源,可以使用加锁对象的Unlock成员函数(例如,CSingleLock::Unlock),或者是让加锁对象超越范围。
CSemaphore(
LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName=
NULL,       LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );
LInitialCount:信号的初始使用计数。必须是大于或等于0,并且小于或等于lMaxCount。
LMaxCount:信号的使用计数的最大值。必须大于0。
PstrName:
信号的名字。如果此信号将在整个进程中被访问,则必须提供这个名字。如果是NULL,则对象将是没有名字的。如果这个名字与一个已经存在的信号的名字一
样,则构造函数创建一个新的CSemaphore对象,此对象引用具有这个名字的对象。如果这个名字与一个已经存在的但不是一个信号的同步对象的名字一
样,则构造函数会失败。
API使用:
HANDLE CreateSemaphore(
                LPSECURITY_ATTRIBUTES lpSecAttr,        // 安全属性指针
                LONG InitialCoutn,                       // 初始信号量数目
                LONG lMaxCount,                        // 允许的最大信号量数目
                LPCTSTR lpszSemName);                 // 信号量对象名指针

信号量数目大于0时,就处于信号状态。若lpszSemName参数为NULL时,信号量局限于一个进程的线程中;若给予一个字符串对象名,则其它进程也
可以使
用该信号量。别的进程中可以在调用CreateSemaphore()和OpenSemaphore()时把这个字符串名作参数,以打开该信号量,这样可
以实现多个进程间的同步。WaitForSingleObject()和WaitForMultipleObjects()来等待一个信号量。当完成同步
任务时,线程应用ReleaseSemaphore()来释放信号量:
BOOL ReleaseSemaphore(HANDLE hSema, LONG lReleaseCount, LPLONG  lpPrevious);
12.用户界面线程的创建:
创建用户界面线程时,必须首先从 CWinThread 派生类。必须使用 DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREAT宏声明并实现此类。
用户界面线程通常用于处理用户输入和响应用户事件。
13.进程的创建和终止;
调用CreateProcess函数创建新的进程,运行指定的程序。CreateProcess的原型如下:
BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
其中:
lpApplicationName指向包含了要运行模块名字的字符串。
lpCommandLine指向命令行字符串。
lpProcessAttributes描述进程的安全性属性,NT下有用。
lpThreadAttributes描述进程初始线程(主线程)的安全性属性,NT下有用。
bInHeritHandles
表示子进程(被创建的进程)是否可以继承父进程的句柄。可以继承的句柄有线程句柄、有名或无名管道、互斥对象、事件、信号量、映像文件、普通文件和通讯端
口等;还有一些句柄不能被继承,如内存句柄、DLL实例句柄、GDI句柄、URER句柄等等。
子进程继承的句柄由父进程通过命令行方式或者进程间通讯(IPC)方式由父进程传递给它。
dwCreationFlags表示创建进程的优先级类别和进程的类型。创建进程的类型分控制台进程、调试进程等;
优先级类别用来控制进程的优先级别,分Idle、Normal、High、Real_time四个类别。
lpEnviroment指向环境变量块,环境变量可以被子进程继承。
lpCurrentDirectory指向表示当前目录的字符串,当前目录可以继承。
lpStartupInfo指向StartupInfo结构,控制进程的主窗口的出现方式。
lpProcessInformation指向PROCESS_INFORMATION结构,用来存储返回的进程信息。
从其参数可以看出创建一个新的进程需要指定什么信息。
从上面的解释可以看出,一个进程包含了很多信息。若进程创建成功的话,返回一个进程信息结构类型的指针。进程信息结构如下:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
}PROCESS_INFORMATION;
进程信息结构包括进程句柄,主线程句柄,进程ID,主线程ID。
进程的终止
进程在以下情况下终止:
调用ExitProcess结束进程;
进程的主线程返回,隐含地调用ExitProcess导致进程结束;
进程的最后一个线程终止;
调用TerminateProcess终止进程。
当要结束一个进程时,发送WM_QUIT消息给主窗口,当然也可以从它的任一线程调用ExitProcess。

9.边界 测试
在测试用例部分已经说过,设计测试用例时要考虑边界输入和非法输入,这里统称为特殊输入,程序员在编写代码时也要考虑特殊输入,要为特殊输入编写处理代
码。在实际工作中,程序员没有考虑到某些特殊输入是很常见的,这也是程序错误的一个重要来源。不幸的是,如果编写代码和建立测试用例时都没有考虑这些输
入,那么白盒覆盖也不能自动发现,因为白盒覆盖是以代码为基础的,如果相应的代码根本不存在,白盒覆盖当然不会告诉用户"某某代码未覆盖"。

  特殊输入通常与数据类型有关,例如,如果一个参数是指针,空指针就是一个特殊输入,对于一个整数类型,最大值、最小值、0、1、-1都可以算是特殊输
入;另一方面,当输入特殊数据时,如果程序未作合适的处理,运行结果常常是产生异常。根据这两个条件,如果预先为各个数据类型定义特殊值,然后由测试工具
自动生成测试用例,使用这些特殊值或其组合作为输入数据进行测试,通常可以发现"未处理特殊输入"而产生的程序错误,这就是边界测试。
 

2007年7月19日星期四

ls 命令

  ls 命令可以说是Linux下最常用的命令之一。它有众多的选项,其中有很多是很有用的,你是否熟悉呢?下面列出了 ls 命令的绝大多数选项。
  
  -a 列出目录下的所有文件,包括以 . 开头的隐含文件。
  -b 把文件名中不可输出的字符用反斜杠加字符编号(就象在C语言里一样)的形式列出。
  -c 输出文件的 i 节点的修改时间,并以此排序。
  -d 将目录象文件一样显示,而不是显示其下的文件。
  -e 输出时间的全部信息,而不是输出简略信息。
  -f -U 对输出的文件不排序。
  -g 无用。
  -i 输出文件的 i 节点的索引信息。
  -k 以 k 字节的形式表示文件的大小。
  -l 列出文件的详细信息。
  -m 横向输出文件名,并以","作分格符。
  -n 用数字的 UID,GID 代替名称。
  -o 显示文件的除组信息外的详细信息。
  -p -F 在每个文件名后附上一个字符以说明该文件的类型,"*"表示可执行的普通
  文件;"/"表示目录;"@"表示符号链接;"|"表示FIFOs;"="表示套
  接字(sockets)。
  -q 用?代替不可输出的字符。
  -r 对目录反向排序。
  -s 在每个文件名后输出该文件的大小。
  -t 以时间排序。
  -u 以文件上次被访问的时间排序。
  -x 按列输出,横向排序。
  -A 显示除 "."和".."外的所有文件。
  -B 不输出以 "~"结尾的备份文件。
  -C 按列输出,纵向排序。
  -G 输出文件的组的信息。
  -L 列出链接文件名而不是链接到的文件。
  -N 不限制文件长度。
  -Q 把输出的文件名用双引号括起来。
  -R 列出所有子目录下的文件。
  -S 以文件大小排序。
  -X 以文件的扩展名(最后一个 . 后的字符)排序。
  -1 一行只输出一个文件。
  --color=no 不显示彩色文件名
  --help 在标准输出上显示帮助信息。
  --version 在标准输出上输出版本信息并退出。

Linux必学的60个命令(3)-系统管理

Linux必学的60个命令:系统管理相关命令
Linux必学的系统管理命令
作者:曹元其 发文时间:2004.10.18
对于Linux系统来说,无论是中央处理器、内存、磁盘驱动器、键盘、鼠标,还是用户等都是文件,Linux系统管理的命令是它正常运行的核心。熟悉了Linux常用的文件处理命令以后,这一讲介绍对系统和用户进行管理的命令。

df

1.作用
df命令用来检查文件系统的磁盘空间占用情况,使用权限是所有用户。

2.格式
df [options]

3.主要参数
-s:对每个Names参数只给出占用的数据块总数。
-a:递归地显示指定目录中各文件及子目录中各文件占用的数据块数。若既不指定-s,也不指定-a,则只显示Names中的每一个目录及其中的各子目录所占的磁盘块数。
-k:以1024字节为单位列出磁盘空间使用情况。
-x:跳过在不同文件系统上的目录不予统计。
-l:计算所有的文件大小,对硬链接文件则计算多次。
-i:显示inode信息而非块使用量。
-h:以容易理解的格式印出文件系统大小,例如136KB、254MB、21GB。
-P:使用POSIX输出格式。
-T:显示文件系统类型。

4.说明
df命令被广泛地用来生成文件系统的使用统计数据,它能显示系统中所有的文件系统的信息,包括总容量、可用的空闲空间、目前的安装点等。

超级权限用户使用df命令时会发现这样的情况:某个分区的容量超过了100%。这是因为Linux系统为超级用户保留了10%的空间,由其单独支配。也就是说,对于超级用户而言,他所见到的硬盘容量将是110%。这样的安排对于系统管理而言是有好处的,当硬盘被使用的容量接近100%时系统管理员还可以正常工作。

5.应用实例
Linux支持的文件系统非常多,包括JFS、ReiserFS、ext、ext2、ext3、ISO9660、XFS、Minx、vfat、MSDOS等。使用df -T命令查看磁盘空间时还可以得到文件系统的信息:

#df -T
文件系统 类型 容量 已用 可用 已用% 挂载点
/dev/hda7 reiserfs 5.2G 1.6G 3.7G 30% /
/dev/hda1 vfat 2.4G 1.6G 827M 66% /windows/C
/dev/hda5 vfat 3.0G 1.7G 1.3G 57% /windows/D
/dev/hda9 vfat 3.0G 2.4G 566M 82% /windows/E
/dev/hda10 NTFS 3.2G 573M 2.6G 18% /windows/F
/dev/hda11 vfat 1.6G 1.5G 23M 99% /windows/G

从上面除了可以看到磁盘空间的容量、使用情况外,分区的文件系统类型、挂载点等信息也一览无遗。

top

1.作用
top命令用来显示执行中的程序进程,使用权限是所有用户。

2.格式
top [-] [d delay] [q] [c] [S] [s] [i] [n]

3.主要参数
d:指定更新的间隔,以秒计算。
q:没有任何延迟的更新。如果使用者有超级用户,则top命令将会以最高的优先序执行。
c:显示进程完整的路径与名称。
S:累积模式,会将己完成或消失的子行程的CPU时间累积起来。
s:安全模式。
i:不显示任何闲置(Idle)或无用(Zombie)的行程。
n:显示更新的次数,完成后将会退出top。

4.说明
top命令是Linux系统管理的一个主要命令,通过它可以获得许多信息。这里我们结合图1来说明它给出的信息。

图1 top命令的显示

在图1中,第一行表示的项目依次为当前时间、系统启动时间、当前系统登录用户数目、平均负载。第二行显示的是所有启动的进程、目前运行的、挂起 (Sleeping)的和无用(Zombie)的进程。第三行显示的是目前CPU的使用情况,包括系统占用的比例、用户使用比例、闲置(Idle)比例。第四行显示物理内存的使用情况,包括总的可以使用的内存、已用内存、空闲内存、缓冲区占用的内存。第五行显示交换分区使用情况,包括总的交换分区、使用的、空闲的和用于高速缓存的大小。第六行显示的项目最多,下面列出了详细解释。
PID(Process ID):进程标示号。
USER:进程所有者的用户名。
PR:进程的优先级别。
NI:进程的优先级别数值。
VIRT:进程占用的虚拟内存值。
RES:进程占用的物理内存值。
SHR:进程使用的共享内存值。
S:进程的状态,其中S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值是负数。
%CPU:该进程占用的CPU使用率。
%MEM:该进程占用的物理内存和总内存的百分比。
TIME+:该进程启动后占用的总的CPU时间。
Command:进程启动的启动命令名称,如果这一行显示不下,进程会有一个完整的命令行。
top命令使用过程中,还可以使用一些交互的命令来完成其它参数的功能。这些命令是通过快捷键启动的。
<空格>:立刻刷新。
P:根据CPU使用大小进行排序。
T:根据时间、累计时间排序。
q:退出top命令。
m:切换显示内存信息。
t:切换显示进程和CPU状态信息。
c:切换显示命令名称和完整命令行。
M:根据使用内存大小进行排序。
W:将当前设置写入~/.toprc文件中。这是写top配置文件的推荐方法。

可以看到,top命令是一个功能十分强大的监控系统的工具,对于系统管理员而言尤其重要。但是,它的缺点是会消耗很多系统资源。

5.应用实例
使用top命令可以监视指定用户,缺省情况是监视所有用户的进程。如果想查看指定用户的情况,在终端中按"U"键,然后输入用户名,系统就会切换为指定用户的进程运行界面,见图2所示。

图2 使用top命令监视指定用户

free

1.作用
free命令用来显示内存的使用情况,使用权限是所有用户。

2.格式
free [-b|-k|-m] [-o] [-s delay] [-t] [-V]

3.主要参数
-b -k -m:分别以字节(KB、MB)为单位显示内存使用情况。
-s delay:显示每隔多少秒数来显示一次内存使用情况。
-t:显示内存总和列。
-o:不显示缓冲区调节列。

4.应用实例
free命令是用来查看内存使用情况的主要命令。和top命令相比,它的优点是使用简单,并且只占用很少的系统资源。通过-S参数可以使用free命令不间断地监视有多少内存在使用,这样可以把它当作一个方便实时监控器。
#free -b -s5

使用这个命令后终端会连续不断地报告内存使用情况(以字节为单位),每5秒更新一次。

quota

1.作用
quota命令用来显示磁盘使用情况和限制情况,使用权限超级用户。

2.格式
quota [-g][-u][-v][-p] 用户名 组名

3.参数
-g:显示用户所在组的磁盘使用限制。
-u:显示用户的磁盘使用限制。
-v:显示没有分配空间的文件系统的分配情况。
-p:显示简化信息。

4.应用实例
在企业应用中磁盘配额非常重要,普通用户要学会看懂自己的磁盘使用情况。要查询自己的磁盘配额可以使用下面命令(下例中用户账号是caojh):

#quota caojh
Disk quotas for user caojh(uid 502):
Filesystem blocks quota limit grace files quota limit grace
/dev/hda3 58 200000 400000 41 500 1000

以上显示ID号为502的caojh账号,文件个数设置为500~1000个,硬盘空间限制设置为200MB~400MB。一旦磁盘配额要用完时,就需要删除一些垃圾文件或向系统管理员请求追加配额。

at

1.作用
at命令用来在指定时刻执行指定的命令序列。

2.格式
at [-V] [-q x] [-f file] [-m] time

3.主要参数
-V:显示标准错误输出。
-q:许多队列输出。
-f:从文件中读取作业。
-m:执行完作业后发送电子邮件到用户。
time:设定作业执行的时间。time格式有严格的要求,由小时、分钟、日期和时间的偏移量组成,其中日期的格式为MM.DD.YY,MM是分钟,DD是日期,YY是指年份。偏移量的格式为时间+偏移量,单位是minutes、hours和days。

4.应用实例
#at -f data 15:30 +2 days

上面命令表示让系统在两天后的17:30执行文件data中指明的作业。

lp

1.作用
lp是打印文件的命令,使用权限是所有用户。

2.格式
lp [-c][-d][-m][-number][-title][-p]

3.主要参数
-c:先拷贝文件再打印。
-d:打印队列文件。
-m:打印结束后发送电子邮件到用户。
-number:打印份数。
-title:打印标题。
-p:设定打印的优先级别,最高为100。

4.应用实例
(1)使用lp命令打印多个文件
#lp 2 3 4
request id is 11 (3 file(s))

其中2、3、4分别是文件名;"request id is 11 (3 file(s)) "表示这是第11个打印命令,依次打印这三个文件。

(2)设定打印优先级别
#lp lp -d LaserJet -p 90 /etc/aliases

通过添加"-p 90",规定了打印作业的优先级为90。它将在优先级低于90的打印作业之前打印,包括没有设置优先级的作业,缺省优先级是50

useradd

1.作用
useradd命令用来建立用户帐号和创建用户的起始目录,使用权限是超级用户。

2.格式
useradd [-d home] [-s shell] [-c comment] [-m [-k template]] [-f inactive] [-e expire ] [-p passwd] [-r] name

3.主要参数
-c:加上备注文字,备注文字保存在passwd的备注栏中。 
-d:指定用户登入时的启始目录。
-D:变更预设值。
-e:指定账号的有效期限,缺省表示永久有效。
-f:指定在密码过期后多少天即关闭该账号。
-g:指定用户所属的群组。
-G:指定用户所属的附加群组。
-m:自动建立用户的登入目录。
-M:不要自动建立用户的登入目录。
-n:取消建立以用户名称为名的群组。
-r:建立系统账号。
-s:指定用户登入后所使用的shell。
-u:指定用户ID号。

4.说明
useradd可用来建立用户账号,它和adduser命令是相同的。账号建好之后,再用passwd设定账号的密码。使用useradd命令所建立的账号,实际上是保存在/etc/passwd文本文件中。

5.应用实例
建立一个新用户账户,并设置ID:
#useradd caojh -u 544

需要说明的是,设定ID值时尽量要大于500,以免冲突。因为Linux安装后会建立一些特殊用户,一般0到499之间的值留给bin、mail这样的系统账号。

groupadd

1.作用
groupadd命令用于将新组加入系统。

2.格式
groupadd [-g gid] [-o]] [-r] [-f] groupname

3.主要参数
-g gid:指定组ID号。
-o:允许组ID号,不必惟一。
-r:加入组ID号,低于499系统账号。
-f:加入已经有的组时,发展程序退出。

4.应用实例
建立一个新组,并设置组ID加入系统:
#groupadd -g 344 cjh

此时在/etc/passwd文件中产生一个组ID(GID)是344的项目。

kill

1.作用
kill命令用来中止一个进程。

2.格式
kill [ -s signal | -p ] [ -a ] pid ...
kill -l [ signal ]

3.参数
-s:指定发送的信号。
-p:模拟发送信号。
-l:指定信号的名称列表。
pid:要中止进程的ID号。
Signal:表示信号。

4.说明
进程是Linux系统中一个非常重要的概念。Linux是一个多任务的操作系统,系统上经常同时运行着多个进程。我们不关心这些进程究竟是如何分配的,或者是内核如何管理分配时间片的,所关心的是如何去控制这些进程,让它们能够很好地为用户服务。

Linux 操作系统包括三种不同类型的进程,每种进程都有自己的特点和属性。交互进程是由一个Shell启动的进程。交互进程既可以在前台运行,也可以在后台运行。批处理进程和终端没有联系,是一个进程序列。监控进程(也称系统守护进程)时Linux系统启动时启动的进程,并在后台运行。例如,httpd是著名的 Apache服务器的监控进程。

kill命令的工作原理是,向Linux系统的内核发送一个系统操作信号和某个程序的进程标识号,然后系统内核就可以对进程标识号指定的进程进行操作。比如在top命令中,我们看到系统运行许多进程,有时就需要使用kill中止某些进程来提高系统资源。在讲解安装和登陆命令时,曾提到系统多个虚拟控制台的作用是当一个程序出错造成系统死锁时,可以切换到其它虚拟控制台工作关闭这个程序。此时使用的命令就是kill,因为kill是大多数Shell内部命令可以直接调用的。

5.应用实例
(1)强行中止(经常使用杀掉)一个进程标识号为324的进程:
#kill -9 324

(2)解除Linux系统的死锁
在Linux 中有时会发生这样一种情况:一个程序崩溃,并且处于死锁的状态。此时一般不用重新启动计算机,只需要中止(或者说是关闭)这个有问题的程序即可。当 kill处于X-Window界面时,主要的程序(除了崩溃的程序之外)一般都已经正常启动了。此时打开一个终端,在那里中止有问题的程序。比如,如果 Mozilla浏览器程序出现了锁死的情况,可以使用kill命令来中止所有包含有Mozolla浏览器的程序。首先用top命令查处该程序的PID,然后使用kill命令停止这个程序:
#kill -SIGKILL XXX
其中,XXX是包含有Mozolla浏览器的程序的进程标识号。

(3)使用命令回收内存
我们知道内存对于系统是非常重要的,回收内存可以提高系统资源。kill命令可以及时地中止一些"越轨"的程序或很长时间没有相应的程序。例如,使用top命令发现一个无用 (Zombie) 的进程,此时可以使用下面命令:
#kill -9 XXX
其中,XXX是无用的进程标识号。

然后使用下面命令:
#free
此时会发现可用内存容量增加了。

(4)killall命令
Linux下还提供了一个killall命令,可以直接使用进程的名字而不是进程标识号,例如:
# killall -HUP inetd

crontab

1.作用
使用crontab命令可以修改crontab配置文件,然后该配置由cron公用程序在适当的时间执行,该命令使用权限是所有用户。

2.格式
crontab [ -u user ] 文件
crontab [ -u user ] { -l | -r | -e }

3.主要参数
-e:执行文字编辑器来设定时程表,内定的文字编辑器是vi。
-r:删除目前的时程表。
-l:列出目前的时程表。

crontab 文件的格式为"M H D m d cmd"。其中,M代表分钟(0~59),H代表小时(0~23),D代表天(1~31),m代表月(1~12),d代表一星期内的天(0~6,0为星期天)。cmd表示要运行的程序,它被送入sh执行,这个Shell只有USER、HOME、SHELL三个环境变量。

4.说明
和at命令相比,crontab命令适合完成固定周期的任务。

5.应用实例
设置一个定时、定期的系统提示:
[cao @www cao]#crontab -e
此时系统会打开一个vi编辑器。

如果输入以下内容:35 17 * * 5 wall "Tomorrow is Saturday I will go CS",然后存盘退出。这时在/var/spool/cron/目录下会生产一个cao的文件,内容如下:

# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (/tmp/crontab.2707 installed on Thu Jan 1 22:01:51 2004)
# (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
35 17 * * 5 wall "Tomorrow is Saturday I will play CS "

这样每个星期五17:35系统就会弹出一个终端,提醒星期六可以打打CS了!显示结果见图3所示。

图3 一个定时、定期的系统提示

动手练习

1.联合使用kill和top命令观察系统性能的变化

首先启动一个终端运行top命令,然后再启动一个终端使用kill命令,见图4所示。

图4 观察kill命令对top终端的影响

这时利用上面介绍的kill命令来中止一些程序:
#kill SIGKILL XXX

然后再看top命令终端的变化,包括内存容量、CPU使用率、系统负载等。注意,有些进程是不能中止的,不过学习Linux命令时可以试试,看看系统有什么反应。

2.使用at和halt命令定时关机
首先设定关机时间是17:35,输入下面代码:

#at 17:35
warning: commands will be executed using (in order) a) $SHELL b) login shell c) /bin/sh
at>halt `-i -p
at> <EOT>
job 6 at 2004-01-01 17:35

此时实际上就已经进入Linux系统的Shell,并且编写一个最简单程序:halt -i -p。上面Shell中的文本结束符号表示按"Ctrl+D"组合键关闭命令,提交任务退出Shell。"Job 6 at 2004-01-01 17:35"表示系统接受第6个at命令,在"2004-01-01 17:35"时执行命令:先把所有网络相关的装置停止,关闭系统后关闭电源。

3.用crontab命令实现每天定时的病毒扫描
前面已经介绍了一个简单的crontab命令操作,这里看一些更重要的操作。

(1)建立一个文件,文件名称自己设定,假设为caoproject:
#crontab -e

(2)文件内容如下:
05 09 * * * antivir
用vi编辑后存盘退出。antivir是一个查杀Linux病毒的软件,当然需要时先安装在系统中。

(3)使用crontab命令添加到任务列表中:
#crontab caoproject
这样系统内所有用户在每天的9点05分会自动进行病毒扫描。

4.用kill使修改的配置文件马上生效
Windows用户一般都知道,重要配置文件修改后往往都要重新启动计算机才能使修改生效。而Linux由于采用了模块化设计,可以自己根据需要实时设定服务。这里以网络服务inetd为例介绍一些操作技巧。

inetd 是一个监听守护进程,监听与提供互联网服务进程(如rlogin、telnet、ftp、rsh)进行连接的要求,并扩展所需的服务进程。默认情况下, inetd监听的这些daemon均列于/etc /inetd.conf文件中。编辑/etc/inetd.conf文件,可以改变inetd启动服务器守护进程的选项,然后驱使inetd以 SIGHUP(signal 1)向当前的inetd进程发送信号,使inetd重读该文件。这一过程由kill命令来实现。
用vi或其它编辑器修改inetd.conf后,首先使用下面命令:
#ps -ef |grep inetd

上面代码表明查询inetd.conf的进程号(PID),这里假设是1426,然后使用下面命令:
# kill -1426 inetd
这样配置文件就生效了。

这一讲介绍的系统管理命令都是比较重要的,特别是crontab命令和quota命令使用起来会有一定难度,需要多做一些练习。另外,使用kill命令要注意"-9"这个参数,练习时最好不要运行一些重要的程序。

2007年7月17日星期二

转义字符

&                      ===〉   &amp;
space               ===>    &nbsp;
<                       ===>    &lt;
>                       ===>    &gt;
"                        ===>    &quot;