Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Your Own Token-Ring Network Manager


FEB94: Your Own Token-Ring Network Manager

Your Own Token-Ring Network Manager

An IEEE 802.5 MAC-layer manager with a Windows front end

Andy Yuen

Andy is a software engineer in Sydney, Australia and specializes in automation and system management. Andy can be reached through the DDJ offices.


When a local area network (LAN) has problems, the first troubleshooting tool you usually reach for is either Novell's LANalyzer or Network General's Sniffer. While both are useful for analyzing large networks interconnected with routers and gateways, there's a class of token-ring network problems that can be solved using the IEEE 802.5 built-in network-management functions. These functions include gathering the network-configuration data, detecting marginally operating token-ring cards and cabling systems, and contending with other physical network problems.

In this article, I present a token-ring network-management application consisting of a Windows front end called TRMGR and a network-management agent called TRAGN. Among other things, this tool lets you list all the active token-ring adapters on the ring, associate descriptive names with token-ring adapters, identify soft errors and the fault domain, monitor the network status, record the fault domain when a fault or hard error (beaconing) occurs, and remove a token-ring adapter from the ring. I've tested TRAGN and TRMGR on a 33-MHz 386 with the IBM 4/16 token-ring adapter, DXMA0MOD.SYS/DXMC0MOD.SYS drivers, and the Novell IPX/NETX LAN requester.

An IEEE 802.5 MAC-layer Network-management Primer

The IEEE 802.5 Token Ring Access Method Standard defines both the physical layer and the medium access control (MAC) layer. The physical layer defines physical properties such as data-symbol encoding, decoding, and timing. The MAC layer defines the control and mediation on ring access and station management.

The 802.5 standard defines a set of data collection and distribution points, called "servers," to collect reports from other network stations. You use these servers to manage the stations. The configuration-report server (CRS) collects information on station-configuration changes due to insertion or removal of stations, and removes stations on the ring according to the manager's request. The ring-error monitor (REM) gathers and analyzes both hard- and soft-error reports sent by stations on a ring and assists in fault isolation and correction. Finally, the ring-parameter server (RPS) provides a set of operating parameters to stations during the insertion process.

Any token-ring station can declare itself to be a CRS, REM, or RPS by setting the functional address, a set of locally administered group addresses; see Table 1(a). Functional addresses are analogous to TCP/IP sockets, in which a particular socket number identifies the application. A station can declare itself to be both a CRS and an REM simply by logically ORing their addresses together: X'00000018', for example.

Stations send reports to the servers via MAC frames; see Figure 1. The information field in a frame consists of a vector (the fundamental unit of information) that contains a length field, a function-identifier field, and zero or more subvectors. Only one vector is allowed in a MAC frame. Figure 2 shows the MAC information-field structure. There are 23 vectors and 21 subvectors defined by the IEEE 802.5 standard. Luckily, we only need the vectors and subvectors in Table 2 to implement the token-ring network manager.

The most-significant byte of the vector identifier contains the destination class (high nybble) and source class (low nybble). The least-significant byte contains the vector code. The class information provides a means to route the frame to the appropriate management function. Table 1(b) lists the function classes. For example, the report-error vector X'6029' means that the frame is sent by a ring station (source class X'0') to REM (destination class X'6'), and the vector code is X'29' (report error).

The BCN (beaconing) vector is sent by a station which detects a beaconing condition. It contains the UNA subvector and the BCN type subvector. The reporting station and its nearest active upstream neighbor (NAUN) constitute the fault domain.

The AMP, SMP, and Report SUA Change vectors all contain the UNA subvector, which can be used to find out which stations are active on the ring. The AMP MAC frame is normally generated every three seconds by the active monitor. SMP frames are then sent by standby monitors to complete the neighbor-notification process.

The report-error vector contains the isolating error count, nonisolating error count, and UNA subvectors. These subvectors provide information on the type and number of soft errors which have occurred on the ring. Together, the source address of the MAC frame and the UNA subvector define the fault domain.

The remove-ring-stations vector is used to remove a ring station from the ring. It doesn't contain subvectors.

