[Operating Systems: Three Easy Pieces Introduction to the Operating System] Chapters 4 ~ 6 (Process | Process API | Restricted Direct Execution)

[Reading Notes] Operating Systems: Three Easy Pieces Introduction to the Operating System

product

Chapter 4, Abstraction: Processes

4.1 What is a process?

  • The abstraction provided by the operating system for running programs
  • The memory (called address space) that a process can access is part of that process.
  • Another part of the machine state of a process is registers.
  • For example, the Program Counter (PC) (sometimes called the Instruction Pointer, or IP) tells us which instruction the program is currently executing; similarly, the stack pointer and associated frame pointer are used to manage the function parameter stack, local variables, and return addresses.

4.2 Process API:

 Create ( create): The operating system must contain some method for creating new processes. When you type a command in the shell or double-click an application icon, the operating system is invoked to create a new process that runs the specified program.
 Destroy ( destroy): Since there is an interface for creating a process, the system also provides an interface for forcing the process to be destroyed. Of course, many processes exit on their own when they are done running. However, the user may wish to terminate them if they do not exit, so an interface to stop runaway processes is useful.
 wait( wait): Sometimes it is useful to wait for the process to stop running, so some kind of wait interface is often provided.  Other control (miscellaneous control): In addition to killing or waiting for the process, sometimes there may be other 4.3 Process creation: more details 21
control. For example, most operating systems provide some way to suspend a process (stop running for a period of time), and then resume (continue running).
 status ( statu): There are also usually some interfaces to get status information about the process, such as how long it has been running, or what state it is in.
 Other control (miscellaneous control): In addition to killing or waiting for the process, sometimes there may be other controls. For example, most operating systems provide some way to suspend a process (stop running for a period of time), then resume (continue running)

4.3 Process creation: more details

  • How does the operating system start and run a program? How does process creation actually work?
    insert image description here
  • The first thing an operating system has to do to run a program is load (load) the code and any static data (such as initialized variables) into memory,
  • Programs initially reside on disk (disk, or in some modern systems, on a flash-based SSD) in some executable format. So the process of loading programs and static data into memory requires the operating system to read those bytes from disk and put them somewhere in memory.

4.4 Process status

 Running: In the running state, the process is running on the processor. This means it is executing instructions.
 ready (ready): In the ready state, the process is ready to run, but for some reason the operating system has chosen not to run at this time.
 Blocked: In the blocked state, a process performs some operation and is not ready to run until other events occur. A common example is when a process makes an I/O request to disk, it blocks so other processes can use the processor.

insert image description here

  • IO will cause the process to block
    insert image description here
  • The operating system has to make many decisions to keep CPY busy to improve resource utilization.

4.5 Data Structure

insert image description here

  • state state and init zomibe...

Chapter 5 Narrative: The Process API

5.1 fork() system call

The system call fork() is used to create a new process

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

int main(int argc, char *argv[])
{
    
    
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
    
    
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
    
    
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
    } else {
    
    
        // parent goes down this path (original process)
        printf("hello, I am parent of %d (pid:%d)\n",
	       rc, (int) getpid());
    }
    return 0;
}
运行这段程序(p1.c),将看到如下输出: 
prompt> ./p1
hello world (pid:29146) hello, I am parent of 29147 (pid:29146) 
hello, I am child (pid:29147) 
  • The child process and I are a complete copy of the parent process. Specifically, although it has its own address space (that is, has its own private memory), registers, program counter, etc., the value it returns from fork() is different. The return value obtained by the parent process is the PID of the newly created child process, and the return value obtained by the child process is 0.
  • In other cases, the child process may run first, there will be different situations, depending on cpu scheduling

5.2 wait() system call

  • Please use manthe manual for details
  • Once the process calls wait, it will block itself immediately, and wait will automatically analyze whether a child process of the current process has exited. If it finds such a child process that has become a zombie, wait will collect the information of the child process, destroy it completely and return; if it does not find such a child process, wait will block here until one appears.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    
    
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
    
    
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
    
    
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
	sleep(1);
    } else {
    
    
        // parent goes down this path (original process)
        int wc = wait(NULL);
        printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
	       rc, wc, (int) getpid());
    }
    return 0;
}

insert image description here

  • The system call will not return until the child process finishes running ①. Therefore, even if the parent process runs first, it will politely wait for the child process to finish running, then wait() returns, and then the parent process outputs its own information.

