Sie sind auf Seite 1von 22

Mixed-mode on Itanium-based platforms: why and how

Introduction .............................................................................................................................. 2 Executive summary .................................................................................................................... 2 Mixed-mode: why...................................................................................................................... 2 Mixed-mode: in the past............................................................................................................. 3 Mixed-mode: how ..................................................................................................................... 3 Mixed-mode: shared memory .................................................................................................. 5 Mixed-mode: remote procedure call ......................................................................................... 7 Mixed-mode: performance ...................................................................................................... 8 Mixed-mode: guidelines.......................................................................................................... 9 Summary ................................................................................................................................ 10 Glossary................................................................................................................................. 10 For more information ............................................................................................................... 10 Appendix ............................................................................................................................... 11 HP-UX 11i release names and release identifiers...................................................................... 22

Introduction
As HP customers make the transition from Precision Architecture (PA) to the Intel Itanium architecture, some are going to find themselves in the position of wanting to execute PA binaries on the Itanium-based platform. HP has provided the Dynamic Object Code Translator (code name Aries) to accommodate this need. Aries transparently translates the PA code into Intel Itanium processor code on-the-fly, and offers performance sufficient for many programs that are not compute intensive. However, Aries must operate at the level of a complete program unit; you cannot have a main program written for the Itanium architecture call a PA function, nor vice versa. This can be problematic in situations where the PA code is a third-party library that does not exist on Itanium-based platforms or when there is not time to port/recompile all the parts of the application. To overcome this problem, this paper offers two methods of creating mixed-mode applications, i.e., applications where code for one architecture is making calls to code in the other architecture. The two methods offered are the use of shared memory and the use of Remote Procedure Calls (RPC). Many customers will not need mixed-mode code, but those who need it will have few alternatives. The major issues in constructing mixed-mode applications are process synchronization, data transfer and alignment, and re-entrant code. This paper provides information on how to address these issues in constructing mixed-mode applications. It also provides guidelines for the use of this technology and performance comparisons that may help to determine what the result of implementing a mixed-mode application will be.

Executive summary
In the transition from Precision Architecture to the Intel Itanium architecture, it may be necessary to create mixedmode programs, i.e., programs where a library function written for one architecture must be called by code written for the other architecture. The tools provided for migrating applications between these architectures do not directly support this facility. This paper outlines a general method for accomplishing mixed-mode and two different implementations of the method. The issues associated with the method and implementations are discussed, and guidelines for the use of the methods and the implementations are provided. The method involves encapsulating the library in a server program, which can then be run under the HP-provided Dynamic Object Code Translator (DOCT/Aries). The calling program is modified by creating a stub for the library that sends data to the server and receives the results back. There are performance impacts with this approach, but they are not always significant. This paper outlines situations where the performance impact will be less significant and where it will be more significant.

Mixed-mode: why
In migrating from Precision Architecture to the Intel Itanium architecture, there are situations where the need for mixed-mode applications can arise. Some examples are: The application requires a third-party library that is no longer in production (or the supplier has ceased operation). All that exists is the PA library binaryno source code is available. The application consists of complicated legacy (not easily ported) user interface code that feeds a computeintensive core function that needs to be run faster. The core function can be ported quickly, but the user interface code would take too long. The application is large, consisting of many modules; and resource limitations dictate that it can only be ported incrementally, one source module at a time, but there isnt time to wait for all of it to be finished. All of these situations, and others, have a need for the ability to run programs that consist of a mix of Intel Itanium architecture and Precision Architecture modules. If the module needed is actually a program, the situation can be handled by the Dynamic Object Code Translator (Aries)and data can be passed between translated and native programs. If the module is a function or group of functions, Aries cannot be invokedit operates only at the program level. The need, then, is for a method to create a separate program that contains the needed function(s), and then to provide a way to pass data back and forth between it and the main portion of the program.

Mixed-mode on Itanium-based Platforms

Mixed-mode: in the past


Historically, in the transition from the HP 3000 classic architecture to Precison Architecture, support was provided for mixed-mode applications. Tools were created to describe the interface between the routines, and stubs that would change the processor mode were created to enable mixed-mode processing to occur. In the transition from PA to the Intel Itanium architecture, it was decided that adding the overhead of checking for mixed-mode to every function/subroutine/method call would be too large a penalty to pay for the convenience of mixed-mode. This decision was based on the fact that there are fewer organizations running on home-grown software; more companies now run on purchased applications that will be ported completely to Intel Itanium architecture by the application vendor. The number of companies who will need mixed-mode is expected to be much smaller.

Mixed-mode: how
The key to allowing code from the two different architectures (PA and Intel Itanium) to interoperate at a function level is to raise the granularity of the interoperation from module-to-module to program-to-program. In this way, Aries can be used to run the PA portion of the code (because it is a program, not a function), and the Intel Itanium processor code can run natively. The following graphic illustrates the situation:

Figure 1. Mixed-mode: general architecture

The key element in this method of creating a two-process mixed-mode application is the inter-process communication that occurs between the two processes. This communication must provide four basic functions: 1. Transmission of the data from the calling process to the server process. 2. Notification of the server process that data is ready. 3. Notification of the calling process that the data has been processed and results are available. 4. Transmission of the results back from the server process to the calling process.

Mixed-mode on Itanium-based Platforms

