Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ukma!rutgers!cbmvax!jesup From: jesup@cbmvax.UUCP (Randell Jesup) Newsgroups: comp.sys.amiga.tech Subject: Re: Unix V7 functionality under (or along with) AmigaDOS? (*LONG*) Keywords: Unix V7 Minix AmigaDOS shells ... CATS? Randell?? :-) Message-ID: <6157@cbmvax.UUCP> Date: 7 Mar 89 01:26:31 GMT References: <6124@cbmvax.UUCP> <6140@cbmvax.UUCP> Reply-To: jesup@cbmvax.UUCP (Randell Jesup) Organization: Commodore Technology, West Chester, PA Lines: 324 In article shadow@pawl.rpi.edu writes: >In article <6140@cbmvax.UUCP> jesup@cbmvax.UUCP (Randell Jesup) writes: >> I remember these problems well, I wrote a csh clone, SeaShell, before >>I came to commodore. > >Hmm. I have a shell you wrote several years ago resembling Matt >Dillon's shell. (I got the disk from Sandro) Is that the one? >(Hardcoded to "expire" sometime in 1986? [setting back the clock >works great, by the way... :-)] I'll surely erase it soon, but the >extra utilities you had on the disk seemed to be of some possible >value...) Ugh! Boy, that's an old copy! Burn it before it gets you. (Naughty Sandro, Naughty!) That one has a LOT of bugs in it - it's so old I don't even remember what they are. Did that one even have process control? The utilities are more or less ok. >I want a shell which will work correctly whether or not exit(), >_exit() or Exit() is called, and without regard to what compiler is >being used. I want the shell to compile under Lattice or Aztec (but >I'm using Lattice) or any other compiler there may be (PDC if it >works, etc.) Well, as I said, Exit() can be made to work by playing with pr_ReturnAddr. Note that if you play with it, restore it before you exit. As I said before: >> Lattice pops the stack back to the starting point, then does an RTS. >>The trick to making Exit() work is in the setting of pr_ReturnAddr. Note >>this is somewhat magic. > >So to have Exit() handled correctly, have the startup module (or >perhaps the shell?) look up the return address at the start of the >stack and copy it to pr_ReturnAddr? Or must something else be done? All I'll say is figure out how it works: The tech manual and some experimentation shoul do it (it's really very simple). >> Shells are shells, period. The Workbench is a visual shell. >>Exit() itself does almost nothing, except return control to the "shell" >>as if the program had exited. > >So Exit() only ever releases resources if pr_ReturnAddr points to some >such cleanup routine? What does CreateProc() normally initialize >pr_ReturnAddr to, then? Exactly such a routine? It sets up pr_returnaddr so you'll end up in the same place as if you RTS'ed from your program. >> Nowhere, unless (a) you're willing to use AddTask for everything, >>and therefor are willing to reimplement the functionality of CreateProc >>(fully, including endcode for cleaning up things like directory locks, >>pr_SegList pointer (which is bizarre), etc). > >I'm willing to use AddTask if it will still operate as a DOS process >and be able to call dos.library routines. How should I go about doing >this? Also, what makes pr_SegList bizare? (aside from usage of >BPTRs?) I REALLY don't advise it, plus it almost certainly will break in the future. >On the other hand, in the include file, the >Process struct is defined with pr_task, pr_MsgPort, pr_Pad, and then >BPTR pr_SegList, not SegArray. But the comment on the line says: >/* Array of seg lists used by this process */ >Is there then no difference but the name used? Names were changed, that's all. >>>Two, make an extended structure with a process structure as its first >>>element, and add fields after it for my own use. >> >> See above. > >Hmm. This seems the only reasonable way to go; replace CreateProc(), >and use AddTask(). I take it the extra segments in the SegArray >(pr_SegList) are such as that cleanup code for CreateProc() which are >not to be unloaded. May I safely assume that if I replace >CreateProc() with my own function which sets up a structure starting >with a Process structure (as the Process struct begins with a Task >structure) that I may use the pr_SegList field as I see fit to manage >the memory, without affecting operation of programs ran by the shell? >(i.e. does anything depend on the way CreateProc() sets up >pr_SegList?) I _think_ you'll be ok. I prefer to avoid the problem myself, and find ways to not run into this. >(running down the list...) > >May pr_Pad safely be ignored? I'd zero it. >pr_StackSize seems straightforward enough. > >I don't understand what the pr_GlobVec field is used for. Will >non-BCPL programs ever use it? What do I need to initialize it to? Non-BCPL shouldn't be using it. >I'll set pr_TaskNum to zero. > >For pr_StackBase, do I just allocate a memory area the size of >pr_StackSize, (and should I/need I allocate an extra longword to hold >the size of the allocated block, as AmigaDOS does?) and set >pr_StackBase to the last longword of the stack? Or is it the last >byte? Last longword (actually, you can set it to the loagword after the last one, since when you push it pre-decrements.) >Regarding pr_Result2, do I initialize it to zero and forget it, or do >I need to actually do something to get the secondary result from the >last call? (And exactly what defines the secondary result? The >Exit()/exit() value?) Dos calls set pr_Result2. >For pr_CIS and pr_COS, should I set them to be the standard input and >output of the executed program, or set them to zero and define the >standard input, output and error channels in my own structure? (I >want stderr as a passed file descriptor, along with stdin and >stdout...) I want to break dependency from the CLI, yet still be able >to run programs which depend on the CLI themselves. (possibly by >using or replacing the AmigaDOS RUN command.) Input() and Output() return the values in pr_CIS and pr_COS. They must be valid for CLI-type programs. >Similarly, should I zero pr_ConsoleTask and pr_FileSystemTask or not? >(I intend to have read(), write(), etc. calls... functionally similar >to Lattice's, but not dependant on the Lattice compiler or lc.lib >library.) pr_FileSystemTask is where to look if you have a 0 lock. pr_ConsoleTask can be dangerous to clone unless you KNOW it won't go away (used for Open("*", ...). >pr_CLI will be set to zero. CLI-only programs may expect it. >What's the best way to set pr_ReturnAddr? Set it to the PC plus a >constant? JSR to a piece of code which stores the return address in >pr_ReturnAddr and JMP's to the code? Hmm. Silly question. The >latter, of course. Look how BCPL sets it up. >I'm undecided on what to do with pr_WindowPtr. Normally it's set to 0, though programs may modify it (but they should set it back before exit). >My added structure would then follow. Do you forsee any compatibility >problems caused by a setup like this? In 1.4, quite possibly, but for 1.3 (modulo above) it might be ok. Having it run BCPL programs is harder. >>>On a related point, is this an equivalent function for UnLoadSeg(), or >>>does UnLoadSeg() do something else/more? >> >> Much more. First there's overlaid programs. Releasing them is >>somewhat tricky, since they have open filehandles, and tables to be freed. >>Plus UnloadSeg must work for a NULL seglist. And then there are "resident >>libraries" (yech). > >Hmm. Ok, then. Maybe I won't try to rewrite UnLoadSeg(), at least >not unless I also rewrite LoadSeg() (which I may.) Note: UnloadSeg is MUCH simpler than LoadSeg. I know, I rewrote both. Overlays are TRICKY. >Hey, maybe I could join CATS sometime! :-) (But being on the >Internet is just so nice...) Send a resume to carolyn@cbmvax >I have programmed quite a bit under Unix and rather prefer the Unix >file system and system calls to many of those available on the Amiga. >I don't much care for AmigaDOS or BCPL. Some of the user interface >issues between Unix and AmigaDOS are trivial, like "../" vs. "/" for >parent directory. See my posting on why this is very hard to switch (because handlers and applications understand the syntax, and shells not knowing what is a path versus a regular arguement.) >Other differences, such as the way directories are implemented, differ >more significantly. Unix-style directories are far more efficient at >directory listing (but no the hash-lookup searching AmigaDOS does) >than AmigaDOS with its backpointers. Also, the lack of links in >AmigaDOS is a big loss. But AmigaDos is much faster at opening files and testing for existance. Unix slows down a lot with large directories, especially creation. (Mach uses both hashing and regular unix-style directories). >I want to write a shell which will be more cleanly implemented than >the CLI (i.e. no BCPL) and which will start programs with argv AND >envp arrays, and have Unix-type calls available. The trick is to not break other programs by changing their environment too much. >I have the source code to Minix available, but I don't think I want to >do a direct port. For one thing, there are some aspects of Minix I >would like to improve on, and Minix, while it would be very nice, >might not be ideal for an Amiga without a hard drive. Tannenbaum has two students doing a port. >What I would like to do is write a file system based in part on Minix, >and in part on AmigaDOS (but *no* BCPL!) which would work more >effectively with floppy-based systems. (i.e. Mount file systems like >in Minix/Unix, but volume-oriented, as in AmigaDOS.) 1.4 should have FFS in ROM (and available for floppies). >Using BCPL for AmigaDOS seems a colossal error. Exec was done in C, >but not AmigaDOS. (I've often wondered just what TriPOS was/is like, >though...) I understand pressures to get the OS out the door, but it >would be best to get rid of BCPL stuff totally, and the sooner the >better. Already AmigaDOS is getting entrenched enough that it may be >too late. But I do hope that AmigaDOS V2.0 will be written in and for >C code, with no BCPL thrown in. Exec? In C? That's a nasty thought. Exec is in ASM, of course, for speed. I'll say this: your dislike for BCPL is shared by many here. >But what I DO want now is the fork() and exec() system calls from >Unix. (execve() for purists) And I'm willing to take a stab at >writing them myself. > >Being the simpler of the two, let's look at fork() first. > >I want a *real* fork(), not a fake one like the Lattice library >offers, which is similar in function to Execute(). (Well, closer to >LoadSeg() followed by CreateProc, actually.) I want a fork() which >duplicates the current process with the only difference being the >returned value, as in Unix. VERY hard to do without an MMU, since you MUST duplicate the stack and data, and those contain absolute pointers. There is no way to know where these pointers are. The ST/Amiga-Minix gets around this by copying the data/stack of the two processes to save areas, and "swapping" in the data/stack to the original position whenever one needs to run. Luckily, almost all fork() calls are followed almost immediately by exec(), so the overhead isn't too horrible. >Finally, (I hope) how to handle file descriptors. The solution for >this would seem to be to have file i/o operations in this library - >open(), close(), read(), write(), etc. and have a table of file >descriptors, file pointers, and AmigaDOS file handles (for now, at >least.) that the "parent" and "child" share. I don't know whether or >not to try to preserve the parent/child relationship as Unix does, or >try some other setup. I'll have to think about it. there is no way to dup a filehandle. >So much for the "easy" one. Phew! Now, the hard one. Coding exec(). > >Here is the steps Minix uses for its exec() system call: > >1. Check permissions - is the file executable? >2. Read the header to get the segment and total sizes. >3. Fetch the arguments and environment from the caller. >4. Release the old memory and allocate the new one. >5. Copy stack to new memory image. >6. Copy text and data segments to new memory image. >7. Check for and handle setuid, setgid bits. >8. Fix up process table entry. >9. Tell kernal that process is now runnable. > >Now, some difficulties are immediately apparent. > >One significant obstacle is attempting to have an exec() call as a >scanned library linked with to make an executable program. As such, >the text image to be completely replaced by the new program will >contain the code to do the replacing. Clearly this poses a problem. So keep the code around until the new code is loaded. may increase fragmentation a bit, but will work. >So, there is the problem of separating the exec() call from the text >image. What would seem to be the most usable method would be to have >exec() start by duplicating itself (sans copying code) into a newly >allocated single-segment seglist, which is passed to CreateProc() with >a high priority and a minimal stack. This is somewhat of a kludge, >but should be workable, as CreateProc() has the cleanup code for the >exec() function itself, once it has done its work and is no longer >needed. This will work if done right. Priority shouldn't come into the picture. >If any of the checks, allocations, or the CreateProc were to fail, >then the exec() call would deallocate any allocated memory, and return >with a -1 and an error number, probably in the (global) variable errno. >Otherwise, it would simply wait forever, (pick some event to wait for >which will never happen) and the newly created process would RemTask >the task and handle the switchover and deallocation of the original >task, and starting the new process up. (Then it would return, falling >into CreateProc's cleanup code.) No, Wait(0) to wait forever. >Alternatively, most allocation types could be duplicated in the >library, with tracking versions, for automatic deallocation upon exit >or exec(). (Oboy, now I AM digging myself a grave here, aren't I?) Sounds like ARP. I think you're working at too low a level. There are better ways to implement the functionality you want without making the internal semantics the same as in Unix. Define your functionality first, then figure out what you need to get that. -- Randell Jesup, Commodore Engineering {uunet|rutgers|allegra}!cbmvax!jesup