5.3 Finally, the exec() system call

  • This system call allows the child process to execute the same program as the parent process. For example, who calls fork() in p2.c, this is only useful for whoever you want to run a copy of the same program. However, I often want to run my same program, and exec() does just that (
  • There are several variants of exec(): execl(), execle(), execlp(), execv(), and execvp(). Please read the man page for more information.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int
main(int argc, char *argv[])
{
    
    
    printf("hello world (pid:%d)\n", (int) getpid());
    int rc = fork();
    if (rc < 0) {
    
    
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
    
    
        // child (new process)
        printf("hello, I am child (pid:%d)\n", (int) getpid());
        char *myargs[3];
        myargs[0] = strdup("wc");   // program: "wc" (word count)
        myargs[1] = strdup("p3.c"); // argument: file to count
        myargs[2] = NULL;           // marks end of array
        execvp(myargs[0], myargs);  // runs word count
        printf("this shouldn't print out");
    } else {
    
    
        // parent goes down this path (original process)
        int wc = wait(NULL);
        printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
	       rc, wc, (int) getpid());
    }
    return 0;
}
  • Use fork(), wait() and exec() (p3.c)

5.4 Why the API is designed this way

insert image description here

  • The separation of fork() and exec() allows the shell to conveniently implement many useful functions.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    
    
    int rc = fork();
    if (rc < 0) {
    
    
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
    
    
	// child: redirect standard output to a file
	close(STDOUT_FILENO); 
	open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);

	// now exec "wc"...
        char *myargs[3];
        myargs[0] = strdup("wc");   // program: "wc" (word count)
        myargs[1] = strdup("p4.c"); // argument: file to count
        myargs[2] = NULL;           // marks end of array
        execvp(myargs[0], myargs);  // runs word count
    } else {
    
    
        // parent goes down this path (original process)
        int wc = wait(NULL);
	assert(wc >= 0);
    }
    return 0;
}
prompt> wc p3.c > newfile.txt
prompt> ./p4
prompt> cat p4.output 
		32  109  846 p4.c

-- p4 我实调用了 fork 来创建新的子进程,之后调用 execvp()来执行 wc。
-- 屏幕上谁有看到输出, 是由于结果被重我向到文件 p4.output。

  • Supplement: RTF(Friendly)M - read the man manual

job

insert image description here

Chapter 6 Mechanisms: Restricted Direct Execution

  • The OS needs to somehow have many tasks share the physical CPU
  • Run one process for a while, then run another process, and so on. By time sharing the CPU in this way, virtualization is achieved. However, the problem is performance (no additional overhead) and control (permissions).

6.1 Basic Technique: Restricted Direct Execution

  • The technique used to make programs run as fast as possible is called constrained direct execution (limited direct execution) LDE, simply by running the program directly on the CPU.

  • Use normal call and return to jump to the program main(), and back to the kernel later.
    insert image description here

  • It's not really that simple, if there were no restrictions on running programs, the OS wouldn't be able to control anything, so it would be "just a library"

6.2 Problem 1: Restricted Operations (Privilege Problem)

  • Issues with hardware and operating systems: Key question: How to perform restricted operations??

TIP: Employ Protected Control Transfer
Hardware assists the operating system by providing different modes of execution. Below用户模式(user mode) , the application cannot have 完全访问hardware resources. Below内核模式(kernel mode) , the operating system has access to the machine's 全部资源. 陷入Special instructions for the (trap) kernel and (return-from-trap) to user-mode programs are also provided 陷阱返回, as well as instructions for the operating system to tell the hardware where the trap table is located in memory.

The approach we take is by introducing a new processor mode:

user mode

Code that runs in user mode will 受到限制. For example, a process cannot issue I/O requests while running in user mode. Doing so will cause the processor to throw an exception and the operating system may terminate the process.

kernel mode

The operating system (or kernel) runs in this mode. In this mode, the running code can do what it likes, including 特权操作, for example, issuing I/O requests and executing all kinds of restricted instructions.

system call

系统调用Allows the kernel to carefully expose certain things to user programs 关键功能, such as accessing the file system, creating and destroying processes, communicating with other processes, and allocating more memory . .
If the user wants to do something 特权操作(such as reading from disk), he can use the functions provided by the hardware 系统调用.
To execute a system call, a program must execute a special 陷阱(trap) instruction. This instruction simultaneously jumps into the kernel and elevates the privilege level to 内核模式. Once inside the kernel, the system can do whatever is needed 特权操作(if allowed) to perform the required work for the calling process. When it's done, the operating system calls a special 从陷阱返回(return-from-trap) instruction, which, as you'd expect, returns to the calling user program and lowers the privilege level back to user mode.
When executing a trap, the hardware needs to be careful because it must ensure that enough caller registers are stored to be able to when the operating system issues a return from trap instruction 正确返回.

