【LittleXi】Chapter 5 Process API exercise

【LittleXi】Chapter 5 Process API exercise

Chapter 5 (simulator)

Experiment preparation

Download the corresponding code from github https://github.com/remzi-arpacidusseau/ostep-homework/

Find the cpu-api file for experiment

  1. Run ./fork.py -s 10 and see which actions are taken. Can you predict what the process tree looks like at each step? Use the -c flag to check your answers. Try some different random seeds (-s) or add more actions (-a) to get the hang of it.

Answer: The process tree can be predicted by the order before and after the process is generated and exited.

Please add image description
Please add image description

\2. One control the simulator gives you is the fork percentage, controlled by the -f flag. The higher it is, the more likely the next action is a fork; the lower it is, the more likely the action is an exit. Run the simulator with a large number of actions (e.g., -a 100) and vary the fork percentage from 0.1 to 0.9. What do you think the resulting final process trees will look like as the percentage changes? Check your answer with -c.

Answer: I think the larger the fork percentage, the more likely this process will be created, and then the larger the process tree will be, otherwise the smaller it will be.

verify:

Please add image description
Please add image description

\3. Now, switch the output by using the -t flag (e.g., run ./fork.py -t). Given a set of process trees, can you tell which actions were taken?

Answer: After using -t, the probability is close to that set by -f 0.5 and is randomly generated.

Please add image description

\4. One interesting thing to note is what happens when a child exits; what happens to its children in the process tree? To study this, let’s create a specific example: ./fork.py -A a+b,b+c,c+d,c+e,c-. This example has process ’a’ create ’b’, which in turn creates ’c’, which then creates ’d’ and ’e’. However, then, ’c’ exits. What do you think the process tree should like after the exit? What if you use the -R flag? Learn more about what happens to orphaned processes on your own to add more context.

Answer: The process tree will be built as given by us

Please add image description

\5. One last flag to explore is the -F flag, which skips intermediate steps and only asks to fill in the final process tree. Run ./fork.py -F and see if you can write down the final tree by looking at the series of actions generated. Use different random seeds to try this a few times.

Answer: Just observe the structure of the generated process tree. The generated process tree is different every time.

Please add image description

\6. Finally, use both -t and -F together. This shows the final process tree, but then asks you to fill in the actions that took place. By looking at the tree, can you determine the exact actions that took place? In which cases can you tell? In which can’t you tell? Try some different random seeds to delve into this question.

Answer: In this case, the process tree produced every time is different, and the number created is relatively close to the tree under the -t parameter. There is no need to record or describe every step and operation in the simulation process, only Use to observe the structure of the generated process tree.

Please add image description

Chapter 5(code)

1. Write a program that calls fork(). Before who calls fork(), let the main process access a variable (for example, x) and set its value to some value (for example, 100). What values ​​do variables in the child process have? What happens to the variable when both the child process and the parent process change the value of x?

Answer: The variables of the child process are completely the value of the initial variable x. Modifications of the parent process do not affect the x of the child process, and modifications of the child process do not affect the x of the parent process.

#include <stdio.h>
#include <unistd.h>

int main() {
    
    
    int x = 100;
    int pid = fork();

    if (pid < 0) {
    
    
        fprintf(stderr, "fork failed\n");
        return 1;
    } else if (pid == 0) {
    
    
        printf("Child process before_change: x = %d\n", x);
        x = 200;    
        printf("Child process after_change: x = %d\n", x);
    
    } else {
    
    
        printf("Parent process before_change: x = %d\n", x);
        x = 300;
        printf("Parent process after_change: x = %d\n", x);

    }
    return 0;
}

Please add image description

2. Write a program that opens a file (using the open() system call) and then calls fork() to create a new process. Can both the child process and the parent process access the file descriptor returned by open()? What happens when I write to a file concurrently (i.e. with whom)?

