home / infca / progs (navigation links)

Cuando las barbas de tu vecino veas cortar, pon las tuyas a remojar.

WCT | GTS - generat TCP server | Servei | c-TOD | c-Args | c-Err | printf | Raw-HexDmp | c-HexDmp | dev-c++ Borland | Links | End | mapa

Projectes actuals

go 2 top
LBT MB Threads vs Processes Client / Server Server Structure WCT Registry
GTS - general TCP server Services Envir vars Tips (code) Borland PHP Links

Tinc el meu cervell ocupat en aquestos temes :


LBT


MessageBox

In C : [url]

//
// Lets try to show up a Message Box ...
//
// Compile using :
//
//   Microsoft -    CL        UNO.C     /GD       USER32.LIB
//
//   Borland -      BCC32     -tW       UNO.C
//

#include <windows.h>

int WINAPI WinMain (
                     HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     PSTR      lpCmdLine,
                     int       nCmdShow ) 
{

 int on_button ;

     on_button = MessageBox ( NULL,
                              TEXT("Do you like Nekomimi?"),
                              TEXT("Question title"),
                              MB_YESNO | MB_ICONQUESTION ) ;

     if ( on_button == IDYES )

          MessageBox ( NULL,
                       TEXT("You are nice!"),
                       TEXT("Good Answer title") , MB_OK ) ;

     else
          MessageBox ( NULL,
                       TEXT("Be Killed!"),
                       TEXT("Bad Answer title"),
                       MB_OK ) ;
     return 0 ;
}

Compile with BCC32 -tW nom.c

Error: unresolver external '_main' referenced from c:\borland\bcc55\lib\C0X32.OBJ Error: Unresolved external 'WinMain' referenced from c:\borland\bcc55\lib\C0X32.OBJ

Error al Linkar : no es troben les LIB requerides

The file c0x32.obj is the startup code for a console mode programs, the type of program that begins at the function main. A GUI program, the type that starts at the function WinMain, uses a different startup code file.

Solution : remove the "-tW" compiler flag, that creates a windows application.

In Delphi :

if ( i < 0 ) then application.messagebox ( 'Error', 'Record format not supported', mb_OK ) ;

MessageBeep : URL

BOOL MessageBeep ( UINT uType ); as if DoBeep then MessageBeep ( MB_OK ) ;


Amunt! Top Amunt!

Threads / Processes

Es tracta de investigar que veu el WTM i/o el PROCEXP (SysInternals) quan un thread en llença un altre i després desapareix.

Punt interessants :

URL (4/17) :

A process contains its own independent virtual address space with both code and data, protected from other processes. Each process, in turn, contains one or more independently executing threads. A thread running within a process can create new threads, create new independent processes, and manage communication and synchronization between the objects.

By creating and managing processes, applications can have multiple, concurrent tasks processing files, performing computations, or communicating with other networked systems. It is even possible to exploit multiple processors to speed processing.

This chapter explains the basics of process management and also introduces the basic synchronization operations that will be used throughout the rest of the book.

Windows Processes and Threads

Every process contains one or more threads, and the Windows thread is the basic executable unit. Threads are scheduled on the basis of the usual factors: availability of resources such as CPUs and physical memory, priority, fairness, and so on. Windows has supported symmetric multiprocessing (SMP) since NT4, so threads can be allocated to separate processors within a system.

From the programmer's perspective, each Windows process includes resources such as the following components:

Each thread in a process shares code, global variables, environment strings, and resources. Each thread is independently scheduled, and a thread has the following elements:

Process Creation

The fundamental Windows process management function is CreateProcess, which creates a process with a single thread. It is necessary to specify the name of an executable program file as part of the CreateProcess call.

It is common to speak of parent and child processes, but these relationships are not actually maintained by Windows. It is simply convenient to refer to the process that creates a child process as the parent.

CreateProcess has ten parameters to support its flexibility and power. Initially, it is simple to use default values. Just as with CreateFile, it is appropriate to explain all the CreateProcess parameters. Related functions then become easier to understand.

Note first that the function does not return a HANDLE; rather, two separate handles, one each for the process and the thread, are returned in a structure specified in the call. CreateProcess creates a new process with a primary thread. The example programs are always very careful to close both of these handles when they are no longer needed in order to avoid resource leaks; a common defect is to neglect to close the thread handle. Closing a thread handle, for instance, does not terminate the thread; the CloseHandle function only deletes the reference to the thread within the process that called CreateProcess.

BOOL CreateProcess ( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpsaProcess, LPSECURITY_ATTRIBUTES lpsaThread, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurDir, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcInfo )

Return: TRUE only if the process and thread are successfully created.

Parameters

Some parameters require extensive explanations in the following sections, and many are illustrated in the program examples.

lpApplicationName and lpCommandLine (this is an LPTSTR and not an LPCTSTR) together specify the executable program and the command line arguments, as explained in the next section.

lpsaProcess and lpsaThread point to the process and thread security attribute structures. NULL values imply default security and will be used until Chapter 15, which covers Windows security.

bInheritHandles indicates whether the new process inherits copies of the calling process's inheritable open handles (files, mappings, and so on). Inherited handles have the same attributes as the originals and are discussed in detail in a later section.

dwCreationFlags combines several flags, including the following.

Several of the flags control the priority of the new process's threads. The possible values are explained in more detail at the end of Chapter 7. For now, just use the parent's priority (specify nothing) or NORMAL_PRIORITY_CLASS.

lpEnvironment points to an environment block for the new process. If NULL, the process uses the parent's environment. The environment block contains name and value strings, such as the search path.

lpCurDir specifies the drive and directory for the new process. If NULL, the parent's working directory is used.

lpStartupInfo specifies the main window appearance and standard device handles for the new process. Use the parent's information, which is obtained from GetStartupInfo. Alternatively, zero out the associated STARTUPINFO structure before calling CreateProcess. To specify the standard input, output, and error handles, set the standard handler fields (hStdInput, hStdOutput, and hStdError) in the STARTUPINFO structure. For this to be effective, also set another STARTUPINFO member, dwFlags, to STARTF_USESTDHANDLES, and set all the handles that the child process will require. Be certain that the handles are inheritable and that the CreateProcess bInheritHandles flag is set. The Inheritable Handles subsection gives more information and an example.

lpProcInfo specifies the structure for containing the returned process, thread handles, and identification. The PROCESS_INFORMATION structure is as follows:

typedef struct PROCESS_INFORMATION { HANDLE hProcess ; HANDLE hThread ; DWORD dwProcessId ; DWORD dwThreadId ; } PROCESS_INFORMATION ;

Why do processes and threads need handles in addition to IDs? The ID is unique to the object for its entire lifetime and in all processes, whereas a given process may have several handles, each having distinct attributes, such as security access. For this reason, some process management functions require IDs, and others require handles. Furthermore, process handles are required for the general-purpose, handle-based functions. Examples include the wait functions discussed later in this chapter, which allow waiting on handles for several different object types, including processes. Just as with file handles, process and thread handles should be closed when no longer required.

Note: The new process obtains environment, working directory, and other information from the CreateProcess call. Once this call is complete, any changes in the parent will not be reflected in the child process. For example, the parent might change its working directory after the CreateProcess call, but the child process working directory will not be affected, unless the child changes its own working directory. The two processes are entirely independent.

The UNIX and Windows process models are considerably different. First, Windows has no equivalent to the UNIX fork function, which makes a copy of the parent, including the parent's data space, heap, and stack. fork is difficult to emulate exactly in Windows, and, while this may seem to be a limitation, fork is also difficult to use in a multithreaded UNIX system because there are numerous problems with creating an exact replica of a multithreaded system with exact copies of all threads and synchronization objects, especially on an SMP system. Therefore, fork, by itself, is not really appropriate in any multithreaded system.

CreateProcess is, however, similar to the common UNIX sequence of successive calls to fork and execl (or one of five other exec functions). In contrast to Windows, the search directories in UNIX are determined entirely by the PATH environment variable.

As previously mentioned, Windows does not maintain parent-child relationships among processes. Thus, a child process will continue to run after the creating parent process terminates. Furthermore, there are no process groups in Windows. There is, however, a limited form of process group that specifies all the processes to receive a console control event.

Windows processes are identified both by handles and by process IDs, whereas UNIX has no process handles.

Specifying the Executable Image and the Command Line

Either lpApplicationName or lpCommandLine specifies the executable image name. The rules are as follows.

The new process can obtain the command line using the usual argv mechanism, or it can invoke GetCommandLine to obtain the command line as a single string.

Notice that the command line is not a constant string. This is consistent with the fact that the argv parameters to the main program are not constant. A program could modify its arguments, although it is advisable to make any changes in a copy of the argument string.

The new process is not required to be built with the same UNICODE definition as that of the parent process. All combinations are possible. Using _tmain as discussed in Chapter 2 is helpful in developing code for either UNICODE or ASCII operation.

Inheritable Handles

Frequently, a child process requires access to an object referenced by a handle in the parent; if this handle is inheritable, the child can receive a copy of the parent's open handle. The standard input and output handles are frequently shared with the child in this way. To make a handle inheritable so that a child receives and can use a copy requires several steps.

The bInheritHandles flag on the CreateProcess call determines whether the child process will inherit copies of the inheritable handles of open files, processes, and so on. The flag can be regarded as a master switch applying to all handles.

It is also necessary to make an individual handle inheritable; it is not done by default. To create an inheritable handle, use a SECURITY_ATTRIBUTES structure at creation time or duplicate an existing handle.

The SECURITY_ATTRIBUTES structure has a flag, bInheritHandle, that should be set to TRUE. Also, recall that nLength should be set to sizeof (SECURITY_ATTRIBUTES).

The following code segment shows how an inheritable file or other handle is typically created. In this example, the security descriptor within the security attributes structure is NULL; Chapter 15 shows how to include a security descriptor.

HANDLE h1, h2, h3 ; SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE } ; ... h1 = CreateFile ( ..., & sa, ... ) ; /* Inheritable. */ h2 = CreateFile ( ..., NULL, ... ) ; /* Not inheritable. */ h3 = CreateFile ( ..., & sa, ... ) ; /* Inheritable. sa can be reused. */

A child process still needs to know the value of an inheritable handle, so the parent needs to communicate handle values to the child using an interprocess communication (IPC) mechanism or by assigning the handle to standard I/O in the STARTUPINFO structure, as is done in the first example of this chapter (Program 6-1) and in several additional examples throughout the book. This is generally the preferred technique because it allows I/O redirection in a standard way and no changes are needed in the child program.

Alternatively, nonfile handles and handles that are not used to redirect standard I/O can be converted to text and placed in a command line or in an environment variable. This approach is valid if the handle is inheritable because both parent and child processes identify the handle with the same handle value. Exercise 6-2 suggests how to demonstrate this, and a solution is presented on the book's Web site.