There are quite a few forms of inter-process communication that are capable of meeting these basic requirements. Some of the candidates are: Shared filesrelatively high overhead, but suitable for really LARGE data. Synchronization must be provided by some other method. Pipesgood choice, but requires marshaling of data to maintain alignment between the processes and has a moderate amount of system call overhead. Shared memoryfast; requires attention to alignment between 64- and 32-bit processes; requires a form of synchronization. Remote procedure callsbased on distributed computing environment (DCE) or open network computing (ONC). This method provides automatic data marshaling but has significant overhead. It can be inter-machine as well as inter-process, perhaps helping in situations with data migration issues. Synchronization is inherent in the mechanism. Both mechanisms are available on HP-UX for Itanium-based systems, and obsolescence is not planned for either. There are many choices for the inter-process communication. The two chosen for this discussion were based on available knowledge and the contrast between the two. They are: Shared memory with semaphore synchronization. Remote procedure calls based on DCE. In both cases, the two-process mixed-mode is implemented in several pieces: 1. Code to create the communication infrastructuredata movement and synchronization. 2. A stub routine to replace the original routine. This stub accepts the input data, sends the data to the other (server) process, accepts the returned results, and returns the results to the main program. 3. The server process that encapsulates the original routine. This process accepts the data sent from the stub in the main, passes this data to the original routine, accepts the results back from the routine, and sends these results back to the main program. 4. The Dynamic Object Code Translator (DOCT)it runs the process containing the original (PA) routine. The following sections provide greater detail on how these pieces are created in the original (main) process, and in the new (server) process.

Mixed-mode on Itanium-based Platforms

Mixed-mode: shared memory


In the shared-memory implementation of two-process mixed-mode, data is passed between the two processes through a shared-memory segment, and synchronization is accomplished by using two semaphoresone to indicate input is available, and the other to indicate that results are available. For the sake of illustration, we will assume that the main program will run natively on the Intel Itanium architecture, and the server process will be running under DOCT. Note that this means that the server process must be compiled and linked with the PA library binary on a PA system. This method is illustrated in the figure below.

Figure 2. Mixed-mode using shared memory

One of the issues in client/server systems is the need for re-entrant code. What happens if several programs are using the library routine at once? In this case, each instance of the main program, assumed to be single-threaded, will create its own copy of the server process so that the routine will always be executing in response to only one call to it. Therefore, the code need not be re-entrant. To implement this method of mixed-mode, the following additions are made to the original main program: 1. The shared memory segment and semaphores are created. 2. The server program is started up. The keys to accessing the shared memory and semaphores are passed to it on startup. 3. The original routine is replaced by the stub routine that places the input data into shared memory and raises the input ready semaphore. The stub then waits on the results ready semaphore. When the semaphore is raised, the stub reads the results from shared memory and then returns them to the calling program. 4. At the end of the program, the server process is killed and the shared memory and semaphores are released.

Mixed-mode on Itanium-based Platforms

The server process (which is compiled and linked on a PA system in Figure 2) consists of several parts: 1. The attachment to the shared memory and semaphores, based on the keys passed in. 2. Waiting on the input ready semaphore. When it is raised, read in the data from shared memory. 3. Call the original routine, passing it the input data. 4. Take the results returned by the routine and place them in shared memory. 5. Raise the results ready semaphore to signal the main. 6. Return to waiting on the input ready semaphore. The major issues that are liable to be encountered in this method have to do with differing data sizes and alignments between 32- and 64-bit program models. These differences occur only when the library and main are not running under the same model (32- or 64-bit). This is not a problem if both programs are running under the same model. The size differences are as follows:

Data type Pointer Long integer

Size in 32-bit model 32 bits (4 bytes) 32 bits (4 bytes)

Size in 64-bit model 64 bits (8 bytes) 64 bits (8 bytes)

Most data types are aligned on byte addresses equivalent to their size, i.e., 8-byte data types are aligned on byte addresses that are divisible by 8 (8-byte boundaries), and 4-byte data types are aligned on byte addresses that are divisible by 4 (4-byte boundaries). The exception to this is the long double data type. This data type is 16 bytes (128 bits) long and is aligned on 16-byte boundaries on PA in both the 32- and 64-bit models. On the Intel Itanium architecture, it aligns on 16-byte boundaries in the 64-bit model, but it aligns on 8-byte boundaries under the 32-bit model. Generally, alignment problems occur only if structures are being passed in shared memory. The rationale for doing this is to create a structure containing all the parameters to be passed to the routine and then copy the structure to shared memory. This is elegant, but it can create problems due either to alignment or to differing data sizes. Unless it is known that there are no data size differences, and no alignment (long double) issues, it is better to use a different method for placing the parameters into shared memory. The most foolproof method is to use an appropriately incremented pointer to place each parameter in shared memory individually and extract them in a similar manner in the server process. If long integers are being passed between 64-bit and 32-bit programs, the size must be taken into account and overlap or incorrect pointers avoided. Sometimes debugging the parameter placement by printing the addresses of the variables in shared memory from both original and server processes is a good idea, as this can quickly show misalignment of where variables are deemed to be placed by the two processes.

Mixed-mode on Itanium-based Platforms

Mixed-mode: remote procedure call


In the remote procedure call (RPC) implementation of two-process mixed-mode, the data is passed between the two processes through the provided remote procedure call mechanism. This mechanism provides data marshaling and conversion, where needed, and is inherently synchronized. The mechanism allows remote calls, i.e., calls to a procedure on another system, made via the network. This can allow the routine to run natively on a PA system, which might be useful if the routine reads or writes data files that would have alignment problems on the Intel Itanium architecture (mainly as an interim step). For this illustration, we will assume that the main program is running natively on the Itanium architecture, and the server process has been created on a PA system and is running on an Itanium-based system under DOCT. In either case, the overhead of making each call is relatively highthis method may not be best for routines that expect to be called thousands of times per second. This method is illustrated in the figure below.

Figure 3. Mixed-mode using RPC

The DCE RPC mechanism handles the re-entrancy requirement transparentlyno further consideration of this issue is needed. To implement this method of mixed-mode, the following changes are made to the original program: 1. The DCE connection to the server is initialized. This is best accomplished using boilerplate code. 2. The original routine is replaced by the stub routine that translates the function call to the RPC call and then returns the results to the main. It is best to use the templates and Makefiles created for this paper to accomplish this activityDCE RPC is too complex to figure out on your own just for this project. Makefiles and templates are available on the HP DSPP Web site at http://atwnt959.external.hp.com/dspp/files/unprotected/ItaniumMixedMode.tar The server program is created in two partsthe server process and a container function that calls the original function and returns the results. They are linked together at link time. The server program has three main functional sections: 1. Registration of this service with the rpcd daemon. This is best left to the boilerplate code. 2. Listen for the remote procedure calls and process them by calling the container function. This is done in one call that actually contains the processing loop. 3. Code to unregister the servicethis is normally never used, as the server waits for more calls until it is killed.