Implementing TRMGR

TRMGR gets information about the network from TRAGN and presents it to you. TRMGR also requests the agent to carry out any action in the management of the network. The agent is simply a real-mode DOS program started before Windows that performs all the token-ring API calls for the Windows front end.

TRMGR and TRAGN communicate via a software interrupt in exactly the same way that DOS does with INT 21h. I arbitrarily chose an unused software interrupt, INT 78h, as the agent service interrupt. True, the Windows front end still requires the DPMI interface to invoke a real-mode interrupt, but this is easier than doing everything via DPMI.

TRMSG.H (Listing One, page 84) shows the message structure. There are ten messages--five requests and five data responses. TRMGR actively polls TRAGN by sending it any of the five supported request messages. Table 3 lists the request messages, while Table 4 lists the messages sent by TRAGN in response to TRMGR's MSG_REQUEST_MSG request.

TRMGR is written in C and C++. I used Zortech C++ 3.0 for development and Gpf (from GPF Systems, Moodus, CT) for the user interface. Gpf, a visual tool for generating GUI apps, functions much like Visual C++'s AppStudio and AppWizard except that it generates C, not C++, code. I used the OS/2-hosted Version 1.3 of Gpf, which has code generators for 16- and 32-bit OS/2 and 16-bit Windows.

I used the Gpf editor to design TRMGR's GUI and the Gpf code generator to generate a skeleton program which handles the Windows GUI. I actually use Gpf only as an event dispatcher, so that when certain events (double clicking, selecting a menu item, and the like) occur, it passes control to one of my functions. The events handled by my functions are shown in Table 5. TRMGR source files include TRMGR.H, TRMGR.C (Gpf-generated Windows GUI skeleton), TRMGR.EXT (Gpf-generated header file), TRMGR.IDS (Gpf-generated resource identifiers), TRMGR.RC (Gpf-generated resource file), TRFNS.H, TRFNS.C (functions to handle all Windows events that TRMGR is interested in), TRSMT.H, TRSMT.CPP (the only C++ module, which provides services to TRFNS.C and accesses TRAGN via TRMSG for LAN management), TRMSG.H, and TRMSG.C (message layer which interfaces to TRAGN via DPMI). All of these files, along with executables, are available electronically; see "Availability," page 3.

TRSMT and TRMSG constitute the network-management part of the GUI front end. TRSMT is written in C++ because I wanted to use the C++ hashed search-table classes zHashTable and zGHSearch that come with the Zortech compiler. A hash table is used to implement a database within TRSMT to record information about token-ring stations (adapters), soft errors, and faults. The information recorded (defined in the structure AdapterEntry in TRSMT.C) includes the adapter's address and symbolic name, soft-error counts, beacon type, and occurrence time of soft error and fault. The structure members pNaun and pNext link the information regarding adapters, soft errors, and faults into separate linked-list structures. Figures 3 and 4 show how these linked lists work. The entries in these figures are labeled Entry 1, 2, 3, and so on, to identify the entry in the discussion. They don't represent the relative position in the database because, in a hash-table implementation, the entries are bound to scatter within the hash table.

pRingHead always points to the TRMGR adapter entry. Only the pNaun fields of the entries are used to form a linked list. By going through this linked list, you can find out which stations are active on the ring. In Figure 3, the ring configuration in network order is: entry 1, 2, 5, 6, and 4. The adapter at entry 3 is not active on the ring.

The soft-error and fault linked lists are formed differently from the configuration linked list. In the soft-error list, pHead points to an entry in the database, say, entry 1. The pNext fields are used to link all the adapters which have reported errors. For each adapter, pNaun points to its NAUN. As you may recall, the reporting adapter, its NAUN, and the medium between them constitute the fault domain. The time at which the soft errors occurred can also be found in the adapter entry. In Figure 4, soft errors have occurred in fault domains which consist of adapters pairs (1, 2), (3, 4), (4, 5), and (5, 2).

