[Linux process foundation and simple use of exec family functions]

Linux process foundation and simple use of exec family functions

In the previous chapter, I learned file programming under linux, and I have a little understanding of the simple operation of linux. This chapter shares and records the creation and usage scenarios of processes under linux, as well as the huge family of exec. Of course, different My way of learning is to know first, and then I will find relevant information by myself, and I can write a small demo to demonstrate the operation of some common functions, so that my mastery of this knowledge point will be more profound. (Common in the man manual under Linux to view the prototypes of different functions)


foreword

This chapter briefly analyzes the basic use of processes under linux and the exec function family. While recording my own learning, I hope that it can also help other small partners.

Tip: The following is the main text of this article, the following cases are for reference

1. Process related concepts

From the user's point of view: a process is an execution of a program.
From the core of the operating system, a process is the basic unit of resources such as memory and CPU time slices allocated by the operating system.
A process is the smallest unit of resource allocation, and each process has its own independent address space and execution state. Multitasking operating systems like Linux allow many programs to run simultaneously, and each running program constitutes a process.

Second, the process creation function fork

2.1 fork experiment one

The following is a simple demo to expand the content of this chapter (process creation) fork()
function prototype
pid_t fork(void);

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    
    
	pid_t pid;
	pid = getpid(); //获取进程pid
	//fork一次有两个返回值 大于0父进程 等于0为子进程
	printf("start pid:%d\n",pid);
	fork();  //创建进程
	//getpid() 获取当前进程的Pid号
	//如果当前获取的pid与fork之前的Pid相等说明为父进程,否则为子进程的Pid
	if(pid == getpid())
	{
    
    
		printf("this is father pid:%d\n",pid);
	}
	else
	{
    
    
		printf("this is child pid:%d\n",getpid());
	}
	return 0;
}

Experimental results
insert image description here

2.2 fork experiment 2

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>


int main(void)
{
    
    
	pid_t pid;
	pid_t pid2;
	pid_t ret_pid;
	pid = getpid(); //获取进程pid
	//fork一次有两个返回值 大于0父进程 等于0为子进程
	printf("before fork pid:%d\n",pid);
	ret_pid = fork();  //创建进程
	pid2 = getpid();
	printf("after fork pid:%d\n",pid2);
	if(pid == pid2) //父进程
	{
    
    
		printf("this is father pid:%d retpid:%d\n",getpid(),ret_pid);
	}
	else  //子进程
	{
    
    
		printf("this is child pid:%d retpid:%d\n",getpid(),ret_pid);
	}
	return 0;
}

Experimental results
insert image description here

From the above experiment, we can see that when fork creates a process successfully, it will return two values, one is greater than the value returned to the parent process, that is, the pid number of the child process, and the pid number returned in the child process is 0

3. Practical application of creating a new process (simulating client access)

Now we simulate such a scenario, the server has been waiting for the client's access, when our customer service input is 1, we will access the server. After the access is successful, fork creates a child process and waits for the client's command to execute in the child process. The pid of the current child process is printed, and if the input is not 1, it will print waiting and do nothing.
Fork will be used again when programming the socket network later.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>


//模拟服务器 客户端接入


int main(void)
{
    
    
	pid_t pid;
	int data  = 0;
	while(1)
	{
    
    
		printf("please input data:");
		scanf("%d",&data);
		//当我们输入为1时接入
		if(data == 1)
		{
    
    
			pid = fork(); //创建进程
			if(pid > 0) //父进程中什么都不做
			{
    
    
					
			}
			else if(pid == 0) //子进程中获取请求并将当前子进程的pid打印出来
			{
    
    
				while(1)
				{
    
    
					printf("do net request...,pid = %d\n",getpid());
					sleep(3); //每睡眠3s打印一次
				}
			}
		}
		else
		{
    
    
			printf("wait do nothing\n");
		}
	}

	return 0;
}

Experimental results
insert image description here

From the above results, we can clearly see that a child process will be created every time we enter 1, and when the child process is running, we can still input commands, which is a good use of the process. Each process does not interfere with each other.

Fourth, vfork creation process

Function prototype
pid_t vfork(void);
The function prototypes of vfork and fork can be said to be the same, but their functions are different when they are used. We can clearly see the difference between vfork and fork with the demo below.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    
    
	int count = 0;
	pid_t pid;
	//vfork 直接使用父进程存储空间不拷贝
	//vfokr保证子进程先运行,当子进程调用exit退出后,父进程才执行
	pid = vfork(); 

	if(pid > 0)
	{
    
    
		while(1)
		{
    
    
			printf("this is father pid:%d\n",getpid());
			sleep(1);
			printf("count = %d\n",count);
		}
	}
	else if(pid == 0)
	{
    
    
		while(1)
		{
    
    
			printf("this is child pid:%d count:%d\n",getpid(),count);
			sleep(1);
			if(count++ == 5)
			{
    
    
				exit(0);
			}
		}
	}
	return 0;
}

vfork experiment result
insert image description here
fork experiment result
insert image description here