Mixed-mode on Itanium-based Platforms

Construction of the main and server includes the creation of an Interface Definition file in the Interface Definition Language (IDL). This file describes the parameters to the call being made, in terms of what data type each parameter is, and whether it is an input, output, or both. The compilation and linking together of these elements is best done with the Makefiles provided. See the code and Makefile examples in the Appendix. The major issues that are likely to be encountered with this method involve the complexity of the DCE code (handled by using the templates referenced) and the overhead cost of each call if many are being made. See the following section for details.

Mixed-mode: performance
Both of these methods involve executing at least a process context switch for each call, so the call mechanism is much more expensive, in terms of time taken, than the direct native procedure call. The RPC call overhead is approximately 0.45 milliseconds on an HP Server rx2600 (Itanium 2-based 900 MHz system), and the sharedmemory overhead is less than 0.01 milliseconds on the same system. From this, we can see that if the legacy library routine is called very often (hundreds to thousands of times a second), it may cause noticeable performance degradation. If the legacy routine is not called this often, and its normal operation takes greater than 1 millisecond, the effect may not be noticeable, especially with the sharedmemory method. Performance of code running under the DOCT, compared with the performance when running on the PA system will vary, depending on the application characteristics and which Itanium-based system it is running on. Measurements with the first generation of Itanium-based systems showed performance that was typically 3080% of the native PA performance. As new Intel Itanium processor generations have come into production, typical performance under the DOCT has improved and will continue to improve. We will likely reach a point where performance will be equal to or greater than the native PA performance on the systems customers would typically be migrating from (usually PA systems that are several years old). The following graph illustrates the performance comparison of a simple application so far.

Figure 4. Example comparison of native PA and Intel Itanium architecture Aries execution times

Mixed-mode on Itanium-based Platforms

The PA systems used were: D380 (180 MHz, 1998), HP Server rp5450 (440 MHz, 2001), and HP Server rp7410 (750 MHz, 2003). The Itanium-based systems used were: Lion (i2000) pre-production (533 MHz, 2001), and HP Server rx2600 (900 MHz, 2002). The example used is a small program that calls a function to increment an argument, and it adds up the results for 100,000,000 calls. This is a compute-bound process; other types of programs will have different performance, but it is apparent that the difference between Aries performance and native PA performance is diminishing. Existing performance on a 1 GHz Itanium 2-based system approaches the performance of the HP Server rp5450 system from two years ago. The 2003 release of the next generation of Itanium 2-based systems (Madison) is expected to run approximately 50% faster than the existing 1 GHz Intel Itanium 2 processors.

Mixed-mode: guidelines
The general guidelines for using the DOCT and mixed-mode are as follows: 1. In making the transition to a new architecture, code that is recompiled and optimized for the new platform will always be faster than code running under the Dynamic Object Code Translator. 2. Code running under the DOCT on new Itanium 2-based systems may run as fast as it did on its nearly-current PA system. 3. Two-process mixed-mode works best for applications that have the following characteristics: The legacy routine does a significant amount of work before returning, i.e., the work done takes as long or longer than the call overhead. The application or legacy routine is more interactive or I/O bound than compute intensive. The overhead of the call is essentially irrelevantthe functionality is needed, and it is called tens of times per second or less. In choosing which of the two methods to use for your application, the following points may be helpful. Advantages of RPC method: Automatic data marshalinggood if the data passed is complex or if 32-/64-bit size differences are an issue. Can be inter-systemuseful if data needs to be accessed directly on a PA system. Conceptually simplerre-entrancy and synchronization are handled. Disadvantages of RPC method: Requires purchase of PA product B6192AADCE programming environment to build the PA portion. Slower call mechanismmore overhead. More complicated codingDCE based, though using the templates, instructions, and Makefiles provided can help. Advantages of the shared-memory method: Lowest costnothing additional to buy. Fasterorder of magnitude faster call than RPC. Simpler codeuses well-known system programming calls. Disadvantages of the shared-memory method: Have to manage data alignment if 32-/64-bit model differs between library and main program. Not inter-systemdata must be available on Itanium-based platform.

Mixed-mode on Itanium-based Platforms

Summary
For those PA legacy libraries that cannot or will not be ported to the Intel Itanium architecture, there is a possible solutiontwo-process mixed-mode. This paper has outlined two methods for implementing two-process mixedmode applications. The DCE RPC-based method offers more flexibility and easier data handling, but it is slower and more complex than the shared-memory method. The shared-memory and semaphore-based method requires all components to be on one Itanium-based system; it requires care in handling the data transfer but executes more quickly. Both methods require modification to the program that calls the legacy routine and the creation of a server program around the legacy routine.

Glossary
Mixed-modea program that contains procedures compiled for different architectures, i.e., some procedures are compiled for Precision Architecture, while others are compiled for the Intel Itanium architecture. DCEdistributed computing environmenta standard for performing multi-system computing, developed by the Open Software Foundation. ONCopen network computinga standard for performing multi-system computing, developed by Sun Microsystems. RPCremote procedure calla mechanism for calling a procedure on one system from a procedure or program on another system via the intervening network.

For more information


Makefiles and templates for doing this are available on the HP DSPP Web site at http://atwnt959.external.hp.com/dspp/files/unprotected/ItaniumMixedMode.tar. These are supplied on an as-is basisthey are not warranted or supported.

Mixed-mode on Itanium-based Platforms

10