Answer: Yes, both the child process and the parent process will access the file descriptor returned by open(). Because the running order of the child process and the parent process is uncertain, concurrent writing may occur, that is, the content in the file is completely Interleaved, so in normal development, we cannot run two processes to write the same file at the same time, so we should set a semaphore. When process A accesses, the file is locked to prevent concurrent writing.

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <cstring>
int main() {
    
    
    int filedesc = open("testfile.txt", O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
    if (filedesc < 0) {
    
    
        fprintf(stderr, "Failed to open or create file\n");
        return 1;
    }

    int pid = fork();

    if (pid < 0) {
    
    
        fprintf(stderr, "fork failed\n");
        return 1;
    } else if (pid == 0) {
    
    
        const char *child_msg = "Child process was here\n";
        write(filedesc, child_msg, strlen(child_msg));
    } else {
    
    
        const char *parent_msg = "Parent process was here\n";
        write(filedesc, parent_msg, strlen(parent_msg));
    }

    close(filedesc);
    return 0;
}

Please add image description

3.Use fork() to write another program. The child process should print "hello" and the parent process should print "goodbye". You should try to ensure that the child process always prints first. Can you do this using the parent process calling wait()?

Answer: Yes, just put wait before printf.

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

int main() {
    
    
    int status = 0;
    pid_t pid = fork();

    if (pid < 0) {
    
    
        fprintf(stderr, "fork failed\n");
        return 1;
    } else if (pid == 0) {
    
    
        printf("hello\n");
    } else {
    
    
        wait(&status); 
        printf("goodbye\n");
    }
    return 0;
}

Please add image description

4.Write a program that calls fork() and then some form of exec() to run the program /bin/ls. See if you can try all the variations of exec(), including execl(), execle(), execlp(), execv(), execvp(), and execvP(). Why are there so many variations of the same basic call?

Answer: Because different variants can accept different parameters and adapt to different environment variables

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    
    
    pid_t pid;

    pid = fork();

    if (pid < 0) {
    
    
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
    
    
        // 子进程

        // 使用 execl() 来运行 /bin/ls
        execl("/bin/ls", "ls", "-l", NULL);

        // 如果 execl() 失败,打印错误信息
        perror("execl");
        exit(EXIT_FAILURE);
    } else {
    
    
        // 父进程
        wait(NULL); // 等待子进程结束
    }

    return 0;
}

Please add image description
5. Now whoever writes a program uses wait() in the parent process to wait for the child process to complete. Who does wait() return? What will happen if you use wait() in a child process?

Answer: wait returns the status code of the child process. Wait cannot be used in child processes. In theory, since the child process has no child processes, it will wait()return immediately and set the error status because there are no child processes to wait for. In fact, it runs out to 0
Please add image description

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

int main() {
    
    
    int status = 0;
    pid_t pid = fork();

    if (pid < 0) {
    
    
        
        fprintf(stderr, "fork failed\n");
        return 1;
    } else if (pid == 0) {
    
    
        wait(&status); 
        printf("%d\n",status);
    } else {
    
    
        printf("%d\n",status);
    }
    return 0;
}

6. A slight modification to the previous program, this time using waitpid() and me wait(). Who waitpid() will be useful?

Answer: You waitpid()can use to wait for the end of a specific child process, not just waiting for the first child process.

ps: WIFEXITED(status) can parse the status code in status

7. Write a program that creates a child process, and then closes standard output (STDOUT_FILENO) in the child process. What will happen if the child process calls printf() to print output after closing the descriptor?

Answer: When the child process closes the standard output ( STDOUT_FILENO), it will no longer be able to perform normal output through the standard output stream. If the child process attempts to use printout after closing standard output printf(), the output will not be displayed on the terminal because standard output has already been closed and printf()the function cannot send data to the closed file descriptor.

8. Write a program that creates two child processes and uses the pipe() system call to connect the standard output of one child process to the standard input of the other child process.

answer:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
    
    
    int pipe_fd[2];
    pid_t child1_pid, child2_pid;

    // 创建管道
    if (pipe(pipe_fd) == -1) {
    
    
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    // 创建第一个子进程
    child1_pid = fork();

    if (child1_pid < 0) {
    
    
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (child1_pid == 0) {
    
    
        // 子进程 1
        close(pipe_fd[0]); 
        dup2(pipe_fd[1], STDOUT_FILENO);
        execlp("ls", "ls", NULL);
        perror("execlp");
        exit(EXIT_FAILURE);
    }

    child2_pid = fork();

    if (child2_pid < 0) {
    
    
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (child2_pid == 0) {
    
    
        // 子进程 2
        close(pipe_fd[1]); 
        dup2(pipe_fd[0], STDIN_FILENO);
        execlp("sort", "sort", NULL);
        perror("execlp");
        exit(EXIT_FAILURE);
    }

    close(pipe_fd[0]);
    close(pipe_fd[1]);

    wait(NULL);
    wait(NULL);

    return 0;
}

Guess you like

Origin blog.csdn.net/qq_68591679/article/details/133126711