How does the trap know OSwhat code to run inside the ? The kernel does this by setting up a trap table at boot time.

trap table

陷阱表The kernel realizes the initialization of the trap address by setting (trap table) at startup .

When the machine boots, the system executes in privileged (kernel) mode, so you are free to configure the machine hardware as needed. One of the first things the operating system does is tell the hardware which code to run when some unusual event occurs . For example, what code should run when a hard disk interrupt occurs, when a keyboard interrupt occurs, or when a program makes a system call? The operating system usually informs the hardware of the location of these trap handlers through some special instruction. Once the hardware is notified, it remembers the location of these handlers until the next reboot of the machine , and the hardware knows what to do (i.e. which piece of code to jump to) when system calls and other unusual events occur. Improve security! !

insert image description here

Problem 2: Switching between processes

Key question: How to regain control of the CPU
How does the operating system regain control of the CPU so that it can switch between processes?

Cooperation method: wait for system call

  • Long-running processes are assumed to give up CPU periodically
  • System calls eg, yield

Non-cooperative mode: clock interrupt

时钟中断(timer interrupt). Clock devices can be programmed to generate interrupts every few milliseconds . When an interrupt is generated, the currently running process stops and the pre-configured 中断处理程序(interrupt handler) in the operating system runs. At this point, the operating system regains control of the CPU, so it can do what it wants: stop the current process, and start another one.

Note that 硬件there 发生中断is some responsibility, especially when an interrupt occurs, to save enough state for the running program so that a subsequent return from the trap instruction can properly resume the running program. This operation can be considered as an implicit operation, very similar to an explicit system call.

Save and restore context

  • When the operating system obtains control through the above two methods, it can decide whether to switch processes. This decision is made by the scheduler.

  • When the operating system decides to switch processes, it needs to do it first 上下文切换(context switch), that is, for the currently executing process 保存一些寄存器的值(eg, to its kernel stack), and for the soon-to-be-executed process 恢复一些寄存器的值(from its kernel stack). In this way, the operating system can ensure that when the return instruction from the trap is finally executed, it does not return to the previously running process, but continues to execute another process.

Context switching not only saves and restores registers, but also includes other operations, such as page table switching.

  • The operating system decides to switch from running process A to process B. At this point, it calls switch()the routine that carefully saves the current register values ​​(to A's process structure), restores the registers to process B (from its process structure), and then switches contexts ( ), specifically by changing the stack pointer to use B's kernel stack (instead of A's) switch context. Finally, the operating system returns from the trap, restores B's registers, and starts running it.
    insert image description here
    xv6 的上下文切换代码 :
OS_CPU_PendSVHandler:
    CPSID   I                                                   @ Prevent interruption during context switch
    MRS     R0, PSP                                             @ PSP is process stack pointer

    CMP     R0, #0
    BEQ     OS_CPU_PendSVHandler_nosave                         @ equivalent code to CBZ from M3 arch to M0 arch
                                                                @ Except that it does not change the condition code flags

    SUBS    R0, R0, #0x10                                       @ Adjust stack pointer to where memory needs to be stored to avoid overwriting
    STM     R0!, {
    
    R4-R7}                                        @ Stores 4 4-byte registers, default increments SP after each storing
    SUBS    R0, R0, #0x10                                       @ STM does not automatically call back the SP to initial location so we must do this manually

    LDR     R1, =OSTCBCur                                       @ OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            @ R0 is SP of process being switched out
                                                                @ At this point, entire context of process has been saved

problem causes:

When the code is optimized, rbuf_len is saved in the register r8. When the context is switched, the r8 register is not saved, resulting in the value of the r8 register being modified by other processes. After switching back to the current process, the value of r8 cannot be restored.

Thinking: The impact of concurrency on interrupts

What happens when an interrupt occurs while another is being processed?
One way is during interrupt processing 禁止中断(disable interrupt). Doing this ensures that while one interrupt is being serviced, no other interrupts are handed off to the CPU. Of course, the operating system has to be careful doing this. Disabling interrupts for too long can lead to missing interrupts, which is (technically) bad.

TIP: Rebooting is useful, after rebooting the OS first (at boot time) sets trap handlers and starts clock interrupts, and then only runs processes in restricted mode. The OS can be sure that processes can run efficiently, only performing privileged operations, or when they monopolize the CPU for too long and therefore need to switch, OS intervention is required.

Guess you like

Origin blog.csdn.net/weixin_49486457/article/details/130178790