Appendix
Below are program and Makefile examples: DCE RPC example: The comments surrounding and explaining the DCE code in these examples have been deleted for brevity. Fully commented templates and examples, and instruction files, can be found in the tar archive located on the Web at http://atwnt959.external.hp.com/dspp/files/unprotected/ItaniumMixedMode.tar. This is an example of the client program. This code would need to be integrated into the source of the main (calling) program.
/* adder_client.c *************************************************************************** * This is the code for the client program. It takes two arguments, a hostname * to contact for the server and a number which may be used as to control * how many iterations of a subroutine call (to or witin the old PA library) * are performed. To avoid having to create a full DCE cell environment, it * locates the server using the hostname provided and the endpoint mapper on * the server's host -- the client does not contact the name service for * server location information. The client uses the explicit binding * method, so it uses the hostname argument to construct a binding handle * (rpc_binding_handle_t bh). The client passes this binding handle to the * invocation of the remote procedure. * * In a production application, the code that creates the DCE bindings from * the hostname must be integrated into the production source to allow the * use of the old PA library. The needed hostname may be obtained in any way * that suits - hard-coded, extracted from an environment variable, or, as * done here, from the command line. * * A simple tracing facility is coded in - it is enabled at compile time * using the compiler flag -DTRACING *************************************************************************** */ /* * (c) Copyright 2002 Hewlett-Packard Co. */ /* * @(#)HP DCE/9000 1.7.1 * @(#)Module: client.c $Revision: 1.0$ $Date: 2002/01/17 22:08:09 $ */ /* */ #include #include #include #include #include #include #include #include <pthread.h> <stdlib.h> <strings.h> <stdio.h> <dce/dce_error.h> <dce/exc_handling_kt.h> "hpdcemacros.h" "adder.h" /* DCE Pthread facility */ Standard POSIX defines */ str*() routines */ Standard IO library */ DCE error facility */ /* DCE error facility */ /* Common HP defs for this app */ /* Output from adder.idl */ /* /* /* /* /* Initialize for client */

#ifdef TRACING tr_handle_t * tr_handle = NULL; #endif /* TRACING */