A hash table is used because all reports or data messages have the adapter field set. Adapter addresses are unique, making them good keys for locating an item quickly in a database. Remember that each MSG_DATA_CONFIG message has information only on the reporting adapter and its NAUN, and it takes several reports to figure out who is on the ring. Since these reports are started by the AMP MAC frame every three seconds, we must locate the database entry for each adapter quickly. A hash table allows just that. Basically, TRSMT provides TRFNS access to this database for displaying the network information.

TRSMT also provides TRFNS with the Poll function, which is called whenever Windows is idle; that is, it is called within the PeekMessage loop, which replaces the standard Windows GetMessage loop in TRMGR.C. Poll keeps sending MSG_REQUEST_DATA to TRAGN to solicit network reports until either TRAGN says there isn't any more report (by returning a ERR_NO_DATA return code) or Poll has processed the maximum number of messages in a row. In either case, Poll returns control to PeekMessageLoop and gives up the CPU so that other Windows applications can run. If any report is returned from TRAGN, Poll processes it and updates the database.

All database information items, except for adapter addresses and their symbolic names saved in the text file TRMGR.CFG, are discarded when you terminate TRMGR. The TRMGR.CFG file is read by PeekMessageLoop at startup time such that names associated with adapters in a previous TRMGR session are maintained.

TRMSG.C is the module which interfaces between TRSMT and TRAGN. Since Windows is running in protected mode, TRMsg uses DPMI to invoke the TRAGN's functions. It passes the message identifier msg in the EAX register and simulates a real-mode interrupt using the Zortech dpmi_SimRealModeInterrupt function (INT 31h function 300h). The first time this function is called, it only passes msg to TRAGN and not the whole message. (MSG_REQUEST_ACTIVATE should always be the first message sent to TRAGN.) Upon returning from the interrupt, the address to a block of DOS memory which holds the message structure is returned in the EDX:EAX register pair. TRMsg converts the real-mode SEG:OFFSET address into a protected-mode SELECTOR:OFFSET pointer using dpmi_SegToDescriptor (INT 31h function 0002h) and saves it for all future use. From then on, all message exchanges use this memory block. If TRMGR were a real-mode app, I would have passed the message pointer from TRMGR to TRMSG, instead of the other way around. In Windows protected mode, however, I couldn't allocate DOS memory using DPMI. Hence, I let TRAGN provide the memory block for communication. When a MSG_REQUEST_INACTIVATE message is encountered, TRmsg frees the descriptor by calling dpmi_FreeDescriptor (INT 31h function 0001h).

The advantage of this message-based architecture is that I can go on to program the Windows front end before writing any code for the network-management agent. The UI can be tested simply by faking message exchanges. In this project, I wrote the entire UI before the agent. It is almost plug-and-play when the agent is ready. Under Windows, you can allocate DOS memory using the Windows GlobalDosAlloc, which returns both a DOS segment and a protected-mode selector. I chose not to use it because I wanted to isolate all Windows-related code to TRFNS.C. In retrospect, this isn't really necessary, and using GlobalDosAlloc may prove even more convenient.

Implementing TRAGN

To implement TRAGN, I started with the programs on the diskette that comes with IBM's Technical Reference: Token-ring Network PC Adapter manual, although I eventually wrote my own version of the API headers. Overall, the TRAGN program consists of: DLCCONST.H, DLCPTBLS.H (token-ring API constants and structures); TRAGN.C (source file for the network-management agent; TRUTL.H, TRUTL.C (a set of utility functions); TRDIR.H, TRDIR.C (a set of functions to access the token-ring API's direct interface); TRAPP.H, TRAPP.ASM (assembly program to setup API appendages); and TRMSG.H (message definitions). All of these files are available electronically.

TRAGN handles all message exchanges with TRMGR and carries out TRMGR's requests: activating the token-ring adapter, removing a station, reporting on soft errors and faults, and the like. It interfaces to the token-ring API via TRDIR.

When run, TRAGN first checks to see if token-ring API support has been installed. If not, TRAGN terminates with an error message. In general, all applications using the token-ring API should check for its availability by checking if the token-ring API support INT 5Ch interrupt vector has been set, and if so, whether it actually is the token-ring API support. TRAGN then installs an interrupt handler for INT 78h (chosen arbitrarily), which is used for message exchange with TRMGR, and sets up the data received and ring-status appendages by calling appinit in TRAPP.ASM. TRAGN then terminates and stays resident.

