Fork
() The main task is to initialize the data structure to create the process, and its main steps are
(The following is taken from JOYFIRE notes
):
1
Apply for an idle page to save Task_struct;
2
) Find an empty process slot (Find_empty_Process
())
3
) Apply for kernel_stack_page to apply for another idle memory page as a stack;
4
) Copy the LDT table of the parent process to the child process;
5
) Copy the memory mapping information of the parent process;
6
) Management file descriptors and link points.
So, if an object is created in the parent process, what special performance will there be for the object? Take a look at the procedure below:
#include
Class CA
{
public
:
CA
() {Printf
(
Construct CA / N "
); ~ CA
() {Printf
(
"deStruct CA / N"
}};
int
main
(
Int Argcc
,
charr
** Argv
) {
PID_T PID
;
PID_T WPID
;
Int Status
;
CA A
;
PID
= Fork
();
IF
(PID)
== (PID_T
)
1
) {
FPRINTF
(stderr
,
"% s: failed to fork () / n"
STRERROR
(Errno)
));
Exit
(
13
}
Else IF
(PID)
==
0
) {
PRINTF
(
"PID% ld: Child Started, Parent IS% LD. / N"
, (
Long
GetPid
(),
Long
GetPPID
())
Return
0
}
PRINTF
(
"PID% LD: Started Child PID% ld. / N"
, (
Long
GetPid
(),
Long
PID
);
WPID
= WAIT
(& status)
);
IF
(WPID
== (PID_T
)
1
)
PERROR
(
Wait (2) "
);
Return
0
}
The following is the program a certain run:
Construct CA
PID
29423
: Child Started
, Parent IS
March 29422.
Destruct CA
PID
29422
: Started Child PID
29423.
Destruct CA
The results show that the object is constructed once, but it is destructed twice.
Why is this so? This is the result of the Fork to copy the memory mapping information of the parent process. For code like this:
Void
main
() {
CA A
;
// CA is a class
}
The equivalent assembly code is about this: 10
:
Void
main
()
11
: {
00401030 PUSH EBP
00401031 MOV EBP
ESP
00401033 SUB ESP
44h
00401036 PUSH EBX
00401037 PUSH ESI
00401038 PUSH EDI
00401039 LEA EDI
[EBP
-44h
]
0040103C MOV ECX
11h
00401041 MOV EAX
, 0cccccccch
00401046 Rep STOS DWORD PTR
[edi
]
12
: CA A
;
00401048 LEA ECX
[EBP
-
4
]
0040104B Call @ilt
0
(CA
:: CA
) (
00401005
)
13
}
00401050 Lea ECX
[EBP
-
4
]
00401053 Call @ilt
5
(CA
:: ~ CA
) (0040100A)
)
00401058 POP EDI
00401059 POP ESI
0040105A POP EBX
0040105B Add ESP
44h
0040105E CMP EBP
ESP
00401060 Call __chkesp
(
00401120
)
00401065 MOV ESP
EBP
00401067 POP EBP
00401068 RET
That is, when is inserted into the configuration
/ Destructure function call code, which is determined during compilation. After the Fork, the child process will continue to perform the instruction specified by the call stack due to the complete copy of the parent process memory mapping information (including code segment and calling stack information), so the latter Call @ilt
5
(CA
:: ~ CA
) (0040100A)
) Will be executed twice.
The above features are sometimes used to deliver information between parent son processes (by the parent process)
-> Sub process, this transmission is unidirectional).
So, how do we block the destructure news that repeat the output, by discussing online, have the following recommendations:
1. Advisory: Do not use reusable statements in Fork, such as Printf;
2, the following policy: Add a flag to the CA and output according to the logo in the destructor;
For the second solution, the modified procedure is as follows:
#include
Class CA
{
public
:
CA
(); ~ CA
();
Int_Childflag
};
CA
:: CA
() {
_Childflag
=
0
;
PRINTF
(
"construct ca / n");
CA
:: ~ CA
() {
IF
(! _Childflag
) {
PRINTF
(
"deStruct CA / N"
}}
int
main
(
Int Argcc
,
charr
** Argv
) {
PID_T PID
;
PID_T WPID
;
Int Status
;
CA A
;
PID
= Fork
();
IF
(PID)
== (PID_T
)
1
) {
FPRINTF
(stderr
,
"% s: failed to fork () / n"
STRERROR
(Errno)
));
exit
(
13
}
Else IF
(PID)
==
0
) {
a
._Childflag
=
1
;
PRINTF
(
"PID% ld: Child Started, Parent IS% LD. / N"
, (
Long
GetPid
(),
Long
GetPPID
())
Return
0
}
PRINTF
(
"PID% LD: Started Child PID% ld. / N"
, (
Long
GetPid
(),
Long
PID
);
WPID
= WAIT
(& status)
);
IF
(WPID
== (PID_T
)
1
)
PERROR
(
Wait (2) "
);
Return
0
}
Note: The above is a very boring topic, but it was asked in a few weeks ago, which is recorded here.