/* * The DCE RPC binding handle is global so the 'fake' legacy routine can find it * to build the remote call */ rpc_binding_handle_t bh; /* "points" to the server */ void main(int argc, char *argv[]) { error_status_t dce_error_string_t ndr_char unsigned long unsigned_char_t long total, adder(),i; #ifdef TRACING start_dce_tracing (); Mixed-mode on Itanium-based Platforms

st, _ignore; /* returned by DCE calls */ dce_err_string; /* text describing error code */ *string_binding; /* used to create binding */ counter; /* routine iteration counter */ *netaddr; /* network address of server */

11

#endif /* TRACING */ if (argc != 3) { fprintf(stderr, "Usage: %s hostname counter\n", argv[0]); exit(1); } else { netaddr = (unsigned_char_t *)argv[1]; counter = atoi(argv[2]); } /* Initialize the DCE connection */ get_my_dce_connection (netaddr); /* * Insert the test function call here */ total=0; for ( i=0;i<counter;i++ ) { total=total+adder(i); } fprintf(stdout, "Result for %d is %d \n",counter,total); exit (0); /* * No status information was passed back. If the call failed the RPC * runtime will have raised an exception and caused an exit. */ PRINT_FUNC(PRINT_HANDLE, "Returned from remote_adder(%d)\n", counter); exit(0); } /* * The "onion-skin" that redirects calls to the legacy library to the * DCE RPC based version running either under DOCT (Dynamic Object Code * Translation - code name Aries) on an IPF system such as the local one, * or remotely on a PA system in native mode. The difference is transparent * from this point of view. * The remote call uses the global binding handle (bh) created earlier. */ long adder ( unsigned long counter) { long result; TRY { result=remote_adder(bh, counter); } CATCH_ALL { PRINT_FUNC(PRINT_HANDLE, "Caught an exception!\n"); exit(1); } ENDTRY; return result; } /* * Initialize DCE tracing facility */ void start_dce_tracing() { char* trace_name="adder"; #ifdef TRACING if (tr_handle == NULL) { tr_handle = tr_init("TR_DCE", /* environment variable name */ NULL, /* selector level defaults */ NULL, /* filename for output */ trace_name); /* prefix string in output */ if (tr_handle == NULL) { fprintf(stderr, "Unable to initialize tracing interface!\n"); } } #endif /* TRACING */ } /* * Create the DCE RPC binding handle,and place it in the global variable bh Mixed-mode on Itanium-based Platforms

12

*/ int get_my_dce_connection (unsigned_char_t *netaddr) { error_status_t st, _ignore; /* returned by DCE calls */ dce_error_string_t dce_err_string; /* text describing error code */ ndr_char *string_binding; /* used to create binding */ rpc_string_binding_compose(NULL, /* no object UUID */ (unsigned_char_t *)"ip", /* protocol to use */ netaddr, /* network addr of server */ NULL, /* use a dynamic endpoint */ NULL, /* misc. network options */ &string_binding, /* returned string binding */ &st); /* error status for this call */ if (st != rpc_s_ok) { dce_error_inq_text(st, dce_err_string, (int *)&_ignore); PRINT_FUNC(PRINT_HANDLE, "Cannot compose string binding: %s\n", dce_err_string); exit(1); } rpc_binding_from_string_binding(string_binding, /* created above */ &bh, /* allocated and returned */ &st); /* error status for this call */ if (st != rpc_s_ok) { dce_error_inq_text(st, dce_err_string, (int *)&_ignore); PRINT_FUNC(PRINT_HANDLE, "Cannot get a binding handle: %s\n", dce_err_string); exit(1); } /* * At this point the application is not actually connected to any * server, but it has all the information needed to establish a * connection to the remote server. Connection establishment happens in * the client stub function call. */ PRINT_FUNC(PRINT_HANDLE, "Bound to %s\n", string_binding); rpc_string_free(&string_binding, &_ignore); } /* DCE string to free */ /* DCE return status */

The following code is a corresponding example of the client code. This is the code that will be wrapped around the original legacy library to create the second process (the one that runs under the DOCT) in the two-process mixed-mode. This code would usually be compiled on a PA system, then moved to the Itanium-based system, and run (transparently) under DOCT. In the case of difficulty with migrating data being accessed by the library routine, this program could remain on the PA system and communicate with the rest of the application over the network. The program consists of two source modules: the main program, which establishes the communication infrastructure; and a container function, which is the wrapper around the original library. In the real implementation, the binary library is linked into the container function at build time.
/* adder_server.c *************************************************************************** * This is the server program for the basic IPF/PA mixed mode * DCE RPC application. It must be built on an HP-UX 11.x PA system. It * will register the interface named "adder" with the local RPC runtime * and with the endpoint mapper daemon (rpcd) on the local host. It then * listens for incoming requests and serves each request in a separate * thread. The container function (see adder_container.c) is invoked to * serve the requests after the inbound arguments are unmarshalled. * *************************************************************************** */ /* * (c) Copyright 1992, 1993, 1994 Hewlett-Packard Co. */ /* * @(#)HP DCE/9000 1.7.1 * @(#)Module: server.c $Revision: 1.1.6.2 $ $Date: 1993/07/08 00:13:10 $ */ /* */ #include #include <pthread.h> <stdlib.h> /* POSIX threads facility */ /* Standard POSIX defines */

Mixed-mode on Itanium-based Platforms

13

#include #include #include #include #include

<strings.h> <stdio.h> <dce/dce_error.h> "hpdcemacros.h" "adder.h"

/* str*() routines */ /* Standard IO library */ /* DCE error facility */ /* Common defs for this app */ /* Output from adder.idl */ /* Initialize for server */

#ifdef TRACING tr_handle_t * tr_handle = NULL; #endif /* TRACING */

void main(int argc, char *argv[]) { rpc_binding_vector_t *bvec; /* used to register w/runtime */ error_status_t st, _ignore; /* returned by DCE calls */ dce_error_string_t dce_err_string; /* text describing error code */ ndr_char *string_binding; /* printable rep of binding */ int i; /* index into bvec */ #ifdef TRACING /* * Initialize tracing. */ if (tr_handle == NULL) { char trace_name_buf[40]; sprintf(trace_name_buf, "%s-%d", trace_name, getpid()); tr_handle = tr_init("TR_DCE_SERVER", /* environment variable name */ NULL, /* selector level defaults */ NULL, /* filename for output */ trace_name_buf); /* prefix string in output */ if (tr_handle == NULL) { fprintf(stderr, "Unable to initialize tracing interface!\n"); } } #endif /* TRACING */ rpc_server_use_protseq((unsigned char *)"ip", /* prot seq to listen on */ rpc_c_protseq_max_calls_default, &st); /* error status for this call */ if (st != rpc_s_ok) { dce_error_inq_text(st, dce_err_string, (int *)&_ignore); PRINT_FUNC(PRINT_HANDLE, "Cannot use protocol sequence ip: %s\n", dce_err_string); exit(1); } rpc_server_register_if(adder_v1_0_s_ifspec, /* generated interface spec */ NULL, /* No type UUIDs */ NULL, /* Use supplied epv */ &st); /* error status for this call */ if (st != rpc_s_ok) { dce_error_inq_text(st, dce_err_string, (int *)&_ignore); PRINT_FUNC(PRINT_HANDLE,"Cannot register interface with runtime: %s\n", dce_err_string); exit(1); } rpc_server_inq_bindings(&bvec, /* runtime's binding vector */ &st); /* error status for this call */ if (st != rpc_s_ok) { dce_error_inq_text(st, dce_err_string, (int *)&_ignore); PRINT_FUNC(PRINT_HANDLE, "Cannot get bindings: %s\n", dce_err_string); exit(1); } else PRINT_FUNC(PRINT_HANDLE, "Bindings:\n"); for (i = 0; i < bvec->count; i++) { rpc_binding_to_string_binding(bvec->binding_h[i], /* a binding handle */ &string_binding,/* returned string form */ &st); /* error status for this call */ if (st != rpc_s_ok) { dce_error_inq_text(st, dce_err_string, (int *)&_ignore); PRINT_FUNC(PRINT_HANDLE, "Cannot get string binding: %s\n", dce_err_string); } else PRINT_FUNC(PRINT_HANDLE, " %s\n", string_binding); rpc_string_free(&string_binding, &_ignore); } rpc_ep_register(adder_v1_0_s_ifspec, bvec, Mixed-mode on Itanium-based Platforms /* generated interface spec */ /* runtime's binding vector */

14

NULL, /* no objects supported */ (unsigned_char_t *) sleeper_description, &st); /* error status for this call */ if (st != rpc_s_ok) { dce_error_inq_text(st, dce_err_string, (int *)&_ignore); PRINT_FUNC(PRINT_HANDLE, "Cannot register with endpoint map: %s\n", dce_err_string); exit(1); } PRINT_FUNC(PRINT_HANDLE, "Listening...\n"); rpc_server_listen(rpc_c_listen_max_calls_default, &st); /* error status for this call */ if (st != rpc_s_ok) { dce_error_inq_text(st, dce_err_string, (int *)&_ignore); PRINT_FUNC(PRINT_HANDLE, "Listen returned with error: %s\n", dce_err_string); } else PRINT_FUNC(PRINT_HANDLE, "Stopped listening...\n"); /************************************************************************ * IMPORTANT NOTE: We will probably never reach here. If you interrupt * the server with an asynchronous signal, such as a ^C (or SIGINT) from * the keyboard or a "kill <PID>" (a SIGTERM signal), it will cause the * process to exit; it will not reach here. See the lookup sample * application for code that is able to properly clean up after the * listen call. ************************************************************************/ PRINT_FUNC(PRINT_HANDLE, "Unregistering endpoints and interface...\n"); rpc_ep_unregister(adder_v1_0_s_ifspec, bvec, NULL, &_ignore); rpc_binding_vector_free(&bvec, &_ignore); rpc_server_unregister_if(adder_v1_0_s_ifspec, /* IDL-generated ifspec */ NULL, /* No object UUID */ &_ignore); /* ignore any errors */ exit(0); } /* /* /* /* IDL-generated ifspec */ this server's bindings */ no object UUIDs supported */ ignore any errors */