The Zortech compiler comes with a TSR package to facilitate writing TSRs. However, it's only available in the small memory model and programming the token-ring API is easier using the large model. The 8086 assembly-language module TRAPP.ASM installs interrupt handlers and handle appendages because the Zortech compiler doesn't support the interrupt keyword like the Microsoft compiler. Again, Zortech provides an interrupt package, but I couldn't get it to work in the large memory model.

When TRMGR requests activation of the token-ring adapter via INT 78H, TRAGN will go through the following steps:

  1. Issue a dir_interrupt call to check if the token-ring adapter has been initialized. If so, go to step #3.
  2. Call dir_initialise to initialize the adapter; return error code to the caller if it fails.
  3. Call dir_openadapter to physically open the adapter, set various open parameters, and register the ring-status appendage so that TRAGN will be informed of all ring-status changes. Return error code to caller if it fails.
  4. Call dir_set_func_address to set the functional address for CRS and REM so that TRAGN can receive configuration and error reports. Return error code to caller if operation fails.
  5. Call receive to start receiving MAC frames and set the data-received appendage such that TRAGN will be informed when MAC frames are received. Return error code to caller if operation fails.
When a MAC frame is received, the data-received appendage will be taken. It checks if the MAC frame is one of the ones we want--BCN, AMP, SMP, Report SUA Change, or Report Error. If so, it saves the information in a message block and queues it. The MAC frame is passed to us in a buffer from the direct-interface buffer pool (setup in the dir_openadapter call). Whether or not the MAC frame is useful, you still need to return it to the buffer pool. Unfortunately, if it is released by calling buffer_free within the appendage, it will cause a system crash. I therefore added the message MSG_HOUSEKEEPING. A housekeeping message is created with the buffer address stored in the buffer_one field if the MAC frame isn't the one we want. It also gets queued. Whenever a MSG_REQUEST_DATA message is received, the first element in the queue will be retrieved. The message is passed back to TRMGR if it is a management report, and the memory for both the message and the MAC frame is freed. If it is a housekeeping message, it frees the MAC frame and message buffers and retrieves the next message until either there is no more message, or the message is a management report. Remember that the MAC-frame vector identifier and length are in big-endian format, unlike that of the PC, which uses Intel's little-endian format. The ring-status appendage is handled in a similar fashion, except it does not come from a MAC frame. Consequently, you don't have to release any memory back to the direct buffer pool.

When TRMGR requests deactivation of the token-ring interface, TRAGN simply calls receive_cancel to cancel the receive command. It does not actually close the adapter. When TRMGR requests the removal of an adapter, TRAGN builds a MAC frame and sends it via dir_transmit.

Conclusions

With the IEEE 802.5 network-management tool presented here, you can begin snooping around your LAN, discovering and fixing potential problems. However, you should be aware of some of this application's limitations. For instance, 802.5 MAC frames don't normally go through a bridge from one LAN segment to another. Consequently, TRMGR can only troubleshoot the LAN on which it is connected. Nor can TRMGR analyze the traffic load. (Use Sniffer or LANalyzer along with specially modified token-ring cards for this.)

TRMGR may be confused when the TRMGR adapter gets removed, but its token-ring adapter driver automatically resets it. For example, in the Novell environment where the IPX/NETX LAN requester has such a "feature," TRMGR works much more reliably if the LAN requester is not activated. Finally, although a station may use up to two token-ring adapters, TRMGR supports only the primary token-ring adapter.

Table 1: (a) Functional addresses for declaring CRS, REM, or both; (b) function classes.

                              
<b>(a)</b>
                             Functional
     Function Name           Address
     -----------------------------------
     Active Monitor          X'00000001'
     CRS                     X'00000010'
     REM                     X'00000008'
     RPS                     X'00000002'
<b>(b)</b>
     Function Class        Value
     ---------------------------
     Ring Station          X'0'
     CRS                   X'4'
     RPS                   X'5'
     REM                   X'6'

