Browser processes and threads

This article is full of definitions and copywriting explanations, which is relatively boring, but as long as you read it a few times, I believe it will bring you unexpected gains.

1. The concepts of processes and threads

First we need to know the concepts of threads and processes:
Process: The basic unit of CPU resource allocation
Thread: the smallest unit of CPU scheduling

These are the two most official and common definitions of processes and threads, but these two concepts are too abstract and difficult to understand.
In layman’s terms: a process can be described as the execution program of an application, and a thread is a program used to execute a certain part within the process.
Here’s another quote from Zhihu’s highly praised answer, which I find very interesting:
Let’s make a simple metaphor: process = train, thread = carriage

1. The thread travels under the process (a simple carriage cannot run)
2. A process can contain multiple threads (a train can have multiple carriages)
3. It is difficult to share data between different processes (it is difficult for passengers on one train to change to another train, such as station transfer)
4. Data is easily shared between different threads in the same process (it is easy to change from carriage A to carriage B)
5. Processes consume more computer resources than threads (using multiple trains consumes more resources than multiple carriages)
6. Processes will not affect each other. If one thread hangs up, the entire process will hang up (one train will not affect another train.
But if a car in the middle of a train catches fire, it will affect all the cars)
7. The process can be expanded to multiple machines, and the process is suitable for multiple cores at most (different trains can run on multiple tracks, and the carriages of the same train cannot run on different tracks)
8. The memory address used by the process can be locked, that is, when a thread uses some shared memory, other threads must wait for it to end before they can use this piece of memory. (such as the bathroom on the train) – “mutex lock”
9. The memory address used by the process can limit the usage (for example, a restaurant on a train can only allow a maximum number of people to enter. If it is full, you need to wait at the door until someone comes out before you can enter) – “Semaphore”

2. How applications schedule processes and threads

When an application starts, a process is created. The application may create some threads to help it complete certain tasks,
But this is not required. The operating system will allocate a portion of memory to this process, and all the status of the current application will be saved in this private memory space.
When you close the application, the process will automatically evaporate, and the operating system will release the previously occupied memory space.
A program does not necessarily have only one process. A process can allow the operating system to start another process to handle different tasks. When this happens,
The new process will occupy another memory space. When two processes need to communicate, they engage in inter-process communication.
Many applications are designed to work this way, so when one of the processes dies, it can be restarted directly while the other processes are still running.

3. Process and multi-threading

After understanding the above, let’s reorganize the concepts of multi-process and multi-thread:

Multiple processes means that two or more processes are allowed to be running in the same computer system at the same time.
The benefits of multiple processes are obvious. For example, you can open an editor and type code while listening to music, and the processes of the editor and the music-listening software will not interfere with each other at all.

Multi-threading means that a program contains multiple execution streams, that is, multiple different threads can be run simultaneously in a program to perform different tasks.
This means that a single program is allowed to create multiple threads of parallel execution to complete their respective tasks.

4. Chrome’s multi-process architecture

Since there is no unified specification for the browser itself, the architecture of different browsers may be completely different. When the browser was first designed,
The web pages at that time were very simple, and the resource occupancy of each web page was very low, so it was feasible for one process to process multiple web pages.
Then today, large numbers of web pages are becoming increasingly complex. Browsers that put all web pages into one process face problems in terms of robustness, response speed,
Security challenges, so most modern browsers are multi-process.
From the above picture we can clearly see that Chrome is a multi-process architecture.
When we open a browser, multiple different processes are started to assist the browser in presenting the page to us:

Browser process
Plug-in process
GPU process
rendering process

1. Browser process
The core process of the browser is responsible for managing the creation and destruction of each tab page, page display and functions (forward, backward, favorites, etc.), network resource management, downloads, etc.

2. Plug-in process
Responsible for the use of each third-party plug-in. Each third-party plug-in will create a corresponding process when used.
This can prevent third-party plug-in crashes from affecting the entire browser, and also facilitates the use of the sandbox model to isolate plug-in processes and improve browser stability.

3.GPU process
Responsible for 3D rendering and hardware acceleration

4. Rendering process
The browser will allocate a rendering process to each window, which is what we often call the browser kernel.
This prevents a single page crash from affecting the entire browser.

5. Multi-threading of browser kernel

The browser kernel is the browser rendering process, from receiving the downloaded file to rendering the entire page.
It is the responsibility of the browser rendering process. The browser kernel is multi-threaded. Under the control of the kernel, each thread cooperates with each other to maintain synchronization. A browser usually consists of the following resident threads:
GUI rendering thread
timed trigger thread
event trigger thread
Asynchronous http request thread
JavaScript engine thread

1.GUI rendering thread
The GUI rendering thread is responsible for rendering HTML elements of the browser interface. This thread will be executed when the interface needs to be repainted (Repaint) or when a reflow is caused by a certain operation.

