Linux also has a vfork(), even more optimised, which shares just about 一切 between the two processes. Because of that, there are certain restrictions in what the child can do, and the parent halts until the child calls exec() or _exit().
exec turns the current process into another program. If you ever watched Doctor Who, then this is like when he regenerates -- his old body is replaced with a new body.
The way that this happens with your program and exec is that a lot of the resources that the OS kernel checks to see if the file you are passing to exec as the program argument (first argument) is executable by the current user (user id of the process making the exec call) and if so it replaces the virtual memory mapping of the current process with a virtual memory the new process and copies the argv and envp data that were passed in the exec call into an area of this new virtual memory map. Several other things may also happen here, but the files that were open for the program that called exec will still be open for the new program and they will share the same process ID, but the program that called exec will cease (unless exec failed).
int X = open("./output_file.txt", O_WRONLY);
pid_t fk = fork();
if (!fk) { /* in child */
dup2(X, 1); /* fd 1 is standard output,
so this makes standard out refer to the same file as X */
close(X);
/* I'm using execl here rather than exec because
it's easier to type the arguments. */
execl("/bin/echo", "/bin/echo", "hello world");
_exit(127); /* should not get here */
} else if (fk == -1) {
/* An error happened and you should do something about it. */
perror("fork"); /* print an error message */
}
close(X); /* The parent doesn't need this anymore */
当一个进程使用 fork ()时,它会创建一个自身的副本,这个副本将成为该进程的子进程。Fork ()是在 Linux 中使用 clone ()系统调用实现的,该调用从内核返回两次。
非零值(子进程 ID)返回给父进程。
将零值返回给子级。
如果由于内存不足等问题而未能成功创建子级,则将 -1返回到 fork ()。
让我们通过一个例子来理解这一点:
pid = fork();
// Both child and parent will now start execution from here.
if(pid < 0) {
//child was not created successfully
return 1;
}
else if(pid == 0) {
// This is the child process
// Child process code goes here
}
else {
// Parent process code goes here
}
printf("This is code common to parent and child");
在这个示例中,我们假设在子进程中不使用 exec ()。
But a parent and child differs in some of the PCB(process control block) attributes. These are:
Record Locks - The child doesn’t inherit its parent’s record locks. Record locks are associated with a file block or an entire file.
对于子进程,进程资源利用率和 CPU 时间消耗被设置为零。
子级也不从父级继承计时器。
但是子内存怎么办呢? 是否为子内存创建了一个新的地址空间?
The answers in no. After the fork(), both parent and child share the memory address space of parent. In linux, these address space are divided into multiple pages. Only when the child writes to one of the parent memory pages, a duplicate of that page is created for the child. This is also known as copy on write(Copy parent pages only when the child writes to it).
让我们通过一个例子来理解书面复制。
int x = 2;
pid = fork();
if(pid == 0) {
x = 10;
// child is changing the value of x or writing to a page
// One of the parent stack page will contain this local variable. That page will be duplicated for child and it will store the value 10 in x in duplicated page.
}
else {
x = 4;
}
但是为什么需要书面复印呢?
典型的进程创建是通过 fork ()-exec ()组合进行的。
Exec() group of functions replaces the child’s address space with a new program. Once exec() is called within a child, a separate address space will be created for the child which is totally different from the parent’s one.
If there was no copy on write mechanism associated with fork(), duplicate pages would have created for the child and all the data would have been copied to child’s pages. Allocating new memory and copying data is a very expensive process(takes processor’s time and other system resources). We also know that in most cases, the child is going to call exec() and that would replace the child’s memory with a new program. So the first copy which we did would have been a waste if copy on write was not there.
pid = fork();
if(pid == 0) {
execlp("/bin/ls","ls",NULL);
printf("will this line be printed"); // Think about it
// A new memory space will be created for the child and that memory will contain the "/bin/ls" program(text section), it's stack, data section and heap section
else {
wait(NULL);
// parent is waiting for the child. Once child terminates, parent will get its exit status and can then continue
}
return 1; // Both child and parent will exit with status code 1.