Table 2: (a) Vectors used by TRMGR (*does not contain any subvector); (b) subvectors used by TRMGR.

<b>(a)</b>
     Vector                      Vector
     Name                        Identifier
     --------------------------------------
     BCN (beacon)                X'0002'
     Active monitor present      X'0005'
     Stand-by monitor present    X'0006'
     Remove ring stations*       X'0408'
     Report SUA change           X'4026'
     Report error                X'6029'

<b>(b)</b>
Subvector                Subvector      Subvector      Subvector
Name                     Identifier     Length         Value
--------------------------------------------------------------------------
BCN Type                 X'01'          4              2-byte beacon type
Upstream neighbor's      X'02'          8              6-byte node address
  address
Isolating error count    X'2D'          8              Five 1-byte error counters
                                                         and one byte reserved
Nonisolating error       X'2E'          8              Five 1-byte error counters


  count                                                  and one byte reserved

Table 3: TRMGR request messages.

Message                   Description
-------------------------------------------------------------------
MSG_REQUEST_ACTIVATE      This is the first message the TRMGR sends
                          to TRAGN. It requests TRAGN to prepare the
                          token-ring adapter and establish itself as
                          both REM and CRS so that it can      receive
                          management reports. No other parameter in the
                          message structure is required. On return, the
                          message member <I>adapter</I> contains the token-ring
                          node address of the TRMGR station.

MSG_REQUEST_MSG           TRMGR uses this to get management reports from
                          TRAGN. On return, the <I>msg</I> and information fields
                          will be changed to the appropriate message type
                          and report, respectively, if there is any report
                          available. If no report is available, <I>retcode 
                          </I>will be set to ERR_NO_DATA.

MSG_REQUEST_RESET         TRMGR uses this to reset the adapter on which
                          TRMGR is running. Necessary if TRMGR adapter
                          gets removed automatically as a result of fault
                          isolation or by another station running TRMGR
                          or another network manager.

MSG_REQUEST_REMOVE        The only active network-management facility in
                          TRMGR. The <I>adapter</I> field contains the address
                          of the ring station to be removed. TRMGR uses
                          this message to remove a station from the ring
                          if the administrator has determined that a
                          station is causing performance problem by
                          generating many soft or hard errors.

MSG_REQUEST_INACTIVATE    TRMGR sends this to TRAGN before it terminates
                          to tell TRAGN it can stop gathering management
                          reports.

Table 4: Messages sent by TRAGN in response to TRMGR's MSG_REQUEST_MSG request.

Message               Description
--------------------------------------------------------------------
MSG_DATA_ERROR        Fields <I>adapter</I> and <I>naun</I> contain the fault
                      domain. <I>isoerr</I> and <I>noniso</I> contain the
                      soft-error counts.

MSG_DATA_INDICATION   Field <I>ringstatus</I> contains current status of
                      the ring. Generated only when there is a
                      change in ring status.

MSG_DATA_CONFIG       Field <I>adapter</I> contains reporting-station
                      address; <I>naun</I> contains its neighbor's address. 
                      Collecting this report for all stations on
                      the ring allows the ring's overall
                      configuration to be determined.

MSG_DATA_BEACON       Fields <I>adapter</I> and <I>naun</I> make up the fault
                      domain, and <I>beacontype</I> contains the beacon type.

MSG_HOUSEKEEPING      For TRAGN's internal use only. Used for release
                      of MAC-layer buffers. Not included in original
                      design--added later due to certain limitations
                      of the DOS token-ring API.

Table 5: Function/event table.

