èããŠïŒ
çµå±ã®ãšãããæãç¹ç¯ããŠããå Žåã誰ãããããå¿ èŠãšããŸããïŒ
V.V.ãã€ã³ãã¹ããŒã1914
ç§ã¯çµã¿èŸŒã¿ã·ã¹ãã ã®ããã°ã©ãã³ã°ã«æºãã£ãŠããã forkïŒïŒããã³vforkïŒïŒã·ã¹ãã ã³ãŒã«ã䜿çšããåé¡ãããããç解ããããã«ããã®èšäºãæžãããšã«ããŸããã ãããã®2çªç®ã¯ãã°ãã°äœ¿çšããªãããã«å§ããããŸããã圌ãçç±ã§çŸããããšã¯æããã§ãã
ç¹å®ã®åŒã³åºãã䜿çšããæ¹ãè¯ãçç±ãšçç±ãèŠãŠã¿ãŸãããã
ããŸããšããŠã ãããžã§ã¯ãã§ã®vforkïŒïŒ / forkïŒïŒå®è£ ã®èª¬æãæäŸãããŸã ã ãŸããç§ã®èå³ã¯çµã¿èŸŒã¿ã·ã¹ãã ã§ã®ãããã®åŒã³åºãã®äœ¿çšã«é¢é£ããŠããããããã®å®è£ ã®äž»ãªæ©èœã¯ä»®æ³ã¡ã¢ãªã®äžè¶³ã§ãã ãããããã·ã¹ãã ããã°ã©ãã³ã°ãšçµã¿èŸŒã¿ã·ã¹ãã ã«ç²ŸéããŠããããããã¹ã¯ã®äœæ°ãã¢ããã€ã¹ãæäŸããçµéšãå ±æããã§ãããã
ç«ã®äžã§èª°ãæ°ã«ããŠãã ããã
å®çŸ©ãã€ãŸãããããã®é¢æ°ãå®çŸ©ãããŠããPOSIXæšæºããå§ããŸãã
forkïŒïŒã¯ãããã€ãã®å€æ°ãé€ããããã»ã¹ã®æ£ç¢ºãªã³ããŒãäœæããŸãã æ£åžžã«å®è¡ããããšãé¢æ°ã¯ãŒãã®å€ãåããã»ã¹ã«è¿ããåããã»ã¹ã®æ°ã芪ã«è¿ããŸãïŒãã®åŸãããã»ã¹ã¯ãèªåã®ç掻ããå§ããŸãïŒã
vforkïŒïŒã¯ forkïŒïŒãšããŠå®çŸ©ãããŸããã次ã®å¶éããããŸãããã«ãã§äœæãããããã»ã¹ã次ã®ã¢ã¯ã·ã§ã³ã®å°ãªããšã1ã€ãå®è¡ããå Žåãé¢æ°ã®åäœã¯å®çŸ©ãããŸããã
- vforkïŒïŒãåŒã³åºãããé¢æ°ããæ»ããŸãã
- _exitïŒïŒãŸãã¯exec *ïŒïŒä»¥å€ã®é¢æ°ãåŒã³åºããŸãã
- vforkïŒïŒé¢æ°ã«ãã£ãŠè¿ãããå€ãä¿åãããŠããå€æ°ä»¥å€ã®ããŒã¿ãå€æŽããŸãã
ãã®ãããªåŒ·ãå¶éãæã€ã·ã¹ãã ã³ãŒã«ãããçç±ãç解ããã«ã¯ãããã»ã¹ã®æ£ç¢ºãªã³ããŒãäœã§ããããç解ããå¿ èŠããããŸãã
ãã·ã¢èªã®ãã®ãããã¯ã«é¢ããæ€çŽ¢ãšã³ãžã³ã®æåã®ãªã³ã¯ã®1ã€ã¯ã Linuxã®ããã»ã¹ã¯ããŒã³ãªãã·ã§ã³ã®èª¬æã§ãã äžéšã®ãã©ã¡ãŒã¿ãŒã¯ã芪ããã»ã¹ãšåããã»ã¹ã«å ±éã«ããããšãã§ããŸãã
- ã¢ãã¬ã¹ç©ºéïŒCLONE_VMïŒ;
- ãã¡ã€ã«ã·ã¹ãã æ å ±ïŒCLONE_FSïŒ;
- éããŠãããã¡ã€ã«ã®ããŒãã«ïŒCLONE_FILESïŒ;
- ã·ã°ãã«ãã³ãã©ãŒããŒãã«ïŒCLONE_SIGHANDïŒ;
- 芪ããã»ã¹ïŒCLONE_PARENTïŒã
POSIXã§ã¯ã vforkïŒïŒã®å€æ°ãå€æŽã§ããŸãããããã¯ãã¯ããŒãã³ã°ãã¢ãã¬ã¹ç©ºéã§ããããšã瀺åããŠããŸãã ãã®ãªã³ã¯ã¯ä»®å®ã確èªããŸãïŒ
forkïŒïŒãšã¯ç°ãªãã vforkïŒïŒã¯èŠªããã»ã¹ã®ã³ããŒãäœæããŸãããã _exité¢æ°ãŸãã¯execé¢æ°ã®ãããããåŒã³åºããããŸã§ã芪ããã»ã¹ãšå ±æãããã¢ãã¬ã¹ç©ºéãäœæããŸãã
芪ããã»ã¹ã¯ãã®æç¹ã§å®è¡ãåæ¢ããŸãã ããããã䜿çšã«é¢ãããã¹ãŠã®å¶éãç¶ããŸã-åããã»ã¹ã¯ã芪ããã»ã¹ãšå ±æãããã°ããŒãã«å€æ°ãäžè¬å€æ°ãããå€æŽã§ããŸããã
ã€ãŸãããã®ã¹ããŒãã¡ã³ããtrueã®å Žåã vforkïŒïŒãåŒã³åºããåŸãäž¡æ¹ã®ããã»ã¹ã§åãããŒã¿ã衚瀺ãããŸãã
å®éšããŠã¿ãŸãããã ãããåœãŠã¯ãŸãå Žåãçæãããããã»ã¹ã®ããŒã¿ã«å ããããå€æŽã¯ã芪ããã»ã¹ã«è¡šç€ºããããã®éãåæ§ã§ãã
ä»®å®ããã§ãã¯ããã³ãŒãã
static int create_process(void) { pid_t pid; int status; int common_variable; common_variable = 0; pid = fork(); if (-1 == pid) { return errno; } if (pid == 0) { /* */ common_variable = 1; exit(EXIT_SUCCESS); } waitpid(pid, &status, 0); if (common_variable) { puts("vfork(): common variable has been changed."); } else { puts("fork(): common variable hasn't been changed."); } return EXIT_SUCCESS; } int main(void) { return create_process(); }
ãã®ããã°ã©ã ããã«ãããŠå®è¡ãããšãçµè«ãåŸãããŸãã
forkïŒïŒïŒå ±éå€æ°ã¯å€æŽãããŠããŸããã
forkïŒïŒãvforkïŒïŒã«çœ®ãæãããšãåºåãå€ãããŸãïŒ
vforkïŒïŒïŒå ±éå€æ°ãå€æŽãããŸããã
å€ãã®äººã¯ãã®ããããã£ã䜿çšããŠããã»ã¹éã§ããŒã¿ã転éããŸããããã®ãããªããã°ã©ã ã®åäœã¯POSIXã«ãã£ãŠå®çŸ©ãããŠããŸããã ããã«ããã vforkïŒïŒã䜿çšããªãããšããå§ãããŸãã
å®éãéçºè ãæèçã«å€æ°ã®å€ãå€æŽããããšãšãåããã»ã¹ãvforkïŒïŒãåŒã³åºãããé¢æ°ããæ»ãããšãã§ããªãããšãå¿ãããšãããã¯å¥ã®ããšã§ãïŒããã¯èŠªããã»ã¹ã®ã¹ã¿ãã¯æ§é ãç Žå£ããããã§ãïŒ ïŒïŒã ãããŠããã€ãã®ããã«æ æã«è¡åããããšãããããªãèªèº«ã®å±éºãšãªã¹ã¯ã§ææžåãããŠããªãæ©äŒã䜿çšããŸãã
次ã«ãããã»ã©æçœã§ãªãåé¡ãããã€ã瀺ããŸãã
- ãSecure Programming for Linux and Unix HOWTOã ãšããæ¬ã¯ ãããšãåããã»ã¹ãé«ã¬ãã«èšèªã³ãŒãã®ããŒã¿ãå€æŽããªããŠãããã·ã³ã³ãŒãã®å Žåã¯ããã§ã¯ãªããããããªããšè¿°ã¹ãŠããŸãïŒããšãã°ãé ãããäžæå€æ°ã®åºçŸã®ããïŒã
- ãã®ããã°ã§ã¯ã次ã®è³ªåãåæããŸããvforkïŒïŒããã«ãã¹ã¬ããã¢ããªã±ãŒã·ã§ã³ã§åŒã³åºãããå Žåã¯ã©ããªããŸããïŒ Linuxã§ã®vforkïŒïŒã®å®è£ ãæ€èšããŠãã ãããããã¥ã¢ã«ã§ã¯ã芪ããã»ã¹ã¯åŒã³åºããããšãã«åæ¢ãããšè¿°ã¹ãŠããŸãããå®éã«ã¯çŸåšã®ã¹ã¬ããã§ã®ã¿çºçããŸãïŒãã¡ããå®è£ ã¯ç°¡åã§ãïŒã ããã¯ãåããã»ã¹ãä»ã®ã¹ã¬ãããšäžŠè¡ããŠå®è¡ãç¶ããããšãæå³ããäŸãã°ã芪ããã»ã¹ã®æš©éãå€æŽã§ããŸãã ãããŠãããã§ã¯ãã¹ãŠãéåžžã«æªããªããŸããåãã¢ãã¬ã¹ç©ºéã§ç°ãªãæš©éãæã€2ã€ã®ããã»ã¹ãååŸããã»ãã¥ãªãã£ããŒã«ãéããŸãã
ããã§ã exec *ãã¡ããªãŒã®æ©èœãæ€èšããŸãã vforkïŒïŒã䜿çšããŠååŸããããã»ã¹ã§åŒã³åºãããšãã§ããã®ã¯ããããïŒ _exitïŒïŒãã«ãŠã³ãããªãïŒã®ã¿ã§ãã æ°ããã¢ãã¬ã¹ç©ºéãäœæããæå®ããããã¡ã€ã«ããã³ãŒããšããŒã¿ãããŒãããŸãã åæã«ãå€ãã¢ãã¬ã¹ç©ºéã¯æ¬è³ªçã«ç Žå£ãããŸãã
ãã®ããã forkïŒïŒã䜿çšããŠããã»ã¹ãäœæããŠããexec *ïŒïŒãåŒã³åºãã forkïŒïŒãåŒã³åºããšãã«ã¢ãã¬ã¹ç©ºéãäœæïŒã³ããŒïŒããããšã¯åé·ã§ãããããã¯ããªãæéã®ãããæäœã§ããã forkãåŒã³åºãã®ã«æéããããå ŽåããããŸãïŒïŒ ã ããšãã°ããŠã£ãããã£ã¢ã§ã¯ããã®ç¬éãæã泚ç®ãããŠãããæšæºãšã¯ç°ãªãã次ã®ããã«æ瀺ãããŠããŸãïŒ
forkïŒïŒæäœã¯ãåçšã«åå¥ã®ã¢ãã¬ã¹ã¹ããŒã¹ãäœæããŸãã åããã»ã¹ã«ã¯ã芪ããã»ã¹ã®ãã¹ãŠã®ã¡ã¢ãªã»ã°ã¡ã³ãã®æ£ç¢ºãªã³ããŒããããŸãã
ãã¡ãããä»®æ³ã¡ã¢ãªãåããã»ãšãã©ã®ææ°ã·ã¹ãã ã§ã¯ãã³ããŒã¯è¡ãããŸãã;芪ããã»ã¹ã®ã¡ã¢ãªã®ãã¹ãŠã®ããŒãžã¯ã ã³ããŒãªã³ã©ã€ããã©ã°ã§ããŒã¯ãããŠããŸã ã ãã ããããŒãã«ã®éå±€å šäœã調ã¹ãå¿ èŠããããããã«ã¯æéãããããŸãã
vforkïŒïŒåŒã³åºãã¯forkïŒïŒãããé«éã§ããããšãå€æããŠããŸã ãããã¯LinuxManããŒãžã«ãèšèŒãããŠããŸã ã
å¥ã®å®éšãè¡ãããããå®éã«ããã§ããããšã確èªããŸãã åã®äŸãå°ãå€æŽããŸãããµã€ã¯ã«ãè¿œå ããŠ1000åã®ããã»ã¹ãäœæããå ±éå€æ°ãåé€ããŠç»é¢ã«è¡šç€ºããŸãã
åä¿¡ããã³ãŒãã
#include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <sys/wait.h> static int create_process(void) { pid_t pid; int status; pid = vfork(); if (-1 == pid) { return errno; } if (pid == 0) { /* child */ exit(EXIT_SUCCESS); } waitpid(pid, &status, 0); return EXIT_SUCCESS; } int main(void) { int i; for (i = 0; i < 1000; i ++) { create_process(); } return EXIT_SUCCESS; }
timeã³ãã³ããå®è¡ããŸãã
forkïŒïŒäœ¿çšæã®åºå | vforkïŒïŒäœ¿çšæã®åºå |
|
|
æ§ããã«èšã£ãŠããçµæã¯å°è±¡çã§ãã éå§ããéå§ãŸã§ãããŒã¿ã¯ãããã«ç°ãªããŸããã vforkïŒïŒã¯4ã5åéããªããŸãã
çµè«ã¯æ¬¡ã®ãšããã§ãã
forkïŒïŒã¯ããéãåŒã³åºãã§ããã vforkïŒïŒãåŒã³åºãããšãã§ããå Žåã¯ã䜿çšããããšããå§ãããŸãã
vforkïŒïŒã¯å®å šæ§ã®äœãåŒã³åºãã§ãããèªåã§è¶³ãæã¡ãããã®ã§ãããã«å¿ããŠææ矩ã«äœ¿çšããå¿ èŠããããŸãã
forkïŒïŒ / vforkïŒïŒã¯ãããã»ã¹çšã«åå¥ã®ãªãœãŒã¹ïŒinodeããŠãŒã¶ãŒãäœæ¥ãã©ã«ããŒïŒãäœæããå¿ èŠãããå Žåã«äœ¿çšããå¿ èŠããããŸãã
forkïŒïŒã¯ ãå¥ã®ã¢ãã¬ã¹ã¹ããŒã¹ãäœæããå¿ èŠãããå Žåã«æé©ã§ãã ãã ããä»®æ³ã¡ã¢ãªã®ããŒããŠã§ã¢ãµããŒããªãã§å°èŠæš¡ãªããã»ããµãã©ãããã©ãŒã ã«å®è£ ããããšã¯éåžžã«å°é£ã§ãã
èšäºã®2çªç®ã®éšåã«é²ãåã« ãPOSIXã«ã¯posix_spawnïŒïŒé¢æ°ãããããšã«æ³šæããŠãã ããã å®éããã®é¢æ°ã«ã¯vforkïŒïŒãšexecïŒïŒãå«ãŸããŠããããã forkïŒïŒã®ããã«ã¢ãã¬ã¹ç©ºéãåäœæããªããŠãã vforkïŒïŒã«é¢é£ããåé¡ãåé¿ã§ããŸãã
次ã«ãMMUããµããŒãããªãforkïŒïŒ / vforkïŒïŒã®å®è£ ã«é²ã¿ãŸãããã
Vforkã®å®è£
ã·ã¹ãã ã«vforkïŒïŒãå®è£ ãããšãã vforkïŒïŒã®åŒã³åºãã¯æ¬¡ã®ããã«ãã¹ãã§ãããšæ³å®ããŸããïŒèŠªã¯ã¹ã¿ã³ãã€ã¢ãŒãã«ãªããåããã»ã¹ã¯æåã«vforkïŒïŒããè¿ããã _exitïŒïŒãŸãã¯exec *ïŒïŒé¢æ°ãåŒã³åºããããšãã«èŠªãåŒã³èµ·ãããŸã ã€ãŸããåå«ã¯èŠªã¹ã¿ãã¯ã§å®è¡ã§ããŸãããä»ã®ã¿ã€ãã®ç¬èªã®ãªãœãŒã¹ïŒiããŒããã·ã°ãã«ã®ããŒãã«ãªã©ïŒã䜿çšã§ããŸãã
ãããžã§ã¯ãå ã®ããŸããŸãªã¿ã€ãã®ãªãœãŒã¹ã®ã¹ãã¬ãŒãžã¯ã¿ã¹ã¯ïŒ struct task ïŒã§ãã 䜿çšå¯èœãªã¡ã¢ãªãiããŒãããã®ããã»ã¹ã«å±ããã¹ã¬ããã®ãªã¹ããªã©ãããã»ã¹ã®ãã¹ãŠã®ãªãœãŒã¹ãèšè¿°ããã®ã¯ãã®æ§é ã§ãã ã¿ã¹ã¯ã«ã¯åžžã«ã¡ã€ã³ã¹ã¬ããããããŸã-åæåããããšäœæãããŸãã ç§ãã¡ã®ã·ã¹ãã ã®ã¹ããªãŒã ã¯ãèšç»ã®ãªããžã§ã¯ããšåŒã°ããŸããããã«ã€ããŠã¯ãç§ã®ååã®èšäºã§èª¬æããŠããŸãã ãããŒã¯ã¿ã¹ã¯ã§ã¯ãªãã¹ã¿ãã¯ãå¶åŸ¡ããããã2ã€ã®å®è£ ãªãã·ã§ã³ãæäŸã§ããŸãã
- æ°ããäœæãããã¹ã¬ããã®ã¹ã¿ãã¯ã芪ã¹ã¿ãã¯ã«å€æŽããŸãã
- åãå®è¡ã¹ã¬ããã®ã¿ã¹ã¯ãæ°ããã¿ã¹ã¯ã«ã眮ãæãããŸãã
äœããã®æ¹æ³ã§ãã¿ã¹ã¯ãäœæãããã芪ããç¶æ¿ããå¿ èŠããããŸããä¿¡å·ã®ããŒãã«ãç°å¢å€æ°ãªã©ãè€è£œãããŸãã ãã ããã¢ãã¬ã¹ç©ºéã¯ç¶æ¿ãããŸããã
vforkïŒïŒããã®æ»ãã¯ã芪ããã»ã¹ãšåããã»ã¹ã«å¯ŸããŠ2åè¡ãããŸãã ãã®ãããã©ããã§ã vforkïŒïŒãåŒã³åºãããã¹ã¿ãã¯ãã¬ãŒã ã®ã¬ãžã¹ã¿ãä¿åããå¿ èŠããããŸãã åããã»ã¹ãå®è¡æã«ãããã®å€ãäžæžãããå¯èœæ§ããããããããã¯ã¹ã¿ãã¯äžã§å®è¡ã§ããŸããã ãã ãã vforkïŒïŒã·ã°ããã£ã¯ãããã¡ã®ååšãæ瀺ããŠããªããããæåã«ã¬ãžã¹ã¿ãã¹ã¿ãã¯ã«ä¿åããããããã芪ã¿ã¹ã¯ã®ã©ããã«ä¿åãããŸãã ã¹ã¿ãã¯ãžã®ã¬ãžã¹ã¿ã®ä¿åã¯ã·ã¹ãã ã³ãŒã«ã䜿çšããŠå®è¡ã§ããŸãããããã䜿çšããã«ç¬èªã«å®è¡ããããšã«ããŸããã åœç¶ã vforkïŒïŒé¢æ°ã¯ã¢ã»ã³ãã©ãŒã§èšè¿°ãããŠããŸãã
i386ã¢ãŒããã¯ãã£çšã®ã³ãŒãã
vfork: subl $28, %esp; pushl %ds; pushl %es; pushl %fs; pushl %gs; pushl %eax; pushl %ebp; pushl %edi; pushl %esi; pushl %edx; pushl %ecx; pushl %ebx; movl PT_END(%esp), %ecx; movl %ecx, PT_EIP(%esp); pushf; popl PT_EFLAGS(%esp); movl %esp, %eax; addl $PT_END+4, %eax; movl %eax, PT_ESP(%esp); push %esp; call vfork_body
ãããã£ãŠãæåã«ã¬ãžã¹ã¿ãã¹ã¿ãã¯ã«ä¿åããã次ã«C çªç®ã®é¢æ°vfork_bodyïŒïŒãåŒã³åºãããŸãã åŒæ°ãšããŠãã¬ãžã¹ã¿ã®ã»ãããæã€æ§é äœãžã®ãã€ã³ã¿ãæž¡ãããŸãã
i386ã®æ§é ã«èšåããŸããã
typedef struct pt_regs { /* Pushed by SAVE_ALL. */ uint32_t ebx; uint32_t ecx; uint32_t edx; uint32_t esi; uint32_t edi; uint32_t ebp; uint32_t eax; uint32_t gs; uint32_t fs; uint32_t es; uint32_t ds; /* Pushed at the very beginning of entry. */ uint32_t trapno; /* In some cases pushed by processor, in some - by us. */ uint32_t err; /* Pushed by processor. */ uint32_t eip; uint32_t cs; uint32_t eflags; /* Pushed by processor, if switching of rings occurs. */ uint32_t esp; uint32_t ss; } pt_regs_t;
vfork_bodyïŒïŒã³ãŒãã¯ãã¢ãŒããã¯ãã£çã«ç¬ç«ããŠããŸãã 圌ã¯ã¿ã¹ã¯ãäœæããçµäºã«å¿ èŠãªã¬ãžã¹ã¿ãç¶æãã責任ããããŸãã
é¢æ°ã³ãŒãã¯vfork_bodyïŒïŒã§ãã
void __attribute__((noreturn)) vfork_body(struct pt_regs *ptregs) { struct task *child; pid_t child_pid; struct task_vfork *task_vfork; int res; /* can vfork only in single thread application */ assert(thread_self() == task_self()->tsk_main); /* create task description but not start its main thread */ child_pid = task_prepare(""); if (0 > child_pid) { /* error */ ptregs_retcode_err_jmp(ptregs, -1, child_pid); panic("vfork_body returning"); } child = task_table_get(child_pid); /* save ptregs for parent return from vfork() */ task_vfork = task_resource_vfork(child->parent); memcpy(&task_vfork->ptregs, ptregs, sizeof(task_vfork->ptregs)); res = vfork_child_start(child); if (res < 0) { /* Could not start child process */ /* Exit child task */ vfork_child_done(child, vfork_body_exit_stub, &res); /* Return to the parent */ ptregs_retcode_err_jmp(&task_vfork->ptregs, -1, -res); } panic("vfork_body returning"); }
ã³ãŒãã®ç°¡åãªèª¬æã
æåã«ããã«ãã¹ã¬ããããã§ãã¯ãããŸãïŒ vforkïŒïŒã䜿çšããå Žåã®ãã«ãã¹ã¬ããã«é¢é£ããåé¡ã¯äžèšã§èª¬æããŸããïŒã 次ã«ãæ°ããã¿ã¹ã¯ãäœæãããæåãããšã vforkïŒïŒããæ»ãããã®ã¬ãžã¹ã¿ããã®ã¿ã¹ã¯ã«æ ŒçŽãããŸãã
ãã®åŸã vfork_child_startïŒïŒé¢æ°ãåŒã³åºãããŸã ãããã¯ãååã瀺ãããã«ãåããã»ã¹ããéå§ãããŸãã ããã§ã®åŒçšã¯å¶ç¶ã§ã¯ãããŸãããå®éãã¿ã¹ã¯ã¯åŸã§èµ·åã§ããããããããžã§ã¯ãã«2ã€ããç¹å®ã®å®è£ ã«ãã¹ãŠäŸåããŸãã 説æã«é²ãåã«ãé¢æ°_exitïŒïŒããã³exec *ïŒïŒãæ€èšããŠãã ããã
åŒã³åºããããšã芪ã¹ã¬ããã¯ããã¯è§£é€ãããªããã°ãªããŸããã ãã®æç¹ã§ãåããã»ã¹ãã·ã¹ãã å ã®å¥ã®ãšã³ãã£ãã£ãšããŠéå§ãããŸãã
execvæ©èœã³ãŒã
exec *ãã¡ããªãŒã®ä»ã®æ©èœã¯ã execvïŒïŒã®åŒã³åºããéããŠè¡šçŸãããŸãã
int execv(const char *path, char *const argv[]) { struct task *task; /* save starting arguments for the task */ task = task_self(); task_resource_exec(task, path, argv); /* if vforked then unblock parent and start execute new image */ vfork_child_done(task, task_exec_callback, NULL); return 0; }
æ©èœã³ãŒã_exitïŒïŒ
void _exit(int status) { struct task *task; task = task_self(); vfork_child_done(task, task_exit_callback, (void *)status); task_start_exit(); { task_do_exit(task, TASKST_EXITED_MASK | (status & TASKST_EXITST_MASK)); kill(task_get_id(task_get_parent(task)), SIGCHLD); } task_finish_exit(); panic("Returning from _exit"); }
ããããäžèšã®ã³ãŒããããããããã«ã芪ããã»ã¹ã®ããã¯ã解é€ããããã«vfork_child_doneïŒïŒé¢æ°ã䜿çšããããã©ã¡ãŒã¿ãŒã®1ã€ãšããŠãã³ãã©ãŒã瀺ãããŸãã ç¹å®ã®äœæ¥ã¢ã«ãŽãªãºã ãå®è£ ããã«ã¯ã以äžãå®è£ ããå¿ èŠããããŸãã
- vfork_child_startïŒïŒ -ã¯ããŒã³äœæããã»ã¹ã®éå§æã«åŒã³åºãããé¢æ°ã¯ã芪ããã»ã¹ããããã¯ããå¿ èŠããããŸãã
- vfork_child_doneïŒïŒ -åããã»ã¹ã®æçµèµ·åæã«åŒã³åºãããé¢æ°ã芪ããã»ã¹ã¯ããã¯è§£é€ãããŠããŸãã
- task_exit_callbackïŒïŒ -åããã»ã¹ãå®äºãããã³ãã©ã
- task_exec_callbackïŒïŒ -åããã»ã¹ãå®å šã«èµ·åãããã³ãã©ãŒã
æåã®å®è£
æåã®å®è£ ã®èãæ¹ã¯ãåãã¹ã¿ãã¯ã«å ããŠåãå¶åŸ¡ãããŒã䜿çšããããšã§ãã å®éããã®å Žåã vfork_child_doneïŒïŒãåŒã³åºããããšãã«åã¿ã¹ã¯ãå®å šã«éå§ããããŸã§ãçŸåšã®ã¹ã¬ããã®ã¿ã¹ã¯ãåãšã眮æãããã ãã§ãã
Vfork_child_startïŒïŒé¢æ°ã³ãŒã
int vfork_child_start(struct task *child) { thread_set_task(thread_self(), child); /* mark as vforking */ task_vfork_start(child); /* Restore values of the registers and return 0 */ ptregs_retcode_jmp(&task_resource_vfork(child->parent)->ptregs, 0); panic("vfork_child_start returning"); return -1; }
次ã®ããšãèµ·ãããŸãïŒå®è¡ã®çŸåšã®ã¹ã¬ããïŒã€ãŸãã芪ïŒã¯thread_set_taskïŒïŒé¢æ°ã«ãã£ãŠçæãããããã»ã¹ã«ã¢ã¿ãããããŸã-çŸåšã®ã¹ã¬ããã®æ§é å ã®å¯Ÿå¿ãããã€ã³ã¿ãŒãå€æŽããã ãã§ãã ããã¯ãã¿ã¹ã¯ã«é¢é£ä»ãããããªãœãŒã¹ã«ã¢ã¯ã»ã¹ãããšãã«ãã¹ã¬ãããã¿ã¹ã¯ã以åã®ããã«èŠªã§ã¯ãªãåãšããŠåç §ããããšãæå³ããŸãã ããšãã°ãã¹ã¬ãããã©ã®ã¿ã¹ã¯ã«å±ããŠãããã確èªããããšãããšïŒ task_selfïŒïŒé¢æ°ïŒãåã¿ã¹ã¯ãåãåããŸãã
ãã®åŸãåã¿ã¹ã¯ã¯vforkã®çµæãšããŠäœææžã¿ãšããŠããŒã¯ãããŸããvfork_child_doneïŒïŒé¢æ°ãå¿ èŠã«å¿ããŠå®è¡ãããããã«ããã®ãã©ã°ãå¿ èŠã«ãªããŸãïŒããã«ã€ããŠã¯åŸã§èª¬æããŸãïŒã
次ã«ã vforkïŒïŒãåŒã³åºããããšãã«ä¿åãããã¬ãžã¹ã¿ãä¿åãããŸãã POSIXã«ãããšã vforkïŒïŒåŒã³åºãã¯åããã»ã¹ã«ãŒãã®å€ãè¿ãå¿ èŠãããããšãæãåºããŠãã ãããããã¯ptregs_retcode_jmpïŒptregsã0ïŒåŒã³åºãã§èµ·ããããšã§ãã
æ¢ã«è¿°ã¹ãããã«ãåããã»ã¹ã _exitïŒïŒãŸãã¯execvïŒïŒé¢æ°ãåŒã³åºããšãã vfork_chlid_doneïŒïŒé¢æ°ã¯èŠªã¹ã¬ãããããã¯è§£é€ããå¿ èŠããããŸãã ããã«ãç®çã®ãã³ãã©ãŒãå®è¡ããããã®åã¿ã¹ã¯ãæºåããå¿ èŠããããŸãã
Vfork_child_doneïŒïŒé¢æ°ã³ãŒã
void vfork_child_done(struct task *child, void * (*run)(void *), void *arg) { struct task_vfork *vfork_data; if (!task_is_vforking(child)) { return; } task_vfork_end(child); task_start(child, run, NULL); thread_set_task(thread_self(), child->parent); vfork_data = task_resource_vfork(child->parent); ptregs_retcode_jmp(&vfork_data->ptregs, child->tsk_id); }
ãã³ãã©ãŒã³ãŒãtask_exit_callbackïŒïŒ
void *task_exit_callback(void *arg) { _exit((int)arg); return arg; }
ãã³ãã©ãŒã³ãŒãtask_exec_callbackïŒïŒ
void *task_exec_callback(void *arg) { int res; res = exec_call(); return (void*)res; }
vfork_child_doneïŒïŒãåŒã³åºããšãã¯ã vfork ïŒïŒãªãã§execïŒïŒ / _exitïŒïŒã䜿çšããå Žåãèæ ®ããå¿ èŠããããŸãã芪ã®ããã¯ã解é€ããå¿ èŠããªããããçŸåšã®é¢æ°ãçµäºããã ãã§ãããã«åãèµ·åã§ããŸãã ããã»ã¹ãvforkïŒïŒã䜿çšããŠäœæãããå Žåã次ã®ããšãçºçããŸããæåã«ã task_vfork_endïŒïŒã䜿çšããŠis_vforkingãã©ã°ãåã¿ã¹ã¯ããåé€ãããæåŸã«åã¿ã¹ã¯ã®ã¡ã€ã³ã¹ã¬ãããéå§ããŸãã runé¢æ°ã¯ãšã³ããªãã€ã³ããšããŠæå®ãããŸããããã¯ãåè¿°ã®ãã³ãã©ãŒïŒ task_exec_callback ã task_exit_callback ïŒã®ããããã§ããå¿ èŠããããŸããvfork ïŒïŒãå®è£ ããå Žåã«å¿ èŠã§ãã ãã®åŸãã¹ã¬ããã¯ã¿ã¹ã¯ã«å±ããŸããåã§ã¯ãªãã芪ãåã³ç€ºãããŸãã æåŸã«ãåããã»ã¹IDãæ»ãå€ãšããŠvforkïŒïŒåŒã³åºããã芪ã¿ã¹ã¯ã«æ»ããŸãã ããã¯ã ptregs_retcode_jmpïŒïŒãåŒã³åºãããšã§è¡ããããšæ¢ã«èšãããŠããŸãã
2çªç®ã®vforkå®è£
2çªç®ã®å®è£ ã®ã¢ã€ãã¢ã¯ãæ°ããã¿ã¹ã¯ãšãšãã«äœæãããæ°ããã¹ã¬ããã§èŠªã¹ã¿ãã¯ã䜿çšããããšã§ãã ããã¯ã芪ã¹ããªãŒã ã«ä»¥åã«ä¿åãããã¬ãžã¹ã¿ãåã¹ããªãŒã ã«åŸ©å ãããšèªåçã«çºçããŸãã ãã®å Žåã åè¿°ã®èšäºã§èª¬æãããŠããããã«ãã¹ã¬ããéã§å®éã®åæã䜿çšã§ããŸãã ãã¡ãããããã¯ããçŸããã§ããããœãªã¥ãŒã·ã§ã³ãå®è£ ããããšãå°é£ã§ããããã¯ã芪ã¹ã¬ãããåŸ æ©ããŠãããšãã«ããã®åå«ãåãã¹ã¿ãã¯ã§å®è¡ãããããã§ãã ãã®ãããåŸ æ©äžã«äžéã¹ã¿ãã¯ã«åãæ¿ããå¿ èŠããããŸããäžéã¹ã¿ãã¯ã§ã¯ã _exitïŒïŒãŸãã¯exec *ïŒïŒã®åå«ã«ããåŒã³åºããå®å šã«åŸ æ©ã§ããŸãã
2çªç®ã®å®è£
ã®Vfork_child_starté¢æ°ã³ãŒã
int vfork_child_start(struct task *child) { struct task_vfork *task_vfork; task_vfork = task_resource_vfork(task_self()); /* Allocate memory for the new stack */ task_vfork->stack = sysmalloc(sizeof(task_vfork->stack)); if (!task_vfork->stack) { return -EAGAIN; } task_vfork->child_pid = child->tsk_id; /* Set new stack and go to vfork_waiting */ if (!setjmp(task_vfork->env)) { CONTEXT_JMP_NEW_STACK(vfork_waiting, task_vfork->stack + sizeof(task_vfork->stack)); } /* current stack was broken, can't reach any old data */ task_vfork = task_resource_vfork(task_self()); sysfree(task_vfork->stack); ptregs_retcode_jmp(&task_vfork->ptregs, task_vfork->child_pid); panic("vfork_child_start returning"); return -1; }
ã³ãŒãã®èª¬æïŒ
æåã«ãã¹ã¿ãã¯ã«ã¹ããŒã¹ãå²ãåœãŠããããã®åŸã芪ã¯vforkïŒïŒããæ»ãããã«ãããå¿ èŠãšãããããåã®pid ïŒããã»ã¹IDïŒãä¿åãããŸãã
setjmpïŒïŒãåŒã³åºããšã vforkïŒïŒãåŒã³åºãããã¹ã¿ãã¯äžã®å Žæã«æ»ãããšãã§ããŸãã æ¢ã«è¿°ã¹ãããã«ãåŸ æ©ã¯äžéã¹ã¿ãã¯ã§å®è¡ããå¿ èŠããããåãæ¿ãã¯CONTEXT_JMP_NEW_STACKïŒïŒãã¯ãã䜿çšããŠå®è¡ãããŸãããã®ãã¯ãã¯çŸåšã®ã¹ã¿ãã¯ãå€æŽããå¶åŸ¡ãvfork_waitingïŒïŒé¢æ°ã«æž¡ããŸãã ãã®äžã§ã vfork_child_doneïŒïŒãåŒã³åºããããŸã§ãåå«ãã¢ã¯ãã£ãåãããç¥å ããããã¯ãããŸãã
Vfork_waitingã³ãŒã
static void vfork_waiting(void) { struct sigaction ochildsa; struct task *child; struct task *parent; struct task_vfork *task_vfork; parent = task_self(); task_vfork = task_resource_vfork(parent); child = task_table_get(task_vfork->child_pid); vfork_wait_signal_store(&ochildsa); { task_vfork_start(parent); task_start(child, vfork_child_task, &task_vfork->ptregs); while (SCHED_WAIT(!task_is_vforking(parent))); } vfork_wait_signal_restore(&ochildsa); longjmp(task_vfork->env, 1); panic("vfork_waiting returning"); }
ã³ãŒããããããããã«ããŸããåããã»ã¹ã®ã·ã°ãã«ããŒãã«ãä¿åãããŸãã å®éã SIGCHLDã·ã°ãã«ã¯ãªãŒããŒã©ã€ããããŸããããã¯ãåããã»ã¹ã®ã¹ããŒã¿ã¹ãå€æŽããããšãã«éä¿¡ãããŸãã ãã®å Žåã芪ã®ããã¯ã解é€ããããã«äœ¿çšãããŸãã
æ°ããSIGCHLDãã³ãã©ãŒ
static void vfork_parent_signal_handler(int sig, siginfo_t *siginfo, void *context) { task_vfork_end(task_self()); }
ã·ã°ãã«ããŒãã«ã®ä¿åãšåŸ©å ã¯ãPOSIX call sigactionïŒïŒã䜿çšããŠè¡ãããŸãã
ãã³ãã©ãŒã®ä¿å
static void vfork_wait_signal_store(struct sigaction *ochildsa) { struct sigaction sa; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = vfork_parent_signal_handler; sigemptyset(&sa.sa_mask); sigaction(SIGCHLD, &sa, ochildsa); }
ãã³ãã©ãŒã®å埩
static void vfork_wait_signal_restore(const struct sigaction *ochildsa) { sigaction(SIGCHLD, ochildsa, NULL); }
ã·ã°ãã«ãã³ãã©ã眮ãæããåŸãã¿ã¹ã¯ã¯ã¹ã¿ã³ãã€ã¢ãŒããšããŠããŒã¯ãããŸãããã®ã¢ãŒãã§ã¯ã _exitïŒïŒ / exec *ïŒïŒãåŒã³åºããããšãã«ãåã¿ã¹ã¯ã®çŸåšã®èµ·åãŸã§ä¿æãããŸãã é¢æ°vfork_child_taskïŒïŒã¯ ãã¿ã¹ã¯ãžã®ãšã³ããªãã€ã³ããšããŠäœ¿çšããã以åã«ä¿åãããã¬ãžã¹ã¿ã埩å ãã vforkïŒïŒããæ»ããŸãã
Vfork_child_taskïŒïŒé¢æ°ã³ãŒã
static void *vfork_child_task(void *arg) { struct pt_regs *ptregs = arg; ptregs_retcode_jmp(ptregs, 0); panic("vfork_child_task returning"); }
_exitïŒïŒããã³exec *ïŒïŒãåŒã³åºããããšã SIGCHLDãéä¿¡ãããã·ã°ãã«ãã³ãã©ãŒã¯ãåãéå§ããã®ãåŸ ã£ãŠãããšããããŒã¯ãåé€ããŸãã ãã®åŸãå€ãSIGCHLDã·ã°ãã«ãã³ãã©ã埩å ãããå¶åŸ¡ãlongjmpïŒïŒã䜿çšããŠvfork_child_startïŒïŒé¢æ°ã«æ»ããŸãã ãã®é¢æ°ã®ã¹ã¿ãã¯ãã¬ãŒã ã¯ãåããã»ã¹ã®å®è¡åŸã«ç ŽæãããããããŒã«ã«å€æ°ã«ã¯å¿ èŠãªãã®ãå«ãŸããªãããšã«æ³šæããŠãã ããã 以åã«éžæããã¹ã¿ãã¯ã解æŸããåŸãåã¿ã¹ã¯çªå·ãvforkïŒïŒé¢æ°ããè¿ãããŸãã
vforkã®ããã©ãŒãã³ã¹ã確èªãã
vforkïŒïŒã®æ£ããåäœããã¹ãããããã«ãããã€ãã®ç¶æ³ãã«ããŒããäžé£ã®ãã¹ããäœæããŸããã
ãã®ãã¡ã®2ã€ã¯ã _exitïŒïŒããã³execvïŒïŒãåããã»ã¹ã«ãã£ãŠåŒã³åºããããšãã«vforkïŒïŒããã®æ£ããæ»ãããã§ãã¯ããŸãã
æåã®ãã¹ã
TEST_CASE("after called vfork() child call exit()") { pid_t pid; pid_t parent_pid; int res; parent_pid = getpid(); pid = vfork(); /* When vfork() returns -1, an error happened. */ test_assert(pid != -1); if (pid == 0) { /* When vfork() returns 0, we are in the child process. */ _exit(0); } wait(&res); test_assert_not_equal(pid, parent_pid); test_assert_equal(getpid(), parent_pid); }
äºæ¬¡è©Šéš
TEST_CASE("after called vfork() child call execv()") { pid_t pid; pid_t parent_pid; int res; parent_pid = getpid(); pid = vfork(); /* When vfork() returns -1, an error happened. */ test_assert(pid != -1); if (pid == 0) { close(0); close(1); close(2); /* When vfork() returns 0, we are in the child process. */ if (execv("help", NULL) == -1) { test_assert(0); } } wait(&res); test_assert_not_equal(pid, parent_pid); test_assert_equal(getpid(), parent_pid); }
å¥ã®ãã¹ãã§ã¯ã芪ããã»ã¹ãšåããã»ã¹ã«ããåãã¹ã¿ãã¯ã®äœ¿çšã確èªããŸãã
第äžãã¹ã
TEST_CASE("parent should see stack modifications made from child") { pid_t pid; int res; int data; data = 1; pid = vfork(); /* When vfork() returns -1, an error happened. */ test_assert(pid != -1); if (pid == 0) { data = 2; /* When vfork() returns 0, we are in the child process. */ _exit(0); } wait(&res); test_assert_equal(data, 2); }
ããããç§ã¯ããã€ãã®å®éã®ããããŠãµãŒãããŒãã£ã®ããã°ã©ã ã§ã®äœæ¥ã®æ£ç¢ºæ§ããã§ãã¯ããããšæããŸãããããŠãã®ããã«ãæåãªdropbearããã±ãŒãžãéžã°ããŸãã ã èšå®ããããšã forkïŒïŒããã§ãã¯ããèŠã€ãããªãå Žåã¯vforkïŒïŒã䜿çšã§ããŸãã ããã¯ãããã©ãŒãã³ã¹ãæ¹åããããã§ã¯ãªãã ucLinuxããµããŒãããããã«è¡ããããšããã«èšããªããã°ãªããŸãã ã
OSã¯ããã«å¿ããŠèšå®ããïŒdropbearãvforkïŒïŒã䜿çšããããã«ïŒãsshã䜿çšããŠäž¡æ¹ã®å®è£ ãšã®æ¥ç¶ãæ£åžžã«ç¢ºç«ãããŸããã
ã¹ã¯ãªãŒã³ã·ã§ãã
PSãŸãããã®ãããžã§ã¯ãã§ã¯ãMMUã䜿çšããã«forkïŒïŒèªäœãå®è£ ããããšãã§ããŸãããçŸæç¹ã§ã¯ãããã«ã€ããŠã®èšäºãå·çãããŠããŸãã