Changeset 1281
- Timestamp:
- Mar 6, 2004, 10:48:26 PM (21 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/doc/Fork.os2
-
Property cvs2svn:cvs-rev
changed from
1.2
to1.3
r1280 r1281 162 162 3. Extended versions of some memory related OS/2 APIs must be implemented. 163 163 164 The implemen etation will further make the following assumption about the164 The implementation will further make the following assumption about the 165 165 operation of OS/2: 166 166 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 :/ ) 168 169 169 170 … … 171 172 --------------------------------- 172 173 173 The fork() implementation requires a method oftelling the child process174 The fork() implementation requires a method for telling the child process 174 175 that it's being forked and must take a very different startup route. For 175 some other LIBC apis there isneed for parent -> child and child -> parent176 some other LIBC apis there are need for parent -> child and child -> parent 176 177 information exchange. More specifically, the inheritance of sockets, 177 178 signals, the different scheduler actions of a posix_spawn[p]() call, and 178 179 possibly some process group stuff related to posix_spawn too if we get it 179 180 figured out eventually. All this was parent -> child during spawn/fork. A 180 need exist alsofor child -> parent notification and possibly exchange for181 need also exist for child -> parent notification and possibly exchange for 181 182 process termination. It might be necessary to reimplement the different 182 183 wait apis and implement SIGCHLD, it's likely that those tasks will make … … 184 185 185 186 The 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. 187 specific to each LIBC version as a shared segement or try to make it 188 survive normal LIBC updates. Making is specific have advantages in code 189 size and memory footprint (no reserved fields), however it have certain 190 disadvantages when LIBC is updated. The other option is to use a named 191 shared memory object, defining the content with reserved space for later 192 extensions so several versions of LIBC with more or less features 193 implemented can co use the memory space. 192 194 193 195 The latter option is prefered since it allows more applications to … … 196 198 processes using multiple versions of LIBC. 197 199 198 The shared memory mustbe named \SHAREMEM\INNOTEKLIBC.V01, the version200 The shared memory shall be named \SHAREMEM\INNOTEKLIBC.V01, the version 199 201 number being the one of the shared memory layout and contents, it will 200 202 only be increased when incompatible changes are made. 201 203 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 low204 and the result of a messup may be disastrous. Care must be take for204 The shared memory shall be protected by an standard OS/2 mutex semaphore. 205 It shall not use any fast R3 semaphore since the the usage frequency is 206 low and the result of a messup may be disastrous. Care must be take for 205 207 avoiding creation races and owner died scenarios. 206 208 207 The memory will have a fixed size, since adding segments is very hard.209 The memory shall have a fixed size, since adding segments is very hard. 208 210 Thus the size must be large enough to cope with a great deal of 209 processes, butbearing in mind that OS/2 normally doesn't support more211 processes, while bearing in mind that OS/2 normally doesn't support more 210 212 than a 1000 processes, with a theoritical max of some 4000 (being the 211 213 max thread count). A very simplistic allocation scheme will be … … 214 216 lists a linked list based heap would do fine. 215 217 216 The process blocks will be rounded up to in size adding a reasonable218 The process blocks shall be rounded up to in size adding a reasonable 217 219 amount of space resevered for future extensions. Reserved space must be 218 220 all zeroed. 219 221 220 The fork() specific members of the process block will be a pointer to222 The fork() specific members of the process block shall be a pointer to 221 223 the shared memory object for the fork operation (the fork handle) and 222 224 list of forkable modules. The fork handle will it self contain … … 227 229 the forking of each module. (more details in section 4.0) 228 230 229 The parent will before spawn, fork and exec (essentially before DosExecPgm231 The parent shall before spawn, fork and exec (essentially before DosExecPgm 230 232 or 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 233 link it into an embryo list in the shared memory block. The child shall 234 find it's process block by searching the embryo list using the parent pid 235 as key. All DosExecPgm and DosStartSession calls shall be serialized within 236 one LIBC version. (If some empty headed programmer manages to link together 237 a program which may end up using two or more LIBC versions and having two 238 or more thread doing DosExecPgm at the very same time, well then he really 239 deserves what ever trouble he gets! At least don't blame me!) 240 241 Process blocks shall have to stay around after the process terminated 241 242 (for child -> parent term exchange), a cleanup mechanism will be invoked 242 243 whenever a free memory threshold is reached. All processes will register … … 250 251 251 252 252 The implementation will bebased on a fork handle and a set of primitives.253 The implementation is based on a fork handle and a set of primitives. 253 254 The fork handle is a pointer to an shared memory object allocated for the 254 255 occation and which will be freed before fork() returns. The primitives … … 261 262 262 263 The support for fork() is an optional feature of LIBC. The default 263 executable produced with LIBC and GCC willnot be forkable. The fork264 executable produced with LIBC and GCC is not be forkable. The fork 264 265 support will be based on registration of the DLLs and EXEs in their 265 266 LIBC supplied startup code (crt0/dll0). A set of fork versions of these 266 modules will be made.267 modules exist with the suffix 'fork.o'. 267 268 268 269 The big differnece between the ordinary crt0/dll0 and the forkable … … 270 271 handling of the return code of that call. 271 272 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 273 The 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 296 The fork callback function which crt0/dll0 references when initializing 297 the fork modules structure is called _atfork_callback. It takes the fork 279 298 handle, module structure, and an operation enum as arguments. LIBC will 280 299 contain 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. 300 duplicates the data segment, and processes the two set vectors 301 (_CRT_FORK_*1). 302 303 crt0/dll0 will register the fork module structure and detect a forked 304 child by calling __libc_ForkRegisterModule(). 305 306 Prototypes: 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 288 328 289 329 … … 292 332 293 333 These primitives are provided by the fork implementation in the fork 294 handle structure. We will define a set of these primitives now, if295 laternew ones are added the users of these must check that they are334 handle structure. We define a set of these primitives now, if later 335 new ones are added the users of these must check that they are 296 336 actually present. 297 337 … … 347 387 */ 348 388 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); 349 414 ... 415 416 417 418 4.3 The Flow Of A fork() Operation 419 ---------------------------------- 420 421 When 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 521 4.4 Forking LIBC 522 ---------------- 523 524 To make LIBC forkable a bunch of things have to be fixed up in the child 525 process. Some of these must be done in a certain order other doesn't have to. 526 To solve this we are using set vectors (as we do for init and term, see 527 emx/startup.h). The fork set vectors are be defined in InnoTekLIBC/fork.h 528 and be called _CRT_FORK_PARENT1() and _CRT_FORK_CHILD1(). They take two 529 arguments, the callback and the priority. Priority is 0 to 4G-1 where 4G-1 530 is the highest priority. The set vectors are started in crt0/dll0 called 531 ___crtfork_parent1__, ___crtfork_chidl1__ 532 533 The _atfork_callback() iterates the set vectors when called for doing 534 parent and child fork stuff. 535 536 537 The _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 565 The _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 587 4.4.1 Heap Memory 588 ----------------- 589 590 The LIBC heaps will use the extended DosAllocMemEx. This means that the 591 memory used by the heaps will be reserved at LIBC init time and duplicated 592 as the very first thing in the LIBC _atfork_callback(). 593 594 4.4.1.1 DosAllocMemEx 595 --------------------- 596 597 TODO. Fixed address allocation and allocation recording. 598 599 4.4.1.2 DosFreeMemEx 600 -------------------- 601 602 TODO 603 604 605 4.4.2 Semaphores 606 ---------------- 607 608 Use _CRT_FORK_*1() for each of the LIBC semaphores, or we'll make an extended 609 API, DosCreateEventSemEx/DosCreateMutexSemEx, for semaphores as we do with 610 memory APIs. 611 612 There is a forkable class of semaphores, we might wanna kick that out if 613 we decide to use extended APIs (which I guess we'll do). 614 615 616 617 4.4.3 Filehandles 618 ----------------- 619 620 Two imporant things, 1) _all_ handles are inherited (even the close-on-exec ones), 621 and 2) non-OS/2 handles must be inherited too. 622 623 For 1) we must temporarily change the inherit flag on all the close-on-exec 624 flagged handles during the fork. 625 The major question here is when we're gonna restore the OS/2 no inherit flag 626 for close-on-exec handles. The simplest option option is a 627 __LIBC_FORK_DONE_PARENT _atfork_callback() operation. The best option is to 628 have a primitive to registering fork-done routines (which are called both 629 on success and failure). 630 631 For 2) we need to extend the libc filehandle operation interface to include 632 atfork operation. This will be called for every non-standard handle and it 633 must it self use the fork primitives to cause something to happen in the 634 child. This is not the fastest way, but it's the most flexible one and it's 635 one which is probably will work too. 636 637 638 4.4.3.1 Sockets 639 --------------- 640 641 Implement the atfork handle operation. Use the fork primitives to invoke 642 a function in the child duplicating that handle. The function invoked in 643 the child basically use adds the socket to the socket list of the new 644 process (tcpip api for this). 645 646 Note that WS4eB tcpip level have a bug in the api adding sockets to a 647 process. The problem is that a socket cannot be added twice, it'll cause 648 an breakpoint instruction. 649 650 651 652 4.4.4 Locale/Iconv/Stuff 653 ------------------------ 654 655 Record and create once again in the child. Use _CRT_FORK_*1() to register 656 callbacks. Requires a little bit of recording of create parameters in the 657 iconv() case but that's nothing spectacular. 658 659 660 661 662 663 8.0 Coding Conventions 664 ---------------------- 665 666 New 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 678 9.0 Abbreviation and such 679 ------------------------- 680 681 SuS 682 The Single Unix Specification version 6 as published on www.opengroup.org. 683 684 -
Property cvs2svn:cvs-rev
changed from
Note:
See TracChangeset
for help on using the changeset viewer.