Function          Event             Window/Control
------------------------------------------------------------
<i>ChangeName</i>        BN_CLICKED        Config/ChangeName button
<i>FillAdapters </i>     WM_INITDIALOG     Config
<i>FillErrors </i>       WM_INITDIALOG     Errors
<i>FillFaults </i>       WM_INITDIALOG     Faults
<i>PaintMain </i>        WM_PAINT          Main
<i>QuitTRMGR  </i>       WM_CLOSE          Main
<i>RecordAdapter</i>     LBN_DBCLK         Config/Errors/Faults list box
<i>ResetAll</i>          BN_CLICKED        Config/Reset button
<i>ResetErrors</i>       BN_CLICKED        Errors/Clear button
<i>ResetFaults</i>       BN_CLICKED        Faults/Clear button
<i>SelectAdapter</i>     LBN_SELCHANGE     Config list box
<i>SetOption</i>         BN_CLICKED        Display menu: dynamic, static
<i>ShowDetails</i>       LBN_DBCLK         Config/Errors/Faults list box

Figure 1: MAC frame format.

------------------------------------------------------
| SD | AC | FC | DA | SA | RI | INFO | FCS | ED | FS |
------------------------------------------------------

SD     Starting Delimiter (1 byte)
AC     Access Control (1 byte)
FC     Frame Control
DA     Destination Address (6 bytes)
SA     Source Address (6 bytes)
RI     Routing Information (0--18 bytes)
INFO   Information (0 or more bytes)
FCS    Frame Check Sequence (4 bytes)
ED     Ending Delimiter (1 byte)
FS     Frame Status (1 byte)

Figure 2: MAC frame information-field format.

-----------------------------------------------------
| VL | VI | SVL | SVI | SVV | ... | SVL | SVI | SVV |
-----------------------------------------------------

VL      Vector Length (2 bytes)
VI      Vector Identifier (2 bytes)
SVL     Subvector Length (1 byte)
SVI     Subvector Identifier (1 byte)
SVV     Subvector Value (n bytes)

Figure 3: Linked-list structure for Config.

Figure 4: Linked-list structure for soft errors and faults.

[LISTING ONE]


#ifndef __TRMSG
#define __TRMSG

//#include "trdir.h"
#ifndef DLCPTBLS

typedef unsigned char  BYTE;
typedef unsigned int   WORD;
typedef unsigned long  DWORD;
typedef unsigned char  *ADDRESS;

#endif

#define SERVINT                 0x78

/* lower half to upper half messages */
#define MSG_HOUSEKEEPING        0
#define MSG_DATA_ERROR          1
#define MSG_DATA_INDICATION     2
#define MSG_DATA_BEACON         3
#define MSG_DATA_CONFIG         4

/* upper half to lower half messages */
#define MSG_REQUEST_MSG         5
#define MSG_REQUEST_RESET       6
#define MSG_REQUEST_REMOVE      7
#define MSG_REQUEST_ACTIVATE    8
#define MSG_REQUEST_INACTIVATE  9

#define MAXMSGS                 9

/* error return codes */
#define ERR_OK                  0
#define ERR_INVALID_MSG         1
#define ERR_FAILURE             2
#define ERR_NO_DATA             3

/* ring status constants */
#define STS_SIGNALLOSS          0x8000
#define STS_HARDERROR           0x4000
#define STS_SOFTERROR           0x2000
#define STS_XMITBEACON          0x1000
#define STS_WIREFAULT           0x0800
#define STS_REMOVAL1            0x0400
#define STS_REMOVERECVD         0x0100
#define STS_CNTOVERFLOWED       0x0080
#define STS_SINGLESTN           0x0040
#define STS_RECOVERY            0x0020



/* definition all all messages for communicating with the upper half */
typedef struct node{
    struct node *next;              //points to the next message
    ADDRESS buffer_one;             //DLC buffer address
    WORD msg;                       //message type
    BYTE retcode;                   //return code
    BYTE dir_retcode;               //direct interface return code
    BYTE more;                      //number of messages still queued
    BYTE beacontype;                //becaon type
    WORD ringstatus;                //ring status
    BYTE adapter[6];                //reporting adapter's address
    BYTE naun[6];                   //and its neighbour
    BYTE isoerr[5];                 //isolating error counts
    BYTE noniso[5];                 //non-isolating error counts
} Msgs;

#if __cplusplus
extern "C" {
#endif

int TRMsg(Msgs *msg);
void TRPrintMsg(Msgs *msg);

#if __cplusplus
}
#endif

#endif
End Listing


Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.