The inherited handles are distinct copies. Therefore, a parent and child might be accessing the same file using different file pointers. Furthermore, each of the two processes can and should close its own handle.

Figure 6-2 shows how two processes can have distinct handle tables with two distinct handles associated with the same file or other object. Process 1 is the parent, and Process 2 is the child. The handles will have identical values in both processes if the child's handle has been inherited, as is the case with Handles 1 and 3.

06fig02.gif = Figure 6-2 Process Handle Tables

On the other hand, the handle values may be distinct. For example, there are two handles for File D, where Process 2 obtained a handle by calling CreateFile rather than by inheritance. Finally, as is the case with Files B and E, one process may have a handle to an object while the other does not. This would be the case when the child process creates the handle or when a handle is duplicated from one process to another, as described in the upcoming Duplicating Handles section.

Process Handle Counts

A common programming error is to neglect to close handles when they are no longer needed; this can result in resource leakage, which in turn can degrade performance, cause program failures, and even impact other processes. NT 5.1 added a new function that allows you to determine how many handles any process has open. In this way, you can monitor your own process or other processes.

Here is the function definition, which is self-explanatory:

BOOL GetProcessHandleCount ( HANDLE hProcess, PDWORD pdwHandleCount )

Process Identities

A process can obtain the identity and handle of a new child process from the PROCESS_INFORMATION structure. Closing the child handle does not, of course, destroy the child process; it destroys only the parent's access to the child. A pair of functions is used to obtain current process identification.

HANDLE GetCurrentProcess (VOID) DWORD GetCurrentProcessId (VOID)

GetCurrentProcess actually returns a pseudohandle and is not inheritable. This value can be used whenever a process needs its own handle. You create a real process handle from a process ID, including the one returned by GetCurrentProcessId, by using the OpenProcess function. As is the case with all sharable objects, the open call will fail if you do not have sufficient security rights.

HANDLE OpenProcess ( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId )

Return: A process handle, or NULL on failure.

Parameters

dwDesiredAccess determines the handle's access to the process. Some of the values are as follows.

bInheritHandle specifies whether the new handle is inheritable. dwProcessId is the identifier of the process requiring a handle.

Finally, a running process can determine the full pathname of the executable used to run it with GetModuleFileName or GetModuleFileNameEx, using a NULL value for the hModule parameter. A call from within a DLL will return the DLL's file name, not that of the .EXE file that uses the DLL.

Amunt! Top Amunt!

Client / Server

Servidors i clients que he codificat
Panel for PS/2 9595
\\Linux\progs\panel\Sebas\panelsrv.c i panelcli.c
Character Window (ncurses.h)
\\Linux\progs\win\tsrv.c i tcli.c
Web Console
\\cpp\DTF\webco.c

Bons exemples

Amunt! Top Amunt!

Server structure

From WEBCO.C :

iRC = WSAStartup ( ... ) ; iSS = socket ( ... ) ; bind ( iSS, ... ) ; listen ( iSS, ... ) ; iCS := accept ( ISS, ... ) ; iRC = recv ( ICS, ... ) ; iRC = shutdown ( ICS, ... ) ; iRC = closesocket ( iCS ) ; iRC = shutdown ( ISS, ... ) ; iRC = closesocket ( iSS ) ;

From TSRV.C : [*****]

iRC = socket ( iDomain, iType, iProtocol ) ; /* get socket descriptor */ iRC = bind ( iMySocket, (struct sockaddr *) & MyAddr, sizeof ( MyAddr ) ) ; iRC = listen ( iMySocket, kMyBackLog ) ; while ( iServerIsAlive == 1 ) { iClientSocket = accept ( iMySocket, (struct sockaddr *) & RemoteAddr, & sin_size ) ; pSCB = SCB_Create ( ) ; SCB_Init ( pSCB ) ; pTCB = TCB_Create ( "Rcv" ) ; TCB_Init ( pTCB, iClientSocket, "Rcv" ) ; iRC = pthread_create ( & ( pTCB -> Tid ), NULL, Thread_TCP_Receive, pTCB ) ; pSCB -> pTCBrcv = pTCB ; pTCB -> pSCB_Parent = pSCB ; pTCB = TCB_Create ( "Tmt" ) ; TCB_Init ( pTCB, iClientSocket, "Tmt" ) ; iRC = pthread_create ( & ( pTCB -> Tid ), NULL, Thread_TCP_Transmit, pTCB ) ; pSCB -> pTCBtmt = pTCB ; pTCB -> pSCB_Parent = pSCB ; } ; // while iRC = shutdown ( iMySocket, 2 ) ;

Amunt! Top Amunt!
Client structure
\\CPP/Server/GeneralServer/myClient.c iRC = WSAStartup ( wVersionRequested, & myWSAData ) ; iRC = socket ( iDomain, iType, iProtocol ) ; /* get socket descriptor */ shpServerHost = gethostbyname ( Servidor ) ; iRC = connect ( iMySocket, (struct sockaddr *) & ssiServer, sizeof ( ssiServer ) ) ; while iRC = send ( iMySocket, TmtData, strlen ( TmtData ), 0 ) ; iRC = recv ( iMySocket, RcvData, RcvDataMaxLength, RcvFlags ) ; end ; // while iRC = shutdown ( iMySocket, 2 ) ; /* both TO and FROM socket */

Amunt! Top Amunt!
Server's I can think of

WCT = Web Console Thread

Any (multithread) program can include a Web Console Thread code, that implements a basic Web Server, listening on port 80, that is able to generate few HTML pages, to display few internal vars/events on a Web page on any browser that knows only the program's IP address.

A program that has a nice ("internal", not "web") console is Azureus


Amunt! Top Amunt!

Accés al Registre

Browse
Update
URLs

GTS - general TCP server

The General TCP Server is implementing the following commands - see \\cpp\Server\GeneralServer\echoserv.c

Command Description Status
B desti@servidor.mail <texte> send an e-mail to a fixed (or given) destination.   -
C Client selection : normal / complete.   -
D directory management
C = display current, G = go, R = remove (empty).
L = list current - <text> ... <text> <"EOM">
done, v 2.6
E create (own) Event Log.   -
F file management
C = create, D = delete, R = rename.
done, v 2.8
H Help : return the list of accepted commands and their syntax. done, v 2.3
I return IP data : IP address, Hostname, etc.   -
K "Z" / "N" / ... kill Zone Alarm / Norton / Symantec / Panda / McAfee (McShield) ...   -
L Pop-Up a ListBox with some text.
See "owncombo.c sample in MSDN"
  -
M mirror the following text. done, v 2.7
O return OPSYS version.
See f:\cpp\DiskInfo\DI.C + f:\cpp\Registry\GetSystemVersion
  -
P return PROCESS list - <text> ... <text> <"EOM">   -
R return REGISTRY branch - <text> ... <text> <"EOM">   -
S <envir var name> return System Envir value   -
T return TIME. done, v 2.5
V return Server VERSION. done, v 2.2
W or log write into "own" Event Log, if it exists.
Use "E" command to "create" it !
  -
other return Not Implemented Yet. Use "H" for Help.
Obviously, the offending text has to be returned also.
done, v 2.4

Testing

Example of usage: [paul@localhost paul]$ ./echoserv 5555 & [paul@localhost paul]$ telnet localhost 5555 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Echo this line for me, please. Echo this line for me, please. Connection closed by foreign host. [paul@localhost paul]$

Pending


Amunt! Top Amunt!
Service's

To create a Windows NT user-defined service, perform the following steps:

  1. At a command prompt, enter :

    path\INSTSRV.EXE Nom_Del_Meu_Servei path\SRVANY.EXE
  2. Verify that the service was created correctly. Check the registry to verify that the ImagePath value under

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\service_name

    is set to point to SRVANY.EXE. If this is not set correctly, the service will stop shortly after it starts and return an Event ID 7000 "The service name failed to start."

  3. Run Registry Editor (Regedt32.exe) and locate the following subkey:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Nom_Del_Meu_Servei
  4. From the Edit menu, click Add Key. Type the following and click OK:

    Key Name: Parameters Class : <leave blank>
  5. Select the Parameters key.
  6. From the Edit menu, click Add Value. Type the following and click OK:

    Value Name: Application Data Type : REG_SZ String : <path>\<application.ext>

    where <path>\<application.ext> is the drive and full path to the application executable including the extension (i.e., C:\WinNT\Notepad.exe)

Service's URLs
Minimal Service

