kempf@mcrware.UUCP (Kim Kempf, Microware Systems Corporation) (08/20/86)
[This is a response to a question posted in February. Kim Kempf from Microware asked that it be posted for technical understanding. By the way, I have copies of the five January to February digests for those interested. Just send a request to cbosgd!os9-request -JDD] > Can anyone give me a clear explanation of how to use the chain() > and os9fork() calls in the microware 'C' Compiler? One thing > I would like to do is simulate the un*x fork() call where a program > simply clones itself. As to chain(), how do you know when you get > ready to chain to another program what all the other program's > data, parameter, and other needs are? i.e. how do you know what paramters > pass to the call? Replies by mail or net. You forgot to state whether your machine is a Level I or Level II system. OS-9 Level I operates with no memory management hardware. The design basis of OS-9 is to provide a modular UNIX-like environment without the memory and/or disk requirements of real UNIX. Unfortunately, simply cloning a process is not so trivial without memory management or a disk. OS-9 relies on a memory module memory management scheme where all code modules are position-independent and re-entrant. Since OS-9 does not require a disk to operate, the kernel's memory and process scheduling routines were designed to operate without one. The os9fork() (F$Fork) and chain() F$Chain) functions (system calls) create a new process by the following (simplified) algorithm: 1. Create a new process descriptor entry. 2. Link to the desired code module (load in or find in memory) 3. Assign memory for the process' data area of the required size. 4. Activate the new process 5. Forget the parent process if an F$Chain The os9fork() function is equivalent to the UNIX fork()/exec() rolled into a single call. The chain() function is directly equivalent to just the exec() call. This is why os9fork() is called os9fork() and not fork(); the expected semantics are different. To implement the semantics of the fork() function on an OS-9 Level I system (Level I is a 6809 with no memory management hardware to re-arrange the logical address space) requires swapping the forking process' data memory to disk and executing the forked process from exactly the same data addresses as that of the parent process. An implementation such as this would require significant changes to the OS-9 kernel, not to mention deviating from the fundamental design of OS-9. OS-9 Level II requires memory management hardware to 1) allow more than 64k of physical memory and 2) assign a 64k address map to each process. Since each process has its own 64k memory map (addresses 0x0000 to 0xffff), a fork() call with UNIX semantics could be implemented with minor changes to the kernel. Programs that depended on this ability could not be moved downward to OS-9 Level I systems without changes, though. The only difference between os9fork() and chain() is that the control is returned to the calling process afterwards with the former and not in the later. After os9fork(), two processes will exist. After chain() only the newly-created process exists. Both functions use identical arguments, only the action after the call is different: os9fork/chain(modname,paramsize,paramptr,type,lang,datasize) char *modname,*paramptr; unsigned paramsize,datasize; short type,lang; "modname" is the name of the code module to execute. The module directory is searched to locate its address. If the module is not in the module directory, a file of that name is located in the execution directory on disk (if any). "paramsize" and "paramptr" are a pointer to and the size of the data area passed to the new process. Type and lang qualify the desired module. "datasize" is additional memory size to be added to the stack of the new process. If zero is given, the default stack size is determined from the code module value. highest addresses | | | | +-------------------+ <--- sbrk() increases memory here (via F$Mem) | parameter area | +-------------------+ <--- X, SP (D contains size of parameter area) | | (X points to the parameter area, SP starts | stack | here and moves down.) | memory | | | +-------------------+ | | | | V | | | | free memory | | | | ^ | | | | +-------------------+ <-- end | uninitialized | | data area | | (bss) | +-------------------+ <-- edata | initialized | | data area | | (data) | +-------------------+ | direct page | | memory | +-------------------+ <-- Y, DP (DP is set to the high byte of Y) lowest addresses This memory allocation scheme is used by the C compiler execution environment for both Level I and Level II systems. The only difference is on Level I, the Y register is never zero and on Level II the Y register is always zero. For Level I, the data memory is assigned from free memory within the 64k total address space. The kernel uses the lowest memory addresses so the memory assigned is usually somewhere around 0x0400 depending of memory usage. For Level II, each process has its own logical 64k address space. Data memory is always mapped into the lowest logical addresses, i.e. 0x0000. The direct page register DP always reflects the high order byte of the Y register to allow direct page addressing into the first 256 bytes of the data area. The size of the data area and stack is determined from the module header of the module to be executed. The parameter area will be copied into the data area of the new process and its address and size made available via registers when the process is born. It is important that no pointers be passed in this area as the pointers point into the parent process' memory and not the child's (hidden feature?). This parameter area is unstructured, that is, OS-9 makes no assumptions as to the context of this area. It is up to the parent and child to determine the structure and contents of this data. The chain() and os9fork() and the cstart.r startup routine communicate the argc, argv business to C programs. An example of os9fork() usage follows: main() { int pid; . . . if ((pid = os9fork("dir","-e\n",3,0,0,0)) != -1) { . . . wait(0); } else printf("can't fork dir\n"); . . . } Note the \n terminating the parameter string. This is a convention used by the OS-9/6809 assembly-language utilities. Always pad the parameter area with a \n. Since I do not know exactly what you are trying to do, I will suggest a couple of hints I use when porting UNIX-style forks to OS-9. It seems to me that most forks are used to create a process that does an immediate exec(). In this case, change the code to do an OS-9 chain() instead of fork()/exec(). If the fork() is used to create two processes that run concurrently, isolate the code from each side of the fork into a separate program and use os9fork() to execute the other half of the program. I hope this clarifies one of the major operational differences of OS-9 and UNIX. ---------------- Kim Kempf, Microware Systems Corporation {{cornell,decvax,ihnp4,sdcsvax,tektronix}!uw-beaver}\ {allegra,gatech!sb1,hplabs!lbl-csam,decwrl!sun,sunup} >!fluke!mcrware!kim {ssc-vax,hplsla,wavetek,physio,cae780,tikal,telematic}/