The following code is the container function, which makes the call to the legacy routine.
/* adder_container.c *************************************************************************** * This is the server-side RPC container function; this is the function that * actually implements the remote procedure defined in the .idl file. The * server stub (called by the RPC runtime) calls this function when an RPC * request comes in for this interface. * * The container function takes the arguments defined in the .idl file, * performs its function and returns results as defined in the .idl file. *************************************************************************** */ /* * (c) Copyright 2002 Hewlett-Packard Co. */ /* * @(#)HP DCE/9000 1.7.1 * @(#)Module: container.c $Revision: 1. $ $Date: 2002/01/18 00:11:59 $ */ /* */ #include #include #include #include <stdlib.h> <stdio.h> "hpdcemacros.h" "adder.h" /* Standard POSIX defines */ /* Standard IO library */ /* Common defs for this app */ /* Output from the idl compiler */

/* * The container function simply accepts the arguments passed from the client * RPC call, and returns an results. Since the .idl file speficies use of explicit * binding the manager must take a binding handle as its first argument. Mixed-mode on Itanium-based Platforms

15

* * Note: the code in this container function must be (and is) reentrant as it * may be running simultaneously in multiple server threads. */ long remote_adder ( /* [in] */ handle_t h, /* Use explicit binding */ /* [in] */ long xin /* Input parameter */ ) { long result; /* * Insert the call to the legacy library routine here */ result=adder (xin); return result; }

Another piece of this system is the IDL (Interface Definition Language) file that defines the Remote Procedure Call interface. It contains a Unique Universal Identifier (UUID) that must be generated for each interface created. The UUID is created by the uuidgen command and copied into the file.
/* adder.idl **************************************************************************** * This .idl file declares an interface with a set of remotely-callable * procedures. This file is compiled by the idl compiler into a C interface * declaration (.h) and a C client stub (_cstub.c) and server stub * (_sstub.c) that interface with the RPC runtime. ****************************************************************************/ /* * (c) Copyright 1992, 1993, 1994 Hewlett-Packard Co. */ /* * @(#)HP DCE/9000 1.7.1 * @(#)Module: sleeper.idl $Revision: 1.1.6.2 $ $Date: 1993/07/08 00:14:24 $ */ /* */ /* * This definition declares the interface for this application and * associates it with a globally (universally) unique identifier, or UUID. * The RPC runtime uses the UUID to identify this interface. If you * leverage this code, BE SURE TO CHANGE THE UUID! Do this by running the * program "uuidgen" and putting the uuidgen output in place of the one * supplied. Failure to do this may cause bizarre results. */ [uuid(6fae8c4e-2163-11d6-8328-080009046d0b), /* NOTE: CHANGE THIS!!! */ version(1.0)] interface adder { long remote_adder ( [in] handle_t h, /* Use explicit binding */ [in] int intin /* Input integer */ ); }

The final piece of this system is the Makefile for creating it.
# Makefile # # (c) Copyright 2002 Hewlett-Packard Co. # # @(#)HP DCE/9000 1.7.1 # @(#)Module: Makefile $Revision: 1.0 $ $Date: 2002/01/17 16:19 UTC $ # # # The application name: O_P_L=adder # The Optional HP-AnsiC compiler is required for HP DCE/9000 Mixed-mode on Itanium-based Platforms

16

# application development (see: /opt/ansic) CC = /opt/ansic/bin/cc DEBUG INCENV ANSI_FLAGS THREADS_FLAGS HP_FLAGS CFLAGS LIBS LDFLAGS PROGRAMS server_OFILES client_OFILES IDLFLAGS IDLFILES IDLGEN IDL all: objects: fresh: clean:; rm -f ${server_OFILES} ${client_OFILES} ${PROGRAMS} ${IDLGEN} clobber: clean rm -f a.out core ERRS make.out *~ ${O_P_L}_server: ${server_OFILES} $(CC) ${LDFLAGS} ${server_OFILES} ${LIBS} -o $@ ${O_P_L}_client: ${client_OFILES} $(CC) ${LDFLAGS} ${client_OFILES} ${LIBS} -o $@ ${O_P_L}_cstub.c ${O_P_L}_sstub.c ${O_P_L}.h: $(IDL) ${IDLFLAGS} ${IDLFILES} ${IDLFILES} = -g = -I. -I/usr/include -I/opt/dce/include = -Aa = -D_REENTRANT -D_POSIX_C_SOURCE=199506L = -D_HPUX_SOURCE = ${ANSI_FLAGS} ${THREADS_FLAGS} ${HP_FLAGS} ${INCENV} = -lpthread -ldcekt = -lm -L/opt/dce/lib = ${O_P_L}_server ${O_P_L}_client = ${O_P_L}_sstub.o ${O_P_L}_container.o ${O_P_L}_server.o ${O_P_L}.o = ${O_P_L}_cstub.o ${O_P_L}_client.o = = = = -keep c_source ${INCENV} ${O_P_L}.idl ${O_P_L}.h ${O_P_L}_*stub.c idl

objects ${PROGRAMS} ${server_OFILES} ${client_OFILES} clean all

${O_P_L}_cstub.o ${O_P_L}_sstub.o ${O_P_L}_container.o server.o client.o: ${O_P_L}.h ${O_P_L}_container.o ${O_P_L}_server.o ${O_P_L}_client.o: hpdcemacros.h

