Introduction and usage of Linux C language fork () and EXEC functions

Source: Internet
Author: User
Tags sprintf


What if we wanted to call 1 shell scripts or execute 1 Bash shell commands when we were writing 1 C programs?

In fact, the <stdlib.h> header file contains 1 function system () that calls the shell command or script, and it is convenient to simply pass the shell command as an argument to the system function. One such introduction about system is that fork () is automatically enabled internally during system execution () to create 1 new processes, with no direct use of fork () and EXEC functions.


So this article is actually about the usage of fork () and Exec functions, and how to use them instead of the system function.


1. Functions of the fork () function 1.1 fork () function

Generally speaking, we write 1 ordinary C program, run this program until the end of the program, the system will only assign 1 PID to this program, also said, the system will only have a process on the program.


But executing the fork () function is different.

Fork this English word in English is "forked" meaning, fork () This function is also very consistent with this meaning. Its role is to replicate the current process (including the stack data in memory) for 1 new mirrors. The new mirror is then executed simultaneously with the old process. Equivalent to 1 processes, the fork () function is encountered and split into two processes at the same time. And the two processes are not mutually affected.


Refer to this applet below:

int Fork_3 () {
	printf ("It ' s" main process step 1!! \ n ');

	Fork ();

	printf ("Step2 after Fork" ()!! \ n ');

	int i; scanf ("%d", &i);   Prevent exiting return
	0;
}


In this function, there are two printf statements, but 3 lines of information are played when execution is performed. The following figure:


Why, because the fork () function forked the program, see the following diagram:



You can see that the program has only 1 main processes when the fork () function executes, so step 1 is printed out 1 times.

After the fork () function is executed, the program fork becomes two processes, one is the original main process, the other 1 are new subprocess, they will perform the code behind the fork () function, so Step2 will be printed out by two processes each time, a total of 3 printf statements on the screen!


You can see the last side of this function I used the scanf () function to prevent the program from exiting, when the process of viewing the system, you will find two of the same name of the process:


As shown above, PID 8808 that is the main process, and PID 8809 that is the child process ah, because its parent PID is 8808 Ah!


It should be noted that if no special processing is done, the subprocess will always exist, even if the fork_3 () function is called, and the child process, like the main program, returns the previous level of the call to the Fork_3 () function until the entire program exits.


It can be seen that if the fork_3 () is executed 2 times, the main program will fork two times, and eventually become 4 processes, is not a bit dangerous. So the above so-called special treatment is very important AH!


1.2 distinguish between main program and subroutine.

In practical applications, simply making the program fork is not very meaningful, we add a subroutine, most likely to let the child process to execute a piece of code alone. Implement functions that are different from the main process.

To implement the functionality described above, the child process and the main process are actually executing different code.

So fork () actually has the return value, and the return value in the two process is different, and in the main process the fork () function returns the PID of the main process and returns 0 in the child process! So we can judge the process by the return value of fork (), we can use if statement to execute different code!


As the following applet Fork_1 ():

int Fork_1 () {
	int childpid;
	int i;

	if (fork () = = 0) {
		//child process for
		(i=1. i<=8; i++) {
			printf ("This is Child process\n");
		}
	else{
		//parent process for
		(i=1. i<=8; i++) {
			printf ("This is parent process\n");
		}

	printf ("Step2 after Fork" ()!! \ n \ nthe ");
}

I've judged the return value of the fork () function, and if the return value is 0, I'll let it be a subprocess, or it's the main program. Then I can let the two processes output different information.


The output information is shown below:



You can see the subroutine and the main program output 8 different information, but they are not the alternate rule output, because their two processes are parallel to each other, whose hands on the screen first output, each run results may be different oh.


Here is the diagram:



All two processes are judged by the fork () return value, and the respective code is executed separately in the IF judgment statement. But if the judgment is complete, the following code will be executed back to the individual. So the Step2 still output 2 times. 1.4 uses the exit () function to end the child process within the if judgment.

Refer to the above function, although using if to judge the return value of fork (), the child process and the main process execute different code within the scope of the If judgment, but as the above flowchart, they will execute the following code as soon as the if execution completes.

Usually this is not what we expect, we more often want the child process to execute a special code to let him end, the following code to let the main program to execute the line.

This implementation is very simple, in the subroutine if the final addition of the exit () function is OK.


Modify the Fork_1 () function above, plus the exit statement:

int Fork_1 () {
	int childpid;
	int i;

	if (fork () = = 0) {
		//child process for
		(i=1; i<=8; i++) {printf ("It is a child
			process\n");
		}
		Exit (0);
	} else{
		//parent process for
		(i=1. i<=8; i++) {
			printf ("This is parent process\n");
		}

	printf ("Step2 after Fork" ()!! \ n \ nthe ");
}

And look at the output:




As you can see, the Step2 only prints 1 times, because the subroutine ends in the if condition, and once the if is judged, only 1 main processes execute the following code, which is what we want!

Note: the exit () function is in the stdlib.h header file


Flow chart:




1.4 Use the Wait () function main program, such as subroutine execution completion (exit) before execution.

The above example finds that the execution order of the main program and subroutine is random, but in practice, we usually want the subprocess to execute before proceeding with the main process.

For example, for the above Fork_1 () function, I would like to first output the 8 "This was child process" of the subprocess and then output 8 main processes "This is parent process".

This function is provided by the wait () function, adding the wait () function to the main process in the IF condition allows the main process to execute the fork () function first hold, after which the child process exits and then executes, usually with the exit () function of the subprocess.


I modified the fork_1 () function and added the wait () statement:

int Fork_1 () {
	int childpid;
	int i;

	if (fork () = = 0) {
		//child process for
		(i=1; i<=8; i++) {printf ("It is a child
			process\n");
		}
		Exit (0);
	} else{
		//parent process wait
		();
		For (I=1 i<=8; i++) {
			printf (' is parent process\n ');
		}
	}

	printf ("Step2 after Fork" ()!! \ n \ nthe ");
}

Output:



See this time the screen output is very regular!

In fact, the wait () function also has 1 functions, that is, you can receive 1 pid_t (in the unistd.h, in fact, is int) pointer type parameter, give this parameter the system PID value before the child process exits

Flow chart:





2. exec function Group

It should be noted that exec is not 1 functions, in fact it is just a group of functions collectively, it includes the following 6 functions:

#include <unistd.h>

int execl (const char *path, const char *arg, ...);

int EXECLP (const char *file, const char *arg, ...);

int execle (const char *path, const char *arg, ..., Char *const envp[]);

int execv (const char *path, char *const argv[]);

int EXECVP (const char *file, char *const argv[]);

int Execve (const char *path, char *const argv[], char *const envp[]);

You can see that these 6 function names are different, and the parameters they use to accept are different.

In fact, their functions are similar, because to accept different parameters so to use different names to distinguish them, after all, C language does not function overloaded function.

But in fact their names are regular:

Exec[l or V][p][e]

The parameters in the EXEC function can be divided into 3 parts, the Execution file section, the command parameter section, and the Environment variable section.

For example, I'm going to execute 1 commands ls-l/home/gateman

The execution file section is "/usr/bin/ls."

The command entry part is "LS", "L", "/home/gateman", null to see is with LS at the beginning of every 1 spaces must be divided into 2 parts, and null-terminated AH.

The environment variable section, which is an array, the final element must be NULL such as char * env[] = {"Path=/home/gateman", "User=lei", "status=testing", NULL};


Okay, next. Naming rules:

e Subsequent, the parameter must take the environment variable part, the environment Change 0 part parameter will become executes the EXEC function period the environment variable, comparatively less uses

l Subsequent, the command parameter portion must be separated by "," and the last 1 command arguments must be null

v subsequent, the command parameter portion must be the head pointer to an array of 1 null-terminated string pointers. For example, char * pstr is a pointer to a string, and char * pstr[] is an array, pointing to each string individually.

p follow up, execution file section can be without path, EXEC function will be found in $path



Another 1 note is that the EXEC function replaces the process that executes it, that is, once the EXEC function succeeds, it does not return and the process ends. However, if the EXEC function fails, it returns the failed message, and the process continues to execute the following code!


Typically, exec is placed in the child process part of the fork () function in place of the child process execution, and the subroutine disappears when the execution succeeds, but the exit () function must be used to exit the child if the execution fails!

Here are the examples:


2.1 execv Function

	int childpid;
	int i;

	if (fork () = = 0) {
		//child process
		char * execv_str[] = {"echo", "executed by Execv", NULL};
		if (Execv ("/usr/bin/echo", Execv_str) <0) {
			perror ("error on exec");
			Exit (0);
		}
	} else{
		//parent process wait
		(&childpid);
		printf ("Execv done\n\n");
	}
Note the definition and assignment of the string pointer array

2.2 EXECVP function

	if (fork () = = 0) {
		//child process
		char * execvp_str[] = {"echo", "executed by EXECVP", ">>", "~/abc.txt", NULL };
		if (EXECVP ("echo", Execvp_str) <0) {
			perror ("error on exec");
			Exit (0);
		}
	} else{
		//parent process wait
		(&childpid);
		printf ("EXECVP done\n\n");
	}

2.3 execve Function

	if (fork () = = 0) {
		//child process
		char * execve_str[] = {"env", NULL};
		char * env[] = {"Path=/tmp", "User=lei", "status=testing", NULL};
		if (Execve ("/usr/bin/env", execve_str,env) <0) {
			perror ("error on exec");
			Exit (0);
		}
	} else{
		//parent process wait
		(&childpid);
		printf ("Execve done\n\n");
	}


2.4 execl function

	if (fork () = = 0) {
		//child process
		if (execl ("/usr/bin/echo", "echo", "executed by Execl", NULL) <0) {
			Perror ("error on exec");
			Exit (0);
		}
	} else{
		//parent process wait
		(&childpid);
		printf ("Execv done\n\n");
	}

2.5 EXECLP function

	if (fork () = = 0) {
		//child process
		if (EXECLP ("echo", "echo", "executed by EXECLP", NULL) <0) {
			perror (" Error on Exec ");
			Exit (0);
		}
	} else{
		//parent process wait
		(&childpid);
		printf ("EXECLP done\n\n");
	}


2.6 execle Function

	if (fork () = = 0) {
		//child process
		char * env[] = {"Path=/home/gateman", "User=lei", "status=testing", NULL};
		if (Execle ("/usr/bin/env", "env", null,env) <0) {
			perror ("error on exec");
			Exit (0);
		}
	} else{
		//parent process wait
		(&childpid);
		printf ("Execle done\n\n");
	}



Output:




3. Fork () and EXEC functions are compared with system () functions

See the output of the EXECVP function above. You'll find that the EXEC function is just a system call, and it doesn't support pipeline processing.

The system () function is supported. His interior will automatically fork () 1 sub processes, but the efficiency is not fork () and exec with good use.


But exec supports executing scripts. So commands or scripts that do not require pipeline processing can be executed using the fork () and EXEC functions.


4. Replace the system () function with the fwrite (), fork () and EXEC functions.

As mentioned above, although the EXEC function does not support pipelines and command parameters are complex, it supports executing scripts, so we can use fwrite to write commands with pipeline processing into 1 scripts and then execute the script using the EXEC function.

The following writes 1 base_exec (char *) functions, receives 1 string parameters, and executes it.


The logical steps of this function are only probably written here:

1. Using the Getuid function to obtain the current PID, and then use PID to obtain the current unique file name, to avoid the same program execution conflict!

2. Use the Fwrite function to create 1 script files above the file name under/tmp/. Because/tmp/can read and write to any user.

3. Write command parameters to script

4. Execute this script using fork () and exec ()

5. If necessary, when Exec executes, log.


Here's the I code:

Header file:

base_exec.h

#ifndef __base_exec_h_
#define __BASE_EXEC_H_

	int base_exec (char *);

#endif/* Base_exec_h_ * *

Source file:

BASE_EXEC.C

#include "base_exec.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <  unistd.h> #include <time.h> #define LOGFILE "/home/gateman/logs/c_exec.log" int base_exec (char * pcmd) {FILE *
	pf
	pid_t pid = Getpid ();
	Char pfilename[20];

	sprintf (Pfilename, "/tmp/base_exec%d.sh", PID); Pf=fopen (Pfilename, "w"); W is overwrite, A is add if (NULL = = PF) {printf (fail to open the file base_exec.sh!!!
		\ n ");
	return-1;
	} fwrite ("#!/bin/bash\n", 1, PF);
	Fwrite (Pcmd, strlen (Pcmd), 1, pf);

	Fwrite ("\ n", 1,1, PF);

	Fclose (PF);
		if (fork () ==0) {//child processj char * execv_str[] = {"Bash", Pfilename, NULL};
			if (Execv ("/bin/bash", Execv_str) < 0) {perror ("fail to Execv");
		Exit (-1);
		}else{//current process Wait ();

		Pf=fopen (LOGFILE, "a"); if (NULL = pf) {printf ("fail to open" logfile!!!
			\ n ");
		return-1;
		} time_t t;
		struct TM * PTM;
		Time (&t);
		PTM = Gmtime (&t);
		Char cstr[24]; Sprintf (CStr, "Time:%4d-%02d-%02d%02d:%02d:%02d\n", 1900+ptm->tm_year,ptm->tm_mon,ptm->tm_mday,ptm->
		TM_HOUR,PTM-&GT;TM_MIN,PTM-&GT;TM_SEC);

		Fwrite (CStr, strlen (CStr), 1, pf);
		int uid = Getuid ();
		sprintf (CStr, "UID:%d\ncommand:\n", UID);

		Fwrite (CStr, strlen (CStr), 1, pf);
		Fwrite (Pcmd, strlen (Pcmd), 1, pf);
		Fwrite ("\n\n\n", 3,1, PF);
		Fclose (PF);
		Remove (pfilename);
	return 0;
return 0;
 }










Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.