Codi : see MemoryStatus.c, Seco.c.
Compilació :
bcc32 -Ic:\Borland\bcc55\include -Lc:\Borland\bcc55\lib MemoryStatus.c
SC is a command line program used for communicating with the NT Service Controller and services.
Instalació :
sc create MemoryStatus binpath= c:\MyServices\MemoryStatus.exe [SC] CreateService SUCCESS
Com està :
c:\MyServices>sc query MemoryStatus SERVICE_NAME: MemoryStatus TYPE : 10 WIN32_OWN_PROCESS STATE : 1 STOPPED (NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN) WIN32_EXIT_CODE : 1077 (0x435) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0
Engeguem-lo :
c:\MyServices>sc start MemoryStatus SERVICE_NAME: MemoryStatus TYPE : 10 WIN32_OWN_PROCESS STATE : 2 START_PENDING (NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x7d0 PID : 488 FLAGS :
Com està :
c:\MyServices>sc query MemoryStatus SERVICE_NAME: MemoryStatus TYPE : 10 WIN32_OWN_PROCESS STATE : 4 RUNNING (STOPPABLE,NOT_PAUSABLE,ACCEPTS_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0
Aturem-lo :
c:\MyServices>sc stop MemoryStatus SERVICE_NAME: MemoryStatus TYPE : 10 WIN32_OWN_PROCESS STATE : 1 STOPPED (NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0
Com està :
c:\MyServices>sc query MemoryStatus SERVICE_NAME: MemoryStatus TYPE : 10 WIN32_OWN_PROCESS STATE : 1 STOPPED (NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN) WIN32_EXIT_CODE : 0 (0x0) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0
Problema : no sobreviu un Restart del Guindous.
Veiam la configuració :
c:\MyServices>sc qc MemoryStatus [SC] GetServiceConfig SUCCESS SERVICE_NAME: MemoryStatus TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 3 DEMAND_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : c:\MyServices\MemoryStatus.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : MemoryStatus DEPENDENCIES : SERVICE_START_NAME : LocalSystem
Canviem la configuració :
c:\MyServices>sc config MemoryStatus start= auto [SC] ChangeServiceConfig SUCCESS
Amb aquesta configuració sí sobreviu un Restart !
c:\MyServices>sc qc MemoryStatus [SC] GetServiceConfig SUCCESS SERVICE_NAME: MemoryStatus TYPE : 10 WIN32_OWN_PROCESS START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : c:\MyServices\MemoryStatus.exe LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : MemoryStatus DEPENDENCIES : SERVICE_START_NAME : LocalSystem
Els parametres de configuració son :
c:\MyServices>sc config Modifies a service entry in the registry and Service Database. SYNTAX: sc <server> config [service name] <option1> <option2>... CONFIG OPTIONS: NOTE: The option name includes the equal sign. type= <own | share | interact | kernel | filesys | rec | adapt> start= <boot | system | auto | demand | disabled> error= <normal | severe | critical | ignore> binPath= <BinaryPathName> group= <LoadOrderGroup> tag= <yes|no> depend= <Dependencies(separated by / (forward slash))> obj= <AccountName | ObjectName> DisplayName= <display name> password= <password>

 
Instalador

Així, l'instalador (POSA.BAT) caldrà que faci :

  1. cd whatever letter is the new drive
  2. .\eines\SC STOP MeuServei
  3. .\eines\SC DELETE MeuServei
  4. copy .\seco\*.exe /s c:\w\s32\desti.exe
    Can't access the file because it is being used by another process
  5. .\eines\SC CREATE MeuServei binpath= c:\w\s32\desti.exe
  6. .\eines\SC CONFIG MeuServei start= auto
  7. .\eines\SC START MeuServei

I els pre-requisits son :

  1. h:\posa.bat
  2. h:\eines\SC.exe
  3. h:\seco\seco.exe

Envir vars

URL

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> #include <cmqc.h> char * envvar ; /*************************************************************/ /* Print the original environment variable */ /* Change the environment variable */ /* Print the changed environment variable to show it changed */ /*************************************************************/ envvar = getenv ( "MQSERVER" ) ; if ( envvar != NULL ) { printf ( "Original MQSERVER is [%s].\n", envvar ) ; } else { printf ( "MQSERVER not found\n" ) ; } /* endif */ /********************************************************/ /* Make sure to change <hostname> to appropriate value! */ /********************************************************/ _putenv ( "MQSERVER=SYSTEM.DEF.SVRCONN/tcp/<hostname>" ) ; envvar = getenv ( "MQSERVER" ) ; if ( envvar != NULL ) { printf ( "New MQSERVER is [%s].\n", envvar ) ; } /* endif */

Aquest codi agafa TOT de la DLL - URL :

 void ( * sconn ) ( char *, MQHCONN *, MQLONG *, MQLONG * ) ;
 void ( * sopen ) ( MQHCONN, MQOD *, MQLONG, MQHOBJ *, MQLONG *, MQLONG * ) ;
 HINSTANCE hinstLib ;

 hinstLib = LoadLibrary ( "MQIC32" ) ;
 sconn = ( void (*) ( char *, MQHCONN *, MQLONG *, MQLONG * ) ) GetProcAddress ( hinstLib, "MQCONN" ) ;
 sopen = ( void (*) ( MQHCONN, MQOD *, MQLONG, MQHOBJ *, MQLONG *, MQLONG * ) ) GetProcAddress ( hinstLib, "MQOPEN" ) ;

 sconn ( QMName, & Hcon, & CompCode, & CReason ) ;
 sopen ( Hcon, & od, O_options, & Hobj, & OpenCode, & Reason ) ;

From Performance Tuning for Linux Servers

IPC

What is Interprocess Communication?

Interprocess communication allows processes to synchronize with each other and exchange data. In general, System V (SysV) IPC facilities provide three types of resources:

In addition to these resources, IPC pipes and FIFOs are among the most commonly used IPC facilities in UNIX-based systems:

The ipcs command

Almost all Linux distributions include the ipcs command, which provides information about the IPC resources that are currently loaded on the system. ipcs lets you determine the current IPC limits that the system allows and also lets you check the status of the three IPC resources that are currently in use on the system. For example, if your application fails to start, you can check the IPC usage on your system to determine if an IPC limit has been exceeded. To determine the status of the system's IPC resources, as root, issue the ipcs command with the -u option:

# ipcs -u
To determine the limits on the IPC resources reported by the ipcs -u command, use the ipcs -l command:
# ipcs -l

Dynamically modifying the configurable IPC parameters

Beginning with the 2.4 kernel, Linux supports the dynamic modification of most of the IPC parameters through either the /proc file system interface or with the sysctl facility.

# echo 1024 > /proc/sys/kernel/msgmni
# sysctl -w kernel..msgmni= 1024

Code tuning

The basic profiling tools in Linux are the -p (profile) and -pg (profile for gprof) options in gcc, and the prof and gprof utilities. Compiling using -p or -pg causes gcc to insert instructions necessary to obtain profiling information into the object code.

Running the prof command with the application allows you to then obtain the following:

Running the gprof command with the application gathers (among other information) the following:

Because gprof includes the descendents of a procedure in its timings, it is more useful for procedures calling library routines.

Algorithm: Achieving Performance Through Design Choices

All the compiler optimizations and hand-coded tuning methods in a programmer's toolbox do not matter as much in a program's performance as the proper choice of solution for a given problem. The rest of this section examines a sample network-based program and some of the design choices that can lead to a well-performing application.

Problems and Solution Possibilities

To understand some of the choices, we will describe a sample program that will demonstrate some of the issues concerning programming and optimal performance. We will look at a typical problem to understand some performance issues.

Programmers working on network-based solutions must deal with a number of issues, some of which we will demonstrate here by focusing on a simplified multithreaded file I/O Internet server.

We intend to demonstrate the use of threading and sockets, show reasonably high-performance programming techniques, and measure the performance of the resulting Internet page server. Note that we are spelling internet with a lowercase i here because we are not processing HTTP.

A Problem - How Fast Can We Connect, Create a File, Read It, and Disconnect ?

Our program will consist of two parts: a client part and a server part. The server will initialize itself and await a command from a client. The server will have the following usage:

server [-t nnn] [-pooling] [ip_address]:portnum
where -t indicates the number of threads to use (the default is 1). The -pooling option instructs the server to use a pool of threads rather than create and destroy a thread for each incoming work item.

The client has the following usage:

c [-t nnn] -filesize ppp -blksize mmm -qsize jjj [ip_address]:portnum
Here, we can instruct the client to use nnn threads and create files of size ppp using a block size of mmm. Finally, we instruct the client to do this jjj times. Both client and server use the TCP/IP address [ip_address]:portnum. The meaning here is that if the ip_address is unspecified, the program (server or c) uses the local machine address.

Thus, the following command starts the server program, allows the use of up to eight worker threads, and listens on port 4001:

server -t 8  :4001
The command
c -t 4 -qsize 1000 -filesize 2m -blksize 8192 :4001
connects to the server located at port 4001 on the local machine to perform 1,000 operations, each consisting of a file create, writing 2 million (2*1024*1024) bytes, reading 2 million bytes (8192 bytes at a time), sending 2 million bytes (8K at a time) over the TCP/IP connection, and, finally, closing the file and deleting it. By specifying a complete TCP address as follows:
c -t 4 -qsize 1000 -filesize 2m -blksize 8192 10.0.0.4:4001
the client program can be run from any computer that can communicate via TCP/IP with the server program.

The server and client could be written in most of the languages listed earlier and others not listed. However, rather than digress into the reasons why one language is or is not appropriate, let me just say that the C and C++ languages give access to the system entry APIs in Linux (the system calls). These "system call" APIs provide operating system services used by high-performance programs. We will direct our attention to some the system call primitives documented in section 2 of the manual.

The Program

Our program's responsibility will be to accept the command-line parameterization, set up to accept socket connections and distribute the resulting requests across the available threads. Further, we will design the server portion and the client portion to simply be parts of the same source code. When compiled, if the resulting executable is named server, it performs the server actions; otherwise, it performs client actions.

The server portion requires several threads. The first thread initializes all other threads and then listens for incoming requests and queues them. The second thread schedules work found in the queue. The third thread cleans up threads that terminate.

Pseudocode for the first server thread would look like the code shown here:

Server Main

  Parse options
  Create listening socket

  Start cleanup thread
  Start threadscheduler thread

  Forever {
    Accept new socket
    If quit break
    Queue new socket
  }
  exit
Here, the actual socket file descriptor is the object that is queued. Two other threads must be described. The cleanup thread has pseudocode, as shown here:
Thread Cleanup

  While threadactive {
    Locate active thread
    "wait" for thread // cleans up
  }
  exit
The thread scheduler has pseudocode, as shown here:
Thread scheduler

  While qsize > 0 {
    Locate inactive thread
    Start inactive thread with work item
  }
  exit
The thread scheduler sets a thread to work on the queued socket. Surrounding the basic work to be done is the mild protocol, which accepts a text command from the client (over the socket), parses the command, executes the actual work, and finally sends a "DONE" message back to the client. Code that wraps around the actual worker thread is shown here:
Thread wrapper

  Accept work assignment from socket
  Parse text command
    Mark myself as "active"
      Do real work
    Mark myself as "inactive"
  Reply to socket with an "DONE" message
We have described the server-side pseudocode that supports an almost arbitrary threaded work item. Before going into the details of what is involved in using Linux and gcc to create a program, let me also describe the client that issues the "commands" to this server and produces the timed results.

The client must be able to cause all this work to happen and report the timing of the results. If the server is multithreaded, on a multiprocessor, for a given number of threads, the results should improve as we add processors. Our server program will demonstrate that by scaling as we add processors. The client pseudocode has the following command-line behavior:

client -t 8 -qsize 1000 -filesize 2m -blksize 8192 :4001
The command-line options have the same meaning as for the server. When the client receives the last "DONE" message from a server thread, it prints a summary timing of the entire operation. The client pseudocode is shown here:
Client

 Parse command line options
 Put all work into a queue
 Start each thread; each will pull something off the queue
 until the queue is empty.
We will demonstrate some of the performance choices we must make to write a program on Linux. From this brief description, we will go into more detail on the programming aspects and how to measure the resulting performance. The client thread has the pseudocode shown here:
Client Thread

 While there is work to be done
 Get work item of work queue
 Send text string describing work to server
 Read and check date from server until done.
 Wait for "DONE"
 Send 'Q'
 Close socket
The client performs all the timing and reports the results.

Designing the Code

The choice of C or C++ rather than other languages is due to the personal experiences of the author.

Others who have experience in other languages might use FORTRAN or Python. However, more than the individual experiences of one person should be considered. Most programs are part of a product and require multiple people to write. Programming language selection should be based on the experiences of the participants in the programming team, the supportability of the language, and the debugging tools available. C and C++ are safe choices. On Linux, the C compiler that comes with Linux is gcc, the GNU Compiler Collection.

We will go through the various issues one by one and include snapshots of code. The complete program is included in the appendix.

The Server

Our program is designed as a single source file. When invoked, it asks whether it is named server. If it is, it performs the server functions. If it is not named server, it behaves like a client. The point of including the entire source in a single program is to simplify the building of the program and copying it to other systems. It is not necessarily the best choice. The question is asked as shown here:

char *p;
if(strchr(av[1],"/"))
    p = strrchr(av[1],"/");
else
    p = av[1];
if(equal(p,"server"))
    ServerMain();
else
    ClientMain();
This coding style is a convenience. The example brings us to another simplification that really represents personalization of code. Many programmers have idiosyncratic mechanisms that are included in every program they write. There are also reasons to define shortcuts of common names for things that are different on different platforms. In multiple-person developments, the shortcuts can interfere with rapid understanding of the code if they become too plentiful. This shortcut list is reasonably small, but to have a group of people embrace it would require a discussion and consensus. Following are the shortcuts we used:
#   define SLASHC       '/'
#   define SLASHSTR     "/"
#   define SOCKTYPE     int
#   define SLEEP(x)     sleep(x)
#   define Errno        errno
#   define BADSOCK      -1
#   define LCK          pthread_mutex_t
#   define SEMA_T       sem_t       // (man sem_init)
#   define YIELD        Yield()
#   define SOCKET        int
#   define SOCKERR      -1
#   define EXITTHREAD() return
#   define INT64        long long
#   define UINT64       unsigned long long

    typedef pthread_t   THREAD_T;

#   define TVAL         struct timeval

#   define equal        !strcmp
#   define equaln       !strncmp
It can be noted that converting these shortcuts to other platforms that support C/C++ is trivial.

Timing

The tstart(), tend(), and tval() routines provide a mechanism for recording time in the microsecond range. These routines are implemented by using the gettimeofday() routine on Linux. The documentation for gettimeofday() is accessed with the following command: man gettimeofday

Our version here is reentrant. tstart() and tend() record their values in a location specified by an input pointer. The design supports reentrancy, which is important when designing a multiple-thread application.

One issue with timing routines is the possibility that during a measurement session, someone or some program changes the system time. If that happens, you could get results that are not repeatable. For that reason, we make two recommendations:

  1. Turn off all NTP (Network Time Protocol) servers. Using netstat -i, you can determine the list of open ports on the local machine. Make sure that port 123 is not in use. If it is, stop the service that is using it. If an NTP server is not running, there is little likelihood it will change the system time.
  2. Run performance measurement tests multiple times to ensure that the results are repeatable.

With these two guidelines, it is unlikely that you will get into a lot of trouble.

The timing routines work like a stopwatch. To time something, the following sequence is used:

 TVAL ts,te;
 double t;

 tstart ( & ts ) ;
 do_something() ;
 tend ( & te ) ;
 t = tval ( & ts, & te ) ;
 printf ( "do_something() took [%8.5f] seconds.\n", t ) ;
The code for the timing routines looks like this:
void tstart(struct timeval *t)
{
    gettimeofday(t, NULL);
}
void tend(struct timeval *t)
{
    gettimeofday(t,NULL);
}

double tval(struct timeval *tv1, struct timeval *tv2)
{
    double t1, t2;

    t1 =  (double)tv1->tv_sec +
          (double)tv1->tv_usec/(1000*1000);
    t2 =  (double)tv2->tv_sec +
           (double)tv2->tv_usec/(1000*1000);
    return t2-t1;
}
Using the gettimeofday() system call allows resolution to one millionth of a second on an Intel-based Linux machine.

Sockets

Linux supports Berkeley sockets. Our program takes an input command-line parameter and converts it to an IP address and port number, suitable for use with the sockets library. For connection (client) or listening (server), both an IP address and a port number are required. The TcpParse(), ipaddress(), and portnum() routines convert an ASCII string to an IP address or port number. All three require reasonably precise input. If any encounters an error, it prints an error message and causes the program to exit.

Our discussion won't be a tutorial on sockets. We assume you are familiar with the basics of programming sockets using the AF_INET family of protocols. We also assume our interests here are in stream-oriented sockets as opposed to datagrams (TCP versus UDP).

There are two ways to use sockets. The first is to listen for incoming connections. The second is to initiate an outgoing connection. On Linux, sockets can be used between two threads on the same machine or can be directed to a program on another machine; the distinction is only at the command-line level, where the IP address and port number of the server software are located. For our testing and demonstration purposes, we confine ourselves to a single machine. When the program is fully described, we make test runs between two different machines.

The server portion of our program creates a socket and listens for connections. It does this in a listen and accept thread, whose only responsibility is to start new thread work. Server code creates a socket, performs a listen() on it, and then accepts() new connections. The accept action creates a new socket that is handed off to a thread to perform work. The newly created socket is a bidirectional communication channel (a full socket). The following shows the code for the listen and accept routine:

void listen_and_accept(SOCKTYPE *sock)
{
    int rc;
    SOCKTYPE sock3;

    static SOCKTYPE sock1;
    static struct sockaddr_in addr1;
    struct sockaddr_in addr2;
    int addr2len;
    static int first = 1;

    if(first) {
        addr1.sin_family      = AF_INET;
        addr1.sin_addr.s_addr = naddr;   // global
        addr1.sin_port        = port;    // global

        sock1 = socket(AF_INET, SOCK_STREAM, 0);
        if(sock1 == BADSOCK) {
            printf("socket FAILED: err=[%d].\n",Errno);
            exit(1);
        }

        rc = bind(sock1,(const struct sockaddr *)&addr1,
                 sizeof(addr1));
        if(rc == SOCKERR) {
            printf("bind FAILED: err=[%d].\n", Errno);
            exit(1);
        }

        rc = listen(sock1,5);
        if(rc) {
            printf("Listen FAILED: err=[%d].\n", Errno);
            exit(1);
        }
        first = 0;
    }
    addr2len = sizeof(addr2);
    sock3 = accept(sock1, (struct sockaddr *)&addr2,
                (socklen_t *)&addr2len);
    if(sock3 == BADSOCK) {
        printf("Accept FAILED: err=[%d].\n", Errno);
        exit(1);
    }
    *sock = sock3;
}
In this code segment, we have decided that all unexpected results should cause a program termination. Without detailed analysis of why each error might occur, this method makes for a predictable program, notwithstanding errors. Continuing in the presence of any of the preceding errors is difficult; passing a return value back to the calling routine that a failure occurred simply compounds the problems by making the caller attempt to discover what went wrong. By printing a message and exiting the program immediately, discovering an unexpected result focuses programming efforts precisely where the problem occurred. We have found over the years that correctness and debugability are more important than performance, and our code tries to reflect this point of view.

Servers listen and accept while clients connect. The following shows another wrapper program to perform the details of establishing a connection:

extern int econnrefuseretries;

SOCKTYPE clientconnect(int *per_client_refusecnt)
{
    SOCKTYPE sock2;
    struct sockaddr_in addr1;
    int refusedcount;

    addr1.sin_family      = AF_INET;
    addr1.sin_addr.s_addr = naddr;
    addr1.sin_port        = port;
    sock2 = socket(AF_INET, SOCK_STREAM, 0);
    if(sock2 == BADSOCK) {
        printf("socket FAILED: err=[%d].\n", Errno);
        exit(1);
    }

    refusedcount = 0;
    while(connect(sock2, (struct sockaddr *)&addr1,
                  sizeof(addr1))) {
        int err;

        err = Errno;
        if(err != ECONNREFUSED) {
            printf("connect FAILED: err=[%d].\n",Errno);
            exit(1);
        }
        SLEEP(2); // Be polite
        *per_client_refusecnt++;
        if(refusedcount++ >= econnrefuseretries) {
            printf("connect FAILED: ");
            printf("after %d ECONNREFUSED attempts\n", econnrefuseretries);
            exit(1);
        }
    }
    return sock2;
}
These two code segments bear some discussion. The listen_and_accept() routine is written to be used as simply as possible; it either returns a usable socket or prints an error and exits. The clientconnect() routine either returns a usable socket or prints an error and exits. It processes the ECONNREFUSED error return in an attempt to deal with a server that is too busy to accept the socket request. That happens to any listen and accept loop when the queue of requests in the operating system is longer than the default of 5. For instance, if a server issues a listen_and_accept(), receives an incoming socket, and then simply goes to sleep, succeeding incoming requests are queued by the operating system until there are five of them. The sixth connection request is refused, and the ECONNREFUSED error is reported by the client issuing a connect request (clientconnect() in our case). Our code simply sleeps for 2 seconds and tries again. We have allowed, by default, four retries. The number can be changed by editing the program and recompiling. Alternatively, it would be trivial to add a new option that allows the value to be set on the command line.

We have described the actions required to create socket connections. The details of socket connections are seldom the source of performance problems. It is what happens after the socket is created that becomes interesting.

Threads

Because our program uses threads and demonstrates thread pooling, we must describe how threads work and our usage of them. We don't claim that any of the following code is the best-performing code. We do claim that the code demonstrates how threads can be used in both a pooled and nonpooled manner, and that the code is reasonably good. No doubt, improvements are possible.

Recalling from earlier, our description of the server main program where a scheduler and a cleanup thread were created, we will show these two modules. There is a third module we haven't yet mentioned. It is the thread that listens for new work to queue. The work is the newly created socket, and it is the socket that is queued. Our worker program reads a text string from the socket containing the file size and block size to use in doing the work. Other experimentations with this code could replace our worker thread with one written for almost any purpose whatever. To summarize, the following takes place:

  1. The Listener listens for new connections and inserts work (the newly created socket) into the work queue.
  2. The thread scheduler waits for work in the queue and starts a thread passing the socket file descriptor to the thread. The scheduler can be run using pooled threads or in the mode where it creates a new thread for each work item (-pooling command-line option).
  3. The work thread does all the work and finally closes the socket.
  4. The thread cleanup awaits thread deaths and cleans up after them.

Surrounding the worker thread are the fixtures to report back to the client the completion of the task.

The thread listener is coded as shown here:

void ServerMain()
{
    SOCKTYPE sock;

    initq ( & workq ) ;
    newThread ( (void(*)(void *) ) threadScheduler,0, &schedulerT);
    newThread ( (void(*)(void *) ) threadCleanup,0, &cleanupT);
    newThread ( (void(*)(void *) ) threadDbg, 0, &dbgT);

    // Server waits in a listen/accept sequence and hands off
    // request to a thread. Threads are either dynamically
    // created or there is a pool.
    //
    // listen_and_accept creates sockets
    //
    for(;;) {
        listen_and_accept(&sock);
        enqueue(&workq, sock);
        vsema(&workq.sema);
    }
}
The thread scheduler reads information from a queue. The queue is protected by synchronization primitives, which we discuss later in the chapter. The protections allow multiple threads to update the queue without making the queue metadata inconsistent. The thread scheduler is coded as shown here:
void threadScheduler()
{
    SOCKTYPE sock;
    int rc;

    //
    // threadScheduler decrements availableThreads,
    // threadCleanup   increments availableThreads.
    //
    for(;;) {

        schedulerstate = 1;
        psema(&workq.sema);

        schedulerstate = 2;
        psema(&availableThreads);

        schedulerstate = 3;
        if(dequeue(&workq, (int *)&sock)) {
            schedulerstate = 4;
            rc = threadStart(sock); // starting a thread
            if(rc == -1) {
                printf("threadStart FAILED: maxthreads=%d\n", maxthreads); fflush(stdout);
                exit(1);
            }
        }
        else {
            printf("Workq Sema count wrong\n");
            exit(1);
        }
    }
}
Thread cleanup is coded as shown here:
void threadCleanup()
{
    int j = 0;

    //
    // For pooled threads, this loop will last forever.
    //
    for(;;) {
        cleanupstate = 1;
        psema(&ActiveT);
        cleanupstate = 2;

        for(j = 0; j < maxthreads; j++) {
            if(threads[j].exists) {
                threadWait(&threads[j].thrd);
                threads[j].exists = 0;
                threads[j].active = 0;
                nexists--;
                vsema(&availableThreads);
            }
        }
    }
}
These three modules require some background discussion. The listener module simply accepts new sockets and queues them. It is simple, and the only thing to think about is that it must queue the work consistently; therefore, it uses the enqueue() routine. The thread scheduler must create threads or allocate existing (pooled) threads. Threads are created with a pthread_create() call. Because our program wants to start a thread that is either already created (a pooled thread) or create a fresh one, we have chosen to abstract the thread-starting process with the threadStart() routine. threadStart()uses the pooling global variable to determine whether to use a pooled thread or to simply start a new thread. The pooling global defaults to no pooling and is a command-line option.

The command line tells the server and client how many threads to use. The default is one. The server runs no more than the number of threads specified on the command line at one time. This works in the same way for the client. Each server thread performs its work and ends. A pooled thread ends by marking itself as inactive and blocking on a lock. An unpooled thread simply returns, thus destroying the thread. For nonpooled threads, the cleanup routine is essential if the system is not to run out of resources. It is the cleanup routine that returns the resources used by nonpooled threads to the system.

We add one further observation about our thread code. Writing threaded code that schedules work using pooled threads is not exactly trivial. Determining why things are not working properly can be time-consuming. We demonstrate here one method of making the debugging task simpler. Our thread management routines (scheduler and cleanup) each have a global state variable. The state variables represent the current state of the routine and are accessible from all threads in the program. The variables are changed just prior to any function call that could block. By printing these two state variables, we can determine exactly where in the code the scheduler and cleanup threads are currently blocked. We wrote a trivial debug thread that can print the values of these state variables, and together with the state variables the debugging task was significantly accelerated.

Synchronization

The scheduler uses synchronization primitives (semaphores) to block when there is nothing to do. Semaphores are used as a synchronized counting mechanism where, if there are things to do, the number of things to do is reflected in the value of the semaphore. If the count is greater than zero, the psema() routine decrements the count by one and returns to the calling program. If the value of the semaphore is zero, the psema() routine blocks. The vsema() operation increments the semaphore and never blocks. Our psema() and vsema() routines are based on the semaphores(3) interfaces defined in section 3 of the manual (man 3 semaphores). Our interfaces take a name that enables us to debug them with more clarity - we can print the name of the semaphore we are examining by inserting appropriate print statements. We use one semaphore to count the number of available threads (either pooled or nonpooled), one to count the number of tasks (jobs, work items, or whatever we want to call them), and one to count the number of active threads.

The semaphore interfaces are defined as shown here:

void initsema(PVSEMA *s, int initvalue, int hi, char *name)
{
  if(sem_init(&s->pv,0,initvalue) == -1) {
    printf("sem_init() FAILED: sema=[%s] err=[%d].\n",name,Errno);
      exit(1);
  }
  s->name = name;
}

void psema(PVSEMA *s)    // decrements or blocks if s==0
{
  sem_wait(&s->pv);
}
void vsema(PVSEMA *s)    // increments
{
  int rc;

  rc = sem_post(&s->pv);
  if(rc == -1) {
    printf("sem_post FAILED: err=[%d].\n",Errno);
    exit(1);
  }
}
Finally, there are the queuing operations. Their job is to protect the data structures describing the queue of jobs coming in. The queue is operated with enqueue() and dequeue() operations. To support the queuing and dequeuing of data, lck and unlck implement locks on specified objects (the work queue in this case). We will show the enqueue routine and the associated dequeue routine.

You might ask why there are two counting mechanisms. The semaphore counting mechanism is specifically for counting. Because the numbers of threads (active and available) are strictly numbers, the only mechanism we need is counting. The queuing routines were written for more general queuing where objects could be queued. In this case, they require more than a simple count. For our particular demonstration here, they have been detuned to queue only integers (socket file descriptors); thus, they are similar in function to the semaphore operations.

That said, the following shows the queue initialization and the enqueuing routines:

void initq(queue_t *q)
{
    initsema(&q->sema, 0, workqsize, qsema);
    q->val = (int *)Malloc(workqsize*sizeof(int));
    memset(q->val,'\0',workqsize*sizeof(int));
    q->qmax = workqsize;
    initlck(&q->lck, workqname);
    q->head = q->tail = 0;
}

//
// This version stops queuing when it bumps
// into its own tail.
//
int enqueue(queue_t *q, int val)
{
    int ret = 1;
    int h;

    lck(&q->lck);
    h = q->head + 1;
    if(h == q->qmax)
            h = 0;
    if(h != q->tail) {
        q->val[h] = val;
        q->head = h;
        q->qcnt++;
    }
    else
        ret = 0;
    unlck(&q->lck);
    return ret;
}
The dequeue routine is similar.

The locking (lck and unlck) routines are built from Posix thread mutexes. Mutexes are efficient thread synchronization primitives defined in the Posix thread library. They support mutual exclusion between threads, but not between processes. A Posix mutex supports three kinds of mutex:

The FAST variant does not allow reentry into the mutex by the calling thread. Thus, if thread A locks using a pthread_mutex_lock and it attempts to lock the same mutex a second time, it blocks. Depending on the program's design, this might produce a deadlock. The RECURSIVE kind allows multiple pthread_mutex_lock() calls within a thread. However, for each pthread_mutex_lock() call, there must be a corresponding pthread_mutex_unlock() call. Finally, the ERRORCHECK kind returns an error if thread A attempts to lock a mutex more than once.

Our lock and unlock calls use the FAST kind because our design is such that if a thread calls a mutex lock more than once, it is an error in our logic. Debugging such a design is more easily accomplished if the ERRORCHECK kind is substituted during development. The following shows the lock and unlock primitives:

void initlck(LCK *l, char *name)
{
    //
    // Linux default is a "fast" mutex. A "fast" mutex
    // locks when the same thread calls it twice.
    //
    pthread_mutex_init(l,NULL);
}
void lck(LCK *l)
{
    int err;

    err = pthread_mutex_lock(l);
    if(err != 0) {
        printf("pthread_mutex_lck FAILED: err=[%d].\n",err);
        exit(1);
    }
}
void unlck(LCK *l)
{
    pthread_mutex_unlock(l);
}
int islocked(LCK *l)
{
    return (pthread_mutex_trylock(l) != EBUSY) ;
}
Of these primitives, only the mutexes from the thread library are based on the mutual exclusion instructions of the native processor. They should be significantly faster than interprocess synchronization primitives, because frequently no system call is required to execute. Contrast that with a semaphore operation that must maintain a counter that is visible to all processes. To either increment the counter or decrement it, the interface must issue a system call necessitating a transition into the operating system proper. For this overhead reason, mutexes are generally recognized as delivering high performance - or, to put it another way, to take less time to execute.

Using the timing primitives described previously, it is trivial to make a program that executes millions of calls to the interface and prints the time it takes to do it. One of the authors has done this and published the results for Red Hat 7.0. The results are reproduced here:

Interface Linux 2.4.2 (microseconds per call)
SRV5_Semaphores 1.828
Posix Semaphores 0.487
Pthread_mutex 0.262

This measurement was done in a ThinkPad 600X (650MHz, 512MB memory). (SVR5 semaphores are a second variety of semaphores, older in design, and, obviously, as shown in the table, a bit slower.) Documentation for SVR5 semaphores can be seen with the man semop command. Documentation for the Posix semaphores can be found with man sem_init, and documentation for the Posix thread mutexes can be seen using the man pthread_mutex_init command.

FILE I/O

Our file I/O is quite simple. We want to create a file of arbitrary length, write data into it, read it back, and check the data. As we read the data from the file, it is sent over the internet to the client who requested it. We invent data on the fly. Basically, we write blocks of data into a file until it reaches the requisite size. We also write the page number or the block number into each block. Thus, each block is self-identifying to some extent. The following shows the code within the worker thread that creates, writes, reads, closes, and removes the file:

int fd;

fd = open(namebuf, O_RDWR|O_CREAT, S_IRWXU);
if(fd == -1) {
  printf("open [%s] FAILED: err=[%d].\n",namebuf,Errno);
  exit(1);
}

//writeFile();

pageno = 0;
bytesleft = fsz;

while(bytesleft > 0) {
  cnt = (bytesleft < bsz) ? bytesleft : bsz;
  if(cnt > 4)
    memcpy(buf, &pageno, sizeof(pageno));
  if(write(fd, buf, cnt) != cnt) {
    printf("write %d bytes FAILED: err=%d\n",Errno);
    exit(1);
  }
  pageno++;
  bytesleft -= cnt;
}

//readFile();

lseek(fd, 0L, SEEK_SET);

bytesleft = fsz;
rpageno = 0;

while(bytesleft > 0) {
  cnt = (bytesleft < bsz) ? bytesleft : bsz;
  if(read(fd, buf, cnt) != cnt) {
    printf("read %d bytes FAILED: err=%d\n",Errno);
    exit(1);
  }
  if(cnt > 4) {
    if(0 != memcmp(buf, &rpageno, sizeof(rpageno))) {
      printf("Read Compare ERROR: rpageno=%d",rpageno);
      printf(" buf[0] = %x %x %x %x\n",
        buf[0]&0xFF,
        buf[1]&0xFF,
        buf[2]&0xFF,
        buf[3]&0xFF);
      exit(1);
    }
  }
  rc = Send(sock, buf, cnt, 0);
  if(rc != cnt) {
    printf("th[%d]: SERVER: Send FAILED: rc=%d err=%d\n",
        th, rc,Errno);
    exit(1);
  }
  rpageno++;
  bytesleft -= cnt;
}

//closeFile();

if(close(fd) == -1) {
  printf("close FAILED: err=%d\n",Errno);
  exit(1);
}
//deleteFile();

if(unlink(namebuf) == -1) {
  printf("unlink <%s> FAILED: err=%d\n",namebuf,Errno);
  exit(1);
}
As you can see from this example, the code is straightforward. After writing all the data to the file, the program uses lseek() to return to the beginning of the file, where it begins reading the file. Each block of the file is checked for correctness (trivial check, admittedly) and then is transmitted to the client program (the client program also checks the data). There is nothing complex about this code, other than the fact that the file size and the block size can be parameterized.

The Client

The client portion of the code uses some of the facilities previously discussed. Whether or not the server configures itself to use pooled threads, the client simply starts the number of threads specified on the command line and waits until all the work is completed.

Each client thread continues in a loop, taking a single item of the work queue, sending it to the server, receiving all the data the server transmits back, checking the data as it arrives, and finally closing the socket. The client then proceeds to get another item of the work queue, starting the same process all over again. Each client thread continues these operations until the work queue is empty. As each client determines that the queue is empty, it exits.

This particular design methodology represents an asynchronous approach to problem solving. A thread is dedicated to each work item. If any particular work item takes a longer amount of time, the remaining threads continue emptying the queue. Using this methodology, computing fractal pictures could easily be optimized where some pixels take millions of iterations to complete and some take less than 100. The difficult pixels would occupy a thread, while many trivial pixels (fractal points with a small number of iterations) would be completed by the remaining threads.

The client code loops look like this:

while(workqcnt > 0) {
  lck(&workqcntL);
    if(workqcnt == 0) {
      unlck(&workqcntL);
      break;
    }
    workqcnt--;
  unlck(&workqcntL);
  sock = clientconnect(&tp->refusecnt);
  rpageno = 0;

// Send command to server.
  sprintf(tp->cbuf, "F,%d,%d", filesize,fileblksz);
  rc = Send(sock, (char *)tp->cbuf, CBUFSIZE, 0);
  if(rc != CBUFSIZE) {
    printf("\tCLIENT[%d]: CBUF Send FAILED: err=%d\n", th,Errno);
    exit(1);
  }
  tp->sndbytes += CBUFSIZE;

// set buffer size using thread safe reMalloc()
  if(fileblksz > threads[th].bufmax) {
    threads[th].buf = (char *)reMalloc(threads[th].buf, fileblksz);
    threads[th].bufmax = fileblksz;
  }
  threads[th].bufsiz = fileblksz;
  buf = threads[th].buf;

// Receive filesize bytes from Server and check contents.
  bytesleft = filesize;
  while(bytesleft > 0) {
    cnt = (bytesleft < fileblksz) ? bytesleft : fileblksz;
    rc = Recv(sock, buf, cnt, 0);
    if(rc == SOCKERR) {
      printf("CLIENT: th[%d]: Recv failed: rc=%d err=%d\n", th, rc,Errno);
      exit(1);
    }
    else if(rc == 0)
      break;
    if(rc > 4) {
      if(0 != memcmp(&rpageno, buf, 4)) {
        printf("CLIENT: compare error on pageno %d",rpageno);
        printf(" buf[0] = %x %x %x %x\n",
          buf[0]&0xFF,
          buf[1]&0xFF,
          buf[2]&0xFF,
          buf[3]&0xFF);
        exit(1);
      }
    }
    tp->rcvbytes += rc;
    rpageno++;
    bytesleft -= rc;
  }

// Wait for "DONE" from Server
  rc = Recv(sock, (char *)tp->cbuf, 4, 0);
  if(rc != 4 || !equaln(tp->cbuf, "DONE", 4)) {
    printf("\tCLIENT[%d]: Recv 'DONE' FAILED: rc=%d err=%d\n", th,rc,Errno);
    fflush(stdout);
    exit(1);
  }
  tp->rcvbytes += 4;

// Send 'Q'
  tp->cbuf[0] = 'Q';
  rc = Send(sock, (char *)tp->cbuf, 1, 0);
  if(rc != 1) {
    printf("\tCLIENT[%d]: Send 'Q' FAILED: rc=%d err=%d\n", th,rc,Errno);
    fflush(stdout);
    exit(1);
  }
  tp->sndbytes += 1;
  rc = CLOSESOCK(sock);
  if(rc != 0) {
    printf("\tCLIENT[%d]: close socket %d FAILED: Errno=%d\n", th,sock,Errno);
    exit(1);
  }
}

A number of different synchronization mechanisms have been used here. Counting semaphores are used to simply count available resources. The counter blocks when none is available. Both the thread scheduler and cleanup thread use a semaphore to know when something needs to be done. An earlier cleanup design simply looked for active threads every 2 seconds. Although the performance difference is probably negligible, the resulting design using semaphores leaves the system completely idle when there is nothing to do. (psema, vsema, and initsema are based on Posix semaphores.)

Locking primitives are used to count the number of work items in the client. They are based on a memory variable and a critical section lock. This design was devolved from the queuing primitives described in the next paragraph. The desire to queue millions of items suggested that each should take no memory. Therefore, this interface was derived to support decrementing a counter as the mechanism for dequeuing an object. (initlck, lck, and unlck are based on Posix thread mutexes.)

Finally, the third version of synchronization used is to queue objects. A Posix pthread mutex is used to guard an actual memory queue, each element of which can contain a single integer. The integer in our case is a socket file descriptor received from the listen_and_connect() routine in the server's main thread. (initq, enqueue, and dequeue are based on the previously described locking primitives, which in turn are based on Posix pthread mutexes.)

Our design is asynchronous. The server consumes no CPU cycles if there is nothing to do. The client either has something to do or it exits. The client's responsibility is to pass all the work to be done to the server, wait until the server completes all the work, and finally print timing and performance results.

Compilation Options

After we have written our program, we want to compile it and run it. Our program is called srv3.cpp. To compile it, the following command line is used:

g++ -O2 -Wall srv3.cpp -lpthread -o server && cp server c
The g++ command is used to assure we are compiling using the strong typing of C++. This particular program uses almost no C++ features, but the strong typing of C++ is used. The command line above the -Wall option instructs the compiler to print all warnings. Demanding the strictest possible conformance to excellent programming standards is guaranteed to produce code that requires less debugging and less support. One of the authors has seen software projects remove unknown bugs from programs simply by changing the compilation option to more emit warnings and changing the code to remove the warnings. If a program of any size compiles and executes properly and has never endured the removal of all warnings, we challenge you to go through the effort once. If after the effort you aren't convinced that bugs were removed, we would be quite surprised.

The -Wall command line is intended to produce two executables. The first is a program called server, and the second is a program called c. The first is our server program, and the other is our client program. (We didn't name it client for fear of colliding with an existing program that might be called client.)

A useful option to the GNU C compiler is the -v option, which causes g++ to print all the intermediate steps it takes to produce the executables. When using the verbose option to g++, g++ does not instruct the linker to also produce verbose output. To do that, the following addition is required:

-Xlinker -verbose
Thus, the following produces the most output (into a file called xx). From it you can see what the compiler and linker are doing:
g++ -O2 -Wall -v -Xlinker -verbose srv3.cpp -lpthread -o server 2>xx

Libraries

We compiled our program using the Posix thread dynamic link library by specifying -lpthread on the command line. We could have used statically linked libraries. As installed, we could not compile our program using static libraries. That said, why would we want to?

Static versus dynamic libraries is a question whose answer is surprising.

Dynamic link libraries have the following benefits:

Code Discussion

The reasons seem compelling, but the other side of the picture leaves the issue open to design. Here are some counterpoints:

The general recommendation is to use dynamic link libraries where possible, using static linking for libraries that may not be found on the destination platform and that cannot be distributed with your application.


Anatomy of a Web Server Transaction

Next, we'll show the steps a web server takes in response to a client request. For this purpose, we provide the following pseudocode:

s = socket(); /* allocate listen socket */
bind(s, 80);  /* bind to TCP port 80    */
listen(s);    /* indicate willingness to accept */
while (1) {
 newconn = accept(s);                                    /* accept new connection */
 remoteIP = getsockname(newconn);                        /* get remote IP addr */
 remoteHost = gethostbyname(remoteIP);                   /* get remote IP DNS name */
 gettimeofday(currentTime);                              /* determine time of day */
 read(newconn, reqBuffer, sizeof(reqBuffer));            /* read client request */
 reqInfo = serverParse(reqBuffer);                       /* parse client request */
 fileName = parseOutFileName(requestBuffer);             /* determine file name */
 fileAttr = stat(fileName);                              /* get file attributes */
 serverCheckFileStuff(fileName, fileAttr);               /* check permissions */
 open(fileName);                                         /* open file */
 read(fileName, fileBuffer);                             /* read file into buffer */
 headerBuffer = serverFigureHeaders(fileName, reqInfo);  /* determine headers */
 write(newSock, headerBuffer);                           /* write headers to socket */
 write(newSock, fileBuffer);                             /* write file to socket */
 close(newSock);                                         /* close socket */
 close(fileName);                                        /* close file */
 write(logFile, requestInfo);                            /* write log info to disk */
}

This pseudocode is a relatively simple implementation of a server that does not employ any possible optimizations and can handle only one request at a time. This example only hints at the more complex functionality that is required by the server, such as how to parse the HTTP request and determine whether the client has the appropriate permissions to view the file. In addition, it has no error handling-for example, if the client requests a file that does not exist. However, this example gives a good idea of what steps are required by a server.

Performance Tools for Evaluating Web Servers

Many tools are available for evaluating the performance of web servers, also known as workload generators. These are programs that run on client machines, emulating a client's behavior, constructing HTTP requests, and sending them to the server. The workload generator can typically vary the volume of requests it generates, called load, and measures how the server behaves in response to that load. Performance metrics include items such as request latency (how long it took an individual response to come back from the server) and throughput (how many responses a server can generate per second).

Perhaps the most commonly used tool is SPECWeb99. This tool is distributed by the Standard Performance Evaluation Corporation (SPEC) nonprofit organization, whose web site is www.spec.org. This tool is probably the most-cited benchmark, and it is used for marketing purposes by server vendors such as IBM, Sun, and Microsoft. Unfortunately, the tool costs money, although it is available freely to member institutions such as IBM. The benchmark is intended to capture the main performance characteristics that have been observed in web servers, such as the size distribution and popularity of files requested. The tool is considered a macro-benchmark in that it is meant to measure whole system performance.

Another tool frequently used is httperf from HP Labs, which is available freely under an open-source license. This tool is highly configurable, allowing you to stress isolated components of a web server-for example, how well a server handles many idle connections. Thus, it is used more as a microbenchmark.

Many other tools exist for evaluating web server performance, including SURGE, WebBench, and WaspClient. However, describing them all is outside the scope of this chapter. Nevertheless, many options are available for stressing, testing, and measuring servers, and many of these are freely available.

Also useful to web site operators are log analysis tools. These tools look through the logs generated by the server and report information such as how many visits a site received over a period of time and where the visitors came from. Performance can be optimized when the operator understands how visitors are using a site. Logs are typically kept in a standard format called the Apache Common Log format. Many commercial tools are available; however, two freely available open-source tools are analog and webalizer.


(win32) Programming Tips


Identificació

char * Programa = "ProgName" ; char * Autor = "Jo Mateix." ; char * Fecha = "21 de Maig de 2007" ; char * Version = "1.01 a" ; char * Novedad = "Menu bar" ;

Amunt! Top Amunt!
Time of day display
#include <time.h> time_t aclock ; struct tm * newtime ; char * chStrTime ; int iCnt ; time ( & aclock ) ; newtime = localtime ( & aclock ) ; chStrTime = asctime ( newtime ) ; // the form of the string is "Wed Apr 20 15:34:55 1983\n\0" iCnt = strlen ( chStrTime ) ; chStrTime [ iCnt -1 ] = '\0' ; // remove ending NewLine. printf ( "Local time is : [%s].", chStrTime ) ;
#include <time.h> char MiTime [ 9 ] ; // used by _strtime time_t mvqtime_t ; mvqtime_t = time(0) ; strftime ( MiTime, 9, "%T", localtime(&mvqtime_t) ) ;
#include <time.h> time_t aclock ; struct tm * newtime ; time ( & aclock ) ; newtime = localtime ( & aclock ) ; sprintf ( Texte, "%.2d:%.2d:%.2d", newtime->tm_hour, newtime->tm_min, newtime->tm_sec ) ;

Delay (mSecs)

Place following code in DELAY.H and use #include "delay.h"

void MicroDelay (long MicroSecs) { asm { mov cx, word ptr [MicroSecs+2] mov dx, word ptr [MicroSecs] mov ah, 0x86 int 0x15 } } void MilliDelay (long MilliSecs) { MicroDelay (MilliSecs * 1000); }

Now, you need TASM32.exe, isn't it ?

You need to copy the following files from the {C++Builder MainPath}\BIN\: TASM32.EXE TOUCH.EXE TLIBIMP.EXE TLIB.EXE TDUMP.EXE MAKE.EXE IMPLIB.EXE IMPDEF.EXE grep.EXE brcc32.EXE brc32.EXE Another thing you need is the IMPORT32.LIB file from {C++Builder MainPath}\LIB directory. This is the info the linker needs in order to reference API calls.

TASM 2.0 and TLINK 3.01 here


Arguments / paràmetres

 int main ( int argc, char ** argv ) {

 int j, iCnt ;

     iCnt = 0 ;
     while ( iCnt < argc ) {
          printf ( "Argument (%d) = (%s).\n", iCnt+1, argv[ iCnt ] ) ;
          iCnt ++ ;
     } ; /* endwhile */

     for ( j = 0 ; j < argc ; j ++ )
     printf ( "\t argv[%d] : (%s).\n", j, argv [ j ] ) ;
} ;

 if ( argc > 1 ) {

     if (
     ( strcmp ( argv [1], "?" ) == 0 )
     ||
     ( strcmp ( argv [1], "/?" ) == 0 )
     ) {

          printf ( "Hlp - Display some text.\n" ) ;
          return FALSE ;

     } else {

          n = atoi( argv[1] ) ;

     } ;
 } ;

Amunt! Top Amunt!
Error (text) display
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ DWORD Mostrar_Error ( char * szId ) { DWORD dwRetCode ; DWORD dwFlags ; DWORD dwChars ; LPVOID lpMsgBuf ; dwRetCode = GetLastError ( ) ; // set return value. dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ; dwChars = FormatMessage ( dwFlags, // source and processing options NULL, // pointer to msg source dwRetCode, // requested msg identifier MAKELANGID ( LANG_NEUTRAL, SUBLANG_DEFAULT ), // default language (LPTSTR) & lpMsgBuf, // pointer to msg buffer 1024, // max size of msg buffer NULL ) ; // pointer to array of msg inserts if ( dwChars > 0 ) printf ( "*(%u)*%s*%s*", dwRetCode, szId, lpMsgBuf ) ; return dwRetCode ; } ; // Mostrar_Error // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Amunt! Top Amunt!
PRINT(f) tricks
printf ( "MQGET CC/RC [0x%02X/0x%04X].\n", CompCode, Reason ) ; printf ( "Msg from (resolved) Q [%-48.48s].\n", gmo.ResolvedQName ) ;

Amunt! Top Amunt!
Raw Hex Dump
void DumpMsg ( MQBYTE * pDato, int Max_Lng, FILE * Ostr ) { char aux [ 3 ] ; int cnt = 0 ; // fprintf return value. int i ; char DmpTxt_Buffer [ 256 ] ; // string concatenation buffer cnt = fprintf ( Ostr, "Dump (%i) chars from (0x%08X).\n", Max_Lng, pDato ) ; DmpTxt_Buffer [ 0 ] = '\0'; for ( i = 0 ; i < Max_Lng ; i++ ) { sprintf ( aux, "%2.2x", pDato [ i ] ) ; strcat ( DmpTxt_Buffer, aux ) ; strcat ( DmpTxt_Buffer, "-" ) ; if ( strlen ( DmpTxt_Buffer ) >= 60 ) { strcat ( DmpTxt_Buffer, "\n" ) ; cnt = fprintf ( Ostr, DmpTxt_Buffer ) ; /* output to file */ DmpTxt_Buffer [ 0 ] = '\0'; } /* endif */ } ; /* endfor */ if ( strlen ( DmpTxt_Buffer ) > 0 ) { strcat ( DmpTxt_Buffer, "\n" ) ; cnt = fprintf ( Ostr, DmpTxt_Buffer ) ; /* output (remaining) to file */ } ; } ; // DumpMsg

Used in \\MQ\Eines\Default_QmgrName\codiX.c

Sample in "delphi"


Amunt! Top Amunt!
Hex dump with chars
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Dump_Hex_to_String ( char * pActual, // in : source pointer int Longitud_Actual, // in : source count char * Hex_Buffer, // out : destination pointer int Hex_Buffer_Size, // in : destination size int Do_Also_Translate, // in : bool char * Buffer_Translate ) // out : ASCII chars { int i ; int Quants ; int Cnt_Actual ; int cnt ; // sprinf return value. char aux [ 3 ] ; char chTranslate ; Cnt_Actual = ( 2 * Longitud_Actual ) + ( Longitud_Actual - 1 ) ; if ( Cnt_Actual > Hex_Buffer_Size ) { Quants = Hex_Buffer_Size / 3 ; } else { Quants = Longitud_Actual ; } ; /* endif */ Hex_Buffer [ 0 ] = '\0' ; for ( i = 0 ; i < Quants ; i++ ) { if ( i != 0 ) { strcat ( Hex_Buffer, "-" ) ; } ; cnt = sprintf ( aux, "%2.2X", pActual [ i ] ) ; strcat ( Hex_Buffer, aux ) ; if ( Do_Also_Translate ) { chTranslate = pActual [ i ] ; if ( chTranslate < '\x20' ) { chTranslate = '.' ; } else { if ( chTranslate > '\x7E' ) { chTranslate = '.' ; } ; } ; /* endif */ Buffer_Translate [ i ] = chTranslate ; // write (maybe new) char. } ; /* endif */ } ; /* endfor */ } ; // end of Dump_Hex_to_String // **************************************************************************** void Msg_Hex_Dump ( char * Msg2Dump, int MsgLng2Dump, char * Prefix_Txt, FILE * File2Dump ) { char * pActual ; int Longitud_Restante ; int Longitud_Actual ; #define kLongitud_Linea 16 #define Prefix_Buffer_Size 256 #define SG_max_msg_size 256 #define DoTranslate 1 int cnt ; // sprinf return value. char Hex_Buffer [ Prefix_Buffer_Size ] ; // string concatenation buffer char Msg_Buffer [ SG_max_msg_size ] ; // string concatenation buffer char Translate_Buffer [ Prefix_Buffer_Size ] ; Longitud_Restante = MsgLng2Dump ; pActual = Msg2Dump ; while ( Longitud_Restante > 0 ) { if ( Longitud_Restante > kLongitud_Linea ) { Longitud_Actual = kLongitud_Linea ; } else { Longitud_Actual = Longitud_Restante ; } ; /* endif */ if ( DoTranslate ) { memset ( Translate_Buffer, '\0', Prefix_Buffer_Size ) ; } ; Dump_Hex_to_String ( pActual, // source pointer Longitud_Actual, // source count Hex_Buffer, // destination pointer Prefix_Buffer_Size, // destination size DoTranslate, Translate_Buffer ) ; pActual = pActual + Longitud_Actual ; Longitud_Restante = Longitud_Restante - Longitud_Actual ; if ( DoTranslate ) { while ( Longitud_Actual < kLongitud_Linea ) { strcat ( Hex_Buffer, "..." ) ; // each char becomes 2 + separator. strcat ( Translate_Buffer, " " ) ; // here, only one ... Longitud_Actual ++ ; } ; /* endwhile */ cnt = sprintf ( Msg_Buffer, "(%s) %s [%s]", Prefix_Txt, Hex_Buffer, Translate_Buffer ) ; } else { cnt = sprintf ( Msg_Buffer, "(%s) %s", Prefix_Txt, Hex_Buffer ) ; } ; /* endif */ if ( File2Dump != NULL ) { fprintf ( File2Dump, "(%s)\n", Msg_Buffer ) ; fflush( File2Dump ) ; /* Forzar escritura */ } else { printf ( "(%s)\n", Msg_Buffer ) ; } ; } ; /* endwhile */ } ; // Msg_Hex_Dump // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Amunt! Top Amunt!
Write a +H +S +R file
int Fer_Feina ( void ) { // based on "\\CPP\TimeWR\wr2fi.c" char * sTitol = "c:\\FN.TXT" ; // PATH does NOT work ! FILE* fp ; int iFA, iFA2, iFA3 ; int iRC = 0 ; iFA = _chmod ( sTitol, 0 ) ; // get file attribute iFA2 = iFA & ( ~ FA_RDONLY ) & ( ~ FA_HIDDEN ) & ( ~ FA_SYSTEM ) ; _chmod( sTitol, 1, iFA2 ) ; // set file attribute fp = fopen ( sTitol, "w" ) ; // "w" = write ; "a" = write + append if ( fp != NULL ) { fprintf ( fp, "*+*" ) ; fclose ( fp ) ; /* close file */ iRC = 1 ; } else { iRC = 2 ; } ; /* endif */ _chmod( sTitol, 1, iFA ) ; // restore original attributes return iRC ; } ;

chdir()


Amunt! Top Amunt!
Memory Status
// // http://search.microsoft.com/results.aspx?q=GlobalMemoryStatus // http://msdn.microsoft.com/en-us/library/aa366589(VS.85).aspx // // Include = <windows.h> DWORD VerMemoria ( void ) { DWORD dwMemoriaDisponible ; LPMEMORYSTATUS lpBuffer ; dwMemoriaDisponible = 0 ; lpBuffer -> dwLength = sizeof(MEMORYSTATUS) ; GlobalMemoryStatus ( lpBuffer ) ; dwMemoriaDisponible = lpBuffer -> dwAvailVirtual ; return dwMemoriaDisponible ; } ;

Pendent : convertir a GlobalMemoryStatusEx (per màquines amb més de 4 GB de RAM).

Bon article : Customized version on Performance Monitor


TCP/IP

Amunt! Top Amunt!
MAC address

3 ways to do it :

url

send ARP packets (win32 style)

Input : IP ; output : MAC.

DWORD SendARP( __in IPAddr DestIP, __in IPAddr SrcIP, __out PULONG pMacAddr, __inout PULONG PhyAddrLen );

Requirements : minimum supported client = Windows 2000 Professional ; minimum supported server = Windows 2000 Server.
Header = Iphlpapi.h ;
Library = Iphlpapi.lib ;
DLL = Iphlpapi.dll ;

Our code :

\\T400\CPP\Send_ARP\win> send_arp.exe Usage: send_arp.exe [options] ip-address -h help -l length MAC physical address length to set -s src-ip source IP address \\T400\CPP\Send_ARP\win> send_arp.exe 192.168.1.1 Sending ARP request for IP address: 192.168.1.1 00-15-6D-19-C8-95

MSDN using IPHLPAPI.H and WINSOCK2.H

HOSTNAME & IP
#include <winsock2.h> char my_host_name [ 255 ] ; PHOSTENT my_hostinfo ; char my_ip [ 255 ] ; if ( gethostname ( my_host_name, sizeof(my_host_name) ) == 0 ) { if ( ( my_hostinfo = gethostbyname( my_host_name ) ) != NULL ) { strcpy ( my_ip, inet_ntoa (* (struct in_addr *) * my_hostinfo -> h_addr_list ) ) ; } ; } ;
URLs

Process list

Win32::Process::List and Win32::Process:Info, then use Win32::TaskScheduler to kill it.


Amunt! Top Amunt!
Borland

 


Amunt! Top Amunt!
C string functions
#include <string.h>
*note: size_t is generally defined as an unsigned short.
char * strcpy ( char *s1, const char *s2 )
* copies the string s2 into the character array s1. The value of s1 is returned.
char * strncpy ( char *s1, const char *s2, size_t n )
* copies at most n characters of the string s2 into the character array s1. The value of s1 is returned.
char * strcat ( char *s1, const char *s2 )
* appends the string s2 to the end of character array s1. The first character from s2 overwrites the '\0' of s1. The value of s1 is returned.
char * strncat ( char *s1, const char *s2, size_t n )
* appends at most n characters of the string s2 to the end of character array s1. The first character from s2 overwrites the '\0' of s1. The value of s1 is returned.
char * strchr ( const char *s, int c )
* returns a pointer to the first instance of c in s. Returns a NULL pointer if c is not encountered in the string.
char * strrchr ( const char *s, int c )
* returns a pointer to the last instance of c in s. Returns a NULL pointer if c is not encountered in the string.
int strcmp ( const char *s1, const char *s2 )
* compares the string s1 to the string s2. The function returns 0 if they are the same, a number < 0 if s1 < s2, a number > 0 if s1 > s2.
int strncmp ( const char *s1, const char *s2, size_t n )
* compares up to n characters of the string s1 to the string s2. The function returns 0 if they are the same, a number < 0 if s1 < s2, a number > 0 if s1 > s2.
size_t strspn ( char *s1, const char *s2 )
* returns the length of the longest substring of s1 that begins at the start of s1 and consists only of the characters found in s2.
size_t strcspn ( char *s1, const char *s2 )
* returns the length of the longest substring of s1 that begins at the start of s1 and contains none of the characters found in s2.
size_t strlen ( const char *s )
* determines the length of the string s. Returns the number of characters in the string before the '\0'.
char * strpbrk ( const char *s1, const char *s2 )
* returns a pointer to the first instance in s1 of any character found in s2. Returns a NULL pointer if no characters from s2 are encountered in s1.
char * strstr ( const char *s1, const char *s2 )
* returns a pointer to the first instance of string s2 in s1. Returns a NULL pointer if s2 is not encountered in s1.
char * strtok ( char *s1, const char *s2 )
* repeated calls to this function break string s1 into "tokens", that is the string is broken into substrings, each terminating with a '\0', where the '\0' replaces any characters contained in string s2. The first call uses the string to be tokenized as s1; subsequent calls use NULL as the first argument. A pointer to the beginning of the current token is returned; NULL is returned if there are no more tokens.

Amunt! Top Amunt!
Error: Unresolved external

There's a difference between interface and implementation.
A .h file defines the interface for a group of related functions. That is, it tells you (and the compiler) what functions exist, how to call them, what their arguments and return types are, etc.
The implementation of a function is the code that defines its execution. This can be in a .c file or a .cpp file ; The implementation can also be contained in a .lib file (library) containing already-compiled code; we may provide you with .lib files instead of .cpp files in cases where the implementation source is unimportant or needs to remain a secret.

Either way, when you #include a .h file, you've only told the compiler what functions and variables you later intend to define or implement. You also have to supply these implementations; this is done by adding files (.cpp or .lib) containing the implementations to your project.
The compiler compiles your source files one by one, leaving "external" references to things that were mentioned in .h files but never actually defined. Then the linker "links" or combines the results of the compilation and any libraries in the project, patching up the "external" references.

url


Amunt! Top Amunt!
mr Simonyi

Notacio hongaresa : semantics vs the variable's purpose. Errors.

Simonyi’s original concept for Hungarian notation was called, inside Microsoft, Apps Hungarian, because it was used in the Applications Division, to wit, Word and Excel. In Excel’s source code you see a lot of rw and col and when you see those you know that they refer to rows and columns. Yep, they’re both integers, but it never makes sense to assign between them. In Word, I'm told, you see a lot of xl and xw, where xl means "horizontal coordinates relative to the layout" and xw means "horizontal coordinates relative to the window." Both ints. Not interchangeable.

Amunt! Top Amunt!
Eye debug

In general, I have to admit that I’m a little bit scared of language features that hide things. When you see the code

i = j * 5 ;

... in C you know, at least, that j is being multiplied by five and the results stored in i

But if you see that same snippet of code in C++, you don’t know anything. Nothing.
The only way to know what’s really happening in C++ is to find out what types i and j are, something which might be declared somewhere altogether else. That’s because j might be of a type that has operator* overloaded and it does something terribly witty when you try to multiply it. And i might be of a type that has operator= overloaded, and the types might not be compatible so an automatic type coercion function might end up being called. And the only way to find out is not only to check the type of the variables, but to find the code that implements that type, and God help you if there’s inheritance somewhere, because now you have to traipse all the way up the class hierarchy all by yourself trying to find where that code really is; and if there’s polymorphism somewhere, you’re really in trouble because it’s not enough to know what type i and j are declared, you have to know what type they are right now, which might involve inspecting an arbitrary amount of code and you can never really be sure if you’ve looked everywhere thanks to the halting problem (phew!).

When you see i=j*5 in C++ you are really on your own, bubby, and that, in my mind, reduces the ability to detect possible problems just by looking at code.

joelon software


DEV-C++ i el robot de'n Xavi ...

Instalació del Dev-C++ : url.

Identificació del codi
static char * szAutor = "Robot Tinho, v 1.8.b" ; // indicacio de la versio que hi ha dins el EXE
Posicionament del cursor
void gotoXY ( int x, int y ) { COORD pos ; pos.X = x ; pos.Y = y ; SetConsoleCursorPosition ( GetStdHandle ( STD_OUTPUT_HANDLE ), pos ) ; } ;
Why funciona ? (I realy dont know)
void llegir_punts ( int & x, int & y ) { cout << "Introdueix la coordenada x: " << endl ; cin >> x ; cout << "Introdueix la coordenada y: " << endl ; cin >> y ; } ; // llegir_punts ( int & x, int & y )

Quan la cridem sense passar cap adreça explicita:

llegir_punts ( recinte.final.coordx, recinte.final.coordy ) ;

Aixo son references, a C++ concept: An alias (an alternate name) for an object. References are frequently used for pass-by-reference:

void swap ( int & i, int & j ) { int tmp = i; i = j; j = tmp; } int main() { int x, y; ... swap(x,y); ... }

Important note: Even though a reference is often implemented using an address in the underlying assembly language, please do not think of a reference as a funny looking pointer to an object. A reference is the object. It is not a pointer to the object, nor a copy of the object. It is the object.

References as variables do not take space: url

int i ; // takes sizeof(int) int * pi = &i ; // takes sizeof(int*) int & ri = i ; // takes no space. any operations done to ri, are simply done to i

References as parameters use pointers to achieve the end effect:

void foo ( int & i ) { i = 12 ; } void foo_transformed ( int * i ) { * i = 12 ; } int main() { int i; foo ( i ) ; // same as: foo_transformed ( & i ) ; // to the compiler (only sort of) }
Estructura SWITCH
switch ( <expression> ) { case K1 : // if <expression> == K1 <sentence> ; break ; // K1 case K2 : // if <expression> == K2 <sentence> ; break ; // K2 default : // else break ; // other values } ; // switch
Escriure en un fitxer
// basic file operations #include <iostream> #include <fstream> using namespace std; int main () { ofstream myfile ; myfile.open ( "example.txt" ) ; myfile << "Writing this to a file.\n" ; myfile << "Even more !" << endl ; myfile.flush () ; // el FLUSH fa que les dades s'escriguin de la cache al disc, so BareTail can display them ... myfile.close() ; return 0 ; } ; // main()

Files, IOstream,

Crides al sistema
#include <windows.h> system ( "cls" ) ; // clear the DOS screen ; system ( "pause" ) ; // display "Press any key to continue ..." message ;

Get system-calls complete list or url.

Dubtes
int * a, b ; // quin tipus te b ? char * s1, s2 ; // i s2 ?

Gràcies, Bellido !


Amunt! Top Amunt!
PHP

Run PHP code from command line :

[sebas@rhv6-64b cmds]$ cat hello.php #!/usr/bin/php <?php echo "Hola des PHP.\n" ; exit(0) ; ?>

Amunt! Top Amunt!
A program has to be able to ...

Amunt! Top Amunt!
Links

Ep !

Valid HTML 4.01!   Valid CSS!

Escriu-me !
Updated 20130827 (a)
Uf ! Welcome ! Top !