Reflow (reflow): Also called rearrangement. When the size or position of an element changes, the rendering tree needs to be recalculated.
This is reflow, such as the width, height, and position of the element. The browser will re-render the page, which is called reflow, also called layout.
Redraw: When the change of element style does not affect the page layout, such as the color of the element, the browser will update the element, which is called redraw.
After calculating the position, size and other properties of the box model, the browser draws according to the characteristics of each box
Parse HTML, generate DOM tree, parse CSS, generate CSSOM tree
Combine the DOM tree and CSSOM tree to generate a render tree (Render Tree)
Layout (reflow): Perform reflow (Layout) based on the generated rendering tree to obtain the geometric information (position, size) of the node
Painting: Obtain the absolute pixels of the node based on the geometric information obtained from the rendering tree and reflow.
Display: Send pixels to the GPU and display them on the page

Relationship: Reflow will definitely trigger redrawing, and redrawing will not necessarily trigger reflow. The cost of redrawing is small, and the cost of reflowing is high.
When the DOM style changes but does not affect the page layout, redrawing will be triggered but reflow will not be triggered.
Redrawing performs better than reflow because the DOM position information does not need to be updated and the layout process is omitted.

2. Timing trigger thread
The browser timing counter is not counted by the JavaScript engine, because the JavaScript engine is single-threaded.
If the thread is blocked, it will affect the accuracy of timing, so timing and triggering timing through a separate thread is a more reasonable solution.

3. Event trigger thread
When an event is triggered, the thread will add the event to the end of the pending queue and wait for processing by the JS engine.
These events can be the currently executed code block such as scheduled tasks, or they can come from other threads in the browser kernel such as mouse clicks, AJAX asynchronous requests, etc.
However, due to the single-threaded relationship of JS, all these events have to be queued and waited for processing by the JS engine.

4. Asynchronous http request thread
After XMLHttpRequest is connected, a new thread request is opened through the browser. When a status change is detected,
If a callback function is set, the asynchronous thread will generate a state change event and put it in the JavaScript engine’s processing queue to wait for processing.

5.Javascript engine thread
Javascript engine, also known as JS kernel, is mainly responsible for processing Javascript script programs, such as V8 engine.
The Javascript engine thread is of course responsible for parsing Javascript scripts and running code.

Since JavaScript can manipulate the DOM, if you modify the properties of these elements while rendering the interface (that is, the JavaScript thread and the UI thread run at the same time),
Then the element data obtained before and after the rendering thread may be inconsistent. Therefore, in order to prevent unexpected rendering results,
The browser sets the GUI rendering thread and the JavaScript engine to have a mutually exclusive relationship. When the JavaScript engine is executed, the GUI thread will be suspended.
GUI updates are held in a queue and executed immediately when the engine thread is idle.

6. Why JavaScript is designed to be single-threaded

From the above we learned that JavaScript execution is single-threaded, that is, only one thing can be done at the same time.
So why isn’t JavaScript designed for multiple threads? Isn’t this more efficient?
As a browser scripting language, the main purpose of JavaScript is to interact with users and manipulate the DOM. This determines that it can only be single-threaded,
Otherwise it will cause very complex synchronization problems. For example, assume that JavaScript has two threads at the same time, one thread adds content to a certain DOM node,
Another thread deletes this node. At this time, which thread should the browser use?
Therefore, in order to avoid complexity, JavaScript has been single-threaded since its birth. This has become a core feature of the language and will not change in the future.

7. WebWorker multi-threading?

Web Workers provide a simple way to run scripts in a background thread for web content. Threads can perform tasks without interfering with the user interface
So since JavaScript itself is designed to be single-threaded, why is there a multi-threaded API like WebWorker?
Let’s take a look at the core features of WebWorker and we’ll understand:

When creating a Worker, the JS engine applies to the browser to open a child thread (the child thread is opened by the browser, is completely controlled by the main thread, and cannot operate the DOM)
The JS engine thread communicates with the worker thread in a specific way (postMessage API, which requires serializing objects to interact with the thread for specific data)
Therefore, WebWorker does not violate the original intention of the JS engine being single-threaded. Its main purpose is to reduce the burden of CPU-intensive computing logic.

Event loop in browser

JavaScript is a single-threaded scripting language that can only do one thing at a time
JS has many asynchronous operations such as ajax setTimeout setInterval event binding, etc.
We need the help of a multi-threaded browser to add these asynchronous, event, and timer-triggered callbacks to the task queue and wait for js calls.
When js calls methods from the task queue in a loop, we call it an event loop.

Classification of task queues
There are two types of task queues:
One is called macrotask.
One is called microtask, which is also the focus of this article.

Next, let’s take a look at which are macro tasks and which are micro tasks.

Macro tasks: script (overall code), setTimeout, setInterval, I/O (http request), UI rendering, requestAnimationFrame
Microtasks: Promise.then(), catch finally, MutationObserver (listen to changes in dom)

(1) js is single-threaded, but it is divided into synchronous and asynchronous
(2) Microtasks and macrotasks are both asynchronous tasks and they all belong to a queue.
(3) Macro tasks are generally: script, setTimeout, setInterval, postMessage, MessageChannel, setImmediate (Node.js environment)
(4) Microtasks: Promise.then, Object.observe, MutationObserver, process.nextTick (Node.js environment)
(5) Execute synchronization first and then asynchronously. When asynchronous encounters a microtask, execute the microtask first. After execution, if there is no microtask, execute the next macrotask. If there are microtasks, execute the microtasks one by one in order.