LINUX KERNEL INTERNALS
printf(«fork program is starting\n»);
pid = fork();
switch(pid)
case -1:
perror(«fork failed»);
exit(1);
case 0:
message = «This is the child»;
n = 5;
break;
default:
message = «This is the parent»;
n = 3;
break;
>
The parent gets over before the child so we can see the shell prompt appears mixed with the output of child process.
Now, lets try to finish the child process first.
int main()
pid_t pid;
char *message;
int n;
printf(«fork program starting\n»);
pid = fork();
switch(pid)
case -1:
perror(«fork failed»);
exit(1);
case 0:
message = «This is the child»;
n = 3;
break;
default:
message = «This is the parent»;
n = 5;
break;
>
int main()
pid_t pid;
char *message;
int n;
int exit_code;
printf(«fork program starting\n»);
pid = fork();
switch(pid)
case -1:
exit(1);
case 0:
message = «This is the child»;
n = 5;
exit_code = 37;
break;
default:
message = «This is the parent»;
n = 3;
exit_code = 0;
break;
>
/* This section of the program waits for the child process to finish. */
if(pid) int stat_val;
pid_t child_pid;
printf(«Child has finished: PID = %d\n», child_pid);
if(WIFEXITED(stat_val))
printf(«Child exited with code %d\n», WEXITSTATUS(stat_val));
else
printf(«Child terminated abnormally\n»);
>
exit (exit_code);
>
fork() vs vfork() vs exec() vs system() vs clone()
Let us first see the standard definition of these system calls.
Fork : The fork call is used to duplicate the current process, the new process identical in almost every way except that it has its own PID. The return value of the function fork distinguishes the two processes, zero is returned in the child and PID of child in parent process.
Exec : The exec call is a way to basically replace the entire current process with a new program. It loads the program into the current process space and runs it from the entry point. As a new process is not created, the process identifier (PID) does not change, but the machine code , data , heap , and stack of the process are replaced by those of the new program. exec() replaces the current process with a the executable pointed by the function. Control never returns to the original program unless there is an exec() error. exec system call can be executed as execl, execlp, execle, execv, execvp, execvpe
Vfork: The basic difference between vfork and fork is that when a new process is created with vfork(), the parent process is temporarily suspended, and the child process might borrow the parent’s address space. This strange state of affairs continues until the child process either exits, or calls execve(), at which point the parent process continues.
This means that the child process of a vfork() must be careful to avoid unexpectedly modifying variables of the parent process. In particular, the child process must not return from the function containing the vfork() call, and it must not call exit() (if it needs to exit, it should use _exit(); actually, this is also true for the child of a normal fork()).
The intent of vfork was to eliminate the overhead of copying the whole process image if you only want to do an exec* in the child. Because exec* replaces the whole image of the child process, there is no point in copying the image of the parent.
if ((pid = vfork()) == 0) <
execl(. NULL); /* after a successful execl the parent should be resumed */
_exit(127); /* terminate the child in case execl fails */
>
For other kinds of uses, vfork is dangerous and unpredictable.
With most current kernels, however, including Linux, the primary benefit of vfork has disappeared because of the way fork is implemented. Rather than copying the whole image when fork is executed, copy-on-write techniques are used.
Clone : Clone, as fork, creates a new process. Unlike fork, these calls allow the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers.
When the child process is created with clone, it executes the function application fn(arg). (This differs from fork, where execution continues in the child from the point of the original fork call.) The fn argument is a pointer to a function that is called by the child process at the beginning of its execution. The arg argument is passed to the fn function.
When the fn(arg) function application returns, the child process terminates. The integer returned by fn is the exit code for the child process. The child process may also terminate explicitly by calling exit(2) or after receiving a fatal signal.
System : The system () library function uses fork(2) to create a child process that executes the shell command specified in command using execl(3) as follows: execl(«/bin/sh», «sh», «-c», command, (char *) 0); system () returns after the command has been completed.During execution of the command , SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored, in the process that calls system () (these signals will be handled according to their defaults inside the child process that executes command ). If command is NULL, then system () returns a status indicating whether a shell is available on the system
- In situations where performance is critical and/or memory limited, vfork + exec* can therefore be a good alternative to fork + exec* . The problem is that it is less safe and the man page says vfork is likely to become deprecated in the future.
- Because memory page tables are not duplicated, vfork is much faster than fork and vfork ‘s execution time is not affected by the amount of memory the parent process uses
- system() will invoke your systems default command shell, which will execute the command string passed as an argument, that itself may or may not create further processes, that would depend on the command and the system. Either way, at least a command shell process will be created.
- With system() you can invoke any command, whereas with exec(), you can only invoke an executable file. Shell scripts and batch files must be executed by the command shell.
Process Descriptor and the Task Structure
- The kernel stores the list of processes in a circular doubly linked list called the task list .
- Process descriptor is nothing but each element of this task list of the type struct task_struct, which is defined in . The process descriptor contains all the information about a specific process.
- Some texts on operating system design call this list the task array . Because the Linux implementation is a linked list and not a static array, it is called the task list .
- The task_struct is a relatively large data structure, at around 1.7 kilobytes on a 32-bit machine.
- This size, however, is quite small considering that the structure contains all the information that the kernel has and needs about a process.
- The process descriptor contains the data that describes the executing program open files, the process’s address space, pending signals, the process’s state, and much more.
- This linked list is stored in kernel space.
- There is one more structure, thread_info which holds more architecture-specific data than the task_struct..