Changeset 1281


Ignore:
Timestamp:
Mar 6, 2004, 10:48:26 PM (21 years ago)
Author:
bird
Message:

...

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/doc/Fork.os2

    • Property cvs2svn:cvs-rev changed from 1.2 to 1.3
    r1280 r1281  
    162162        3. Extended versions of some memory related OS/2 APIs must be implemented.
    163163       
    164 The implemenetation will further make the following assumption about the
     164The implementation will further make the following assumption about the
    165165operation of OS/2:
    166166        1. DosExecPgm will not return till all DLLs are initated successfully.
    167        
     167        2. DosQueryMemState() is broken if more than one page is specified.
     168       (no idea why/how/where it's broken, but testcase shows it is :/ )
    168169
    169170       
     
    171172---------------------------------
    172173
    173 The fork() implementation requires a method of telling the child process
     174The fork() implementation requires a method for telling the child process
    174175that it's being forked and must take a very different startup route. For
    175 some other LIBC apis there is need for parent -> child and child -> parent
     176some other LIBC apis there are need for parent -> child and child -> parent
    176177information exchange. More specifically, the inheritance of sockets,
    177178signals, the different scheduler actions of a posix_spawn[p]() call, and
    178179possibly some process group stuff related to posix_spawn too if we get it
    179180figured out eventually. All this was parent -> child during spawn/fork. A
    180 need exist also for child -> parent notification and possibly exchange for
     181need also exist for child -> parent notification and possibly exchange for
    181182process termination. It might be necessary to reimplement the different
    182183wait apis and implement SIGCHLD, it's likely that those tasks will make
     
    184185
    185186The choice is now whether or not to make this shared process management
    186 specific to each LIBC version or try to make it survive normal LIBC updates.
    187 Making is specific have advantages in code size and memory footprint (no
    188 reserved field), however it have certain disadvantages when LIBC is updated.
    189 The other option is to use a named shared memory object, defining the
    190 content with reserved space for later extensions so several versions of
    191 LIBC with more or less features implemented can co use the memory space.
     187specific to each LIBC version as a shared segement or try to make it
     188survive normal LIBC updates. Making is specific have advantages in code
     189size and memory footprint (no reserved fields), however it have certain
     190disadvantages when LIBC is updated. The other option is to use a named
     191shared memory object, defining the content with reserved space for later
     192extensions so several versions of LIBC with more or less features
     193implemented can co use the memory space.
    192194
    193195The latter option is prefered since it allows more applications to
     
    196198processes using multiple versions of LIBC.
    197199
    198 The shared memory must be named \SHAREMEM\INNOTEKLIBC.V01, the version
     200The shared memory shall be named \SHAREMEM\INNOTEKLIBC.V01, the version
    199201number being the one of the shared memory layout and contents, it will
    200202only be increased when incompatible changes are made.
    201203
    202 The shared memory will be protected by an standard OS/2 mutex semaphore.
    203 It will not use any fast R3 semaphore since the the usage frequency is low
    204 and the result of a messup may be disastrous. Care must be take for
     204The shared memory shall be protected by an standard OS/2 mutex semaphore.
     205It shall not use any fast R3 semaphore since the the usage frequency is
     206low and the result of a messup may be disastrous. Care must be take for
    205207avoiding creation races and owner died scenarios.
    206208
    207 The memory will have a fixed size, since adding segments is very hard.
     209The memory shall have a fixed size, since adding segments is very hard.
    208210Thus the size must be large enough to cope with a great deal of
    209 processes, but bearing in mind that OS/2 normally doesn't support more
     211processes, while bearing in mind that OS/2 normally doesn't support more
    210212than a 1000 processes, with a theoritical max of some 4000 (being the
    211213max thread count). A very simplistic allocation scheme will be
     
    214216lists a linked list based heap would do fine.
    215217
    216 The process blocks will be rounded up to in size adding a reasonable
     218The process blocks shall be rounded up to in size adding a reasonable
    217219amount of space resevered for future extensions. Reserved space must be
    218220all zeroed.
    219221
    220 The fork() specific members of the process block will be a pointer to
     222The fork() specific members of the process block shall be a pointer to
    221223the shared memory object for the fork operation (the fork handle) and
    222224list of forkable modules. The fork handle will it self contain
     
    227229the forking of each module. (more details in section 4.0)
    228230
    229 The parent will before spawn, fork and exec (essentially before DosExecPgm
     231The parent shall before spawn, fork and exec (essentially before DosExecPgm
    230232or DosStartSession) create a process block for the child to be born and
    231 link it into an embryo list in the shared memory block. The child will
    232 find the process block by looking searching an embryo list using the
    233 parent pid as key. All DosExecPgm and DosStartSession calls are
    234 serialized within one LIBC version. (If some empty headed programmer
    235 manages to link together a program which may end up using two or more
    236 LIBC versions and having two or more thread doing DosExecPgm at the
    237 very same time, well then he really deserves what ever trouble he gets!
    238 At least don't blame me!)
    239 
    240 Process blocks will have to stay around after the process terminated
     233link it into an embryo list in the shared memory block. The child shall
     234find it's process block by searching the embryo list using the parent pid
     235as key. All DosExecPgm and DosStartSession calls shall be serialized within
     236one LIBC version. (If some empty headed programmer manages to link together
     237a program which may end up using two or more LIBC versions and having two
     238or more thread doing DosExecPgm at the very same time, well then he really
     239deserves what ever trouble he gets! At least don't blame me!)
     240
     241Process blocks shall have to stay around after the process terminated
    241242(for child -> parent term exchange), a cleanup mechanism will be invoked
    242243whenever a free memory threshold is reached. All processes will register
     
    250251
    251252
    252 The implementation will be based on a fork handle and a set of primitives.
     253The implementation is based on a fork handle and a set of primitives.
    253254The fork handle is a pointer to an shared memory object allocated for the
    254255occation and which will be freed before fork() returns. The primitives
     
    261262
    262263The support for fork() is an optional feature of LIBC. The default
    263 executable produced with LIBC and GCC will not be forkable. The fork
     264executable produced with LIBC and GCC is not be forkable. The fork
    264265support will be based on registration of the DLLs and EXEs in their
    265266LIBC supplied startup code (crt0/dll0). A set of fork versions of these
    266 modules will be made.
     267modules exist with the suffix 'fork.o'.
    267268
    268269The big differnece between the ordinary crt0/dll0 and the forkable
     
    270271handling of the return code of that call.
    271272
    272 The structure will contain these fields:
    273         - chain pointer.
    274         - data segment base address.
    275         - data segment end address.
    276         - fork callback function.
    277        
    278 The fork callback function is called _atfork_callback, it takes the fork
     273The fork module structure:
     274    typedef struct __libc_ForkModule
     275    {
     276        /** Structure version. (Initially 'FMO1' as viewed in hex editor.) */
     277        unsigned int    iMagic;
     278        /** Fork callback function */
     279        int           (*pfnAtFork)(__LIBC_FORKMODULE *pModule,
     280            __LIBC_FORKHANDLE *pForkHandle, enum __LIBC_CALLBACKOPERATION enmOperation);
     281        /** Pointer to the _CRT_FORK_PARENT1 set vector.
     282         * It's formatted as {priority,callback}. */
     283        void           *pvParentVector1;
     284        /** Pointer to the _CRT_FORK_CHILD1 set vector.
     285         * It's formatted as {priority,callback}. */
     286        void           *pvChildVector1;
     287        /** Data segment base address. */
     288        void           *pvDataSegBase;
     289        /** Data segment end address (exclusive). */
     290        void           *pvDataSegEnd;
     291        /** Reserved - must be zero. */
     292        int             iReserved1;
     293    } __LIBC_FORKMODULE, *__LIBC_PFORKMODULE; /* urg! conventions */
     294
     295
     296The fork callback function which crt0/dll0 references when initializing
     297the fork modules structure is called _atfork_callback. It takes the fork
    279298handle, module structure, and an operation enum as arguments. LIBC will
    280299contain a default implementation of _atfork_callback() which simply
    281 duplicates the data segment.
    282 
    283 The register call, __libc_ForkRegisterModule(), will return:
    284         - 0 if normal process startup. no forking.
    285         - 1 if fork() is in progress. The crt0/dll0 code will then
    286           not call any standard initiation code, but let the
    287           _atfork_callback() do all necessary stuff.
     300duplicates the data segment, and processes the two set vectors
     301(_CRT_FORK_*1).
     302
     303crt0/dll0 will register the fork module structure and detect a forked
     304child by calling __libc_ForkRegisterModule().
     305
     306Prototypes:
     307    /**
     308     * Register a forkable module. Called by crt0 and dll0.
     309     *
     310     * The call links pModule into the list of forkable modules
     311     * which is maintained in the process block.
     312     *
     313     * @returns 0 on normal process startup.
     314     * @returns 1 on forked child process startup.
     315     *          The caller should respond by not calling any _DLL_InitTerm
     316     *          or similar constructs.
     317     * @returns negative on failure.
     318     *          The caller should return from the dll init returning FALSE
     319     *          or DosExit in case of crt0. _atfork_callback() will take
     320     *          care of necessary module initiation.
     321     * @param   pModule     Pointer to the fork module structure for the
     322     *                      module which is to registered.
     323     */
     324    int __libc_ForkRegisterModule(__LIBC_FORKMODULE *pModule);
     325
     326
     327
    288328
    289329
     
    292332
    293333These primitives are provided by the fork implementation in the fork
    294 handle structure. We will define a set of these primitives now, if
    295 later new ones are added the users of these must check that they are
     334handle structure. We define a set of these primitives now, if later
     335new ones are added the users of these must check that they are
    296336actually present.
    297337
     
    347387         */
    348388        int pfnFlush(__LIBC_FORKHANDLE *pForkHandle);
     389       
     390    /**
     391     * Register a fork() completion callback.
     392     *
     393     * Use this primitive to do post fork() cleanup.
     394     * The callbacks are executed first in the child, then in the parent.
     395     *
     396         * @returns     0 on success.
     397         * @returns appropriate non-zero error code on failure. (Usually ENOMEM.)
     398         * @param   pForkHandle Handle of the current fork operation.
     399     * @param   pfnCallback Pointer to the function to call back.
     400     *                      This will be called when fork() is about to
     401     *                      complete (the fork() result is established so to
     402     *                      speak). A zero rc argument indicates success,
     403     *                      a non zero rc argument indicates failure.
     404     * @param   pvArg       Argument to pass to pfnCallback as 3rd argument.
     405     * @param   enmContext  __LIBC_FORKCTX_CHILD, __LIBC_FORKCTX_PARENT, or
     406     *                      __LIBC_FORKCTX_BOTH.
     407     *                      (mental note: check up the naming convention for enums!)
     408         * @remark      Use with care, the memory used to remember these is taken from the
     409         *          fork buffer.
     410     */
     411    int pfnCompletionCallback(__LIBC_FORKHANDLE *pForkHandle,
     412        void (pfnCallback)(__LIBC_FORKHANDLE *, int rc, void *pvArg), void *pvArg,
     413        __LIBC_PARENTCHILDCTX enmContext);
    349414        ...
     415       
     416
     417       
     4184.3 The Flow Of A fork() Operation
     419----------------------------------
     420
     421When a process simple process foo.exe calls fork() the following events occur.
     422(The 'p:) indicates parent process while (c) indicates child process.):
     423
     424    - p: fork() is called. It starts by push all registers (including fpu)
     425         onto the stack and recording the address of that stuff (in the
     426         fork handle when it's initiated).
     427    - p: fork() allocates the shared fork memory and initiatlizes it, thus
     428         creating the fork handle.
     429        - p: fork() calls helper for copying the memory allocations records
     430             to the fork buffer. (Those are FIFO and if there are too many for
     431                 the fork buffer they should be completed after DosExecPgm returns.)
     432    - p: fork() walks the list of registered modules and calls the callback
     433         function asking if it's ok to fork now.
     434         Note: This will work for processes with multiple libc dlls because
     435                       the list head is in the process block.
     436    - p: fork() allocates and initiates the process block of the child process
     437         entering the fork handle and linking it into the embryo list.
     438    - p: fork() takes the exec semaphore.
     439    - p: fork() spawns a child process taking the executable name from the PIB
     440         and giving it "!fork!" as argument.
     441    - c: During DosExecPgm all dll0(hi)fork's will be called, and for forkable
     442         modules __libc_ForkRegisterModule() is called and returns 1. The
     443         init code will the return successfully and not call the _DLL_InitTerm()
     444         or any other DLL init code.
     445        - c: __libc_ForkRegisterModule() will first check if the process block
     446             have been found (global LIBC pointer), and if not try locate it
     447             or allocate a new one. It will the check the fork handle.
     448             Once found it will add the module to the list of forkable modules.
     449             If the fork handle member is not NULL a fork operation is in
     450             progress and the module callback is called with a check if the
     451             module still thing fork is ok and give it a chance to do dllinit
     452             time preperations.
     453             The operations returns 1 if we're working, 0 if we're not working
     454             and -1 if we failed in some way the the dllinit should fail.
     455                         - c: The first time the process block is found and it's forking
     456                              a helper for processing the memory allocations in the
     457                                  for buffer is called.
     458                                  This *must* be done as early as possible!!!
     459    - p: The child have successfully initiated, DosExecPgm/DosStartSession
     460         returns NO_ERROR.
     461    - c: Child blocks while trying to get access to the fork handle (crt0).
     462         (Blocks on fork handle child event semaphore.)
     463    - p: Parent sets the operation enum member of the fork handle to signal
     464         an init operation. It resets the parent event sem member, releases
     465         the mutex, and goes to sleep for a defined max fork() timeout on the
     466         event sem. (The max could be, let's say, 30 seconds.)
     467    - c: The child get the ownership of the fork handle mutex.
     468    - c: If the process is statically linked the memory allocations should
     469         be done now (meaning do it here if not done already).
     470    - c: The child resets the child event sem, posts the parent event sem
     471         and releases the mutex.
     472        - p: fork() acquires the fork handle ownership and checks the return code
     473         from the child.
     474    - p: fork() walks the list of modules calling the callbacks with the
     475         do parent fork operation. This means the each register module will
     476         do what's necessary for replicting it self into the child process
     477         so that it will work as expected there after the fork() returns.
     478         This is done using the primitives. This is the only time the
     479         parent can use the currently defined primitives.
     480    - p: Buffer flush is preformed. This may happen multiple times, but
     481         fork() will allways finish of by doing one after all callbacks
     482         have been called.
     483         The buffer flush means passing the current fork buffer content
     484         to the child for processing. pfnFlush() does this.
     485            - p: pfnFlush() sets the fork handle operation enum to
     486                 process buffer, resets the parent event sem, signals the
     487                 child event semaphore and releases the mutex.
     488            - c: Child takes ownership of the fork handle and performs
     489                 the actions recorded in the fork buffer.
     490            - c: The total result is put in the result member, other stuff
     491                 might may be put in the buffer but that's currently not
     492                 defined. The fork buffer is then transfered to the parent.
     493            - p: pfnFlush() wakes up and reclaims the fork handle ownership
     494                 and returns the value of the result member.
     495                 What it does if the buffer contains data is currently not
     496                 defined.
     497    - p: Once all callbacks have been successfully executed, the stack is
     498         copied to the child. We copy all committed stack pages. The fork()
     499         return stack address is also passed to the child.
     500         NOTE: this step may be relocated to an earlier phase!
     501    - c: Child gets the stack and copies it in two turns, first the upper
     502         part (above current esp/fork return stack address), then it relocate
     503         it self on the stack so that it's ready for returning, before it
     504         copies the low part of the stack.
     505         (low/high here is not address value but logical stack view.)
     506    - c: Iterate the modules calling the callbacks signaling fork child
     507         operation. the callbacks then have the option to iterate the
     508         _CRT_FORK_CHILD1 vector.
     509    - c: Calls any completion callbacks registered.
     510    - c: Put's result code and passes the fork handle to parent after first
     511         freeing its mapping of the shared memory and semphores (not all sems
     512         first of course).
     513    - c: Returns from fork() restoring all registers but setting eax (return
     514         value) to zero indicating child.
     515    - p: fork() calls any completion callbacks registered.
     516    - p: fork() frees the fork handle and related resources and returns
     517         the pid of the child process. (not restoring registers)
     518
     519
     520
     5214.4 Forking LIBC
     522----------------
     523
     524To make LIBC forkable a bunch of things have to be fixed up in the child
     525process. Some of these must be done in a certain order other doesn't have to.
     526To solve this we are using set vectors (as we do for init and term, see
     527emx/startup.h). The fork set vectors are be defined in InnoTekLIBC/fork.h
     528and be called _CRT_FORK_PARENT1() and _CRT_FORK_CHILD1(). They take two
     529arguments, the callback and the priority. Priority is 0 to 4G-1 where 4G-1
     530is the highest priority. The set vectors are started in crt0/dll0 called
     531___crtfork_parent1__, ___crtfork_chidl1__
     532
     533The _atfork_callback() iterates the set vectors when called for doing
     534parent and child fork stuff.
     535
     536
     537The _atfork_callback() have this prototype:
     538    /**
     539     * Called multiple times during fork() both in the parent and the child.
     540     *
     541     * The default LIBC implementation will:
     542     *      1) schedule the data segment for duplication.
     543     *      2) do ordered LIBC fork() stuff.
     544     *      3) do unordered LIBC fork() stuff, _CRT_FORK1 vector.
     545     *
     546     * @returns 0 on success.
     547     * @returns appropriate non-zero error code on failure.
     548     * @param   pModule         Pointer to the module record which is being
     549     *                          processed.
     550         * @param   pForkHandle     Handle of the current fork operation.
     551     * @param   enmOperation    Which callback operation this is.
     552     *                          Any value can be used, the implementation
     553     *                          of this function must just respond to the
     554     *                          one it knows and return successfully on the
     555     *                          others.
     556     *                          Operations:
     557     *                              __LIBC_FORK_CHECK_PARENT
     558     *                              __LIBC_FORK_CHECK_CHILD
     559     *                              __LIBC_FORK_FORK_PARENT
     560     *                              __LIBC_FORK_FORK_CHILD
     561     */
     562     int _atfork_callback(__LIBC_FORKMODULE *pModule, __LIBC_FORKHANDLE *pForkHandle,
     563                          enum __LIBC_CALLBACKOPERATION enmOperation);
     564
     565The _CRT_FORK_*1() callbacks have this declaration:
     566    /**
     567     * Called once in the parent during fork().
     568     * This function will use the fork primitives to move data and invoke
     569     * functions in the child process.
     570     *
     571     * @returns 0 on success.
     572     * @returns appropriate non-zero error code on failure.
     573     * @param   pModule         Pointer to the module record which is being
     574     *                          processed.
     575         * @param   pForkHandle     Handle of the current fork operation.
     576     * @param   enmOperation    Which callback operation this is.
     577     *                          !Important! Later versions of LIBC may call
     578     *                          this callback more than once. This parameter
     579     *                          will indicate what's going on.
     580     */
     581     int _atfork_callback(__LIBC_FORKMODULE *pModule, __LIBC_FORKHANDLE *pForkHandle,
     582                          enum __LIBC_CALLBACKOPERATION enmOperation);
     583
     584       
     585
     586
     5874.4.1 Heap Memory
     588-----------------
     589
     590The LIBC heaps will use the extended DosAllocMemEx. This means that the
     591memory used by the heaps will be reserved at LIBC init time and duplicated
     592as the very first thing in the LIBC _atfork_callback().
     593
     5944.4.1.1 DosAllocMemEx
     595---------------------
     596
     597TODO. Fixed address allocation and allocation recording.
     598
     5994.4.1.2 DosFreeMemEx
     600--------------------
     601
     602TODO
     603
     604
     6054.4.2 Semaphores
     606----------------
     607
     608Use _CRT_FORK_*1() for each of the LIBC semaphores, or we'll make an extended
     609API, DosCreateEventSemEx/DosCreateMutexSemEx, for semaphores as we do with
     610memory APIs.
     611
     612There is a forkable class of semaphores, we might wanna kick that out if
     613we decide to use extended APIs (which I guess we'll do).
     614
     615
     616
     6174.4.3 Filehandles
     618-----------------
     619
     620Two imporant things, 1) _all_ handles are inherited (even the close-on-exec ones),
     621and 2) non-OS/2 handles must be inherited too.
     622
     623For 1) we must temporarily change the inherit flag on all the close-on-exec
     624flagged handles during the fork.
     625The major question here is when we're gonna restore the OS/2 no inherit flag
     626for close-on-exec handles. The simplest option option is a
     627__LIBC_FORK_DONE_PARENT _atfork_callback() operation. The best option is to
     628have a primitive to registering fork-done routines (which are called both
     629on success and failure).
     630
     631For 2) we need to extend the libc filehandle operation interface to include
     632atfork operation. This will be called for every non-standard handle and it
     633must it self use the fork primitives to cause something to happen in the
     634child. This is not the fastest way, but it's the most flexible one and it's
     635one which is probably will work too.
     636
     637
     6384.4.3.1 Sockets
     639---------------
     640
     641Implement the atfork handle operation. Use the fork primitives to invoke
     642a function in the child duplicating that handle. The function invoked in
     643the child basically use adds the socket to the socket list of the new
     644process (tcpip api for this).
     645
     646Note that WS4eB tcpip level have a bug in the api adding sockets to a
     647process. The problem is that a socket cannot be added twice, it'll cause
     648an breakpoint instruction.
     649
     650
     651
     6524.4.4 Locale/Iconv/Stuff
     653------------------------
     654
     655Record and create once again in the child. Use _CRT_FORK_*1() to register
     656callbacks. Requires a little bit of recording of create parameters in the
     657iconv() case but that's nothing spectacular.
     658
     659
     660
     661
     662
     6638.0     Coding Conventions
     664----------------------
     665       
     666New LIBC stuff uses these conventions:
     667    - Full usage of hungarian prefixes. Details on this is found in the
     668      old odin web pages and gradd docs (IIRC).
     669    - As much as possible shall be static. I.e. static int internalhelper(void).
     670    - Internal global function and variables shall be prefixed __libc_[a-z] or
     671      _sys_ depending on what it implements. Will not be exported by LIBCxy.DLL.
     672    - Non-standard LIBC functionality is prefixed __libc_[A-Z].
     673    - Stuff defined in SuS is wrapped by _STD() so that we get __std_ prefix
     674      and the usual two aliases (plain and underscored).
     675    - No warnings.
     676       
     677
     6789.0 Abbreviation and such
     679-------------------------
     680
     681SuS
     682    The Single Unix Specification version 6 as published on www.opengroup.org.
     683       
     684       
Note: See TracChangeset for help on using the changeset viewer.