Translate

Chủ Nhật, 4 tháng 12, 2016

How to create a process

Process creation: fork

Fork() causes creation of new process. A new process (Child process) is an exact copy of the calling process, memory values and open resources except:
  • The child process has a unique process ID
  • The child process resource utilizations are set to 0
The fork() system call is unlike any other function call I have seen so far. It return twice, once in the parent and once in child, and it return different values in the parent and the child. If fork fail , it return -1.
#include <sys/types.h>
#include <unistd.h>

pid_t fork(void)
vfork() function
The vfork function creates the new process, just like fork, without copying the address space of the parent into the child, as the child won’t reference that address space; the child simply calls exec (or exit) right after the vfork. Instead, the child runs in the address space of the parent until it calls either exec or exit. The vforkalso executes the child process first and resumes the parent process when the child terminates.
Example using vfork()
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int globvar = 6;
int main(void)
 
int var; /* automatic variable on the stack */
pid_t pid;
var = 88;
 
printf("before vfork\n"); /* we don’t flush stdio */
if ((pid = vfork()) < 0) {
   err_sys("vfork error");
} else if (pid == 0) { /* child */
   globvar++; /* modify parent’s variables */
   var++;
   _exit(0); /* child terminates */
}
/* parent continues here */
printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var);
exit(0);
}
Running this program give us:
?
1
2
3
$ ./a.out
before vfork
pid = 29039, glob = 7, var = 89

Wait

A parent might want to wait for a child to complete.
A parent might want to know the exit code of a child.
System call to wait for a child:

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *stat_loc)

and


wait() suspends execution of the calling process until one of its children terminates.
After calling wait() a process will:


  • block if all of children process are still running.
  • return immediately with the PID of a terminated child, if there is a terminated child
  • return immediately with an error(-1) if it doesn't have any child processes.
Infor returned by wait(): 
  • Return the pid of the terminated child or -1 on error
  • Status encodes the exit status of the child and how a child exited (normally or killed by signal)
  • There are macros to process exit status:
WIFEXITEDtell you if child terminated normally
WEXITSTATUSgives you the exit status
WIFSIGNALEDNonzero if child is terminated on an caught signal
WTERMSIGif WIFSIGNALED is nonzero, this return a signal number
WIFSTOPPEDNonzero if the child ha stopped
WSTOPSIGif WIFSTOPPED is nonzero, this return a signal number

Example wait1.c, download code here
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 = 5;
      break;
   default:
      message = "This is the parent";
      n = 3;
      break;
   }
   for(; n > 0; n--) {
      puts(message);
      sleep(1);
   }
   if (pid != 0) {
      int stat_val;
      pid_t child_pid;
      child_pid = wait(&stat_val);
   }
}
Running wait1.c

?
1
2
3
4
5
6
7
8
9
10
$ ./wait1
fork program starting
This is the child
This is the parent
This is the parent
This is the child
This is the parent
This is the child
This is the child
This is the child
Example wait2.c, download code  here

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 = 5;
      break;
   default:
      wait(NULL);
      message = "This is the parent";
      n = 3;
      break;
   }
   for(; n > 0; n--) {
      puts(message);
      sleep(1);
   }
}

Running wait2.c 
?
1
2
3
4
5
6
7
8
9
10
$ ./wait2
fork program starting
This is the child
This is the child
This is the child
This is the child
This is the child
This is the parent
This is the parent
This is the parent

Exec()

  • The exec system call replaces the program being run bt a process by a different one
  • The new program starts executing from the beginning.
  • On success, exec never return, on failure exec return -1.
  • Exec() is not a specific function, but a family of function
  • First parameter: name of executable ; then commandline parameter for executable ; these are passed as argv[0], argv[1], …, to the main program of the executable.
  • execl and execv differ from each other only in how the arguments for the new program of the are passed.
  • execlpand execvpdiffer from execland execvonly in that you don’t have to specify full path to new program
Example using execl, download code here.


?
1
2
3
4
5
6
7
int main (void)
{
   printf ("Before exec\n");
   execl ("/bin/ls", "ls", "-l", (char *)NULL);
   perror ("execl");
   exit (1);
}

Example using execv:

?
1
2
3
4
5
6
7
8
int main (void)
{
   printf ("Before exec\n");
   char *argv[] = {"ls", "-l", NULL);
   execv ("/bin/ls", args);
   perror ("execv");
   exit (1);
}

 Example using execvp 

?
1
2
3
4
5
6
7
8
int main (void)
{
   printf ("Before exec\n");
   char *argv[] = {"ls", "-l", NULL);
   execv (args[0], args);
   perror ("execv");
   exit (1);
}

Zombie and orphan process

  • Using fork to create processes can be very useful, but you must keep track of child processes. When a child process terminates, an association with its parent survives until the parent in turn either terminates normally or calls wait.
  • The child process entry in the process table is therefore not freed up immediately. Although no longer active, the child process is still in the system because its exit code needs to be stored in case the parent subsequently calls wait. It becomes what is known as defunct, or a zombie process. Need to avoid zombie process, because they consume resources until parent clean them up.
  • If the parent process terminates before the child. The child process automatically gets the process with PID 1768 (init--user) as parent.
  • There’s another system call that you can use to wait for child processes. It’s called waitpid, and you can use it to wait for a specific process to terminate.
#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t, int *stat_loc, int options);

  • The pid argument specifies the PID of a particular child process to wait for.
  • it will write status information to the location pointed to by stat_loc
  • The options argument allows you to modify the behavior of waitpid. The most useful option is WNOHANG, which prevents the call to waitpid from suspending execution of the caller.

References

[2] W.Richard Stevens, Stephen A.Rogo [Advanced programming in the UNIX Environment]
[3] Beginning Linux Programming 4th Edition  

Không có nhận xét nào:

Đăng nhận xét