The above two different results are the phenomenon of calling vfork and fork respectively. It can be clearly drawn that when we use vfork to create a child process, the parent process starts to execute after the execution of our child process is finished, and fork creates a process. When the parent process and the child process are competing for resources, anyone may execute first. Why there are these two different creation methods, it is also suitable for applications in different scenarios, just figure out the difference between the two during the learning process.

5. Process exit

Function prototype
void exit(int status);
status is a different status, you can check the man manual.
In the above experiment, the function exit(0) of process exit has been applied; the function of this function is to end the running of a process, of course The end of the process will also be divided into normal exit, abnormal exit and others, and the parameters with different states later also indicate different exit states.

6. exec family functions

The so-called exec function family actually has six functions that start with exec, collectively referred to as exec functions:
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[]);
Once the exec function is successfully called, it will execute a new program without returning. Returns only on failure, error value -1. So usually we call perror() and exit() directly after the exec function is called, without if judgment.
l (list) command line parameter list
p (path) use the path variable when searching for files
v (vector) use the command line parameter array
e (environment) use the environment variable array, do not use the original environment variables of the process, set a new loader run environment variables

Among these six exec function families, the ones with the suffix e rarely use l and p, and the following is just a demonstration of the commonly used function families.

6.1 execl
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(void)
{
    
    

	printf("before execl\n");
	//在当前路径下执行echoarg程序  如果失败返回-1  execl末尾已NULL结尾
	/*
	if(execl("./echoarg","echoarg","linux",NULL) == -1)
	{
		printf("execl failed\n");
		perror("why");
	}
	*/
	if(execl("/bin/ls","ls","-l",NULL) == -1)
	{
    
    
		printf("execl failed\n");
		perror("why");
	}
	printf("execl after\n"); //调用成功 该语句不执行

	return 0;
}

operation result

insert image description here

6.2 execlp
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>



int main(void)
{
    
    

	printf("before execlp\n");
	//后面加上P的可以不加绝对路径 会去环境变量下找该指令
	if(execlp("ls","ls","-l",NULL) == -1)
	{
    
    
		printf("execl failed\n");
		perror("why");
	}
	printf("execlp after\n"); //调用成功 该语句不执行

	return 0;
}

The results of the 6.1 and 6.2 experiments are exactly the same, but the exec function family called when used is different. The parameters passed in are different. When execl is used, the absolute path needs to be written out, but when execlp is used, there is no need to write the absolute path.

Next, a fork and exec family functions are used together to specify the modification of the data in config and txt, and change LENGTH=5 to LENGTH=9

Modify the writing of the file

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main(int argc ,char *argv[])
{
    
    
	int src = 0;
	char *ReadBuf = NULL; //缓冲空间
	char *Result = NULL;
	int file_size = 0;//文件大小

	if(argc != 2)
	{
    
    
		perror("param \n");
		exit(-1);
	}

	//打开源文件
	src = open(argv[1],O_RDWR);
	if(src == -1)
	{
    
    
		printf("open src file failed\n");
	}
	//先计算文件大小
	file_size = lseek(src,0,SEEK_END);
	//将文件描述符移值开头
	lseek(src,0,SEEK_SET);                                
	//为readbuf开辟空间
	ReadBuf = (char *)malloc(sizeof(char)*file_size + 8);
	//将文件内容读完到Readbuf
	read(src,ReadBuf,file_size);
	//与目标字符串进行比较
	Result= strstr(ReadBuf,"LENGTH=");
	if(NULL == Result)
	{
    
    
		printf("not find \n");
		exit(-1);
	}
	Result = Result + strlen("LENGTH="); //指针后移
	//修改值
	*Result = '9';
	lseek(src,0,SEEK_SET);
	//将修改后的值重新写入文件
	write(src,ReadBuf,strlen(ReadBuf));
	//关闭文件描述符
	close(src);
	return 0;
}

Combining fork and exec

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

int main(void)
{
    
    
	pid_t pid;
	int data  = 0;
	while(1)
	{
    
    
		printf("please input data:");
		scanf("%d",&data);

		if(data == 1)
		{
    
    
			pid = fork();
			if(pid > 0)
			{
    
    
				wait(NULL); 	
			}
			else if(pid == 0)
			{
    
    
				execl("./changeDat","changeDat","config.txt",NULL);
				exit(0);
			}
		}
		else
		{
    
    
			printf("wait do nothing\n");
		}
	}

	return 0;
}

insert image description here

Seven, system function

Function prototype
int system(const char *command);
Based on the above experiment, replace execl with the implementation of the system function.

system("./changeDat config.txt");

Eight, popen function

Function prototype
FILE *popen(const char *command, const char *type);

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>



int main(void)
{
    
    
	FILE *fp;
	char Readbuf[1024]= {
    
    0};
	//popen 函数将读取到的数据缓存出来
	//FILE *popen(const char *command, const char *type);
	fp = popen("ps","r");

	fread(Readbuf,sizeof(char),sizeof(Readbuf),fp);
	printf("Readbuf = %s\n",Readbuf);
	return 0;
}

operation result
insert image description here

The advantage of the popen function is that the result after execution can be printed out through the file stream


Summarize

The above is the use and sharing of the process creation and use under linux and the exec function family. If you learn how to input, you should learn how to output. Come on.

Guess you like

Origin blog.csdn.net/boybs/article/details/123046907