Operating system experiment -- process programming related experiment

catalogue

1, Experiment content:

2, Experimental results:

      3, Experimental process:

        three point one     Basic implementation

                3.1.1     Basic code

                3.1.2     Remove wait

                3.2.1     Parent and child processes perform different operations

                3.2.2     return operate on global variables before returning

        three point three     Call system function and exec function

                3.3.1     Call the system function

                3.3.2     Call exec function

1, Experiment content:

a) Observe process scheduling

b) Observe the change of global variables in process scheduling

c) call system function and exec function in the sub process.

2, Experimental results:

      (1) After running the program for many times, it is found that the output order is not invariable, which is due to the parallel execution of the system, but the output order of the same code block will not change;

​       (2) After removing the 'wait', we find that the result does not change much. According to the analysis, this is because the child process continues to execute after the parent process ends;

​       (3) After adding a global variable, different operations are performed on the change amount in the parent-child process, but they will be output according to the conclusion in (1), and the value of the variable is different, because the parent-child process has an independent address space;

​       (4) By operating on global variables before 'return', we can find that the parent and child processes each have an output result, and the output results are different. This is because the 'fork' function is called, and the parent and child processes will execute the code segment before 'return';

​       (5) When we call the system function, the parent process creates a new child process, runs the target program, and waits for the child process to run; When calling the exec function, the original child process has been replaced by ` / bin/bash ';

      3, Experimental process:

        three point one     Basic implementation

                3.1.1     Basic code

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

int main()
{
	pid_t pid,pid1;
	//fork a child process
	pid=fork();
	
	if(pid<0)//error occurred
	{
		fprintf(stderr,"Fork Failed");
		return 1;
	}
	else if(pid==0)//child process
	{
		pid1=getpid();
		printf("child:pid=%d",pid);
		printf("child:pid1=%d",pid1);
	}
	else//parent process
	{
		pid1=getpid();
		printf("parent:pid=%d",pid);
		printf("parent:pid1=%d",pid1);
		wait(NULL); 
	}
	return 0;
 } 

According to the requirements of the topic, the basic code is as above, and the program is implemented under the Linux operating system.

Firstly, the program is analyzed. After calling the fork function to create a new process, the child process pid is returned in the parent process and 0 is returned in the child process. Therefore, the else if code block is the code executed by the child process, and the else code block is the code executed by the parent process. The expected output is:

child: pid=0
chile: pid1 = child process PID
parent: pid = child process PID
parent: pid1 = parent process PID

         However, when the system calls two processes, it may execute in parallel and hold stdout locks in any order, resulting in the actual output order changing, but the output order of the same code block remains unchanged.

         Run in Huawei ECs to verify the results:

         After running the results for many times, it is found that the actual situation is the same as expected. Due to the different actual scheduling situation of the operating system, the output order may be staggered in different ways, but the output order of each process itself is always kept unchanged.

                3.1.2     Remove wait

        It is worth mentioning that after the wait is removed, the output results do not change greatly:

          I expect the reason for this is that after the parent process ends, the child process still continues to work. In order to further understand the relationship between parent and child processes at this time, insert an endless loop between parent and child processes, and observe the phenomenon through the kill command. Some codes are implemented as follows:

if (pid < 0) {
  fprintf(stderr, "Fork failed");
  return 1;
} else if (pid == 0) {
  pid1 = getpid();
  printf("child: pid = %d\n", pid);
  printf("child: pid1 = %d\n", pid1);
  while (1);
} else {
  pid1 = getpid();
  printf("parent: pid = %d\n", pid);
  printf("parent: pid1 = %d\n", pid1);
  while (1);
}

        1. When running the program, it is found that the program does not end, the parent and child processes fall into an endless loop, and the CPU utilization remains high:

          2. In this regard, we can use the pstree command to view the process tree:

          3. Next, use the kill command to kill the parent process kill 276581 and observe the process tree changes again. It can be found that the child process survives, becomes an orphan process, is taken in by the init process, and becomes the direct child process of init.

          This shows that after killing the parent process, the child process does not necessarily exit; If the parent process exits before the child process, the child process becomes an orphan process and is taken in by the init process.

                3.2.1     Parent and child processes perform different operations

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

static int a=0;

int main()
{
	pid_t pid,pid1;
	
	//fork a child process
	pid=fork();
	
	if(pid<0)//error occurred
	{
		fprintf(stderr,"Fork Failed");
		return 1;
	}
	else if(pid==0)//child process
	{
		pid1=getpid();
		printf("child:pid=%d\n",pid);
		printf("child:pid1=%d\n",pid1);
        
        printf("child:abefore=%d\n",a);
        a=2;
        printf("child:aafter=%d\n",a);
	}
	else//parent process
	{
		pid1=getpid();
		printf("parent:pid=%d\n",pid);
		printf("parent:pid1=%d\n",pid1);
        
        printf("parent:abefore=%d\n",a);
        a=1;
        printf("parent:aafter=%d\n",a);
	}
	
	return 0;
 } 

  Since the parent and child processes have their own running space, different operations in the parent and child processes do not affect each other. The expected output results are as follows:

parent:abefore=0
parent:aafter=1;
child:abefore=0;
child:aafter=2;

Running in the system, the following conclusions can be verified:

                3.2.2     return operate on global variables before returning

        We operate on global variables before the return instruction. Since the parent and child processes have their own independent running space, this code will be run independently. Our expected output is as follows:

parent:abefore=0;
parent:aafter=1;
pid: parent process PID,before:1;
pid: parent process PID,after:3;
child:abefore=0;
child:aafter=2;
pid: sub process PID,before:2;
pid: sub process PID,after:3;

Add some codes on the basis of 3.2.1 as follows:

printf("pid1:%d,before:%d\n",pid1,a);
a=3;
printf("pid1:%d,after:%d\n",pid1,a);

This confirms that the conclusion is correct.

        three point three     Call system function and exec function

                3.3.1     Call the system function

        System () will call fork() to generate a child process. The child process will call / bin/sh-c string to execute the command represented by the parameter string string. After the command is executed, it will return to the original called process. During the call to system(), sigcld signals will be temporarily suspended, and SIGINT and SIGQUIT signals will be ignored.

The code is as follows:

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

int main()
{
	pid_t pid,pid1;
	
	//fork a child process
	pid=fork();
	
	if(pid<0)//error occurred
	{
		fprintf(stderr,"Fork Failed");
		return 1;
	}
	else if(pid==0)//child process
	{
		pid1=getpid();
		printf("child:pid=%d",pid);
		printf("child:pid1=%d",pid1);
	}
	else//parent process
	{
		pid1=getpid();
		printf("parent:pid=%d",pid);
		printf("parent:pid1=%d",pid1);
        system("sleep 10");
        printf("over");
        wait(NULL);
	}
	return 0;
 } 

        The child process executes normally, and the parent process exits normally after waiting for 10s.

                3.3.2     Call exec function

         After we use the fork function to create a new process, it is often in the new process. When a process calls the exec function, the process is completely replaced with a new program. Because calling the exec function does not create a new process, the ID of the previous and subsequent processes does not change.

We add exec family functions to the code block executed by the sub process:

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


int main()
{
	pid_t pid,pid1;
	
	//fork a child process
	pid=fork();
	
	if(pid<0)//error occurred
	{
		fprintf(stderr,"Fork Failed");
		return 1;
	}
	else if(pid==0)//child process
	{
		pid1=getpid();
		printf("child:pid=%d\n",pid);
		printf("child:pid1=%d\n",pid1);
        execl("/bin/bash","bash","-c","echo Before;sleep 5;echo After");
		while(1); 
	}
	else//parent process
	{
		pid1=getpid();
		printf("parent:pid=%d\n",pid);
		printf("parent:pid1=%d\n",pid1);
		wait(NULL);
        printf("over");
	}
	
	return 0;
 } 

         We can see that the dead loop code after the child process execl function is not executed, so it indicates that the original child process has been replaced by ` / bin/bash '.

Tags: C Operating System

Posted on Wed, 01 Dec 2021 01:26:54 -0500 by psionicwrath