Working on fork, with test.

This commit is contained in:
ptitSeb 2019-01-19 15:07:38 +01:00
parent 4ff1f366f6
commit 332b2a4106
19 changed files with 168 additions and 26 deletions

View File

@ -137,3 +137,8 @@ add_test(test08 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/box86
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test08 -D TEST_OUTPUT=tmpfile.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref08.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake )
add_test(test09 ${CMAKE_COMMAND} -D TEST_PROGRAM=${CMAKE_BINARY_DIR}/box86
-D TEST_ARGS=${CMAKE_SOURCE_DIR}/tests/test09 -D TEST_OUTPUT=tmpfile.txt
-D TEST_REFERENCE=${CMAKE_SOURCE_DIR}/tests/ref09.txt
-P ${CMAKE_SOURCE_DIR}/runTest.cmake )

View File

@ -4,7 +4,7 @@ import os
import glob
import sys
values = ['E', 'v', 'c', 'w', 'i', 'I', 'C', 'W', 'u', 'U', 'f', 'd', 'D', 'L', 'p', 'V']
values = ['E', 'e', 'v', 'c', 'w', 'i', 'I', 'C', 'W', 'u', 'U', 'f', 'd', 'D', 'L', 'p', 'V']
def splitchar(s):
ret = [len(s)]
i = 0
@ -105,7 +105,7 @@ typedef void (*wrapper_t)(x86emu_t* emu, uintptr_t fnc);
// v = void, i = int32, u = uint32, U/I= (u)int64
// p = pointer, P = callback
// f = float, d = double, D = long double, L = fake long double
// V = vaargs, E = current x86emu struct
// V = vaargs, E = current x86emu struct, e = ref to current x86emu struct
// 0 = constant 0, 1 = constant 1
// o = stdout
// C = unsigned byte c = char
@ -139,8 +139,8 @@ typedef void (*wrapper_t)(x86emu_t* emu, uintptr_t fnc);
# First part: typedefs
for v in gbl:
# E v c w i I C W u U f d D L p V
types = ["x86emu_t*", "void", "int8_t", "int16_t", "int32_t", "int64_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t", "float", "double", "long double", "double", "void*", "void*"]
# E e v c w i I C W u U f d D L p V
types = ["x86emu_t*", "x86emu_t**", "void", "int8_t", "int16_t", "int32_t", "int64_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t", "float", "double", "long double", "double", "void*", "void*"]
if len(values) != len(types):
raise NotImplementedError("len(values) = {lenval} != len(types) = {lentypes}".format(lenval=len(values), lentypes=len(types)))
@ -163,6 +163,7 @@ typedef void (*wrapper_t)(x86emu_t* emu, uintptr_t fnc);
arg = [
"emu, ", # E
"&emu, ", # e
"", # v
"*(int8_t*)(R_ESP + {p}), ", # c
"*(int16_t*)(R_ESP + {p}), ", # w
@ -179,8 +180,8 @@ typedef void (*wrapper_t)(x86emu_t* emu, uintptr_t fnc);
"*(void**)(R_ESP + {p}), ", # p
"(void*)(R_ESP + {p}), " # V
]
# E v c w i I C W u U f d D L p V
deltas = [0, 4, 4, 4, 4, 8, 4, 4, 4, 8, 4, 8, 12, 12, 4, 0]
# E e v c w i I C W u U f d D L p V
deltas = [0, 0, 4, 4, 4, 4, 8, 4, 4, 4, 8, 4, 8, 12, 12, 4, 0]
if len(values) != len(arg):
raise NotImplementedError("len(values) = {lenval} != len(arg) = {lenarg}".format(lenval=len(values), lenarg=len(arg)))
if len(values) != len(deltas):
@ -191,6 +192,7 @@ typedef void (*wrapper_t)(x86emu_t* emu, uintptr_t fnc);
f.write("void {0}(x86emu_t *emu, uintptr_t fcn) {2} {1} fn = ({1})fcn; ".format(N, W, "{"))
vals = [
"\n#error Invalid return type: emulator\n", # E
"\n#error Invalid return type: &emulator\n", # e
"fn({0});", # v
"R_EAX=fn({0});", # c
"R_EAX=fn({0});", # w

View File

@ -41,6 +41,9 @@ void FreeBox86Context(box86context_t** context)
{
if(!context)
return;
if(--(*context)->forked >= 0)
return;
if((*context)->emu)
FreeX86Emu(&(*context)->emu);

View File

@ -60,6 +60,8 @@ typedef struct box86context_s {
library_t *sdl2lib; // shortcut to SDL2 library (if loaded)
library_t *sdl2mixerlib;
library_t *sdl2imagelib;
int forked; // how many forks... cleanup only when < 0
} box86context_t;
box86context_t *NewBox86Context(int argc);

View File

@ -169,6 +169,8 @@ int RelocateElfREL(lib_t *maplib, elfheader_t* head, int cnt, Elf32_Rel *rel)
printf_log(LOG_DEBUG, "Apply R_386_32 @%p with sym=%s (%p -> %p)\n", p, symname, *(void**)p, (void*)offs);
*p = offs;
break;
//case R_386_TLS_DTPMOD32:
// try to use _dl_next_tls_modid() ?
case R_386_TLS_DTPOFF32:
case R_386_JMP_SLOT:
offs = FindGlobalSymbol(maplib, symname);
@ -350,6 +352,7 @@ void AddGlobalsSymbols(kh_mapsymbols_t* mapsymbols, elfheader_t* h)
if(( (h->SymTab[i].st_info == 18)
|| (h->SymTab[i].st_info == 17)
|| (h->SymTab[i].st_info == 34)
|| (h->SymTab[i].st_info == 22)
|| (h->SymTab[i].st_info == 2))
&& (h->SymTab[i].st_other==0) && (h->SymTab[i].st_shndx!=0)) {
const char * symname = h->StrTab+h->SymTab[i].st_name;
@ -365,6 +368,7 @@ void AddGlobalsSymbols(kh_mapsymbols_t* mapsymbols, elfheader_t* h)
if(( (h->DynSym[i].st_info == 18)
|| (h->DynSym[i].st_info == 17)
|| (h->DynSym[i].st_info == 34)
|| (h->DynSym[i].st_info == 22)
|| (h->DynSym[i].st_info == 2))
&& (h->DynSym[i].st_other==0) && (h->DynSym[i].st_shndx!=0 && h->DynSym[i].st_shndx<62521)) {
const char * symname = h->DynStr+h->DynSym[i].st_name;

View File

@ -92,13 +92,17 @@ sighandler_t EXPORT my_signal(x86emu_t* emu, int signum, sighandler_t handler)
}
sighandler_t EXPORT my___sysv_signal(x86emu_t* emu, int signum, sighandler_t handler) __attribute__((alias("my_signal")));
sighandler_t EXPORT my_sysv_signal(x86emu_t* emu, int signum, sighandler_t handler) __attribute__((alias("my_signal")));
pid_t EXPORT my_fork()
pid_t EXPORT my_fork(x86emu_t* emu)
{
// should be doable, but the x86emu stack has to be dedup for the child
printf_log(LOG_NONE, "Warning, Ignoring fork()\n");
return EAGAIN;
#if 1
emu->quit = 1;
emu->fork = 1;
return 0;
#else
return 0;
#endif
}
pid_t EXPORT my___fork() __attribute__((alias("my_fork")));
pid_t EXPORT my___fork(x86emu_t* emu) __attribute__((alias("my_fork")));
EXPORT void* my__ZGTtnaX (size_t a) { printf("warning _ZGTtnaX called\n"); return NULL; }
EXPORT void my__ZGTtdlPv (void* a) { printf("warning _ZGTtdlPv called\n"); }
@ -291,6 +295,13 @@ EXPORT void my_qsort_r(x86emu_t* emu, void* base, size_t nmemb, size_t size, voi
FreeCallback(cbemu);
}
EXPORT int32_t my_execvp(x86emu_t* emu, void* a, void* b, va_list v)
{
printf("execvp(%s, %p)\n", (const char*)a, b);
return execvp(a, b);
}
EXPORT int32_t my_execlp(x86emu_t* emu, void* a, void* b, va_list v) __attribute__((alias("my_execvp")));
#define LIBNAME libc
const char* libcName = "libc.so.6";

View File

@ -268,10 +268,10 @@ GO(__errno_location, pFv)
// eventfd_write
// execl
// execle
GO2(execlp, iFppV, execvp) // is that correct?
GOM(execlp, iFEpVV)
GO(execv, iFpp) // maybe need to GOM this one, and check if path is an x86 file...
// execve // Weak
GO(execvp, iFpp) // same remark as for execv
GOM(execvp, iFEpVV)
GOM(exit, vFEi)
GOM(_exit, vFEi)
// _Exit // Weak
@ -345,8 +345,8 @@ GO(fileno, iFp)
GO(fopen, pFpp)
GOW(fopen64, pFpp)
// fopencookie
GOM(fork, pFE) // Weak
GOM(__fork, pFE)
GOM(fork, iFEv) // Weak
GOM(__fork, iFEv)
// __fortify_fail
// fpathconf // Weak
// __fpending

View File

@ -24,6 +24,7 @@ typedef void (*vFu_t)(uint32_t);
typedef void (*vFf_t)(float);
typedef void (*vFd_t)(double);
typedef void (*vFp_t)(void*);
typedef int32_t (*iFE_t)(x86emu_t*);
typedef int32_t (*iFv_t)(void);
typedef int32_t (*iFi_t)(int32_t);
typedef int32_t (*iFu_t)(uint32_t);
@ -622,6 +623,7 @@ void vFu(x86emu_t *emu, uintptr_t fcn) { vFu_t fn = (vFu_t)fcn; fn(*(uint32_t*)(
void vFf(x86emu_t *emu, uintptr_t fcn) { vFf_t fn = (vFf_t)fcn; fn(*(float*)(R_ESP + 4)); }
void vFd(x86emu_t *emu, uintptr_t fcn) { vFd_t fn = (vFd_t)fcn; fn(*(double*)(R_ESP + 4)); }
void vFp(x86emu_t *emu, uintptr_t fcn) { vFp_t fn = (vFp_t)fcn; fn(*(void**)(R_ESP + 4)); }
void iFE(x86emu_t *emu, uintptr_t fcn) { iFE_t fn = (iFE_t)fcn; R_EAX=fn(emu); }
void iFv(x86emu_t *emu, uintptr_t fcn) { iFv_t fn = (iFv_t)fcn; R_EAX=fn(); }
void iFi(x86emu_t *emu, uintptr_t fcn) { iFi_t fn = (iFi_t)fcn; R_EAX=fn(*(int32_t*)(R_ESP + 4)); }
void iFu(x86emu_t *emu, uintptr_t fcn) { iFu_t fn = (iFu_t)fcn; R_EAX=fn(*(uint32_t*)(R_ESP + 4)); }
@ -1213,8 +1215,9 @@ void vFuffiiffiiffiip(x86emu_t *emu, uintptr_t fcn) { vFuffiiffiiffiip_t fn = (v
void vFuddiiddiiddiip(x86emu_t *emu, uintptr_t fcn) { vFuddiiddiiddiip_t fn = (vFuddiiddiiddiip_t)fcn; fn(*(uint32_t*)(R_ESP + 4), *(double*)(R_ESP + 8), *(double*)(R_ESP + 16), *(int32_t*)(R_ESP + 24), *(int32_t*)(R_ESP + 28), *(double*)(R_ESP + 32), *(double*)(R_ESP + 40), *(int32_t*)(R_ESP + 48), *(int32_t*)(R_ESP + 52), *(double*)(R_ESP + 56), *(double*)(R_ESP + 64), *(int32_t*)(R_ESP + 72), *(int32_t*)(R_ESP + 76), *(void**)(R_ESP + 80)); }
void vFuuiiiiuuiiiiiii(x86emu_t *emu, uintptr_t fcn) { vFuuiiiiuuiiiiiii_t fn = (vFuuiiiiuuiiiiiii_t)fcn; fn(*(uint32_t*)(R_ESP + 4), *(uint32_t*)(R_ESP + 8), *(int32_t*)(R_ESP + 12), *(int32_t*)(R_ESP + 16), *(int32_t*)(R_ESP + 20), *(int32_t*)(R_ESP + 24), *(uint32_t*)(R_ESP + 28), *(uint32_t*)(R_ESP + 32), *(int32_t*)(R_ESP + 36), *(int32_t*)(R_ESP + 40), *(int32_t*)(R_ESP + 44), *(int32_t*)(R_ESP + 48), *(int32_t*)(R_ESP + 52), *(int32_t*)(R_ESP + 56), *(int32_t*)(R_ESP + 60)); }
void vFfffffffffffffff(x86emu_t *emu, uintptr_t fcn) { vFfffffffffffffff_t fn = (vFfffffffffffffff_t)fcn; fn(*(float*)(R_ESP + 4), *(float*)(R_ESP + 8), *(float*)(R_ESP + 12), *(float*)(R_ESP + 16), *(float*)(R_ESP + 20), *(float*)(R_ESP + 24), *(float*)(R_ESP + 28), *(float*)(R_ESP + 32), *(float*)(R_ESP + 36), *(float*)(R_ESP + 40), *(float*)(R_ESP + 44), *(float*)(R_ESP + 48), *(float*)(R_ESP + 52), *(float*)(R_ESP + 56), *(float*)(R_ESP + 60)); }
void iFEpvpVV(x86emu_t *emu, uintptr_t fcn) { iFEppVV_t fn = (iFEppVV_t)fcn; R_EAX=fn(emu, *(void**)(R_ESP + 4), *(void**)(R_ESP + 12), (void*)(R_ESP + 16), (void*)(R_ESP + 16)); }
void iFEpvvpVV(x86emu_t *emu, uintptr_t fcn) { iFEppVV_t fn = (iFEppVV_t)fcn; R_EAX=fn(emu, *(void**)(R_ESP + 4), *(void**)(R_ESP + 16), (void*)(R_ESP + 20), (void*)(R_ESP + 20)); }
void iFEvpVV(x86emu_t *emu, uintptr_t fcn) { iFEpVV_t fn = (iFEpVV_t)fcn; R_EAX=fn(emu, *(void**)(R_ESP + 8), (void*)(R_ESP + 12), (void*)(R_ESP + 12)); }
void iFEv(x86emu_t *emu, uintptr_t fcn) { iFE_t fn = (iFE_t)fcn; R_EAX=fn(emu); }
void pFEv(x86emu_t *emu, uintptr_t fcn) { pFE_t fn = (pFE_t)fcn; R_EAX=(uintptr_t)fn(emu); }
void iFEpuvvpVV(x86emu_t *emu, uintptr_t fcn) { iFEpupVV_t fn = (iFEpupVV_t)fcn; R_EAX=fn(emu, *(void**)(R_ESP + 4), *(uint32_t*)(R_ESP + 8), *(void**)(R_ESP + 20), (void*)(R_ESP + 24), (void*)(R_ESP + 24)); }
void iFEpvpVV(x86emu_t *emu, uintptr_t fcn) { iFEppVV_t fn = (iFEppVV_t)fcn; R_EAX=fn(emu, *(void**)(R_ESP + 4), *(void**)(R_ESP + 12), (void*)(R_ESP + 16), (void*)(R_ESP + 16)); }
void iFEpvvpVV(x86emu_t *emu, uintptr_t fcn) { iFEppVV_t fn = (iFEppVV_t)fcn; R_EAX=fn(emu, *(void**)(R_ESP + 4), *(void**)(R_ESP + 16), (void*)(R_ESP + 20), (void*)(R_ESP + 20)); }

View File

@ -14,7 +14,7 @@ typedef void (*wrapper_t)(x86emu_t* emu, uintptr_t fnc);
// v = void, i = int32, u = uint32, U/I= (u)int64
// p = pointer, P = callback
// f = float, d = double, D = long double, L = fake long double
// V = vaargs, E = current x86emu struct
// V = vaargs, E = current x86emu struct, e = ref to current x86emu struct
// 0 = constant 0, 1 = constant 1
// o = stdout
// C = unsigned byte c = char
@ -29,6 +29,7 @@ void vFu(x86emu_t *emu, uintptr_t fnc);
void vFf(x86emu_t *emu, uintptr_t fnc);
void vFd(x86emu_t *emu, uintptr_t fnc);
void vFp(x86emu_t *emu, uintptr_t fnc);
void iFE(x86emu_t *emu, uintptr_t fnc);
void iFv(x86emu_t *emu, uintptr_t fnc);
void iFi(x86emu_t *emu, uintptr_t fnc);
void iFu(x86emu_t *emu, uintptr_t fnc);
@ -620,10 +621,11 @@ void vFuffiiffiiffiip(x86emu_t *emu, uintptr_t fnc);
void vFuddiiddiiddiip(x86emu_t *emu, uintptr_t fnc);
void vFuuiiiiuuiiiiiii(x86emu_t *emu, uintptr_t fnc);
void vFfffffffffffffff(x86emu_t *emu, uintptr_t fnc);
void iFEpvpVV(x86emu_t *emu, uintptr_t fnc);
void iFEpvvpVV(x86emu_t *emu, uintptr_t fnc);
void iFEvpVV(x86emu_t *emu, uintptr_t fnc);
void iFEv(x86emu_t *emu, uintptr_t fnc);
void pFEv(x86emu_t *emu, uintptr_t fnc);
void iFEpuvvpVV(x86emu_t *emu, uintptr_t fnc);
void iFEpvpVV(x86emu_t *emu, uintptr_t fnc);
void iFEpvvpVV(x86emu_t *emu, uintptr_t fnc);
#endif //__WRAPPER_H_

View File

@ -125,10 +125,42 @@ void FreeX86Emu(x86emu_t **emu)
free((*emu)->scratch);
free((*emu)->stack);
free(*emu);
*emu = NULL;
}
void CloneEmu(x86emu_t *newemu, const x86emu_t* emu)
{
memcpy(newemu->regs, emu->regs, sizeof(emu->regs));
memcpy(&newemu->ip, &emu->ip, sizeof(emu->ip));
memcpy(&newemu->eflags, &emu->eflags, sizeof(emu->eflags));
newemu->old_ip = emu->old_ip;
memcpy(newemu->segs, emu->segs, sizeof(emu->segs));
memcpy(newemu->fpu, emu->fpu, sizeof(emu->fpu));
memcpy(newemu->fpu_ld, emu->fpu_ld, sizeof(emu->fpu_ld));
memcpy(newemu->fpu_ll, emu->fpu_ll, sizeof(emu->fpu_ll));
memcpy(newemu->p_regs, emu->p_regs, sizeof(emu->p_regs));
newemu->cw = emu->cw;
newemu->cw_mask_all = emu->cw_mask_all;
memcpy(&newemu->sw, &emu->sw, sizeof(emu->sw));
newemu->top = emu->top;
newemu->fpu_stack = emu->fpu_stack;
memcpy(&newemu->round, &emu->round, sizeof(emu->round));
memcpy(newemu->mmx, emu->mmx, sizeof(emu->mmx));
memcpy(newemu->xmm, emu->xmm, sizeof(emu->xmm));
newemu->mxcsr = emu->mxcsr;
memcpy(&newemu->zero, &emu->zero, sizeof(emu->zero));
memcpy(newemu->sbiidx, emu->sbiidx, sizeof(emu->sbiidx));
newemu->quit = emu->quit;
newemu->error = emu->error;
// addapt R_ESP to new stack frame
uintptr_t oldst = (uintptr_t)((emu->stack)?emu->stack:emu->context->stack);
uintptr_t newst = (uintptr_t)((newemu->stack)?newemu->stack:newemu->context->stack);
newemu->regs[_SP].dword[0] = emu->regs[_SP].dword[0] + (intptr_t)(newst - oldst);
}
uint32_t GetEAX(x86emu_t *emu)
{
return R_EAX;

View File

@ -8,6 +8,7 @@ x86emu_t *NewX86Emu(box86context_t *context, uintptr_t start, uintptr_t stack, i
void SetupX86Emu(x86emu_t *emu, int* shared_gloabl, void* globals);
void SetTraceEmu(x86emu_t *emu, uintptr_t trace_start, uintptr_t trace_end);
void FreeX86Emu(x86emu_t **x86emu);
void CloneEmu(x86emu_t *newemu, const x86emu_t* emu);
uint32_t GetEAX(x86emu_t *emu);
void SetEAX(x86emu_t *emu, uint32_t v);

View File

@ -38,6 +38,7 @@ typedef struct x86emu_s {
// emu control
int quit;
int error;
int fork; // quit because need to fork
// trace
zydis_dec_t *dec;
uintptr_t trace_start, trace_end;
@ -52,6 +53,8 @@ typedef struct x86emu_s {
int clean_cap;
// scratch stack, used for alignement of double and 64bits ints on arm
uint32_t *scratch;
// local stack, do be deleted when emu is freed
void* stack;
} x86emu_t;

View File

@ -2,6 +2,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "debug.h"
#include "stack.h"
@ -14,6 +16,31 @@
#include "wrapper.h"
#include "box86context.h"
x86emu_t* x86emu_fork(x86emu_t* e)
{
x86emu_t *emu = e;
// lets duplicate the emu
void* newstack = 0;
posix_memalign(&newstack, emu->context->stackalign, emu->context->stacksz);
memcpy(newstack, emu->context->stack, emu->context->stacksz);
x86emu_t* newemu = NewX86Emu(emu->context, R_EIP, (uintptr_t)newstack, emu->context->stacksz);
SetupX86Emu(newemu, emu->shared_global, emu->globals);
CloneEmu(newemu, emu);
emu->stack = newstack;
// ready to fork
++emu->context->forked;
int v = fork();
if(!v) {
emu = newemu;
}
if(v==EAGAIN || v==ENOMEM) {
--emu->context->forked;
FreeX86Emu(&newemu); // fork failed, free the new emu
}
R_EAX = v;
return emu;
}
void x86Int3(x86emu_t* emu)
{
if(Peek(emu, 0)=='S' && Peek(emu, 1)=='C') // Signature for "Out of x86 door"

View File

@ -17,6 +17,7 @@ int Run(x86emu_t *emu)
{
//ref opcode: http://ref.x86asm.net/geek32.html#xA1
printf_log(LOG_DEBUG, "Run X86, EIP=%p, Stack=%p\n", (void*)R_EIP, emu->context->stack);
x86emurun:
emu->quit = 0;
while (!emu->quit)
{
@ -1057,4 +1058,9 @@ int Run(x86emu_t *emu)
UnimpOpcode(emu);
}
}
if(emu->fork) {
emu->fork = 0;
emu = x86emu_fork(emu);
goto x86emurun;
}
}

View File

@ -34,6 +34,7 @@ void RunF30F(x86emu_t *emu);
void x86Syscall(x86emu_t *emu);
void x86Int3(x86emu_t* emu);
x86emu_t* x86emu_fork(x86emu_t* e);
const char* GetNativeName(void* p);

View File

@ -316,29 +316,47 @@ void RunD9(x86emu_t *emu)
ST0.d = 0.0;
break;
case 0xF2: /* FTAN */
ST0.d = tan(ST0.d);
fpu_do_push(emu);
ST0.d = 1.0;
break;
case 0xF3: /* FPATAN */
ST(1).d = atan2(ST(1).d, ST0.d);
fpu_do_pop(emu);
break;
case 0xFA: /* FSQRT */
ST0.d = sqrt(ST0.d);
break;
case 0xFB: /* FSINCOS */
d = ST0.d;
ST0.d = sin(d);
fpu_do_push(emu);
ST0.d = cos(d);
break;
case 0xFC: /* FRNDINT */
ST0.d = fpu_round(emu, ST0.d);
break;
case 0xFE: /* FSIN */
ST0.d = sin(ST0.d);
break;
case 0xFF: /* FCOS */
ST0.d = cos(ST0.d);
break;
case 0xE4:
case 0xF0:
case 0xF1:
case 0xF2:
case 0xF3:
case 0xF4:
case 0xF5:
case 0xF6:
case 0xF7:
case 0xF8:
case 0xF9:
case 0xFB:
case 0xFD:
case 0xFE:
case 0xFF:
UnimpOpcode(emu);
break;
default:

2
tests/ref09.txt Normal file
View File

@ -0,0 +1,2 @@
Child has x = 2
Parent has x = 0

BIN
tests/test09 Executable file

Binary file not shown.

20
tests/test09.c Executable file
View File

@ -0,0 +1,20 @@
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void forkexample()
{
int x = 1;
if (fork() == 0)
printf("Child has x = %d\n", ++x);
else {
usleep(20000);
printf("Parent has x = %d\n", --x);
}
}
int main()
{
forkexample();
return 0;
}