The shared-memory/semaphore example: This example consists of two programs, each of which consists of two parts. The client program is the original main program (addtypes_client.c), modified to call a resource cleanup routine. The cleanup routine and the communication stub (called by the legacy routine name) are in a second file (addtypes_shm.c). This file is compiled separately and then linked into the original main at link time to create the client program. The server program consists of the communication stub (addtypes_server.c) and the linked-in legacy library (addtypes.o). A more detailed description of the code is on the Web site. This is the client (original main program) code. It has been modified only at its termination to call a routine to release the resources used for inter-process communication.
/* addtypes_client.c /* The modified 'main' program that calls the legacy library /* The legacy library is the 'addtypes' routine main() { long double myldouble=99999999990000000.0; double mydouble=9900000.0; long mylong=99000; int myint=990; char mychar='9'; long double result,addtypes(); result=addtypes printf ("Client myint++; result=addtypes printf ("Client (mychar,myint,mylong,mydouble,myldouble); Result = %18.1Lf\n",result); (mychar,myint,mylong,mydouble,myldouble); Result = %18.1Lf\n",result); */ */ */

/* ***** the modification to the original program ***** */ return_resources(); exit(0); } Mixed-mode on Itanium-based Platforms

17

This is the client-side communication stub. It creates the shared-memory segment and semaphores, forks the server program, and then uses the shared memory and semaphores to communicate with the server. It also contains the routine (release_resources) called at the end of the main program.
/* /* /* /* /* addtypes_shm.c This is the client side communication stub for the 'addtypes' function. It masquerades as the original function, accepting its arguments and returning the expected result. */ */ */ */ */

#include <sys/sem.h> #include <sys/shm.h> #include <stdio.h> #define SHMSZ 2048 /* Make the shared memory and semaphore ids, the server pid, and the /* shared memory pointer available to all of the code in this source /* module. static int shmid, semid; static pid_t cpid; static char *shmptr; */ */ */

long double addtypes(char mychar,int myint,long mylong,double mydouble, long double myldouble) { extern pid_t cpid; extern int shmid, semid; extern char *shmptr; /* flag for first time through */ static int first_time=1; char semchar[20]="",shmchar[20]=""; char *charptr; long double result; key_t shmkey, semkey; struct sembuf sops[1]; int status,flag; void exit(int status); /* The first time this function is called, it must set up the /* infrastructire - shared memory, semaphores, and server program if ( first_time ) { first_time=0; /* First create the semaphores and shared memory segment */ semkey=IPC_PRIVATE; flag=0660; semid=semget (semkey,2,flag); if ( semid < 0 ) perror ("Semget failed"); /* DEBUG printf ("Semget worked : SEMID=%d\n",semid); */ /* Initialize the semaphores to zero */ status=semctl (semid,0,SETVAL,0); if ( status < 0 ) perror ("Setval 0 failed"); status=semctl (semid,1,SETVAL,0); if ( status < 0 ) perror ("Setval 1 failed"); shmkey=IPC_PRIVATE; flag=0660; shmid=shmget (shmkey,SHMSZ,flag); if ( shmid < 0 ) perror ("Shmget failed"); /* DEBUG printf ("Shmget worked : SHMID=%d\n",shmid); */ shmptr=shmat (shmid,(char *)0,0); if ( shmptr < (char *)0 ) perror ("shmat failed"); /* Before the first call to the routine, create the server */ /* Pass it the semaphore and shared memory id's in ASCII */ strcat(semchar,ltoa((long)semid)); strcat(shmchar,ltoa((long)shmid)); cpid=fork(); switch (cpid) { case -1: exit(-1); break; case 0: status=execl("./addtypes_server","addtypes_server",semchar,shmchar,NULL); if (status <0) perror("Exec failed "); } } Mixed-mode on Itanium-based Platforms */ */

18

/* Now executing in the main flow in the parent */ /* Set the data into shared memory by incrementing a pointer by the size */ /* of the preceding value. Start from the biggest data type and work */ /* down. Then set the semaphore */ charptr=(char *)shmptr; /* leave space for a long double - return value */ charptr=charptr + sizeof (long double); /* Store the long double in shared memory */ *(long double *) charptr=myldouble; charptr=charptr + sizeof (long double); /* Store the double */ *(double *) charptr=mydouble; charptr=charptr + sizeof (double); /* Store the long */ *(long *) charptr=mylong; charptr=charptr + sizeof (long); /* Store the int */ *(int *) charptr=myint; charptr=charptr + sizeof (int); /* Store the char */ *charptr=mychar; /* Set the semaphore values */ sops->sem_num=0; sops->sem_op=1; sops->sem_flg=0; status=semop (semid,sops,1); if ( status < 0 ) printf ("Set failed\n"); /* Wait for the return semaphore */ sops->sem_num=1; sops->sem_op=-1; sops->sem_flg=0; status=semop (semid,sops,1); if ( status < 0 ) printf ("Get failed\n"); /* pick up the returned value from the start of shared memory */ result=*(long double *) shmptr; printf ("returned value in stub is %18.1Lf\n",result); return (result); } /* This routine is called at the termination of the process /* to release the resources used void return_resources() { extern pid_t cpid; extern int shmid, semid; extern char* shmptr; int status; printf ("Returning resources\n"); /* kill the server process */ if((kill(cpid,1)) <0) perror("Kill server failed"); /* Detach from the shared memory segment and destroy it */ status = shmdt (shmptr); if ( status < 0 ) perror ("Shmdetach failed"); status = shmctl (shmid,IPC_RMID,0); if ( status < 0 ) perror ("Shmdestroy failed"); /* Remove the semaphores */ status = semctl (semid,IPC_RMID,0); if ( status < 0 ) perror ("Semdestroy failed"); return; } */ */

Mixed-mode on Itanium-based Platforms

19

