| Unix Content | Register | FAQ | Calendar | Search | Today's Posts | Mark Forums Read |
|
#21
|
| > One could still implement copy-on-write using segmentation. > Only, instead of duplicating the pages as they are written, the data > segment needed to be duplicated all at once. > The only problem would be if the parent process wrote something in the > memory immediately after the fork(). > (now, thinking about that, it's pretty likely that the parent does > that) In fact, it is absolutely certain that both processes will modify their memory. At some point they will call either exec or exit, which causes the return address to be pushed on the stack which would trigger a copy. -- With kind regards, Erik van der Kouwe |
|
#22
|
| "Erik van der Kouwe" >> One could still implement copy-on-write using segmentation. >> Only, instead of duplicating the pages as they are written, the data >> segment needed to be duplicated all at once. >> The only problem would be if the parent process wrote something in the >> memory immediately after the fork(). >> (now, thinking about that, it's pretty likely that the parent does >> that) > In fact, it is absolutely certain that both processes will modify their > memory. At some point they will call either exec or exit, which causes > the return address to be pushed on the stack which would trigger a copy. It is also very common for a program to store the result of fork() in a variable, because you have to do two checks against the value: check for error and check for parent/child status. The only time I have ever seen a program that used an "if (fork()) {....} else {....}" construction (thus not using a variable) it caused incorrect program behaviour: the fork _did_ fail occasionally and the program could not detect it.... So, I think it is safe to say that copy-on-write for the data/stack segment is a nice idea but in practice not very useful ![]() Regards, Jens -- Jens de Smit Student Computer Science | Vrije Universiteit Amsterdam jfdsmit@few.vu.nl | http://www.few.vu.nl/~jfdsmit "[In the end, people] get furious at IT that the goddamn magic isn't working" -- Stewart Dean |
|
#23
|
| > It is also very common for a program to store the result of fork() in > a variable, because you have to do two checks against the value: > check for error and check for parent/child status. The only time I > have ever seen a program that used an "if (fork()) {....} else > {....}" construction (thus not using a variable) it caused incorrect > program behaviour: the fork did fail occasionally and the program > could not detect it.... So, I think it is safe to say that > copy-on-write for the data/stack segment is a nice idea but in > practice not very useful ![]() Storing in a variable is not the same thing as storing in memory. With a good compiler and optimization enabled it would be stored in a register: int run_script() { pid_t pid; if ((pid = fork()) < 0) return -1; if (!pid) { execl("/bin/sh", "myscript.sh", NULL); exit(1); } return 0; } This should produce something like this: _run_script: call _fork test eax, eax jns 0f mov eax, 0xffffffff ret 0: jnz 1f push 0 push param2 push param1 call _execl add esp, 12 push 1 call _exit add esp, 4 1: xor eax, eax ret The only time memory writes are needed is to push the parameters and to perform the call. This suggests, by the way, that it might work if the data and stack segments are separate. There may be a good chance that the data segment does not get written to by the child. -- With kind regards, Erik van der Kouwe |
|
#24
|
| In article <48ae7b6a$0$6019$ba620dc5@text.nova.planet.nl>, Erik van der Kouwe >This suggests, by the way, that it might work if the data and stack >segments are separate. There may be a good chance that the data segment >does not get written to by the child. The data segment and the stack segment have to be the same (or you need a C compiler that is segment-aware). The parent and the child receive different reply messages, so the child's data segment is already dirty before fork even returns. -- That was it. Done. The faulty Monk was turned out into the desert where it could believe what it liked, including the idea that it had been hard done by. It was allowed to keep its horse, since horses were so cheap to make. -- Douglas Adams in Dirk Gently's Holistic Detective Agency |
|
#25
|
| "Erik van der Kouwe" > Storing in a variable is not the same thing as storing in memory. With > a good compiler and optimization enabled it would be stored in a > register: > This should produce something like this: Your statement holds theoretical merit, as it is indeed possible to optimize away the memory access by keeping variable pid in a register. However, gcc -S produces this assembly code: run_script: pushl %ebp movl %esp, %ebp subl $40, %esp call fork movl %eax, -4(%ebp) cmpl $0, -4(%ebp) jns .L2 movl $-1, -20(%ebp) jmp .L4 ..L2: cmpl $0, -4(%ebp) jne .L5 movl $0, 8(%esp) movl $.LC0, 4(%esp) movl $.LC1, (%esp) call execl movl $1, (%esp) call exit ..L5: movl $0, -20(%ebp) ..L4: movl -20(%ebp), %eax leave ret You can see right after the "call fork" that the contents of eax (return register) are stored 4 bytes under the base pointer, i.e. on the stack. I don't have access to a Minix box right here, but I would be highly surprised if ACK does this optimization when GCC does not. So, it could be done, but you'd need compiler support for it. I still say we go for the paging, if we're going to do anything ![]() Regards, Jens -- Jens de Smit Student Computer Science | Vrije Universiteit Amsterdam jfdsmit@few.vu.nl | http://www.few.vu.nl/~jfdsmit "[In the end, people] get furious at IT that the goddamn magic isn't working" -- Stewart Dean |
|
#26
|
| > You can see right after the "call fork" that the contents of eax > (return register) are stored 4 bytes under the base pointer, i.e. on > the stack. I don't have access to a Minix box right here, but I > would be highly surprised if ACK does this optimization when GCC does > not. So, it could be done, but you'd need compiler support for it. Enable optimization (O2) and you'll find that GCC (3.4.3 on Minix) output is nearly identical to what I made up: _run_script: pushl %ebp movl %esp, %ebp subl $8, %esp call _fork testl %eax, %eax movl $-1, %edx jl L1 je L5 xorl %edx, %edx L1: movl %edx, %eax leave ret L5: pushl %eax pushl $0 pushl $LC0 pushl $LC1 call _execl movl $1, (%esp) call _exit The guest only performs memory accesses to call execl and exit (although, as Phillip correctly stated, the implementation of fork itself is a problem). ACK (also with O2) does indeed do much worse: _run_script: push ebp mov ebp,esp sub esp,4 call _fork mov -4(ebp),eax test eax,eax jns I1_1 mov eax,-1 jmp I1_2 I1_1: cmp -4(ebp),0 jne I1_3 push 0 push __II0 push __II1 call _execl push 1 call _exit add esp,16 I1_3: xor eax,eax I1_2: leave ret > I still say we go for the paging, if we're going to do anything ![]() Yes, I can't wait ![]() -- With kind regards, Erik van der Kouwe |