table of Contents
This series of articles is mainly the study notes of "Operating Systems: Three Easy Pieces" and its supporting course CS-537 (Introduction to Operating Systems) of the University of Wisconsin. At the same time, some easy-to-understand materials will be interspersed during this period.
Overview
The introduction of the operating system starts from the essence of the three design operating systems such as virtuality, concurrency and persistence, and runs through the process management, memory management, file management and I / O management of the traditional operating system.
Virtuality | Concurrency | Persistent | |
---|---|---|---|
Process management | Process, CPU scheduling | Thread, deadlock, process synchronization | |
Memory management | Memory management | ||
Storage management | I / O system | File system, large storage |
Why do you need an operating system?
The computer system can be roughly divided into computer hardware, operating system, system and application software from bottom to top. Users work in the software layer and use Word, Matlab and other software to achieve their own purposes. From the perspective of hardware, the computer is only the process of CPU fetch, decode and execute. So how to reasonably connect the application software with the hardware, this requires the introduction of operating system software as the interface between the two.
The essence of the operating system software is an infinitely cyclic program. After booting, it will continue to accept user instructions until the program is forced to shut down. Based macOS, linux and other systems, and downloads common.h
such as headers , we can simulate the process of running the operating system:
// cpu.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <assert.h>
#include "common.h"
int main(int argc, char *argv[]) // argc 命令行参数个数
{ // argv 命令行参数向量
if (argc != 2) {
fprintf(stderr, "usage: cpu <string>\n");
exit(1);
}
char *str = argv[1];
while (1) {
Spin(1);
printf("%s\n", str);
}
return 0;
}
Spin () function: located in common.h, make the program output interval
gcc -o execute file name source file name: compile source file
Wall: display all warnings after compilation
Werror: process all warnings as errors and
execute command hello We can find that the program will continue to output hello until the program is forced to end.
Enter a few more commands, the program will output 3 commands at the same time, so that the user has the illusion that multiple CPUs are executing commands at the same time. This is the virtualization of the CPU.
Virtualization
Virtualization is to map a physical entity into several logical counterparts, and the implementation method is time division multiplexing and space division multiplexing. We can simulate memory virtualization and get the process number through getpid (), update the data with pointer p and get the data storage address.
// mem.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: mem <value>\n");
exit(1);
}
int *p = malloc(sizeof(int));
assert(p != NULL); // getpid() 进程识别码 p 指向内存的指针
printf("(pid:%d) addr of p: %llx\n", (int)getpid(), (unsigned long long)p);
printf("(pid:%d) addr stored in p: %llx\n", (int)getpid(), (unsigned long long)p);
*p = atoi(argv[1]); // assign value to addr stored in p
while (1) {
Spin(1);
*p = *p + 1;
printf("(pid:%d) p: %d\n", getpid(), *p);
}
return 0;
}
At this time, two processes are executed on my machine, the process numbers are 11984 and 11985, the storage addresses are 7fbf56c01720 and 7fb86cc01720, and they increase by 1 each time from 1000 and 2000 respectively.
Concurrence
Concurrency is to run multiple processes within the same time interval on the macro. The operating system implements processes and threads.
// threads.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "common.h"
volatile int counter = 0; // 本条指令不会因编译器的优化而省略,且要求每次直接读值。
int loops;
void *worker(void *arg) { // 计数器
int i;
for (i = 0; i < loops; i++) {
counter++;
}
return NULL;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "usage: threads <value>\n");
exit(1);
}
loops = atoi(argv[1]);
pthread_t p1, p2; // 声明线程id
printf("Initial value : %d\n", counter);
pthread_create(&p1, NULL, worker, NULL);
pthread_create(&p2, NULL, worker, NULL);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
printf("Final value : %d\n", counter);
return 0;
}
pthread_create (): Create a thread and run related thread functions.
The first parameter is a pointer to the thread identifier.
The second parameter is used to set thread attributes.
The third parameter is the starting address of the thread running function.
The last parameter is the parameter of the running function.
pthread_join (): Block the current thread / reclaim thread resources
. The first parameter is the thread identifier. The
second parameter is a user-defined pointer used to store the return value of the waiting thread.
The program creates two thread execution counter worker () functions, the initial value is 0, the parameter loops is 1000, and the result is 2000. But when the parameter is large, the result may be less than the expected value. This is because two counter threads share the same counter, and two threads may take the counter and execute it at the same time. The solution is to lock the counter, that is, PV operation.
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; // 全局信号量初始化
void *worker(void *arg) {
int i;
for (i = 0; i < loops; i++) {
pthread_mutex_lock(&m); // 上锁
counter++;
pthread_mutex_unlock(&m); // 开锁
}
printf("%d\n", counter);
pthread_exit(NULL);
}
Persistence
Persistence is to save transient data (such as objects in memory) to a storage device (such as a disk) that can be permanently saved.
// io.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
void dowork()
{
int fd = open("/tmp/file", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
assert(fd >= 0);
char buffer[20];
sprintf(buffer, "hello world\n");
int rc = write(fd, buffer, strlen(buffer));
assert(rc == (strlen(buffer)));
printf("wrote %d bytes\n", rc);
fsync(fd);
close(fd);
}
int main(int argc, char *argv[]) {
dowork();
return 0;
}
cat command: view file content
Important concept
Operating system: Control and manage the hardware and software resources of the entire computer system, reasonably control the computer workflow, and provide users and other software with convenient interfaces and environments.
Concurrency: During a period of time, there are multiple programs running at the same time on the macro, but in a single-processor system, only one program can be executed at a time, so these programs can only be executed alternately on a microscopic basis.
Parallelism: Two or more events occur at the same time. On the macro level, the CPU and I / O devices are parallel. On the micro level, the CPU runs multiple instructions at the same time. It needs to be implemented by introducing multi-stage pipeline or multi-core processors.
Mutually exclusive sharing: Only a single process is allowed to access critical resources at the same time, and a synchronization mechanism is introduced.
Time-division multiplexing: each process takes turns taking a fixed amount of CPU time, and quickly switches to the next process when the time is up.
Space division multiplexing: memory is divided into fixed-size page frames, and each process is also divided into fixed-size pages, which are partially mapped into memory, and page replacement algorithms are used to replace pages when pages are missing.
Asynchronicity: The process is not completed at one time, but stops and moves forward at an unpredictable speed.
"Introduction to Operating System" study notes (2): CPU virtualization (process)