The server code, which picks up the input data out of shared memory, calls the legacy routine with it, places the return value back into shared memory, and signals the main program (client) by raising the outbound semaphore, as follows:
/* /* /* /* /* /* /* /* /* /* addtypes_server.c This is the 'server' side code for the 'addtypes' example It attachs to the shared memory segment and semaphores, then waits on the input semaphore to signal input data is ready for processing. It then takes the data out of shared memory, calls the legacy routine, places the returned value in shared memory, and raises the output semaphore. It then goes back and waits on the input semaphore. It gets the shared memory and semaphore IDs from command line parameters, passed from the client program. */ */ */ */ */ */ */ */ */ */

#include <sys/sem.h> #include <sys/shm.h> main(argc,argv) int argc; char*argv[]; { int status, semid, shmid; char *shmptr; char *charptr; struct sembuf sops[1]; char mychar; int myint; long mylong; double mydouble; long double myldouble; long double result,addtypes(); semid=atoi(argv[1]); shmid=atoi(argv[2]); /* DEBUG printf ("semid: %d Shmid %d\n",semid,shmid); */ /* attach to the shared memory segment */ shmptr = shmat (shmid,(char *)0,0); /* Loop and wait for input */ while (1) { printf ("Waiting in client\n"); /* Wait on the inbound semaphore */ sops[0].sem_num=0; sops[0].sem_op=-1; sops[0].sem_flg=0; status=semop (semid,sops,1); if ( status < 0 ) perror ("Wait failed "); /* Pick up the input values */ charptr=(char *)shmptr; /* Skip over the space for the result */ charptr=charptr + sizeof(long double); /* get the long double */ myldouble=*(long double *) charptr; charptr=charptr + sizeof(long double); /* get the double */ mydouble=*(double *) charptr; charptr=charptr + sizeof(double); /* get the long */ /* DEBUG printf ("Address of long: %x \n",charptr); */ mylong=*(long *) charptr; charptr=charptr + sizeof(long); /* get the int */ myint=*(int *) charptr; charptr=charptr + sizeof(int); /* get the char */ mychar=*charptr; /* Call the legacy routine */ result=addtypes (mychar,myint,mylong,mydouble,myldouble); /* Move the returned value to shared amaory */ *(long double*)shmptr=result; /* Set the outbound semaphore */ sops[0].sem_num=1; sops[0].sem_op=1; sops[0].sem_flg=0; status=semop (semid,sops,1); Mixed-mode on Itanium-based Platforms

20

if ( status < 0 ) perror ("Set failed "); } exit (0); }

The other piece of the server process is the legacy routine. It would normally exist in binary form only (thats the whole problem!), but we show the source here for completeness.
/* addtypes.c /* The legacy routine */ */

long double addtypes(char mychar,int myint,long mylong,double mydouble, long double myldouble) { char cbuff[2]; long double result; int mycharint; cbuff[0]=mychar; mycharint=atoi(cbuff); result=mycharint+myint+mylong+mydouble+myldouble; printf ("myldouble = %18.1Lf\n",myldouble); printf ("mydouble = %8.1f\n",mydouble); printf ("mylong = %d\n",mylong); printf ("myint = %d\n",myint); printf ("mycharint = %d\n",mycharint); printf ("Result = %18.1Lf\n",result); return (result); }

The final piece is the Makefile. This would exist in two partsone to create the client process on an Itanium-based platform, and the other to create the server process on the PA platform. It is shown here in one piece because the example was built on an Itanium 2-based system for testing, then moved to a PA system for creation of the server code to run under Aries.
# Makefile.template # # The application name: O_P_L=addtypes # The Ansi C compiler was used for this application development # (see: /opt/ansic) CC = /opt/ansic/bin/cc DEBUG INCENV ANSI_FLAGS HP_FLAGS CFLAGS LIBS LDFLAGS PROGRAMS server_OFILES client_OFILES all: objects: fresh: clean:; rm -f ${server_OFILES} ${client_OFILES} ${PROGRAMS} clobber: clean rm -f a.out core ERRS make.out *~ ${O_P_L}_server: ${server_OFILES} $(CC) ${LDFLAGS} ${server_OFILES} ${LIBS} -o $@ ${O_P_L}_client: ${client_OFILES} $(CC) ${LDFLAGS} ${client_OFILES} ${LIBS} -o $@ = = = = -g -I. -I/usr/include -Aa -D_HPUX_SOURCE +DD32

= ${ANSI_FLAGS} ${HP_FLAGS} ${INCENV} = = -lm = ${O_P_L}_server ${O_P_L}_client = ${O_P_L}_server.o ${O_P_L}.o = ${O_P_L}_client.o ${O_P_L}_shm.o objects ${PROGRAMS} ${server_OFILES} ${client_OFILES} clean all

Mixed-mode on Itanium-based Platforms

21

HP-UX 11i release names and release identifiers


With HP-UX 11i, HP delivers a highly available, secure,and manageable operating system that meets the demands of end-to-end Internet-critical computing. HP-UX 11i supports enterprise, mission-critical, and technical computing environments. HP-UX 11i is available on both PA-RISC systems and Itanium-based systems. Each HP-UX 11i release has an associated release name and release identifier. The uname (1) command with the -r option returns the release identifier. The following table shows the releases available for HP-UX 11i.

Table 1. HP-UX 11i releases


Release name HP-UX 11i v1 HP-UX 11i v1.5 HP-UX 11i v1.6 HP-UX 11i v2 Release identifier B.11.11 B.11.20 B.11.22 B.11.23 Supported processor architecture PA-RISC Intel Itanium Intel Itanium Intel Itanium

Copyright 2003 Hewlett-Packard Development Company, L.P. The information contained herein is subject to change without notice and is provided as is without warranty of any kind. The warranties for HP products and services are set forth in the express warranty statements accompanying such products and services. Nothing herein should be construed as constituting an additional warranty. HP shall not be liable for technical or editorial errors or omissions contained herein. Intel and Itanium are trademarks or registered trademarks of Intel Corporation in the U.S. and other countries and are used under license. 5981-7112EN, 06/2003

Mixed-mode on Itanium-based Platforms

22

Das könnte Ihnen auch gefallen