Part 1 of this series covered threads and synchronization mechanisms like semaphores and mutexes. This second installment looks closely at memory management, various IPC mechanisms including shared memory and pipes, and file management.
OS/2 offers developers a 32-bit, linear (flat) memory address space. It allocates, protects, and manipulates memory in terms of pages.
OS/2 applications allocate and manipulate memory in terms of memory objects, which consist of one or more pages. The smallest memory allocation is one page. But an application can suballocate a memory object in memory blocks whose size can range from 1 byte to the size of the memory object. When an application asks OS/2 to allocate memory, a linear address range is reserved. The range is not backed by physical memory until the memory is committed. Commitment assigns physical memory to the linear address range. An attempt to read from or write to uncommitted memory causes an access violation.
Linux also has a 32-bit flat memory address space. Unlike OS/2, however, each process in Linux sees a different set of linear addresses. Any request to allocate dynamic memory by a process results in a new range of linear addresses becoming a part of its address space. This range of addresses is referred to as a memory region. Allocation of physical memory is deferred to the point where it is actually referenced.
Table 1. Memory management mapping
| OS/2 | Linux | Classification |
| Allocation DosAllocMem DosSetMem Suballocation DosAllocMem DosSubSetMem DosSubAllocMem DosSubFreeMem DosSubUnsetMem | Page-aligned with memory protection posix_memalign mprotect Page-aligned without memory protection posix_memalign Non page-aligned without memory protection malloc/new | Context-specific |
| DosFreeMem | free delete (if allocated by new) | Mappable |
| DosAllocSeg | malloc/new | Mappable |
| DosFreeSeg | free/delete | Mappable |
The Classification column indicates whether the OS/2 construct is mappable or context-specific:
- Mappable:
The OS/2 construct can be mapped to the specified Linux construct(s) by closely examining the types, parameters, return codes, and the like. Both the OS/2 and Linux constructs provide similar functionality. - Context-specific:
The given OS/2 construct may or may not have an equivalent construct in Linux, or Linux may have more than one construct that provides similar functionality. In either case, the decision to use a specific Linux construct(s) depends on the application context.
Committing and suballocating memory
In OS/2, the recommended way to manage memory is to make a large memory allocation early in program execution, then to commit or suballocate memory as the need occurs. After the memory object is allocated, the application uses one of two ways to manage the memory object:
- Commit and decommit the memory as it is required (See Listing 1)
- Set up the memory object as a heap and suballocate memory from the heap (See Listing 2)
Committing and decommitting memory gives the application more control over the process, but the application will have to keep track of which pages are committed and which are not.
When suballocating memory from a heap, OS/2 keeps track of commitment and decommitment of physical memory pages, which means the application does not have to worry about it.
In Linux, the application need not worry about committing/decomitting
and suballocation. The same memory allocation system call
(malloc/posix_memalign) can be used for allocating memory of any size. The
memory allocator will always return the memory block from the free-list,
which best fits the required size. The operating system will take care of committing the
allocated memory.
Linux follows an optimistic memory allocation strategy. This means
that when malloc() returns non-NULL, there is no guarantee that the memory
really is available. In case it turns out that the system is out of
memory, one or more processes will be killed by the infamous OOM ("out of memory") killer.
The system call DosAllocMem() is used in OS/2 to allocate and reserve
a range of pages. The memory can be committed during allocation or later
by a call to DosSetMem():
APIRET DosAllocMem(PPVOID ppbBase, ULONG size, ULONG attr);
APIRET DosSetMem(PVOID pbBase, ULONG size, ULONG flag);
ppbBasepoints to the pointer to the allocated memorysizeindicates the requested size in bytesattris the set of flags describing the allocation attributes and desired access protectionflagis a set of flags specifying commitment or decommitment, and desired access protection
A committed page has its access controlled by an access protection
attribute. These protection attributes are read protection (PAG_READ),
write protection (PAG_WRITE), execute protection (PAG_EXECUTE), and guard
page protection (PAG_GUARD). Note that memory allocated by DosAllocMem is
always page-aligned and cannot be resized.
If an application wants to use suballocation, it must first allocate
memory by using DosAllocMem() and then set up the memory for suballocation
by calling DosSubSetMem(). The application can then use DosSubAllocMem() to allocate sections of the heap and the DosSubFreeMem() function to
release the memory. DosSubUnSetMem() ends the use of memory set up for
suballocation:
APIRET DosSubSetMem(PVOID pbBase, ULONG flag, ULONG size);
APIRET DosSubAllocMem(PVOID pbBase, PPVOID ppMemblock, ULONG size);
APIRET DosSubFreeMem(PVOID pbBase, PVOID pOffset, ULONG size);
APIRET DosSubUnsetMem(PVOID pbBase);
pbBasepoints to the memory already allocated byDosAllocMem()sizeis the size in bytesppMemblockis the pointer to a pointer to the memory block that will be allocated byDosSubAllocMem()pOffsetis the pointer to the memory block that will be freed byDosSubFreeMem()
In the context of migration, memory allocation in Linux falls into two types: page-aligned and non-page-aligned.
Page-aligned memory allocation with and without memory protection
The system call posix_memalign() can be used to allocate page-aligned
memory in Linux. By default, the memory area has PROT_READ | PROT_WRITE.
These memory protection attributes can be changed using the call
mprotect(). The library function free() can be used to free the memory
space allocated by posix_memalign():
int posix_memalign(void **memptr, size_t alignment, size_t size);
int mprotect(const void *addr, size_t size, int prot);
*memptrcontains the address of the allocated memory. The address of the allocated memory will be a multiple of alignment, which must be a power of two and a multiple ofsizeof(void *).addris the pointer to the allocated memory whose protection flags have to be changed bymprotect().protare the protection flags and include:PROT_NONE: the memory cannot be accessed at allPROT_READ: the memory can be readPROT_WRITE: the memory can be written toPROT_EXEC: the memory can contain executing code
Non page-aligned memory allocation without memory protection
OS/2, by default, gives a page-aligned memory. If page-aligned memory is
not a requirement and the default access protection of PROT_READ |
PROT_WRITE is sufficient, then malloc() can be used to allocate memory in
Linux, and free() can be used to free the allocated memory:
void *malloc(size_t size)
Alternatively, for applications coded in C++, the keyword new can be used to allocate memory and delete can be used to free the memory space. Make
sure that new is used along with the nothrow option so that if there is any error in
allocating memory, it does not throw an exception but rather returns a
NULL pointer.
OS/2 uses DosFreeMem() to release a previously allocated memory object
from the virtual address space of the subject process:
APIRET DosFreeMem(PVOID pbBase);
Linux uses free() to free the allocated memory:
void free(void *memptr)
The system call DosAllocSeg() can also be used to allocate memory
blocks up to 64K. Use DosMemAvail() to find the amount of available memory
that can be allocated, and DosFreeSeg() to free the memory. Parameter
size indicates the size in bytes (with a maximum of 64KB), selector is a
pointer to an integer to receive the selector, and flags must be zero.
USHORT BLXAPI DosAllocSeg (USHORT size, PSEL selector, USHORT flags)
USHORT BLXAPI DosFreeSeg(SEL selector)
Linux does not provide a segmented memory allocation model.
Listing 1. OS/2 macro memory management
// OS/2 example of memory allocation/de-allocation by committing
// and decommitting memory
APIRET ulrc;
PBYTE pb;
/* Allocate 16KB object */
ulrc = DosAllocMem((PVOID *) &pb, 2097152, PAG_READ | PAG_WRITE);
/* Commit 4KB */
ulrc = DosSetMem(pb, 4096, PAG_COMMIT);
strcpy(pb, "The memory object has just been used.");
printf("%s\n",pb);
/* De-Commit 4KB */
ulrc = DosSetMem(pb, 4096, PAG_DECOMMIT);
//Free the memory area
ulrc = DosFreeMem(pb);
|
Listing 2. OS/2 micro memory management
// OS/2 example of memory suballocation. It sets up 8192 bytes
// for suballocation and then allocates two small blocks of memory
APIRET ulrc;
PBYTE pbBase, pb1, pb2;
/* Allocate 8K object */
ulrc = DosAllocMem((PVOID *) &pbBase, 8192, fALLOC);
/* Set up object or suballocation */
ulrc = DosSubSetMem(pbBase, DOSSUB_INIT, 8192);
/* Suballocate 100 bytes */
ulrc = DosSubAllocMem(pbBase,(PVOID *) &pb1,100);
/* Suballocate 500 bytes */
ulrc = DosSubAllocMem(pbBase,(PVOID *) &pb2,500);
/* Free 1st suballocation*/
ulrc = DosSubFreeMem(pbBase, pb1, 100);
/* Suballocate 50 bytes */
ulrc = DosSubAllocMem(pbBase, (PVOID *) &pb1, 50);
/* Free all suballocation*/
ulrc = DosSubFreeMem(pbBase, pb1, 50);
ulrc = DosSubFreeMem(pbBase, pb1, 500);
/* End the suballocation */
ulrc = DosSubUnsetMem(pbBase)
//Free the memory area
ulrc = DosFreeMem(pbBase);
|
SEL
selector;
/* Allocate a 4KB block */
if (DosAllocSeg(4096, &selector, 0) != 0) {
printf("DosAllocSeg()
failed\n");
return
1;
}
// print the allocated memory address
printf("4 Kb block allocated at :
%04X\n",selector);
// Free the memory segment
if (DosFreeSeg(selector) != 0) {
printf("DosFreeSeg() failed - selector = %04X\n",
selector);
return
1;
}
|
Listing 4. Linux page-aligned memory management
// Page-aligned memory allocation with memory write protection
int main (void)
{
char** ppcBuf; /* Pointer to memory
object */
int ret = 0; /* Return code
*/
//allocate page-aligned memory area. By default both read and write
//are allowed. If that's the requirement then no need to use
mprotect
ret = posix_memalign(ppcBuf, 4096, 4096);
//write protect the memory
ret = mprotect(*ppcBuf, 4096, PROT_READ);
//Free the memory area
free(*ppcBuf);
return 0;
}
|
Listing 5. Linux non-page aligned memory management
// Non page-aligned memory allocation with default memory protection
// both read and write allowed
#define USE_MALLOC 1
char *pcBaseAddress = NULL;
if (USE_MALLOC == 1) {
// Allocate memory using malloc
pcBaseAddress = (char *) malloc(4096L);
}
else {
// Allocate memory using new
pcBaseAddress = new (nothrow) char[4096];
}
if (pcBaseAddress == NULL) {
return 1;
}
if (USE_MALLOC == 1) {
// free the memory space
free(pcBaseAddress);
}
else {
// free memory space using delete
delete [] pcBaseAddress;
}
|
Table 2. Shared memory mapping
| OS/2 | Linux | Classification |
| Named Shared Memory DosAllocSharedMem DosGetNamedSharedMem |
shmget shmat | Mappable |
| DosFreeMem |
shmdt shmctl | Mappable |
Shared memory is memory that two or more applications can read from and write to. Shared memory is prepared in such a way that any application can receive a pointer to the memory and access the data. Applications must explicitly request access to shared memory; the shared memory is protected from applications that are not granted access.
In OS/2 has two kinds of shared memory: named and unnamed.
With named shared memory, any application that knows the name of the shared memory can access it.
With unnamed shared memory, a pointer to the shared memory must be passed from the process that created the shared memory to the process being given access through some IPC mechanism.
In Linux, shared segments are always named (identified by a key). Unnamed shared segments are not supported.
In OS/2, the call DosAllocSharedMem() allocates a shared memory object
within the virtual address space. Allocating a shared memory object causes
the creation of an object that describes a region of memory that can be
shared. The virtual address space for a shared memory object is reserved
at the same location in the virtual address space of every process. This
allows any process to gain access to the shared object at the same virtual
address where it was originally allocated:
APIRET DosAllocSharedMem(PPVOID ppb, PSZ pszName, ULONG cb, ULONG
flag);
ppbis a pointer to a variable that will receive the base address of the allocated range of pagespszNameis a shared memory name; for example, \SHAREMEM\PUBLIC.DATcbis size, in bytes, of the shared memory object to allocateflagis the allocation attribute and desired access protection flags
Other processes can access the shared memory object through the shared
memory name using DosGetNamedSharedMem(). To specify the name for the
shared memory object, the name string provided must include the prefix
"\SHAREMEM\". Getting a named shared memory object allocates the virtual
address (of the shared memory object) in the virtual address space of the
process:
APIRET DosGetNamedSharedMem(PPVOID ppb, PSZ pszName, ULONG flag);
ppbis the pointer to a variable that will receive the base address of the shared memory objectpszNameis the address of the name string associated with the shared memory objectflagis the set of attribute flags that specify the desired access protection for the shared memory object
The system call DosFreeMem() is used to free the shared memory object.
In Linux, the system calls shmget() and shmat() must be used together
to create or obtain access to the shared memory. shmget() returns the
identifier of the shared memory segment associated to the value of the
argument key. This key can be used to simulate the behaviour of the
pszName parameter of DosAllocSharedMem. If shmflg is IPC_CREAT, it will
create a new shared memory segment of a size equal to a multiple of
PAGE_SIZE (it is rounded up to the next whole page). If this flag is not
used, then shmget() will find the existing segment associated with key:
int shmget (key_t key , int size , int shmflg );
The system call shmat() attaches the shared memory segment identified
by shmid to the address space of the calling process. shmid is returned
by the call to shmget(), and the attaching address is specified by
shmaddr. Setting it to zero will cause the operating system to choose the address.
shmflg are the access protection flags. Set to SHM_RDONLY for read-only
access, zero otherwise:
void *shmat ( int shmid, void *shmaddr, int shmflg);
To free shared memory, first make a call to shmdt() to detach the
shared memory segment located at the address specified by shmaddr from the
calling process's data segment:
int shmdt (void *shmaddr);
To completely free the shared memory, call shmctl() with the command
IPC_RMID. This command can be executed only by a process that has an
effective user ID equal to the value of shm_perm.cuid or shm_perm.uid in
the data structure associated with shmid; or to a process that has
superuser privileges. For freeing the memory, set buf to NULL:
int shmctl (int shmid, int cmd, struct shmid_ds *buf);
Listing 6. OS/2 shared memory
/* This example allocates named shared memory as Read/Write, and commits it
during allocation. It also writes to it, and shows how other processes can
access it. */
int main (VOID)
{
PVOID pvShrObject = NULL; /* Pointer to shared memory
object */
PSZ pszMemName = "\\SHAREMEM\\MYTOOL\\APPLICAT.DAT";
/* Object name */
PVOID pvAltObject = NULL; /* Alternate pointer to shared
memory */
APIRET rc = NO_ERROR; /* Return code */
ULONG ulObjSize = 1024; /* Size (system rounds to 4096
- page body */
rc = DosAllocSharedMem(&pvShrObject, /* Pointer to object
pointer */
pszMemName, /* Name for shared
memory */
ulObjSize, /* Desired size of
object */
PAG_COMMIT | /* Commit
memory */
PAG_WRITE ); /* Allocate memory as
read/write */
if (rc != NO_ERROR) {
printf("DosAllocSharedMem error: return code = %u\n",rc);
return 1;
}
strcpy(pvShrObject, "Write your shared application data here.");
rc = DosGetNamedSharedMem(&pvAltObject, /* Pointer to pointer
of object */
pszMemName, /* Name of shared
memory */
PAG_READ); /* Want read-only
access */
if (rc != NO_ERROR) {
printf("DosGetNamedSharedMem error: return code = %u\n",rc);
return 1;
}
printf("Shared data read was \"%s\"\n",pvAltObject);
rc = DosFreeMem(pvShrObject);
if (rc != NO_ERROR) {
printf("DosFreeMem error: return code = %u\n",rc);
return 1; }
return NO_ERROR;
}
|
Listing 7. Linux shared memory
// In the following example, Process 1 allocates the shared memory
// and writes its process id to the shared memory. Process 2 obtains
// access to the memory and prints out process id of Process 1.
// In production code, you would also need to include error-checking;
// this code sample assumes that all calls are successful.
Process 1
#include <sys/types.h>
#include <sys/shm.h>
#define APP_SHARED_MEMORY 0x20
typedef struct {
int iPID;
} shared_t;
shared_t *shared_mem; // The address of the shared memory
int iShmId; // The shared mem identifier
// The shared memory is allocated, It is indentified by the key
// APP_SHARED_MEMORY. This same key must be used by process 2
// to obtain access to the shared memory
iShmId = shmget (APP_SHARED_MEMORY, sizeof(shared_t), IPC_CREAT);
shared_mem = (shared_t *) shmat ( iShmid, 0, 0); // Obtain the shared
memory address
shared_mem-> iPID = getpid(); // Write the process
id to the shared mem
// Wait for a signal or do some processing
:
:
shmdt(shared_mem); // Detach the shared memory reference
Process 2
#include <sys/ipc.h>
#include <sys/shm.h>
#define APP_SHARED_MEMORY 0x20
typedef struct {
int iPID;
} shared_t;
shared_t *shared_mem; // The address of the shared memory
int iShmId; // The shared mem identifier
// Obtain the id of shared memory indentified by key APP_SHARED_MEMORY
iShmId = shmget (APP_SHARED_MEMORY, sizeof(shared_t), 0);
shared_mem = (shared_t *) shmat ( iShmid, 0, 0); // Obtain the shared
// memory address
// print out the process id of process 1
printf ("Process 1 PID %d\n" , shared_mem-> iPID);
// Detach and remove the shared memory
shmdt(shared_mem);
shmctl(iShmId, IPC_RMID, NULL);
:
:
|
In OS/2, DosQuerySysInfo is used to get the total physical memory:
APIRET DosQuerySysInfo (istart, ilast, pBuf, cbBuf)
istartis the ordinal of the first system variable to returnilastis the ordinal of the last system variable to returnpBufis the address of the data buffer where the system returns the variable valuescBufis the length in bytes of the data buffer
The system call DosQuerySysInfo returns the values of static variables.
Common parameters include:
QSV_TOTPHYSMEM: the total number of bytes of physical memory in the system.QSV_TOTRESMEM: the total number of bytes of resident memory in the system.QSV_TOTAVAILMEM: the maximum number of bytes of memory that can be allocated by all processes in the system.
On Linux, the system call sysinfo is used to get the total physical memory:
int sysinfo (struct sysinfo *info )
Table 3. System information mapping table
| OS/2 | Linux | Classification |
| DosQuerySysInfo | sysinfo | Mappable |
Prior to Linux 2.3.16, sysinfo gave sizes in bytes and returned
information in the following structure:
Listing 8. Total physical memory, prior to Linux 2.3.16
struct sysinfo {
long uptime; /* Seconds
since boot */
unsigned long loads[3]; /* 1, 5, and 15 minute
load averages */
unsigned long totalram; /* Total usable main
memory size */
unsigned long freeram; /* Available memory
size */
unsigned long sharedram; /* Amount of shared
memory */
unsigned long bufferram; /* Memory used by
buffers */
unsigned long totalswap; /* Total swap space
size */
unsigned long freeswap; /* swap space still
available */
unsigned short procs; /* Number of current
processes */
char _f[22]; /* Pads structure
to 64 bytes */
};
|
From Linux 2.3.23 (on i386) and 2.3.48 (all architectures), sizes are
given as multiples of mem_unit bytes, and the structure is:
Listing 9. Total physical memory, from Linux 2.3.23 (on i386) and 2.3.48 (all architectures)
struct sysinfo {
long uptime; /* Seconds
since boot */
unsigned long loads[3]; /* 1, 5, and 15 minute
load averages */
unsigned long totalram; /* Total usable main
memory size */
unsigned long freeram; /* Available memory
size */
unsigned long sharedram; /* Amount of shared
memory */
unsigned long bufferram; /* Memory used by
buffers */
unsigned long totalswap; /* Total swap space
size */
unsigned long freeswap; /* swap space still
available */
unsigned short procs; /* Number of current
processes */
unsigned long totalhigh; /* Total high memory
size */
unsigned long freehigh; /* Available high
memory size */
unsigned int mem_unit; /* Memory unit size in
bytes */
char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding
for libc5 */
};
|
Listing 10. Physical memory size in OS/2
usigned long ulPhysMem;
// Get the total number of bytes of physical memory in this system.
DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, &ulPhysMem,
sizeof(ulPhysMem));
// ulPhysMem will have the total physical memory in bytes
printf("The total physical memory in bytes is %ld\n", ulPhysMem);
|
Listing 11. Physical memory size in Linux
struct sysinfo s_info;
....
sysinfo(&s_info);
// the sysinfo structure has the total physical memory of the system.
printf(" The total physical memory available is %ld\n in Bytes",
(s_info.totalram * mem_unit));
|
Pipes are used for interprocess communications in both OS/2 and Linux. Processes can write to or read from the pipe as if it were standard output or standard input. Pipes are bi-directional. OS/2 returns a single handle when a pipe is created, and this handle is used for bi-directional communication between processes. Linux pipes are also bi-directional, but they return two handles: a read handle and a write handle.
Consider the following points when mapping pipes:
- Size
In OS/2, pipe size can be specified with a parameter. On Linux, the kernel sets the pipe size to be 4096 bytes, and this cannot be changed by a user application. - Type
Are the pipes named or unnamed? Unnamed pipes behave the same way in OS/2 and on Linux. Named pipes on OS/2 allows processes from different machines to communicate with each other. Linux restricts such communication to a single machine. To achieve OS/2 behavior on Linux, you can use client-server programming techniques (TCP/IP Sockets).
Table 4. Pipes mapping table
| OS/2 | Linux | Classification |
|
DosCreatePipe DosCreateNPipe (Server side) DosConnectNPipe (Server Side) DosOpen (Client side) |
pipe mkfifo open | Context-specific |
|
DosWrite DosRead |
write read | Mappable |
|
DosDisconnectNPipe (Server) DosClose (Client) | close | Context-specific |
In OS/2, the system call DosCreatePipe() is used to create a pipe:
APIRET DosCreatePipe (HFILE hReadPipe, HFILE hWritePipe, ULONG cb)
hReadPipeandhWritePipeare pointers to the read and write handle of the pipecbis used to set the size of the pipe
In Linux, the system call pipe is used to create an unnamed pipe:
int pipe (int filedes[])
filedes[0]andfiledes[1]are pointers to the read and write handles of the pipe.
In OS/2, named pipes are used to communicate between related or unrelated processes running on the same machine -- or even on different machines. One process (the server process) creates a named pipe and connects to it. The client processes then connect to the other end of the pipe and then pass data back and forth by reading from and writing to the pipe.
The system call DosCreateNPipe() is used to create a named pipe in
OS/2:
APIRET DosCreateNPipe (PSZ pszName, PHPIPE pHpipe, ULONG openmode,
ULONG pipemode, ULONG cbOutbuf, ULONG cbInbuf, ULONG msec)
pszNameis the ASCII name of the pipepHpipeis the pipe handleopenmodeis the set of flags defining the mode in which to open the pipepipemodeis the set of flags defining the pipe modecbOutbufandcbInbufare the number of bytes to allocate for the outbound (server to client) and inbound (client to server) buffermsecis the maximum time to wait in milliseconds for that named pipe instance to become available
The system call DosConnectNPipe() is used by the server process to
connect to the pipe on one end. This puts the pipe to listen mode, and
client processes can now access the pipe using DosOpen():
APIRET DosConnectNPipe (HPIPE hpipe)
hpipeis the pipe handle returned byDosCreateNPipe()
The system call DosOpen() allows the client process to gain access to
the named pipe created by the server in listen mode:
APIRET DosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, ULONG
cbFile,ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode,
ULONG peaop2)
pszFileNameis the ASCII value of the named pipe's namepHfcontains the handle to the named pipe.
In Linux, named pipes (FIFOs) are used to communicate between
processes running on the same machine. mkfifo() is used to create a named
pipe (FIFO) on Linux. After creating the FIFO, the open() system call is
used to open the FIFO and further read/write operations can be performed:
int mkfifo(const char * pathname , mode_t mode )
pathnameis the name of the named pipemodespecifies the FIFO's permissions
In Linux, open() is used to open the FIFO, and the filename is the
ASCII name of the file (FIFO):
int open (const char * filename, int flags )
int open (const char * filename, int flags, mode_t mode)
Reading and writing data using pipes
In OS/2, the DosRead() system call is used to read data from named and
unnamed pipes alike. hFile is the handle to the read end of the pipe:
APIRET DosRead (HFILE hFile, PVOID pBuffer, ULONG cbRead, PULONG
pcbActual)
In Linux, the read() system call is used to read data from the pipe,
while fd is the handle to the read end of the pipe:
size_t read(int fd, void *buf, size_t count)
In OS/2, the DosWrite() system call is used to write to the pipe.
hFile is the handle to the write end of the pipe:
APIRET DosWrite(HFILE hFile, PVOID pBuffer, ULONG cbWrite, PULONG
pcbActual )
In Linux, the write() system call is used to write data to the pipe.
fd is the handle to the write end of the pipe:
ssize_t write(int fd, const void *buf, size_t count)
For named pipes in OS/2, the server process uses DosDisConnectNPipe() to disconnect a client process. Generally, client processes first issues
DosClose() to close the pipe and then the server disconnects the pipe. But
the server can disconnect before the client does, forcing the client to
close the pipe. For unnamed pipes, DosClose() alone is sufficient to close
the connection:
APIRET DosDisConnectNPipe (HPIPE hpipe)
APIRET DosClose (HPIPE hpipe)
hpipeis the handle to the read/write end of the pipe
On Linux, a close call is used to close the
FIFO. fd is the handle to the read/write end of the FIFO:
int close (int fd)
Listing 12. Unnamed pipes in OS/2
/* In this example, thread 1 creates an unnamed pipe, and writes to the pipe. Thread 2 reads the data once it becomes available. */ HFILE ReadHandle = 0; /* Read handle of pipe */ HFILE WriteHandle = 0; /* Write handle of pipe */ ULONG PipeSize = 4096; /* Size of pipe */ APIRET rc = NO_ERROR; /* API return code */ Thread 1 // Create pipe rc = DosCreatePipe(&ReadHandle, &WriteHandle, PipeSize); // get access to the pipe ... // Write to pipe rc = DosWrite(&WriteHandle,&cBuf, &len, &ulWrote); ... // Close write handle rc = DosClose(&WriteHandle); Thread 2 // wait for the data to come // Read from pipe rc = DosRead(&ReadHandle, &cBuf, &ulLen, &ulRead); .. // Close read handle rc = DosClose(&ReadHandle); |
Listing 13. Named pipes in OS/2
// Client program
// This client program opens a named pipe, and connects to the
// server. It writes messages to the pipe and closes the pipe
// when it has no more data to write.
# define PIPE_NAME \\PIPE\\EXAMPLE
#define BUF_SIZE 255
/* Pipe handle */
HFILE PipeHandle = NULLHANDLE;
ULONG bytes = 0;
ULONG Action = 0;
APIRET retCode = NO_ERROR; //Return code
CHAR message[BUF_SIZE +1] = ""; /* Message buffer */
// Open the pipe
rc = DosOpen(PIPE_NAME,
&PipeHandle,
&Action,
0, 0,
FILE_OPEN,
OPEN_ACCESS_READWRITE |
OPEN_SHARE_DENYREADWRITE |
OPEN_FLAGS_FAIL_ON_ERROR, NULL);
do {
rc = DosWrite(PipeHandle, message, strlen(message), &bytes);
....
....
// end of message
done = 1;
} while (!done);
// close the write handle of PIPE
rc = DosClose(PipeHandle);
// Server program
// This Server program opens a named pipe, and waits for a client
// connection. It writes messages to the pipe, and closes the pipe
// when it finds no data to write
# define PIPE_NAME \\PIPE\\EXAMPLE
#define BUF_SIZE 255
HPIPE PipeHandle = NULLHANDLE;//Pipe handle
ULONG ulBytes = 0;//Bytes read /written
CHAR message[BUF_SIZE + 1] = ""; /* Input/Output
buffer */
APIRET retCode = NO_ERROR; /* Return code */
// create a named pipe
retCode = DosCreateNPipe(PipeName, /* Name of pipe to
create */
&PipeHandle, /* Handle returned
for pipe */
NP_ACCESS_DUPLEX, /* Duplex pipe */
NP_WAIT |
NP_TYPE_MESSAGE |
NP_READMODE_MESSAGE |
NP_WMESG | /* Write messages */
NP_RMESG | /* Read messages */
0x01, /* Unique instance of
pipe */
sizeof(message), /* Output buffer size */
sizeof(message), /* Input buffer size */
0L); /* Use default
time-out */
// wait for connection
retCode = DosConnectNPipe(PipeHandle);
do {
// connected, read the data
retCode = DosRead(PipeHandle, message,
sizeof(message),&ulBytes);
// display the message
printf("\n\nMessage received was: %s\n\n", message);
..
// end of message
done = 1;
} while (!done);
// end of message, disconnect
rc = DosDisConnectNPipe(PipeHandle);
|
Listing 14. Unnamed pipes in Linux
/* In this example, thread 1 creates an unnamed pipe, and writes to the pipe. Thread 2 reads the data once it is available in the unnamed pipe. */ Thread 1 int data_processed; int file_pipes[2]; const char some_data[] = "123"; // Create pipe pipe(file_pipes); // Get access to pipe // Write to pipe data_processed = write(file_pipes[1], some_data, strlen(some_data)); // Close the write handle close(file_pipes[1]); Thread 2 int data_processed; char buffer[BUFSIZ + 1]; // get access to pipe .. // read from pipe data_processed = read(file_pipes[0], buffer, BUFSIZ); // close the read handle close(file_pipes[0]); |
Listing 15. Named pipes (FIFOs) in Linux
// Client program
/* This client program logs a message to the FIFO, and closes the
write end when it finds no message to write */
# define FIFO_NAME "/home/guest/testpipe"
# define BUF_SIZE 2552
int retCode = 0 ;
FILE *fd;
int done = 0;
char buff[BUF_SIZE+1];
int dataWrite = 0;
// Open the FIFO with write permission
fd = open (FIFO_NAME, O_WDONLY);
do {
// read from fifo
// display the message
strcpy(buffer, "test message");
dataWrite = write(fd, buffer, BUF_SIZE);
....
// done with all the messages
done =1 ;
} while(!done);
// Close the write end of the FIFO
fclose(fd);
//Server program
/* This Server program creates a FIFO, and waits for a message. If
the message is available, it prints the message to screen.
It closes the read end when it finds no message to read */
# define FIFO_NAME "/home/guest/testpipe"
# define BUF_SIZE 2552
int retCode = 0 ;
FILE *fd;
int done = 0;
char buff[BUF_SIZE+1];
// create a FIFO with user having write and read permission, and
// group and others having read permission.
retCode = mkfifo(FIFO_NAME, S_IWUSR | S_IRUSR |
S_IRGRP | S_IROTH);
// Open the FIFO with read permission
fd = open (FIFO_NAME, O_RDONLY);
do {
// read from fifo
// display the message
dataRead = read(fd, buffer, BUF_SIZE);
// display the message
....
done =1 ;
} while(!done);
fclose(fd);
|
Query information about a file
OS/2 stores additional information about files in Extended attributes. Linux does not support this option.
Table 5. File info mapping table
| OS/2 | Linux | Classification |
| DosQueryPathInfo | stat | Mappable |
In OS/2, the system call DosQueryPathInfo is used to get information
about a file or directory:
APIRET DosQueryPathInfo (pszPathName, ulInfoLevel, pInfoBuf,
cbInfoBuf)
pszPathNameis a pointer to the name of the file or directoryulInfoLevelis the level of the information required. 1, 2, 3, 5 are valid values. Level 4 is reserved.pInfoBufis the address of the storage area containing the requested level of path information:- Level 1: filestatus 3 is obtained, which contains basic file information: things like create date/time, last access date/time, and so on.
- Level 2: filestatus 4 is obtained. This is the same as filestatus 3 with an additional field showing the size of the extended attributes. Level 2 is used to query the size of the extended attributes before reading.
- Level 3: a subset of EA (extended attribute)
information about a file is obtained. This information is used
to:
- Store notes on file objects (for example, the name of the file creator)
- Categorize file objects (for example, source, samples, icons, bitmaps)
- Describe the format of data contained in the file object (for example, a data record)
- Append additional data to the file object
- Level 4: is reserved
- Level 5: gives the fully qualified file name.
cbInfoBufis the length in bytes of the storage areapInfoBuf.
On Linux, the system call stat is used to get information about a file
or directory
int stat (const char *pcPathName, struct stat *pBuf)
pcPathNameis a pointer to the name of the file or directorypBufis a pointer to the structure containing the file/directory information. The structure holding the file or directory information is shown in Listing 16.
Listing 16. Structure containing the file/directory information
struct stat {
dev_t st_dev; /* device */
ino_t st_ino; /* inode */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device type (if inode device) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last change */
};
|
Please note that DosQueryPathInfo fails if the file object is
currently being updated by any other process that holds conflicting
sharing and rights access. To prevent this, open the file object in a deny-write sharing mode.
The Linux stat call does not require any
access rights to the file being accessed; it only requires search rights
to all the directories in the path.
Listing 17. Query information about a file in OS/2
char * pcPathAndCalFileName = "C:/test.Tbl"; // old file name
char * pcPathAndOldFileName = "C:/test.Old"; // new file name
FILESTATUS3 fsStatBuf; // structure filled
by DosQueryPathInfo
//
// Rename the current test file, if it exists, to get ready for
// calibration
if (!DosQueryPathInfo(pcPathAndCalFileName, FIL_STANDARD, &fsStatBuf,
sizeof(fsStatBuf))){
if (!DosQueryPathInfo(pcPathAndOldFileName,
FIL_STANDARD,
&fsStatBuf, sizeof(fsStatBuf))){
// Remove the old calibration data file "test.Old"
remove(&pcPathAndOldFileName[0]);
}
// Rename the current "test.Tbl" test data file to the name
// "test.Old" that is used for old test data.
rename( &pcPathAndCalFileName[0], &pcPathAndOldFileName[0] );
}
|
Listing 18. Query information about a file on Linux
char * pcPathAndCalFileName = "/home/guest/test.tbl"; // old file name
char * pcPathAndOldFileName = "/home/guest/test.old"; // new file name
struct stat buf; // buffer with
// file information
if (stat(pcPathAndCalFileName, &buf) == 0){
// File exists so rename it. Before that check if `test.old' exists.
// If yes then remove that file
if (stat(pcPathAndOldFileName, &buf) == 0){
remove(pcPathAndOldFileName);
// Errors returned by this call should be handled appropriately
}
rename(pcPathAndCalFileName, pcPathAndOldFileName)
// More error handling
}
else
{
//
// File does not exist
//
}
|
There are two ways to duplicate file descriptors: a new handle is allocated, or some known handle is used as a new handle.
On OS/2, DosDupHandle() supports both features. On Linux, if a new
handle is to be allocated, the system call dup() is used, while in cases
where a known handle is used as a new handle, the system call dup2() is
used.
Table 6. Mapping table for duplicating file handles
| OS/2 | Linux | Classification |
| DosDupHandle |
dup dup2 | Mappable |
On OS/2, the system call DosDupHandle() is used to duplicate file
handles:
APIRET DosDupHandle(HFILE hFile, PHFILE pHfile);
hFiletakes the file handle to duplicatephFileis a pointer to where the handle information is stored
Possible values are:
- 0xFFFFFFFF
a new file handle is allocated and returned. - Any other value
Assign this value as the new file handle. A valid value is any of the handles assigned to standard I/O, or the handle of a file currently opened by the process.
Linux uses the dup() system call. When DosDupHandle() is used with a
second paramater (like 0xFFFFFFFF) in OS/2, the 0xFFFFFFFF simply means to
allocate a new handle. On Linux, dup provides the same functionality:
the dup() system call returns the new file descriptor. dup2(), meanwhile,
duplicates the file descriptor onto the passed in file descriptor:
int dup(int oldfd)
int dup2(int oldfd, int newfd)
oldfdis the file descriptor that has to be duplicatednewfdis the file descriptor thatoldfdis duplicated onto
The following examples compare duplication of file handles under OS/2 and under Linux:
Listing 19. Duplicating file handles in OS/2
enum {
StandardOutput = 0x00000001, // for standard output
StandardError = 0x00000002, // for standard error
AllocNewHandle = 0xFFFFFFFF // for allocating a new handle
}
HFILE hfOut, hfErr, hfSaveOut, ulSaveErr;
unsigned long ulRet;
//
// Set the standard handles
//
hfOut = StandardOutput;
hfSaveOut = (HFILE) AllocNewHandle;
hfErr = StandardError;
hfSaveErr = (HFILE) AllocNewHandle;
//
// Save the original handles.
//
DosDupHandle(hfOut, &hfSaveOut);
DosDupHandle(hfErr, &hfSaveErr);
//
// Assign some other file handle or pipe handle to the to the standard
// output & error
//
ulRet = DosDupHandle(hpWrite, &hfOut);
ulRet = DosDupHandle(hpWrite, &hfErr);
: : Do some processing
:
//
// Retrieve the original handles.
//
ulRet = DosDupHandle(hfSaveOut, &hfOut);
ulRet = DosDupHandle(hfSaveErr, &hfErr);
//
// Close the temporary handles
//
ulRet = DosClose(hfSaveErr);
ulRet = DosClose(hfSaveOut);
|
Listing 20. Duplicating file handles in Linux
enum {
StandardOutput = 1, //for standard output
StandardError = 2, //for standard error
}
// Instead of HFILE, in Linux the file descriptor is
// an integer
int fdOut, fdErr, fdSaveOut, fdSaveErr;
int iRet;
//
// Set the standard handles
//
fdOut = StandardOutput;
fdErr = StandardError;
//
// Save the original handles.
//
fdSaveOut = dup(fdOut);
fdSaveErr = dup(fdErr);
//
// Assign some other file handle or pipe handle to the to the
// standard output and error
//
fdOut = dup2 (hpWrite, fdOut);
fdErr = dup2 (hpWrite, fdErr);
: : Do some processing
:
//
// Retrieve the original handles.
//
fdOut = dup2(fdSaveOut, fdOut);
fdErr = dup2(fdSaveErr, fdErr);
//
// Close the temporary handles
//
close(fdSaveErr);
close(fdSaveOut);
|
Table 7. File access mapping table
| OS/2 | Linux | Classification |
| DosSetPathInfo | utime | Mappable |
In OS/2 the system call DosSetPathInfo() is used:
APIRET DosSetPathInfo (PSZ pszPathName, ULONG ulInfoLevel,
PVOID pInfoBuf, ULONG cbInfoBuf, ULONG flOptions)
pszPathNameis a pointer to the ASCII full path name of the file or sub directory.ulInfoLevelis the level of file directory information being defined. Valid values are 1 and 2.pInfoBufis a pointer to the information being set.- Level 1
For Level 1, FILESTATUS3 is passed. It contains the standard information of the file such as file creation date/time, acess date/time etc. - Level 2
For Level 2, EAOP2 structure is passed. This sets the extended attribute value of the file. OS/2 stores the additional information about the file in the extended attrribute list (EA).
- Level 1
The DosSetPathInfo() not only sets the access/modification time of
file or sub directory, it also sets the other parameters like file
creation date/time, file size, and attributes of the file. However, in
this section we only discuss changing the access/modification time. So we
are using Level 1 and passing the FILESTATUS3 structure containing the new
access/modified time.
On Linux, the system call utime() is used for changing the access time
for a specified file:
int utime(const char * filename, struct utimbuf * buf );
filenameis the pointer to the ASCII filename of the file whose access time needs to be modified.bufis the pointer to the structure containing the new access/modified time for the file. utimbuf structure is given below.
struct utimbuf {
time_t actime; /* access time */
time_t modtime; /* modification time */
};
|
Listing 21. Setting file information in OS/2
char *pcFileName= "xyz.ini"; FILESTATUS3 fs3DirInfo; /* get the file information */ DosQueryPathInfo(pcFileName, FIL_STANDARD, &fs3DirInfo, sizeof(fs3DirInfo)); /* get the current time */ tm *ptm = localtime(&lTime); /* update the file information structure with the new modification time */ fs3DirInfo.fdateLastWrite.day = ptm->tm_mday; fs3DirInfo.fdateLastWrite.month = ptm->tm_mon + 1; fs3DirInfo.fdateLastWrite.year = ptm->tm_year - 80; fs3DirInfo.ftimeLastWrite.twosecs = ptm->tm_sec / 2; fs3DirInfo.ftimeLastWrite.minutes = ptm->tm_min; fs3DirInfo.ftimeLastWrite.hours = ptm->tm_hour; /* set the new file information */ DosSetPathInfo(pcFileName, FIL_STANDARD, &fs3DirInfo, sizeof(fs3DirInfo), 0); |
Listing 22. Setting file access time in Linux
struct utimbuf buf;
int iRc;
time_t tTime;
tm *tmr;
/* Get the local time */
tTime = time(0);
localtime_r(&tTime,&tmr);
/* Update the structure with the new modification time */
buf.actime = buf.modtime = mktime(tmr);
/* set the new file information */
iRc = utime("check.txt", &buf);
|
This article covered the mapping of OS/2 to Linux with respect to memory management, IPC mechanisms, and file handling. While not exhaustive in terms of coverage; you can use it as a reference for undertaking any migration activity involving OS/2 to Linux. Please refer to the man pages for more detailed explanations of the Linux calls mentioned in this article. The next article in this series covers the mapping of system calls related to device driver interface, timer calls, and related issues.
- Be sure to read the first installment in this series, Migrate
your apps from OS/2 to Linux: Part 1. Threads, mutexes, and semaphores
(developerWorks, February 2004)
- Be sure to read the next installment in the series, "Migrate your apps from OS/2 to Linux: Part 3. Timer and DLL calls" (developerWorks, August 2004).
- The Design of
OS/2 by H.M. Deitel and M.S. Kogan (Addison-Wesley, 1992) can
still be found at used book sites; the ISBN is 0-201-54889-5.
- Don't forget to check the Linux "info" and man pages for specific
calls for more details on programming calls in Linux.
- C++ programmers can refer to the mapping
between IBM Open class (visual age) and STL classes (open standards).
- IBM's OS/2
Strategy for 2004 recommends a phased transition to the WebSphere software
platform.
- Most of the IBM eServer
servers now come bundled with Linux.
- IBM Redbooks offers a OS/2
Server Transition for OS/2 administrators who are planning a
transition to Windows or to Linux.
- See Migrate / Port your application to Linux with IBM for additional information.
- Porting
OS/2 applications to Linux (in C) (developerWorks, March 2002)
covers issues encountered by the LAN Distributed Platform (LANDP) for
Linux team while porting LANDP from OS/2 to Linux.
- You can also use OS/2 and Linux together on the same machine. Check
out How to
use Linux and OS/2 Together for a list of resources from the Southern
California OS/2 User Group, and the SUSE LINUX portal's OS/2 and
Linux with the OS/2 Boot Manager for easy directions on dual-booting.
- Find more resources for Linux developers in the
developerWorks
Linux zone.
- Browse for books on these and other technical topics.
- Develop and test your Linux applications using the latest IBM tools and middleware with a developerWorks Subscription: you get IBM software from WebSphere, DB2, Lotus, Rational, and Tivoli, and a license to use the software for 12 months, all for less money than you might think.
- Download no-charge trial versions of selected developerWorks Subscription products that run on Linux, including WebSphere Studio Site Developer, WebSphere SDK for Web services, WebSphere Application Server, DB2 Universal Database Personal Developers Edition, Tivoli Access Manager, and Lotus Domino Server, from the Speed-start your Linux app section of developerWorks. For an even speedier start, help yourself to a product-by-product collection of how-to articles and tech support.
Dinakar Guniguntala holds a B.Eng. degree in Computer Engineering from REC Surat. He has been employed with IBM Global Services, India, since February 1998, where he works on operating system internals. He has worked on the OS/2 kernel, graphics engine, and filesytems, as well as on embedded Linux, the Linux kernel, and Linux thread libraries. He can be reached at dgunigun-at-in.ibm.com.
Sathyan Doraiswamy holds a B.Eng. degree in Electronics and Communications from University Visvesvaraya College of Engineering, Bangalore. He joined IBM India in June 1997. He works for Engineering & Technology Services and specializes in firmware for embedded and real time systems. He has vast programming experience on Windows and Linux device drivers and on VxWorks. You can contact him at dsathyan-at-in.ibm.com.
Anand K. Santhanam has a B.Eng. in Computer Science from Madras University, India. He has been in IBM Global Services (Software Labs), India, since July 1999. He is a member of the Linux Group at IBM, where he has worked with ARM-Linux, character/X-based device drivers, power management in embedded systems, PCI device drivers, and multithreaded programming in Linux. Other areas of interest are OS internals and networking. You can contact him at asanthan-at-in.ibm.com.
Srinivasan S. Muthuswamy has a B.Eng. in Computer Engineering from the Government College of Technology, Coimbatore, India. He has been in IBM Global Services, India since August 2000. He has worked on multithreaded applications in Linux and on Web applications using WSBCC/Java technologies/Websphere/MQSeries. He has also worked on Crystal Reports and Lotus Domino. You can contact him at smuthusw-at-in.ibm.com.
Rinson Antony has a Bachelor of Engineering degree in Computer Engineering from Bombay University, India. He has been in IBM Global Services, India since July 2000. He has worked on multithreaded applications in Linux and Web applications using Java technologies, Websphere, and XML. Other areas of interest are PCI device drivers and networking. You can contact him at arinson-at-in.ibm.com.
Brahmaiah Vallabhaneni has a B.Eng. degree from BITS, Pilani, India. He has been in IBM Global Services, India, since August 2002. He has worked on GNU C Compiler porting, character device drivers, power management in embedded systems, PCI device drivers, and multithreaded programming in Linux. Other areas of interest are Linux internals and networking. You can contact him at bvallabh-at-in.ibm.com.
Comments (Undergoing maintenance)





