<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://phantomwolf.github.io//feed.xml" rel="self" type="application/atom+xml" /><link href="https://phantomwolf.github.io//" rel="alternate" type="text/html" /><updated>2023-08-10T01:27:58+08:00</updated><id>https://phantomwolf.github.io//feed.xml</id><title type="html">My Blog</title><subtitle>Notes, Thoughts, Everything</subtitle><entry><title type="html">WSL2 configuration</title><link href="https://phantomwolf.github.io//windows/wsl2-configuration/" rel="alternate" type="text/html" title="WSL2 configuration" /><published>2023-08-04T12:00:00+08:00</published><updated>2023-08-04T12:00:00+08:00</updated><id>https://phantomwolf.github.io//windows/wsl2-configuration</id><content type="html" xml:base="https://phantomwolf.github.io//windows/wsl2-configuration/">&lt;p&gt;Setup WSL2 Ubuntu 22.04 development environment with GUI and audio&lt;/p&gt;

&lt;h2 id=&quot;gui&quot;&gt;GUI&lt;/h2&gt;
&lt;p&gt;2 GUI options:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;WSLg by Microsoft(recommended)&lt;/li&gt;
  &lt;li&gt;VcXsrv X server&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;wslg-recommended&quot;&gt;WSLg (recommended)&lt;/h3&gt;
&lt;p&gt;C:\Users&amp;lt;UserName&amp;gt;.wslconfig&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[wsl2]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;;enable WSLg
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;guiApplications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;py&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;10GB&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;swap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8GB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;C:\Users&amp;lt;UserName&amp;gt;.wslgconfig&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[system-distro-env]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;;scale 2x
&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;WESTON_RDP_DEBUG_DESKTOP_SCALING_FACTOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;vcxsrv&quot;&gt;VcXsrv&lt;/h3&gt;
&lt;p&gt;First of all, download and install VcXsrv. Find vcxsrv.exe, right click -&amp;gt; properties -&amp;gt; compatibility mode &amp;gt; override high DPI &amp;gt; Application.&lt;/p&gt;

&lt;p&gt;Write a PowerShell script to start VcXsrv and WSL: C:\Users&amp;lt;UserName&amp;gt;\start-wsl.ps1&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Start &quot;vcxsrv&quot; if it's not running&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;vcxsrv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ErrorAction&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SilentlyContinue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write-Host&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;VcXsrv is running.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write-Host&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;VcXsrv is not running. Starting&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start-Process&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FilePath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;C:\Program Files\VcXsrv\vcxsrv.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ArgumentList&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-ac -multiwindow -clipboard -noprimary -wgl&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Start windows terminal&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create a shortcut on desktop to start it: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -command &quot;&amp;amp; '%userprofile%\start-wsl.ps1'&quot;&lt;/code&gt;. Remember to change Windows terminal settings and set WSL linux distro as the default profile.&lt;/p&gt;

&lt;p&gt;Double-click the shortcut to start wsl terminal. In .bashrc, add:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# VcXsrv&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DISPLAY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /etc/resolv.conf | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;nameserver | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2; exit;}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;:0.0
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LIBGL_ALWAYS_INDIRECT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1

&lt;span class=&quot;c&quot;&gt;# Scale UI(causes problem with WSLg)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GDK_SCALE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source ~/.bashrc&lt;/code&gt; and try start a GUI app, such as: xfce4-terminal. Note that VcXsrv needs to be running before starting WSL.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/bashonubuntuonwindows/comments/7s3jav/on_high_resolution_monitor_x_apps_are_blurry/&quot;&gt;On high resolution monitor, X apps are blurry&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sourceforge.net/p/vcxsrv/wiki/Using%20VcXsrv%20Windows%20X%20Server/&quot;&gt;VcXsrv Windows X Server Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;fcitx&quot;&gt;Fcitx&lt;/h2&gt;
&lt;p&gt;Install fcitx5: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt install fcitx5 fcitx5-chinese-addons&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In ~/.bashrc, add&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Fcitx
export INPUT_METHOD=fcitx
export QT_IM_MODULE=fcitx
export GTK_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
if ! pgrep dbus-launch &amp;amp;&amp;gt; /dev/null; then
  dbus-launch --sh-syntax --exit-with-session &amp;gt; /dev/null
fi
if ! pgrep fcitx5 &amp;amp;&amp;gt; /dev/null; then
  daemonize -e /tmp/fcitx5.log -o /tmp/fcitx5.log -p /tmp/fcitx5.pid -l /tmp/fcitx5.pid -a /usr/bin/fcitx5 --disable=wayland
fi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;fcitx5 will be automatically started everytime you start WSL.&lt;/p&gt;

&lt;h2 id=&quot;pulseaudio&quot;&gt;PulseAudio&lt;/h2&gt;
&lt;p&gt;Download PulseAudio for windows: https://www.freedesktop.org/wiki/Software/PulseAudio/Ports/Windows/Support/. Extract it to C:\pulseaudio&lt;/p&gt;

&lt;p&gt;Create a config.pa file at C:\pulseaudio\etc\pulse\config.pa&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;172.16.0.0/12
load-module module-esound-protocol-tcp auth-ip-acl=127.0.0.1;172.16.0.0/12
load-module module-waveout sink_name=output source_name=input record=0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Download nssm from https://nssm.cc/download. Extract and copy nssm.exe to C:\pulseaudio\bin\nssm.exe. Open a cmd or PowerShell at C:\pulseaudio\bin\, and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nssm.exe install PulseAudio&lt;/code&gt; for cmd, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.\nssm.exe install PulseAudio&lt;/code&gt; for PowerShell. In the dialogue, set:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Path: C:\pulseaudio\bin\pulseaudio.exe&lt;/li&gt;
  &lt;li&gt;Startup directory: C:\pulseaudio\bin&lt;/li&gt;
  &lt;li&gt;Arguments: -F C:\pulseaudio\etc\pulse\config.pa –exit-idle-time=-1&lt;/li&gt;
  &lt;li&gt;(optional) set a display name in “Details” tab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click “Install” to install PulseAudio as a windows service. Win + R and run services.msc to review it. Now pulseaudio will automatically start on each windows launch.&lt;/p&gt;

&lt;p&gt;In .bashrc, add:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# PulseAudio&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;HOST_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;ip route |awk &lt;span class=&quot;s1&quot;&gt;'/^default/{print $3}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PULSE_SERVER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tcp:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOST_IP&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;forwarding-wsl-ports-to-windows&quot;&gt;Forwarding WSL ports to Windows&lt;/h2&gt;
&lt;p&gt;First of all, make sure ifconfig is installed in your WSL Linux distro. Then write a PowerShell script for starting port forwarding, and save it to C:\Users&amp;lt;User&amp;gt;\Bridge-WslPorts.ps1:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ports&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5432&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$wslAddress&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bash.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ifconfig eth0 | grep -oP '(?&amp;lt;=inet\s)\d+(\.\d+){3}'&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$wslAddress&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-match&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'^(\d{1,3}\.){3}\d{1,3}$'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write-Host&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;WSL IP address: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$wslAddress&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ForegroundColor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Green&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write-Host&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Ports: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ports&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ForegroundColor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Green&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write-Host&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Error: Could not find WSL IP address.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ForegroundColor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Red&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$listenAddress&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'0.0.0.0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$port&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke-Expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;netsh interface portproxy delete v4tov4 listenport=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$port&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; listenaddress=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$listenAddress&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke-Expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;netsh interface portproxy add v4tov4 listenport=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$port&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; listenaddress=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$listenAddress&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; connectport=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$port&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; connectaddress=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$wslAddress&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fireWallDisplayName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'WSL Port Forwarding'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$portsStr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ports&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-join&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke-Expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Remove-NetFireWallRule -DisplayName '&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fireWallDisplayName&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;'&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke-Expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;New-NetFireWallRule -DisplayName '&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fireWallDisplayName&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;' -Direction Outbound -LocalPort &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$portsStr&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -Action Allow -Protocol TCP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Invoke-Expression&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;New-NetFireWallRule -DisplayName '&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$fireWallDisplayName&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;' -Direction Inbound -LocalPort &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$portsStr&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -Action Allow -Protocol TCP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open PowerShell and register a scheduled task to run the script on Windows boot:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New-ScheduledTaskAction&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Execute&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;powershell.exe&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Argument&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-File &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;C:\Users\&amp;lt;User&amp;gt;\Bridge-WslPorts.ps1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;`&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; -WindowStyle Hidden -ExecutionPolicy Bypass&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New-ScheduledTaskTrigger&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-AtLogon&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New-ScheduledTaskSettingsSet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-AllowStartIfOnBatteries&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-DontStopIfGoingOnBatteries&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New-ScheduledTaskPrincipal&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-GroupId&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;BUILTIN\Administrators&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-RunLevel&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Highest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Register-ScheduledTask&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-TaskName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;WSL2PortsBridge&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Action&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Trigger&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$t&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Settings&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Principal&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$p&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now port forwarding will be started each time you start Windows.&lt;/p&gt;</content><author><name></name></author><category term="Windows" /><summary type="html">Setup WSL2 Ubuntu 22.04 development environment with GUI and audio</summary></entry><entry><title type="html">InfluxDB基础概念</title><link href="https://phantomwolf.github.io//influxdb/influxdb-key-concepts/" rel="alternate" type="text/html" title="InfluxDB基础概念" /><published>2018-12-21T15:49:00+08:00</published><updated>2018-12-21T15:49:00+08:00</updated><id>https://phantomwolf.github.io//influxdb/influxdb-key-concepts</id><content type="html" xml:base="https://phantomwolf.github.io//influxdb/influxdb-key-concepts/">&lt;p&gt;请看以下示例数据。&lt;/p&gt;

&lt;p&gt;name: census&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;time&lt;/th&gt;
      &lt;th&gt;butterflies&lt;/th&gt;
      &lt;th&gt;honeybees&lt;/th&gt;
      &lt;th&gt;location&lt;/th&gt;
      &lt;th&gt;scientist&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;2015-08-18T00:00:00Z&lt;/td&gt;
      &lt;td&gt;12&lt;/td&gt;
      &lt;td&gt;23&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;langstroth&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2015-08-18T00:00:00Z&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;30&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;perpetua&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2015-08-18T00:06:00Z&lt;/td&gt;
      &lt;td&gt;11&lt;/td&gt;
      &lt;td&gt;28&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;langstroth&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2015-08-18T00:06:00Z&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;28&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;perpetua&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2015-08-18T05:54:00Z&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;11&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;langstroth&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2015-08-18T06:00:00Z&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;10&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;langstroth&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2015-08-18T06:06:00Z&lt;/td&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;23&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;perpetua&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2015-08-18T06:12:00Z&lt;/td&gt;
      &lt;td&gt;7&lt;/td&gt;
      &lt;td&gt;22&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;perpetua&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;time&quot;&gt;time&lt;/h2&gt;
&lt;p&gt;InfluxDB是时序数据库，所有的数据都带有timestamp。&lt;/p&gt;

&lt;h2 id=&quot;field&quot;&gt;field&lt;/h2&gt;

&lt;p&gt;field是我们要记录的主要数据，由field key和field value组成。上表中，列butterflies和honeybees都是field key，对应的数字即为该field在某个时间的value。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;field value可以是string, float, integer, 或boolean类型&lt;/li&gt;
  &lt;li&gt;field value未被索引，对field value的查询必须遍历所有field value值，因此性能较差。&lt;/li&gt;
  &lt;li&gt;field是InfluxDB中的基础，没有field就无法存储数据。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;拥有同样timestamp的一系列field key-value对构成一个field set，例如butterflies = 12 honeybees = 23就是一个field set&lt;/p&gt;

&lt;h3 id=&quot;实例&quot;&gt;实例&lt;/h3&gt;
&lt;p&gt;在performance测试中，cpu idle可以作为一个field。&lt;/p&gt;

&lt;h3 id=&quot;操作&quot;&gt;操作&lt;/h3&gt;
&lt;p&gt;要查看所有的field key：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SHOW FIELD KEYS [ON &amp;lt;database_name&amp;gt;] [FROM &amp;lt;measurement_name&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;tag&quot;&gt;tag&lt;/h2&gt;
&lt;p&gt;tag是给field打上的标签，存储一些额外的信息。上表中，列location和scientist均为tag key，分别表示记录数据的位置和记录者，其对应的值为tag value。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;field是必须的，tag是可选的&lt;/li&gt;
  &lt;li&gt;tag value为string类型&lt;/li&gt;
  &lt;li&gt;tag有索引，适合搜索。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;tag键值对的不同组合，构成一个个tag field：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;location = 1, scientist = langstroth&lt;/li&gt;
  &lt;li&gt;location = 2, scientist = langstroth&lt;/li&gt;
  &lt;li&gt;location = 1, scientist = perpetua&lt;/li&gt;
  &lt;li&gt;location = 2, scientist = perpetua&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;实例-1&quot;&gt;实例&lt;/h3&gt;
&lt;p&gt;在performance测试中，job id可以作为一个tag。&lt;/p&gt;

&lt;h3 id=&quot;操作-1&quot;&gt;操作&lt;/h3&gt;
&lt;p&gt;要查看所有的tag key：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SHOW TAG KEYS [ON &amp;lt;database_name&amp;gt;] [FROM_clause] [WHERE &amp;lt;tag_key&amp;gt; &amp;lt;operator&amp;gt; ['&amp;lt;tag_value&amp;gt;' | &amp;lt;regular_expression&amp;gt;]] [LIMIT_clause] [OFFSET_clause]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;measurement&quot;&gt;measurement&lt;/h2&gt;
&lt;p&gt;measurement是tag, field, time的容器，概念上类似table。measurement name是对于相关数据的一个描述。&lt;/p&gt;

&lt;p&gt;例如上表中，census是一个measurement，它包含两个field(butterflies和honeybees)、两个tag(location和scientist)。&lt;/p&gt;

&lt;h3 id=&quot;实例-2&quot;&gt;实例&lt;/h3&gt;
&lt;p&gt;在performance测试中，存在一个叫做cpu的measurement，它有4个field：user, idle, iowait, system。&lt;/p&gt;

&lt;h3 id=&quot;操作-2&quot;&gt;操作&lt;/h3&gt;
&lt;p&gt;列出所有measurements：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;show measurements
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;要查看一个measurement中所有field key：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;show field keys from ads;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;retention-policy&quot;&gt;retention policy&lt;/h2&gt;
&lt;p&gt;一个measurement可以属于若干retention policy。retention policy表示数据应该保留多久(duration)、在集群里复制多少份(replication)。&lt;/p&gt;

&lt;p&gt;measurement默认属于名为autogen的retention policy：数据永久保留；只复制一份。&lt;/p&gt;

&lt;h2 id=&quot;series&quot;&gt;series&lt;/h2&gt;
&lt;p&gt;一系列拥有相同的retention policy、measurement、tag set的数据，称为一个series。&lt;/p&gt;

&lt;p&gt;在上表中，所有retention policy: autogen, measurement: census, tag set: location = 1,scientist = langstroth的数据称为一个series。&lt;/p&gt;

&lt;h3 id=&quot;操作-3&quot;&gt;操作&lt;/h3&gt;
&lt;p&gt;删除series：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DROP SERIES FROM &amp;lt;measurement_name[,measurement_name]&amp;gt; WHERE &amp;lt;tag_key&amp;gt;='&amp;lt;tag_value&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name></name></author><category term="influxdb" /><summary type="html">请看以下示例数据。 name: census time butterflies honeybees location scientist 2015-08-18T00:00:00Z 12 23 1 langstroth 2015-08-18T00:00:00Z 1 30 1 perpetua 2015-08-18T00:06:00Z 11 28 1 langstroth 2015-08-18T00:06:00Z 3 28 1 perpetua 2015-08-18T05:54:00Z 2 11 2 langstroth 2015-08-18T06:00:00Z 1 10 2 langstroth 2015-08-18T06:06:00Z 8 23 2 perpetua 2015-08-18T06:12:00Z 7 22 2 perpetua time InfluxDB是时序数据库，所有的数据都带有timestamp。 field field是我们要记录的主要数据，由field key和field value组成。上表中，列butterflies和honeybees都是field key，对应的数字即为该field在某个时间的value。 field value可以是string, float, integer, 或boolean类型 field value未被索引，对field value的查询必须遍历所有field value值，因此性能较差。 field是InfluxDB中的基础，没有field就无法存储数据。 拥有同样timestamp的一系列field key-value对构成一个field set，例如butterflies = 12 honeybees = 23就是一个field set 实例 在performance测试中，cpu idle可以作为一个field。 操作 要查看所有的field key： SHOW FIELD KEYS [ON &amp;lt;database_name&amp;gt;] [FROM &amp;lt;measurement_name&amp;gt;] tag tag是给field打上的标签，存储一些额外的信息。上表中，列location和scientist均为tag key，分别表示记录数据的位置和记录者，其对应的值为tag value。 field是必须的，tag是可选的 tag value为string类型 tag有索引，适合搜索。 tag键值对的不同组合，构成一个个tag field： location = 1, scientist = langstroth location = 2, scientist = langstroth location = 1, scientist = perpetua location = 2, scientist = perpetua 实例 在performance测试中，job id可以作为一个tag。 操作 要查看所有的tag key： SHOW TAG KEYS [ON &amp;lt;database_name&amp;gt;] [FROM_clause] [WHERE &amp;lt;tag_key&amp;gt; &amp;lt;operator&amp;gt; ['&amp;lt;tag_value&amp;gt;' | &amp;lt;regular_expression&amp;gt;]] [LIMIT_clause] [OFFSET_clause] measurement measurement是tag, field, time的容器，概念上类似table。measurement name是对于相关数据的一个描述。 例如上表中，census是一个measurement，它包含两个field(butterflies和honeybees)、两个tag(location和scientist)。 实例 在performance测试中，存在一个叫做cpu的measurement，它有4个field：user, idle, iowait, system。 操作 列出所有measurements： show measurements 要查看一个measurement中所有field key： show field keys from ads; retention policy 一个measurement可以属于若干retention policy。retention policy表示数据应该保留多久(duration)、在集群里复制多少份(replication)。 measurement默认属于名为autogen的retention policy：数据永久保留；只复制一份。 series 一系列拥有相同的retention policy、measurement、tag set的数据，称为一个series。 在上表中，所有retention policy: autogen, measurement: census, tag set: location = 1,scientist = langstroth的数据称为一个series。 操作 删除series： DROP SERIES FROM &amp;lt;measurement_name[,measurement_name]&amp;gt; WHERE &amp;lt;tag_key&amp;gt;='&amp;lt;tag_value&amp;gt;'</summary></entry><entry><title type="html">epoll详解</title><link href="https://phantomwolf.github.io//linux/epoll-introduction/" rel="alternate" type="text/html" title="epoll详解" /><published>2018-04-15T20:32:41+08:00</published><updated>2018-04-15T20:32:41+08:00</updated><id>https://phantomwolf.github.io//linux/epoll-introduction</id><content type="html" xml:base="https://phantomwolf.github.io//linux/epoll-introduction/">&lt;h2 id=&quot;epoll使用方法&quot;&gt;epoll使用方法&lt;/h2&gt;
&lt;p&gt;用epoll_create或epoll_create1创建epoll instance。与epoll_create不同的是，epoll_create1支持一个flag参数。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epfd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoll_create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;// 参数会被忽略，但必须大于0
&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epfd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoll_create1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EPOLL_CLOEXEC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// 给epfd设置FD_CLOEXEC标志位&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;用epoll_ctl注册要监视的fd：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoll_event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EPOLLIN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EPOLLET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// fd可读时通知，启用边缘触发(edge-trigger)
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server_socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;epoll_ctl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;epfd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EPOLL_CTL_ADD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server_socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;用epoll_wait等待事件。函数原型：int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)。其中，若timeout为-1，表示永远等下去。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nfds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoll_wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;epfd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MAX_EVENTS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nfds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// read from events[i].data.fd
&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;epoll的原理&quot;&gt;epoll的原理&lt;/h2&gt;
&lt;h3 id=&quot;select和poll的原理&quot;&gt;select和poll的原理&lt;/h3&gt;
&lt;p&gt;select和poll产生一个fd的数组，包含它们感兴趣的fd，然后发出系统调用，将它们交给内核。内核将fd数组从用户态复制到内核态，然后依次检查每一个fd。最后，select会生成一个bit数组(默认长度FD_SETSIZE被hardcode为1024)，每个bit对应一个fd，表示该fd是否有事件发生；poll则直接操作用户态的pollfd结构。两者的区别是，select只能监视有限数量的fd(FD_SETSIZE被hardcode为1024)。而poll直接操作用户提供的pollfd数组，没有这个限制。&lt;/p&gt;

&lt;p&gt;可见这两个函数的时间复杂度都是O(n)。在服务器只有数千个TCP连接的年代，它们是够用的。但假如服务器有10万个连接，那很多CPU时间会浪费在挨个检查每个fd上。为了解决这个问题，内核引入了epoll。&lt;/p&gt;

&lt;h3 id=&quot;epoll概述&quot;&gt;epoll概述&lt;/h3&gt;
&lt;p&gt;epoll与select/poll的最大区别是，epoll没有每次都把大量的fd传递给内核，而是在内核中获取一个epoll instance，并把fd注册到它上面。epoll没有对整个fd数组进行poll，而是epoll instance监视注册的fd，并将事件“报告”给用户程序。&lt;/p&gt;

&lt;p&gt;当“有事件发生的fd”与“所有监视的fd”的比例较小时，这个机制非常有效。&lt;/p&gt;

&lt;h3 id=&quot;epoll_create代码分析&quot;&gt;epoll_create代码分析&lt;/h3&gt;
&lt;p&gt;epoll instance是epoll子系统的核心。在linux下，我们用epoll_create()和epoll_create1()来获取一个epoll_instance，两个函数都返回一个fd。之所以把epoll也弄成fd，是因为这样做epoll instance自己也是pollable的，我们可以对epoll fd再进行epoll/select/poll。&lt;/p&gt;

&lt;p&gt;epoll instance最重要的部分是struct eventpoll，在fs/eventpoll.c中定义。我们来看系统调用epoll_create1的部分代码：SYSCALL_DEFINE1(epoll_create1, int, flags)。首先，用ep_alloc分配一个struct eventpoll的空间：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventpoll&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Create the internal data structure (&quot;struct eventpoll&quot;)
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ep_alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;之后，获取进程未使用的fd：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_unused_fd_flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;O_RDWR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;O_CLOEXEC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;从系统获取anonymous inode，并保存到之前的struct eventpoll结构体中。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anon_inode_getfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[eventpoll]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eventpoll_fops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                              &lt;span class=&quot;n&quot;&gt;O_RDWR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;O_CLOEXEC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;将inode和fd bind起来，并将fd返回：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;n&quot;&gt;fd_install&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;epoll-instance如何保存要监视的fd&quot;&gt;epoll instance如何保存要监视的fd？&lt;/h3&gt;
&lt;p&gt;epoll instance使用红黑树来保存要监视的fd，红黑树的root就是struct eventpoll中的rbr成员，是在ep_alloc()中初始化的。&lt;/p&gt;

&lt;p&gt;对于每个要监视的fd，红黑树中都有一个struct epitem与之对应。我们来看系统调用epoll_ctl的代码：SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event __user *, event)&lt;/p&gt;

&lt;p&gt;用ep_find来找到fd对应的epitem：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;    &lt;span class=&quot;n&quot;&gt;epi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ep_find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;找到epitem后，如果epoll_ctl的op参数为EPOLL_CTL_ADD，则调用ep_insert将其添加；如果是EPOLL_CTL_DEL，则调用ep_remove将其移除；如果是EPOLL_CTL_MOD，则调用ep_modify将其修改。&lt;/p&gt;

&lt;p&gt;在红黑树结点中，作为key的是struct epitem中的struct epoll_filefd，其定义如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoll_filefd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__packed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;执行key compare的是ep_cmp_ffd()函数，其定义如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;cm&quot;&gt;/* Compare RB tree keys */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;inline&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ep_cmp_ffd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoll_filefd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                             &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;epoll_filefd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;相关链接&quot;&gt;相关链接&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;《The Implementation of epoll (1)》 https://idndx.com/2014/09/01/the-implementation-of-epoll-1/&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><category term="linux" /><summary type="html">epoll使用方法 用epoll_create或epoll_create1创建epoll instance。与epoll_create不同的是，epoll_create1支持一个flag参数。 int epfd = epoll_create(1); // 参数会被忽略，但必须大于0 int epfd = epoll_create1(EPOLL_CLOEXEC); // 给epfd设置FD_CLOEXEC标志位 用epoll_ctl注册要监视的fd： struct epoll_event ev; ev.events = EPOLLIN | EPOLLET; // fd可读时通知，启用边缘触发(edge-trigger) ev.data.fd = server_socket; epoll_ctl(epfd, EPOLL_CTL_ADD, server_socket, &amp;amp;ev); 用epoll_wait等待事件。函数原型：int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)。其中，若timeout为-1，表示永远等下去。 int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); for (int i=0; i &amp;lt; nfds; ++i) { // read from events[i].data.fd } epoll的原理 select和poll的原理 select和poll产生一个fd的数组，包含它们感兴趣的fd，然后发出系统调用，将它们交给内核。内核将fd数组从用户态复制到内核态，然后依次检查每一个fd。最后，select会生成一个bit数组(默认长度FD_SETSIZE被hardcode为1024)，每个bit对应一个fd，表示该fd是否有事件发生；poll则直接操作用户态的pollfd结构。两者的区别是，select只能监视有限数量的fd(FD_SETSIZE被hardcode为1024)。而poll直接操作用户提供的pollfd数组，没有这个限制。 可见这两个函数的时间复杂度都是O(n)。在服务器只有数千个TCP连接的年代，它们是够用的。但假如服务器有10万个连接，那很多CPU时间会浪费在挨个检查每个fd上。为了解决这个问题，内核引入了epoll。 epoll概述 epoll与select/poll的最大区别是，epoll没有每次都把大量的fd传递给内核，而是在内核中获取一个epoll instance，并把fd注册到它上面。epoll没有对整个fd数组进行poll，而是epoll instance监视注册的fd，并将事件“报告”给用户程序。 当“有事件发生的fd”与“所有监视的fd”的比例较小时，这个机制非常有效。 epoll_create代码分析 epoll instance是epoll子系统的核心。在linux下，我们用epoll_create()和epoll_create1()来获取一个epoll_instance，两个函数都返回一个fd。之所以把epoll也弄成fd，是因为这样做epoll instance自己也是pollable的，我们可以对epoll fd再进行epoll/select/poll。 epoll instance最重要的部分是struct eventpoll，在fs/eventpoll.c中定义。我们来看系统调用epoll_create1的部分代码：SYSCALL_DEFINE1(epoll_create1, int, flags)。首先，用ep_alloc分配一个struct eventpoll的空间： struct eventpoll *ep = NULL; // Create the internal data structure (&quot;struct eventpoll&quot;) error = ep_alloc(&amp;amp;ep); 之后，获取进程未使用的fd： fd = get_unused_fd_flags(O_RDWR | (flags &amp;amp; O_CLOEXEC)); 从系统获取anonymous inode，并保存到之前的struct eventpoll结构体中。 file = anon_inode_getfile(&quot;[eventpoll]&quot;, &amp;amp;eventpoll_fops, ep, O_RDWR | (flags &amp;amp; O_CLOEXEC)); ep-&amp;gt;file = file; 将inode和fd bind起来，并将fd返回： fd_install(fd, file); return fd; epoll instance如何保存要监视的fd？ epoll instance使用红黑树来保存要监视的fd，红黑树的root就是struct eventpoll中的rbr成员，是在ep_alloc()中初始化的。 对于每个要监视的fd，红黑树中都有一个struct epitem与之对应。我们来看系统调用epoll_ctl的代码：SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event __user *, event) 用ep_find来找到fd对应的epitem： epi = ep_find(ep, tf.file, fd); 找到epitem后，如果epoll_ctl的op参数为EPOLL_CTL_ADD，则调用ep_insert将其添加；如果是EPOLL_CTL_DEL，则调用ep_remove将其移除；如果是EPOLL_CTL_MOD，则调用ep_modify将其修改。 在红黑树结点中，作为key的是struct epitem中的struct epoll_filefd，其定义如下： struct epoll_filefd { struct file *file; int fd; } __packed; 执行key compare的是ep_cmp_ffd()函数，其定义如下： /* Compare RB tree keys */ static inline int ep_cmp_ffd(struct epoll_filefd *p1, struct epoll_filefd *p2) { return (p1-&amp;gt;file &amp;gt; p2-&amp;gt;file ? +1: (p1-&amp;gt;file &amp;lt; p2-&amp;gt;file ? -1 : p1-&amp;gt;fd - p2-&amp;gt;fd)); } 相关链接 《The Implementation of epoll (1)》 https://idndx.com/2014/09/01/the-implementation-of-epoll-1/</summary></entry><entry><title type="html">LVS的调度算法</title><link href="https://phantomwolf.github.io//system-design/LVS-scheduling-algorithms/" rel="alternate" type="text/html" title="LVS的调度算法" /><published>2018-04-06T17:20:00+08:00</published><updated>2018-04-06T17:20:00+08:00</updated><id>https://phantomwolf.github.io//system-design/LVS-scheduling-algorithms</id><content type="html" xml:base="https://phantomwolf.github.io//system-design/LVS-scheduling-algorithms/">&lt;p&gt;LVS有以下调度算法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;轮流调度Round-Robin Scheduling&lt;/li&gt;
  &lt;li&gt;带权重的轮流调度Weighted Round-Robin Scheduling&lt;/li&gt;
  &lt;li&gt;Least-Connection Scheduling&lt;/li&gt;
  &lt;li&gt;Weighted Least-Connection Scheduling&lt;/li&gt;
  &lt;li&gt;Locality-Based Least-Connection Scheduling&lt;/li&gt;
  &lt;li&gt;Locality-Based Least-Connection with Replication Scheduling&lt;/li&gt;
  &lt;li&gt;Destination Hashing Scheduling&lt;/li&gt;
  &lt;li&gt;Source Hashing Scheduling&lt;/li&gt;
  &lt;li&gt;Shortest Expected Delay Scheduling&lt;/li&gt;
  &lt;li&gt;Never Queue Scheduling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;轮流调度&quot;&gt;轮流调度&lt;/h2&gt;
&lt;p&gt;每次都把请求发给服务器列表中的下一个。在这个算法中，所有服务器都将被视为平等的。&lt;/p&gt;

&lt;p&gt;与轮流DNS(round-robin DNS，每次都将域名解析为列表中的下一个IP地址)相似，但由于DNS缓存的存在，轮流DNS可能会引起流量的不平衡。&lt;/p&gt;

&lt;h2 id=&quot;带权重的轮流调度&quot;&gt;带权重的轮流调度&lt;/h2&gt;
&lt;p&gt;本算法为更好的处理拥有不同性能的服务器而设计。根据处理能力，每个服务器都将被分到一个权重。较高权重的服务器会先收到新请求，最终也会收到更多的请求。相同权重的服务器收到相等数量的请求。例如有3个服务器A、B、C，分别拥有权重4、3、2，一个良好的调度顺序是：AABABCABC。&lt;/p&gt;

&lt;p&gt;当服务器的处理能力不同时，带权重的轮流调度算法比普通的轮流调度要好。但是，如果请求的负载差别很大，仍然有可能导致不平衡。&lt;/p&gt;

&lt;p&gt;以下是本算法的一个实现的伪代码：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;cm&quot;&gt;/*Supposing that there is a server set S = {S0, S1, …, Sn-1};
W(Si) indicates the weight of Si;
i indicates the server selected last time, and i is initialized with -1;
cw is the current weight in scheduling, and cw is initialized with zero; 
max(S) is the maximum weight of all the servers in S;
gcd(S) is the greatest common divisor of all server weights in S;*/&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mod&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gcd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;W&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Si&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;解释：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;设置一个变量cw(current weight)表示本轮遍历可以接受的最低权重。&lt;/li&gt;
  &lt;li&gt;遍历所有服务器，若其权重大于等于cw，就将其返回；若小于cw，则查看下一个服务器。&lt;/li&gt;
  &lt;li&gt;扫描完整个S数组后，i会变成0。cw减去所有权重的最大公约数(gcd)，再开始下一轮遍历。若cw小于等于0，则将其重置为最大的权重。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于权重分别为4、3、2的服务器A、B、C来说，第一轮遍历，i为0，cw为0，但会被置为最大权重4，表示本轮接受所有权重大于等于4的服务器。于是第一轮遍历，只有A符合要求。第二轮遍历，cw减去所有权重的最大公约数1，变为3，表示本轮接受权重大于等于3的服务器，A、B均符合要求。第三轮，cw变为2，A、B、C均符合要求。第四轮，cw变为1，A、B、C均符合要求。所有总的调度序列为：AABABCABC。&lt;/p&gt;

&lt;p&gt;第五轮，cw变为-1，被重置为最大权重4，调度继续进行。&lt;/p&gt;

&lt;p&gt;为什么每轮可接受的最小权重cw要减去最大公约数gcd，而不是每次都固定减去1呢？思考权重分别为4、2的服务器A、B。如果cw每次都固定减去1，调度序列会是：AAABAB。如果cw每次减去最大公约数2，调度序列会是：AABAAB。可见，前者对请求的分配更为密集，后者更好一些。还有一个方法也可以解决前者的问题，可以把所有权重都除以其最大公约数，那么A、B的权重变为2、1，就不会有之前的问题了。&lt;/p&gt;

&lt;h2 id=&quot;least-connection-scheduling&quot;&gt;Least-Connection Scheduling&lt;/h2&gt;
&lt;p&gt;将请求发给目前连接数最少的服务器。属于动态调度算法，需要计算服务器实时的连接数。本算法能很好处理请求的负载差别很大的情况。&lt;/p&gt;

&lt;h2 id=&quot;weighted-least-connection-scheduling&quot;&gt;Weighted Least-Connection Scheduling&lt;/h2&gt;
&lt;p&gt;与上述算法相似，只不过可以给服务器设置权重。当下列公式被满足时，请求会被发送给第j个服务器：(Cj/ALL_CONNECTIONS)/Wj = min { (Ci/ALL_CONNECTIONS)/Wi } (i=1,..,n)&lt;/p&gt;

&lt;p&gt;由于ALL_CONNECTIONS在一次查询中是固定的，所以可以优化为：Cj/Wj = min { Ci/Wi } (i=1,..,n)&lt;/p&gt;

&lt;h2 id=&quot;locality-based-least-connection-scheduling&quot;&gt;Locality-Based Least-Connection Scheduling&lt;/h2&gt;
&lt;p&gt;将用户的IP map到固定的服务器(比如根据用户的位置，map到最近的服务器)。若该服务器已经过载(连接数大于其权重)，且有其他服务器的负载尚未过半，就按Least-Connection Scheduling来处理。&lt;/p&gt;

&lt;h2 id=&quot;locality-based-least-connection-with-replication-scheduling&quot;&gt;Locality-Based Least-Connection with Replication Scheduling&lt;/h2&gt;
&lt;p&gt;将用户的IP map到一系列服务器，在这些服务器中实行Least-Connection Scheduling。如果这些服务器都过载了，再从整个集群里找连接数最少的服务器。&lt;/p&gt;

&lt;h2 id=&quot;destination-hashing-scheduling&quot;&gt;Destination Hashing Scheduling&lt;/h2&gt;
&lt;p&gt;根据用户的IP，将请求发送到最近的服务器上。&lt;/p&gt;

&lt;h2 id=&quot;source-hashing-scheduling&quot;&gt;Source Hashing Scheduling&lt;/h2&gt;
&lt;p&gt;The source hashing scheduling algorithm assigns network connections to the servers through looking up a statically assigned hash table by their source IP addresses.&lt;/p&gt;

&lt;h2 id=&quot;shortest-expected-delay-scheduling&quot;&gt;Shortest Expected Delay Scheduling&lt;/h2&gt;
&lt;p&gt;将请求发给预期延迟最低的服务器。第i个服务器的预期延迟的计算公式为(Ci+1)/Ui，Ci是该服务器上的连接数量，Ui是权重。&lt;/p&gt;

&lt;h2 id=&quot;never-queue-scheduling&quot;&gt;Never Queue Scheduling&lt;/h2&gt;
&lt;p&gt;当有空闲服务器存在时，直接发给空闲服务器；当没有空闲服务器时，按Shortest Expected Delay Scheduling处理。&lt;/p&gt;</content><author><name></name></author><category term="system-design" /><summary type="html">LVS有以下调度算法： 轮流调度Round-Robin Scheduling 带权重的轮流调度Weighted Round-Robin Scheduling Least-Connection Scheduling Weighted Least-Connection Scheduling Locality-Based Least-Connection Scheduling Locality-Based Least-Connection with Replication Scheduling Destination Hashing Scheduling Source Hashing Scheduling Shortest Expected Delay Scheduling Never Queue Scheduling 轮流调度 每次都把请求发给服务器列表中的下一个。在这个算法中，所有服务器都将被视为平等的。 与轮流DNS(round-robin DNS，每次都将域名解析为列表中的下一个IP地址)相似，但由于DNS缓存的存在，轮流DNS可能会引起流量的不平衡。 带权重的轮流调度 本算法为更好的处理拥有不同性能的服务器而设计。根据处理能力，每个服务器都将被分到一个权重。较高权重的服务器会先收到新请求，最终也会收到更多的请求。相同权重的服务器收到相等数量的请求。例如有3个服务器A、B、C，分别拥有权重4、3、2，一个良好的调度顺序是：AABABCABC。 当服务器的处理能力不同时，带权重的轮流调度算法比普通的轮流调度要好。但是，如果请求的负载差别很大，仍然有可能导致不平衡。 以下是本算法的一个实现的伪代码： /*Supposing that there is a server set S = {S0, S1, …, Sn-1}; W(Si) indicates the weight of Si; i indicates the server selected last time, and i is initialized with -1; cw is the current weight in scheduling, and cw is initialized with zero; max(S) is the maximum weight of all the servers in S; gcd(S) is the greatest common divisor of all server weights in S;*/ while (true) { i = (i + 1) mod n; if (i == 0) { cw = cw - gcd(S); if (cw &amp;lt;= 0) { cw = max(S); if (cw == 0) return NULL; } } if (W(Si) &amp;gt;= cw) return Si; } 解释： 设置一个变量cw(current weight)表示本轮遍历可以接受的最低权重。 遍历所有服务器，若其权重大于等于cw，就将其返回；若小于cw，则查看下一个服务器。 扫描完整个S数组后，i会变成0。cw减去所有权重的最大公约数(gcd)，再开始下一轮遍历。若cw小于等于0，则将其重置为最大的权重。 对于权重分别为4、3、2的服务器A、B、C来说，第一轮遍历，i为0，cw为0，但会被置为最大权重4，表示本轮接受所有权重大于等于4的服务器。于是第一轮遍历，只有A符合要求。第二轮遍历，cw减去所有权重的最大公约数1，变为3，表示本轮接受权重大于等于3的服务器，A、B均符合要求。第三轮，cw变为2，A、B、C均符合要求。第四轮，cw变为1，A、B、C均符合要求。所有总的调度序列为：AABABCABC。 第五轮，cw变为-1，被重置为最大权重4，调度继续进行。 为什么每轮可接受的最小权重cw要减去最大公约数gcd，而不是每次都固定减去1呢？思考权重分别为4、2的服务器A、B。如果cw每次都固定减去1，调度序列会是：AAABAB。如果cw每次减去最大公约数2，调度序列会是：AABAAB。可见，前者对请求的分配更为密集，后者更好一些。还有一个方法也可以解决前者的问题，可以把所有权重都除以其最大公约数，那么A、B的权重变为2、1，就不会有之前的问题了。 Least-Connection Scheduling 将请求发给目前连接数最少的服务器。属于动态调度算法，需要计算服务器实时的连接数。本算法能很好处理请求的负载差别很大的情况。 Weighted Least-Connection Scheduling 与上述算法相似，只不过可以给服务器设置权重。当下列公式被满足时，请求会被发送给第j个服务器：(Cj/ALL_CONNECTIONS)/Wj = min { (Ci/ALL_CONNECTIONS)/Wi } (i=1,..,n) 由于ALL_CONNECTIONS在一次查询中是固定的，所以可以优化为：Cj/Wj = min { Ci/Wi } (i=1,..,n) Locality-Based Least-Connection Scheduling 将用户的IP map到固定的服务器(比如根据用户的位置，map到最近的服务器)。若该服务器已经过载(连接数大于其权重)，且有其他服务器的负载尚未过半，就按Least-Connection Scheduling来处理。 Locality-Based Least-Connection with Replication Scheduling 将用户的IP map到一系列服务器，在这些服务器中实行Least-Connection Scheduling。如果这些服务器都过载了，再从整个集群里找连接数最少的服务器。 Destination Hashing Scheduling 根据用户的IP，将请求发送到最近的服务器上。 Source Hashing Scheduling The source hashing scheduling algorithm assigns network connections to the servers through looking up a statically assigned hash table by their source IP addresses. Shortest Expected Delay Scheduling 将请求发给预期延迟最低的服务器。第i个服务器的预期延迟的计算公式为(Ci+1)/Ui，Ci是该服务器上的连接数量，Ui是权重。 Never Queue Scheduling 当有空闲服务器存在时，直接发给空闲服务器；当没有空闲服务器时，按Shortest Expected Delay Scheduling处理。</summary></entry><entry><title type="html">Go channel实现</title><link href="https://phantomwolf.github.io//go/go-channel-implementation/" rel="alternate" type="text/html" title="Go channel实现" /><published>2018-04-05T14:15:03+08:00</published><updated>2018-04-05T14:15:03+08:00</updated><id>https://phantomwolf.github.io//go/go-channel-implementation</id><content type="html" xml:base="https://phantomwolf.github.io//go/go-channel-implementation/">&lt;h2 id=&quot;概述&quot;&gt;概述&lt;/h2&gt;
&lt;p&gt;以下是channel的结构体，在runtime/chan.go中定义：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hchan&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;qcount&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;           &lt;span class=&quot;c&quot;&gt;// buffer中的元素数量
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dataqsiz&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint&lt;/span&gt;           &lt;span class=&quot;c&quot;&gt;// size of the circular queue(buffer的总大小)
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;buf&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;unsafe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pointer&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// points to an array of dataqsiz elements(指向buffer的指针)
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;closed&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt;         &lt;span class=&quot;c&quot;&gt;// 为1时表示channel关闭
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;recvq&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;waitq&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;// list of recv waiters，等待从channel接收数据的goroutine们
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sendq&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;waitq&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;// list of send waiters，等待向channel发送数据的goroutine们
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;      &lt;span class=&quot;c&quot;&gt;// 锁
&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;go-channel-structure.png&quot;&gt;!channel structure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;一般来说，go channel在做操作之前，会先获取锁，结束以后再解锁。不过有时做一些non-blocking操作时，会不获取锁就检查，以提高效率。&lt;/p&gt;

&lt;p&gt;channel可以是同步的(无缓存)，也可以是异步的(有缓存)。&lt;/p&gt;

&lt;h2 id=&quot;同步channel&quot;&gt;同步channel&lt;/h2&gt;
&lt;p&gt;用以下代码创建一个同步的channel并从channel读取数据：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;刚创建的时候，channel的情况如下图。Go不会为同步的channel分配buffer，因此buffer的指针为nil，qcount和dataqsiz都是0。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;go-channel-new.png&quot;&gt;!new channel&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;两个goroutine的运行次序是未知的，我们假定main函数先对goroutine进行读取。goroutine对channel进行读操作时，会先进行一些检查：channel是否已关闭，是否有缓存，sendq中是否有goroutine等待发送数据。在本例中，当main函数中对channel进行读取时，channel没有buffer，sendq中也没有goroutine等待发送数据。这种情况下，goroutine会把自己加到channel的recvq中，并阻塞：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;channel-read-block.png&quot;&gt;!channel read blocks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;细节：该goroutine会使用acquireSudog()获取一个sudog结构，并将自己的waiting设置为它&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chanrecv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hchan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// no sender available: block on this channel.
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;				&lt;span class=&quot;c&quot;&gt;// 获取当前goroutine
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acquireSudog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;			&lt;span class=&quot;c&quot;&gt;// 获取sudog
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;releasetime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
                &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;releasetime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// No stack splits between assigning elem and enqueuing mysg
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// on gp.waiting where copystack can find it.
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitlink&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;gp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waiting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gp&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isSelect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;gp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recvq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enqueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;goparkunlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;chan receive&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;traceEvGoBlockRecv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;关于sudog的解释：sudog represents a g in a wait list, such as for sending/receiving on a channel. sudog is necessary because the g ↔ synchronization object relation is many-to-many. A g can be on many wait lists, so there may be many sudogs for one g; and many gs may be waiting on the same synchronization object, so there may be many sudogs for one object. sudogs are allocated from a special pool. Use acquireSudog and releaseSudog to allocate and free them.&lt;/p&gt;

&lt;p&gt;一段时间后，另一个goroutine向channel发送数据，它会做类似的检查，发现recvq中有个goroutine正在等待，于是将数据写入其stack中，并唤醒它。这是唯一一个go runtime会向另一个goroutine的stack内写入数据的地方。此后，两个goroutine都将返回。&lt;/p&gt;

&lt;p&gt;被唤醒的goroutine会继续执行chanrecv函数的剩余部分：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chanrecv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hchan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// someone woke us up
&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waiting&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;G waiting list is corrupted&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waiting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;releasetime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;blockevent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;releasetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;closed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;releaseSudog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mysg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;closed&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;异步channel带缓存的channel&quot;&gt;异步channel(带缓存的channel)&lt;/h2&gt;
&lt;p&gt;请看以下例子：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt; 
    &lt;span class=&quot;p&quot;&gt;}()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;两个goroutine的运行次序依然是未知的，我们假定两个元素先被发送到channel中，之后另一个goroutine从channel中读取。channel被创建后如下图。与同步channel的区别是，分配了一个buffer，且dataqsiz为1。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;channel-buffered.png&quot;&gt;!buffered channel&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;接下来，main goroutine向channel写入第一个元素。首先做如下检查：检查recvq是否为空；若buffer为空，检查buffer中是否有空闲空间。在本例中，recvq中没有goroutine，buffer仍然有1个空位，因此向buffer写入数据，并返回。此时，channel的情况如下图：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;channel-buffered-with-data.png&quot;&gt;!buffered channel with data&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下一步，main goroutine向channel发送第二个元素，此时buffer已满。当buffer已满时，有缓存的channel与无缓存的行为一致，main goroutine将自己放入sendq中并阻塞，如下图：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;channel-buffered-sendq.png&quot;&gt;!sendq&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最后，另一个goroutine从channel中接收数据。这时问题来了，Go保证channel以FIFO的方式工作，所以它不能从buffer获取数据后就继续执行，否则main goroutine将永远阻塞。为了处理这个问题，它会在从buffer读取数据后，将sendq中的第一个goroutine要写的数据加入到buffer中，并将其从sendq中移除并唤醒。&lt;/p&gt;

&lt;h2 id=&quot;select&quot;&gt;Select&lt;/h2&gt;
&lt;p&gt;我们知道，Go支持用select语句操作channel：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;按照我们之前的分析，如果channel无缓存，或者有缓存但是里面没有元素，那么试图从channel读取数据的goroutine将会阻塞。那还怎么执行select语句的default情况呢？我们来看一下从channel接收数据的函数：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chanrecv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hchan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ep&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;block&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;received&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;其中，c指向channel的struct；ep指向一块内存，从channel中读出的数据应该写到这里；若block == false，将使用非阻塞模式。非阻塞模式下，goroutine检查buffer，以及sendq，如果读到了元素，就写入ep中，如果没有，就立刻返回(false, false)。buffer和queue的检查使用了原子操作，而没有使用锁。&lt;/p&gt;

&lt;h2 id=&quot;关闭channel&quot;&gt;关闭channel&lt;/h2&gt;
&lt;p&gt;关闭channel时，go遍历channel的队列中所有发送者和接受者，并解锁它们。所有接收者会得到该类型的默认值，所有发送者会panic。&lt;/p&gt;

&lt;h2 id=&quot;相关链接&quot;&gt;相关链接&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Golang: channels implementation：http://dmitryvorobev.blogspot.hk/2016/08/golang-channels-implementation.html&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><category term="go" /><summary type="html">概述 以下是channel的结构体，在runtime/chan.go中定义： type hchan struct { qcount uint // buffer中的元素数量 dataqsiz uint // size of the circular queue(buffer的总大小) buf unsafe.Pointer // points to an array of dataqsiz elements(指向buffer的指针) closed uint32 // 为1时表示channel关闭 recvq waitq // list of recv waiters，等待从channel接收数据的goroutine们 sendq waitq // list of send waiters，等待向channel发送数据的goroutine们 lock mutex // 锁 ... } !channel structure 一般来说，go channel在做操作之前，会先获取锁，结束以后再解锁。不过有时做一些non-blocking操作时，会不获取锁就检查，以提高效率。 channel可以是同步的(无缓存)，也可以是异步的(有缓存)。 同步channel 用以下代码创建一个同步的channel并从channel读取数据： func main() { ch := make(chan bool) go func() { ch &amp;lt;- true }() &amp;lt;-ch } 刚创建的时候，channel的情况如下图。Go不会为同步的channel分配buffer，因此buffer的指针为nil，qcount和dataqsiz都是0。 !new channel 两个goroutine的运行次序是未知的，我们假定main函数先对goroutine进行读取。goroutine对channel进行读操作时，会先进行一些检查：channel是否已关闭，是否有缓存，sendq中是否有goroutine等待发送数据。在本例中，当main函数中对channel进行读取时，channel没有buffer，sendq中也没有goroutine等待发送数据。这种情况下，goroutine会把自己加到channel的recvq中，并阻塞： !channel read blocks 细节：该goroutine会使用acquireSudog()获取一个sudog结构，并将自己的waiting设置为它 func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { ... // no sender available: block on this channel. gp := getg() // 获取当前goroutine mysg := acquireSudog() // 获取sudog mysg.releasetime = 0 if t0 != 0 { mysg.releasetime = -1 } // No stack splits between assigning elem and enqueuing mysg // on gp.waiting where copystack can find it. mysg.elem = ep mysg.waitlink = nil gp.waiting = mysg mysg.g = gp mysg.isSelect = false mysg.c = c gp.param = nil c.recvq.enqueue(mysg) goparkunlock(&amp;amp;c.lock, &quot;chan receive&quot;, traceEvGoBlockRecv, 3) ... } 关于sudog的解释：sudog represents a g in a wait list, such as for sending/receiving on a channel. sudog is necessary because the g ↔ synchronization object relation is many-to-many. A g can be on many wait lists, so there may be many sudogs for one g; and many gs may be waiting on the same synchronization object, so there may be many sudogs for one object. sudogs are allocated from a special pool. Use acquireSudog and releaseSudog to allocate and free them. 一段时间后，另一个goroutine向channel发送数据，它会做类似的检查，发现recvq中有个goroutine正在等待，于是将数据写入其stack中，并唤醒它。这是唯一一个go runtime会向另一个goroutine的stack内写入数据的地方。此后，两个goroutine都将返回。 被唤醒的goroutine会继续执行chanrecv函数的剩余部分： func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { ... // someone woke us up if mysg != gp.waiting { throw(&quot;G waiting list is corrupted&quot;) } gp.waiting = nil if mysg.releasetime &amp;gt; 0 { blockevent(mysg.releasetime-t0, 2) } closed := gp.param == nil gp.param = nil mysg.c = nil releaseSudog(mysg) return true, !closed } 异步channel(带缓存的channel) 请看以下例子： func main() { ch := make(chan bool, 1) ch &amp;lt;- true go func() { &amp;lt;-ch }() ch &amp;lt;- true } 两个goroutine的运行次序依然是未知的，我们假定两个元素先被发送到channel中，之后另一个goroutine从channel中读取。channel被创建后如下图。与同步channel的区别是，分配了一个buffer，且dataqsiz为1。 !buffered channel 接下来，main goroutine向channel写入第一个元素。首先做如下检查：检查recvq是否为空；若buffer为空，检查buffer中是否有空闲空间。在本例中，recvq中没有goroutine，buffer仍然有1个空位，因此向buffer写入数据，并返回。此时，channel的情况如下图： !buffered channel with data 下一步，main goroutine向channel发送第二个元素，此时buffer已满。当buffer已满时，有缓存的channel与无缓存的行为一致，main goroutine将自己放入sendq中并阻塞，如下图： !sendq 最后，另一个goroutine从channel中接收数据。这时问题来了，Go保证channel以FIFO的方式工作，所以它不能从buffer获取数据后就继续执行，否则main goroutine将永远阻塞。为了处理这个问题，它会在从buffer读取数据后，将sendq中的第一个goroutine要写的数据加入到buffer中，并将其从sendq中移除并唤醒。 Select 我们知道，Go支持用select语句操作channel： select { case &amp;lt;-ch: foo() default: bar() } 按照我们之前的分析，如果channel无缓存，或者有缓存但是里面没有元素，那么试图从channel读取数据的goroutine将会阻塞。那还怎么执行select语句的default情况呢？我们来看一下从channel接收数据的函数： func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) 其中，c指向channel的struct；ep指向一块内存，从channel中读出的数据应该写到这里；若block == false，将使用非阻塞模式。非阻塞模式下，goroutine检查buffer，以及sendq，如果读到了元素，就写入ep中，如果没有，就立刻返回(false, false)。buffer和queue的检查使用了原子操作，而没有使用锁。 关闭channel 关闭channel时，go遍历channel的队列中所有发送者和接受者，并解锁它们。所有接收者会得到该类型的默认值，所有发送者会panic。 相关链接 Golang: channels implementation：http://dmitryvorobev.blogspot.hk/2016/08/golang-channels-implementation.html</summary></entry><entry><title type="html">Go内存管理 - Stack</title><link href="https://phantomwolf.github.io//go/go-memory-management-stack/" rel="alternate" type="text/html" title="Go内存管理 - Stack" /><published>2018-04-01T16:28:00+08:00</published><updated>2018-04-01T16:28:00+08:00</updated><id>https://phantomwolf.github.io//go/go-memory-management-stack</id><content type="html" xml:base="https://phantomwolf.github.io//go/go-memory-management-stack/">&lt;h2 id=&quot;stack&quot;&gt;Stack&lt;/h2&gt;
&lt;h3 id=&quot;传统的stack&quot;&gt;传统的Stack&lt;/h3&gt;
&lt;h4 id=&quot;普通进程内存布局&quot;&gt;普通进程内存布局：&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;images/linux-memory-layout.jpg&quot;&gt;!内存布局&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;user space stack的大小是固定的，不能动态扩展，linux中默认为8192KB。若程序运行时，栈的内存占用超过上限，程序会出现segment fault错误。&lt;/p&gt;

&lt;h4 id=&quot;每个thread都有自己的stack&quot;&gt;每个thread都有自己的stack&lt;/h4&gt;
&lt;p&gt;每个thread都有自己的stack。在linux上，thread是由clone系统调用创建的(根据参数不同，也可以创建子进程)，其函数原型为：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;child_stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
          &lt;span class=&quot;cm&quot;&gt;/* pid_t *ptid, void *newtls, pid_t *ctid */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;可以看到，创建thread时，需要提供一个child_stack。&lt;/p&gt;

&lt;h4 id=&quot;引发的问题&quot;&gt;引发的问题&lt;/h4&gt;
&lt;p&gt;如果程序大量使用stack空间(比如大量递归调用)，stack的空间会被耗尽。这就导致了一个难题：如果栈设置得太小，有可能会被耗尽，导致segment fault；如果栈设置得太大，即使程序并未使用栈，也会占用很多内存。虽然可以在clone()时设定每个thread栈的大小，但这需要精确计算每个thread所需栈的大小，比较困难。&lt;/p&gt;

&lt;h3 id=&quot;go中的stack&quot;&gt;Go中的“Stack”&lt;/h3&gt;
&lt;h4 id=&quot;每个goroutine都有自己的stack&quot;&gt;每个goroutine都有自己的stack&lt;/h4&gt;
&lt;p&gt;Go的每个goroutine都有自己的“stack”。与thread不同，goroutine的stack被设计为初始大小很小(只有2KB)，但可以在需要的时候扩展。见runtime/runtime2.go对goroutine的定义:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// Stack parameters.
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// stack describes the actual stack memory: [stack.lo, stack.hi).
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// stackguard0 is the stack pointer compared in the Go stack growth prologue.
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// stackguard1 is the stack pointer compared in the C stack growth prologue.
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// It is stack.lo+StackGuard on g0 and gsignal stacks.
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// offset known to runtime/cgo
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stackguard0&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// offset known to liblink
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stackguard1&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// offset known to liblink
&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;goroutine-stack的布局&quot;&gt;goroutine stack的布局&lt;/h4&gt;
&lt;p&gt;由于linux提供的thread stack并不能动态扩展，因此go runtime自己实现了一套stack，goroutine的stack实际上是位于.heap段的。以下是goroutine stack的布局：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;images/goroutine-stack-layout.jpg&quot;&gt;!goroutine stack layout&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;stack.lo: 栈空间的低地址&lt;/li&gt;
  &lt;li&gt;stack.hi: 栈空间的高地址&lt;/li&gt;
  &lt;li&gt;stackguard0: stack.lo + StackGuard, 用于stack overlow的检测&lt;/li&gt;
  &lt;li&gt;StackGuard: 保护区大小，常量Linux上为880字节&lt;/li&gt;
  &lt;li&gt;StackSmall: 常量大小为128字节，用于小函数调用的优化&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;相关链接&quot;&gt;相关链接&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;聊一聊goroutine stack：https://zhuanlan.zhihu.com/p/28409657&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><category term="go" /><summary type="html">Stack 传统的Stack 普通进程内存布局： !内存布局 user space stack的大小是固定的，不能动态扩展，linux中默认为8192KB。若程序运行时，栈的内存占用超过上限，程序会出现segment fault错误。 每个thread都有自己的stack 每个thread都有自己的stack。在linux上，thread是由clone系统调用创建的(根据参数不同，也可以创建子进程)，其函数原型为： int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... /* pid_t *ptid, void *newtls, pid_t *ctid */ ); 可以看到，创建thread时，需要提供一个child_stack。 引发的问题 如果程序大量使用stack空间(比如大量递归调用)，stack的空间会被耗尽。这就导致了一个难题：如果栈设置得太小，有可能会被耗尽，导致segment fault；如果栈设置得太大，即使程序并未使用栈，也会占用很多内存。虽然可以在clone()时设定每个thread栈的大小，但这需要精确计算每个thread所需栈的大小，比较困难。 Go中的“Stack” 每个goroutine都有自己的stack Go的每个goroutine都有自己的“stack”。与thread不同，goroutine的stack被设计为初始大小很小(只有2KB)，但可以在需要的时候扩展。见runtime/runtime2.go对goroutine的定义: type g struct { // Stack parameters. // stack describes the actual stack memory: [stack.lo, stack.hi). // stackguard0 is the stack pointer compared in the Go stack growth prologue. // It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption. // stackguard1 is the stack pointer compared in the C stack growth prologue. // It is stack.lo+StackGuard on g0 and gsignal stacks. // It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash). stack stack // offset known to runtime/cgo stackguard0 uintptr // offset known to liblink stackguard1 uintptr // offset known to liblink ... } goroutine stack的布局 由于linux提供的thread stack并不能动态扩展，因此go runtime自己实现了一套stack，goroutine的stack实际上是位于.heap段的。以下是goroutine stack的布局： !goroutine stack layout stack.lo: 栈空间的低地址 stack.hi: 栈空间的高地址 stackguard0: stack.lo + StackGuard, 用于stack overlow的检测 StackGuard: 保护区大小，常量Linux上为880字节 StackSmall: 常量大小为128字节，用于小函数调用的优化 相关链接 聊一聊goroutine stack：https://zhuanlan.zhihu.com/p/28409657</summary></entry><entry><title type="html">Go netpoller</title><link href="https://phantomwolf.github.io//go/go-netpoller/" rel="alternate" type="text/html" title="Go netpoller" /><published>2018-03-31T16:45:00+08:00</published><updated>2018-03-31T16:45:00+08:00</updated><id>https://phantomwolf.github.io//go/go-netpoller</id><content type="html" xml:base="https://phantomwolf.github.io//go/go-netpoller/">&lt;h2 id=&quot;go的并发模型&quot;&gt;Go的并发模型&lt;/h2&gt;
&lt;p&gt;Go鼓励在goroutine中使用同步的编程方法，并创建多个goroutine，让它们之间通过channel通信。这种同步的编程方法大大方便了我们编写程序，不会出现callback满天飞的情况。&lt;/p&gt;

&lt;p&gt;在之前的Go调度器的介绍中，我们已经知道，当在G(goroutine)中调用syscall时，不仅G会阻塞，相应的M(Thread)也会阻塞。这时go runtime会唤醒sleep状态的M，或创建新的M，来运行其他的G。这样就导致了一个问题：如果频繁调用会阻塞的syscall(例如对socket的fd执行阻塞的read)，go runtime就会不断创建新的M(Thread)，大量的Thread会耗光资源。&lt;/p&gt;

&lt;p&gt;Go使用netpoller来避免这一问题。&lt;/p&gt;

&lt;h2 id=&quot;netpoller&quot;&gt;netpoller&lt;/h2&gt;
&lt;p&gt;netpoller待在它自己的Thread中，从希望做Network I/O的goroutine中接收事件。&lt;/p&gt;

&lt;p&gt;当你在Go中accept一个连接时，相应的fd会被设置O_NONBLOCK flag。若goroutine在读写该fd时遇到了EAGAIN，就会调用netpoller的wait方法，令netpoller在fd可读/写后通知该goroutine。之后，goroutine就会进入等待状态，不再参与调度。netpoller会用系统上的非阻塞网络I/O来监视该fd，在linux上是epoll，在BSD上是kqueue。&lt;/p&gt;

&lt;p&gt;当netpoller从操作系统收到通知说该fd可以读/写时，会检查是否有goroutine被阻塞在该fd上。如果有，就通知它们。这样goroutine就可以重新执行之前的读/写操作。&lt;/p&gt;

&lt;p&gt;所以实际上go使用的是非阻塞网络I/O，但是用了一套机制，令goroutine保持睡眠，直到socket变为可以读/写后，重新进行读/写。将异步变成了同步，简化了编程。&lt;/p&gt;

&lt;h2 id=&quot;相关代码&quot;&gt;相关代码&lt;/h2&gt;
&lt;p&gt;net/fd_unix.go:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;netFD&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pfd&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FD&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;// immutable until Close
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;family&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sotype&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;isConnected&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;net&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;laddr&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;Addr&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;raddr&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;Addr&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;netFD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    	&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pfd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    	&lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;KeepAlive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wrapSyscallError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;read&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;internal/poll/fd_unix.go:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syscall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sysfd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; 
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;syscall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EAGAIN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pollable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitRead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                                        &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eofError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;internal/poll/fd_poll_runtime.go:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pollDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;waitRead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isFile&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'r'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pollDesc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isFile&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runtimeCtx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;waiting for unsupported file type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runtime_pollWait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runtimeCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;convertErr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content><author><name></name></author><category term="go" /><summary type="html">Go的并发模型 Go鼓励在goroutine中使用同步的编程方法，并创建多个goroutine，让它们之间通过channel通信。这种同步的编程方法大大方便了我们编写程序，不会出现callback满天飞的情况。 在之前的Go调度器的介绍中，我们已经知道，当在G(goroutine)中调用syscall时，不仅G会阻塞，相应的M(Thread)也会阻塞。这时go runtime会唤醒sleep状态的M，或创建新的M，来运行其他的G。这样就导致了一个问题：如果频繁调用会阻塞的syscall(例如对socket的fd执行阻塞的read)，go runtime就会不断创建新的M(Thread)，大量的Thread会耗光资源。 Go使用netpoller来避免这一问题。 netpoller netpoller待在它自己的Thread中，从希望做Network I/O的goroutine中接收事件。 当你在Go中accept一个连接时，相应的fd会被设置O_NONBLOCK flag。若goroutine在读写该fd时遇到了EAGAIN，就会调用netpoller的wait方法，令netpoller在fd可读/写后通知该goroutine。之后，goroutine就会进入等待状态，不再参与调度。netpoller会用系统上的非阻塞网络I/O来监视该fd，在linux上是epoll，在BSD上是kqueue。 当netpoller从操作系统收到通知说该fd可以读/写时，会检查是否有goroutine被阻塞在该fd上。如果有，就通知它们。这样goroutine就可以重新执行之前的读/写操作。 所以实际上go使用的是非阻塞网络I/O，但是用了一套机制，令goroutine保持睡眠，直到socket变为可以读/写后，重新进行读/写。将异步变成了同步，简化了编程。 相关代码 net/fd_unix.go: type netFD struct { pfd poll.FD // immutable until Close family int sotype int isConnected bool net string laddr Addr raddr Addr } func (fd *netFD) Read(p []byte) (n int, err error) { n, err = fd.pfd.Read(p) runtime.KeepAlive(fd) return n, wrapSyscallError(&quot;read&quot;, err) } internal/poll/fd_unix.go: func (fd *FD) Read(p []byte) (int, error) { ... for { n, err := syscall.Read(fd.Sysfd, p) if err != nil { n = 0 if err == syscall.EAGAIN &amp;amp;&amp;amp; fd.pd.pollable() { if err = fd.pd.waitRead(fd.isFile); err == nil { continue } } ... } err = fd.eofError(n, err) return n, err } } internal/poll/fd_poll_runtime.go: func (pd *pollDesc) waitRead(isFile bool) error { return pd.wait('r', isFile) } func (pd *pollDesc) wait(mode int, isFile bool) error { if pd.runtimeCtx == 0 { return errors.New(&quot;waiting for unsupported file type&quot;) } res := runtime_pollWait(pd.runtimeCtx, mode) return convertErr(res, isFile) }</summary></entry><entry><title type="html">goroutine调度器</title><link href="https://phantomwolf.github.io//go/goroutine-scheduler/" rel="alternate" type="text/html" title="goroutine调度器" /><published>2018-03-31T10:53:00+08:00</published><updated>2018-03-31T10:53:00+08:00</updated><id>https://phantomwolf.github.io//go/goroutine-scheduler</id><content type="html" xml:base="https://phantomwolf.github.io//go/goroutine-scheduler/">&lt;h2 id=&quot;什么是goroutine&quot;&gt;什么是goroutine？&lt;/h2&gt;
&lt;p&gt;Goroutines是一系列并发执行的函数，它们之间共享相同的地址空间。&lt;/p&gt;

&lt;h3 id=&quot;与thread的区别&quot;&gt;与Thread的区别&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;它们的调度由go runtime完成，不需要内核的参与，一切都在用户态进行，内核甚至不知有goroutine的存在。&lt;/li&gt;
  &lt;li&gt;goroutine比thread轻量，它的栈大小只有2K，(我的linux上默认stack size是8K，可以通过ulimit -a查看)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;goroutine的调度&quot;&gt;goroutine的调度&lt;/h2&gt;
&lt;h3 id=&quot;基本概念&quot;&gt;基本概念&lt;/h3&gt;
&lt;p&gt;goroutine的调度是由go runtime管理的，调度模型又叫做：P-M-G模型。以下内容均在runtime/runtime2.go中的定义：&lt;/p&gt;

&lt;p&gt;G即goroutine，由g结构体表示。它包含当前M(Machine)、goroutine的栈、pc(Program Counter)、goroutine id等信息，存在于P的local队列里，或全局队列里(较少使用)。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;stack&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// stack of goroutine
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;      &lt;span class=&quot;c&quot;&gt;// current m; offset known to arm liblink
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sched&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;gobuf&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;goid&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// goroutine id
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gopc&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// pc of go statement that created this goroutine
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;startpc&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// pc of goroutine function
&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gobuf&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sp&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pc&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;guintptr&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;M即Machine，代表一个线程，由runtime/runtime2.go中的m结构体表示，包含thread当前的状态、当前逻辑Processor、当前运行的goroutine等信息：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g0&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;            &lt;span class=&quot;c&quot;&gt;// goroutine with scheduling stack
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tls&lt;/span&gt;           &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// thread-local storage (for x86 extern register)
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;curg&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;            &lt;span class=&quot;c&quot;&gt;// current running goroutine
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;             &lt;span class=&quot;n&quot;&gt;puintptr&lt;/span&gt;      &lt;span class=&quot;c&quot;&gt;// attached p for executing go code (nil if not executing go code)
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;nextp&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;puintptr&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;            &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spinning&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;          &lt;span class=&quot;c&quot;&gt;// m is out of work and is actively looking for work
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;blocked&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;          &lt;span class=&quot;c&quot;&gt;// m is blocked on a note
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;       &lt;span class=&quot;c&quot;&gt;// thread handle
&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;P即Processor，代表一个逻辑处理器，由p结构体表示，包含id、状态、goroutine队列、当前绑定的M等信息。P的数量可以通过runtime.GOMAXPROCS来设置：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;          &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// one of pidle/prunning/...
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;schedtick&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;// incremented on every scheduler call
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;syscalltick&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;// incremented on every system call
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sysmontick&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;sysmontick&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// last tick observed by sysmon
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;muintptr&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// back-link to associated m (nil if idle)
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;deferpool&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_defer&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// pool of available defer structs of different sizes (see panic.go)
&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen.
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;goidcache&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;uint64&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// Queue of runnable goroutines. Accessed without lock.
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;runqhead&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;runqtail&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;runq&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;guintptr&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// Available G's (status == Gdead)
&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gfree&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gfreecnt&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;此外还有一个重要的结构体schedt，可以将其粗略理解为scheduler本身：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;schedt&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;midle&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;muintptr&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// idle m's waiting for work
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;nmidle&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// number of idle m's waiting for work
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;nmidlelocked&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// number of locked m's waiting for work
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mnext&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// number of m's that have been created and next M ID
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;maxmcount&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// maximum number of m's allowed (or die)
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;nmsys&lt;/span&gt;        &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// number of system m's not counted for deadlock
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;nmfreed&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// cumulative number of freed m's
&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;pidle&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;puintptr&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// idle p's
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;npidle&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;// number of idle p's
&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;// Global runnable queue.
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;runqhead&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guintptr&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;runqtail&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;guintptr&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;runqsize&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;// Global cache of dead G's.
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gflock&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gfreeStack&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;gfreeNoStack&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ngfree&lt;/span&gt;       &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;// Central pool of available defer structs of different sizes.
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;deferlock&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mutex&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;deferpool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_defer&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// freem is the list of m's waiting to be freed when their
&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;// m.exited is set. Linked through m.freelink.
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;freem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;gcwaiting&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// gc is waiting to run
&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stopwait&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;int32&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stopnote&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;note&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sysmonwait&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sysmonnote&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;note&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;调度流程&quot;&gt;调度流程&lt;/h3&gt;
&lt;p&gt;goroutine的调度流程如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;images/go-sched.png&quot; alt=&quot;Go调度流程&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;G被创建后，被放入P的本地队列或schedt中的全局队列。&lt;/li&gt;
  &lt;li&gt;M被创建或唤醒后，与P绑定，进入schedule循环。&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;work-stealing&quot;&gt;work-stealing&lt;/h4&gt;
&lt;p&gt;若M所绑定的P的本地队列已空，将从另一个随机的P中的本地队列中偷取一半的G。&lt;/p&gt;

&lt;h4 id=&quot;系统调用&quot;&gt;系统调用&lt;/h4&gt;
&lt;p&gt;当G调用syscall时，不仅G会阻塞，执行该G的M也会阻塞(因为M其实是线程)。这时，P会与M解绑，若此时有idle状态的M，则P会与新的M绑定，并继续执行P本地队列中的其他G；若没有idle的M，那就创建新的M。&lt;/p&gt;

&lt;h4 id=&quot;channel阻塞或network-io情况下的调度&quot;&gt;channel阻塞或network I/O情况下的调度&lt;/h4&gt;
&lt;p&gt;若G阻塞在channel操作或network I/O操作上时，G会被放置到某个wait队列中，而M会尝试运行下一个runnable的G。若没有runnable的G，M会与P解绑，并进入sleep状态。当I/O available或channel操作完成，在wait队列中的G会被唤醒，标记为runnable，放入到某P的队列中，绑定一个M继续执行。&lt;/p&gt;

&lt;h4 id=&quot;抢占式调度&quot;&gt;抢占式调度&lt;/h4&gt;
&lt;p&gt;为了避免G运行时间过长，导致其他G得不到调度，go支持抢占式调度。当G运行一段时间后，会被标记为可以抢占的，当G下一次调用函数时，就会被抢占，调度器会选取另一个G运行。&lt;/p&gt;

&lt;p&gt;原理是，在每个函数的入口加一段额外代码，使runtime有机会检查是否需要执行抢占式调度。这种解决方案只能说局部解决了“饿死”问题，对于没有函数调用，纯算法循环计算的G，scheduler依然无法抢占。&lt;/p&gt;

&lt;h3 id=&quot;特殊栈g0&quot;&gt;特殊栈：g0&lt;/h3&gt;
&lt;p&gt;每个M都有一个特殊的栈：g0。g0只是用来存放M的call stack等信息。当要执行Go runtime的一些逻辑比如创建G、新建M等，都会首先切换到g0栈然后执行；而执行g任务时，会切换到g的栈上。&lt;/p&gt;

&lt;h3 id=&quot;特殊goroutinesysmon&quot;&gt;特殊goroutine：sysmon&lt;/h3&gt;
&lt;p&gt;sysmon是一个监控用的goroutine，不需要P就可以运行。它有3个作用：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;将长时间没有处理的netpoll的G添加全局队列&lt;/li&gt;
  &lt;li&gt;处理长时间处于Psyscall状态的P&lt;/li&gt;
  &lt;li&gt;设置过长时间运行的G的抢占标志位。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;相关链接&quot;&gt;相关链接&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Go调度详解：https://zhuanlan.zhihu.com/p/27056944&lt;/li&gt;
  &lt;li&gt;Dive into goroutine in Go：http://hustcat.github.io/dive-into-goroutine/&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><category term="go" /><summary type="html">什么是goroutine？ Goroutines是一系列并发执行的函数，它们之间共享相同的地址空间。 与Thread的区别 它们的调度由go runtime完成，不需要内核的参与，一切都在用户态进行，内核甚至不知有goroutine的存在。 goroutine比thread轻量，它的栈大小只有2K，(我的linux上默认stack size是8K，可以通过ulimit -a查看) goroutine的调度 基本概念 goroutine的调度是由go runtime管理的，调度模型又叫做：P-M-G模型。以下内容均在runtime/runtime2.go中的定义： G即goroutine，由g结构体表示。它包含当前M(Machine)、goroutine的栈、pc(Program Counter)、goroutine id等信息，存在于P的local队列里，或全局队列里(较少使用)。 type g struct { stack stack // stack of goroutine m *m // current m; offset known to arm liblink sched gobuf goid int64 // goroutine id gopc uintptr // pc of go statement that created this goroutine startpc uintptr // pc of goroutine function ... } type gobuf struct { sp uintptr pc uintptr g guintptr ... } M即Machine，代表一个线程，由runtime/runtime2.go中的m结构体表示，包含thread当前的状态、当前逻辑Processor、当前运行的goroutine等信息： type m struct { g0 *g // goroutine with scheduling stack tls [6]uintptr // thread-local storage (for x86 extern register) curg *g // current running goroutine p puintptr // attached p for executing go code (nil if not executing go code) nextp puintptr id int64 spinning bool // m is out of work and is actively looking for work blocked bool // m is blocked on a note thread uintptr // thread handle ... } P即Processor，代表一个逻辑处理器，由p结构体表示，包含id、状态、goroutine队列、当前绑定的M等信息。P的数量可以通过runtime.GOMAXPROCS来设置： type p struct { lock mutex id int32 status uint32 // one of pidle/prunning/... schedtick uint32 // incremented on every scheduler call syscalltick uint32 // incremented on every system call sysmontick sysmontick // last tick observed by sysmon m muintptr // back-link to associated m (nil if idle) deferpool [5][]*_defer // pool of available defer structs of different sizes (see panic.go) // Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen. goidcache uint64 // Queue of runnable goroutines. Accessed without lock. runqhead uint32 runqtail uint32 runq [256]guintptr // Available G's (status == Gdead) gfree *g gfreecnt int32 ... } 此外还有一个重要的结构体schedt，可以将其粗略理解为scheduler本身： type schedt struct { lock mutex midle muintptr // idle m's waiting for work nmidle int32 // number of idle m's waiting for work nmidlelocked int32 // number of locked m's waiting for work mnext int64 // number of m's that have been created and next M ID maxmcount int32 // maximum number of m's allowed (or die) nmsys int32 // number of system m's not counted for deadlock nmfreed int64 // cumulative number of freed m's pidle puintptr // idle p's npidle uint32 // number of idle p's // Global runnable queue. runqhead guintptr runqtail guintptr runqsize int32 // Global cache of dead G's. gflock mutex gfreeStack *g gfreeNoStack *g ngfree int32 // Central pool of available defer structs of different sizes. deferlock mutex deferpool [5]*_defer // freem is the list of m's waiting to be freed when their // m.exited is set. Linked through m.freelink. freem *m gcwaiting uint32 // gc is waiting to run stopwait int32 stopnote note sysmonwait uint32 sysmonnote note } 调度流程 goroutine的调度流程如下图： G被创建后，被放入P的本地队列或schedt中的全局队列。 M被创建或唤醒后，与P绑定，进入schedule循环。 work-stealing 若M所绑定的P的本地队列已空，将从另一个随机的P中的本地队列中偷取一半的G。 系统调用 当G调用syscall时，不仅G会阻塞，执行该G的M也会阻塞(因为M其实是线程)。这时，P会与M解绑，若此时有idle状态的M，则P会与新的M绑定，并继续执行P本地队列中的其他G；若没有idle的M，那就创建新的M。 channel阻塞或network I/O情况下的调度 若G阻塞在channel操作或network I/O操作上时，G会被放置到某个wait队列中，而M会尝试运行下一个runnable的G。若没有runnable的G，M会与P解绑，并进入sleep状态。当I/O available或channel操作完成，在wait队列中的G会被唤醒，标记为runnable，放入到某P的队列中，绑定一个M继续执行。 抢占式调度 为了避免G运行时间过长，导致其他G得不到调度，go支持抢占式调度。当G运行一段时间后，会被标记为可以抢占的，当G下一次调用函数时，就会被抢占，调度器会选取另一个G运行。 原理是，在每个函数的入口加一段额外代码，使runtime有机会检查是否需要执行抢占式调度。这种解决方案只能说局部解决了“饿死”问题，对于没有函数调用，纯算法循环计算的G，scheduler依然无法抢占。 特殊栈：g0 每个M都有一个特殊的栈：g0。g0只是用来存放M的call stack等信息。当要执行Go runtime的一些逻辑比如创建G、新建M等，都会首先切换到g0栈然后执行；而执行g任务时，会切换到g的栈上。 特殊goroutine：sysmon sysmon是一个监控用的goroutine，不需要P就可以运行。它有3个作用： 将长时间没有处理的netpoll的G添加全局队列 处理长时间处于Psyscall状态的P 设置过长时间运行的G的抢占标志位。 相关链接 Go调度详解：https://zhuanlan.zhihu.com/p/27056944 Dive into goroutine in Go：http://hustcat.github.io/dive-into-goroutine/</summary></entry><entry><title type="html">算法分析简介</title><link href="https://phantomwolf.github.io//%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/algorithm-analysis/" rel="alternate" type="text/html" title="算法分析简介" /><published>2018-03-31T10:47:00+08:00</published><updated>2018-03-31T10:47:00+08:00</updated><id>https://phantomwolf.github.io//%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/algorithm-analysis</id><content type="html" xml:base="https://phantomwolf.github.io//%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/algorithm-analysis/">&lt;h2 id=&quot;科学的研究方法&quot;&gt;科学的研究方法&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;细致地&lt;strong&gt;观察&lt;/strong&gt;真实世界的特点，精确的测量&lt;/li&gt;
  &lt;li&gt;根据观察结果提出&lt;strong&gt;假设模型&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;根据模型&lt;strong&gt;预测&lt;/strong&gt;未来的事件&lt;/li&gt;
  &lt;li&gt;继续观察并&lt;strong&gt;核实&lt;/strong&gt;预测的准确性&lt;/li&gt;
  &lt;li&gt;如此反复，直到确认预测和观察一致&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;观察&quot;&gt;观察&lt;/h2&gt;
&lt;h3 id=&quot;计时与分析&quot;&gt;计时与分析&lt;/h3&gt;
&lt;p&gt;我们需要测量程序运行的时间。首先，确定输入数据的规模(例如1M个整数)；然后，在程序中或外部记录程序开始时间和结束时间，两者相减即可获得程序运行时间。增加输入的规模，得到新的运行时间。以输入规模为横轴，运行时间为纵轴，即可得到两者关系的粗略图像，从而推断算法的增长数量级。&lt;/p&gt;

&lt;h2 id=&quot;数学模型&quot;&gt;数学模型&lt;/h2&gt;
&lt;p&gt;原则上我们可以构造一个数学模型，来描述任意程序的运行时间。一个程序运行的总时间和两点有关：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;执行每条语句的耗时&lt;/li&gt;
  &lt;li&gt;执行每条语句的频率&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;前者取决于计算机、编译器、操作系统等，后者取决于程序本身和输入。&lt;/p&gt;

&lt;h3 id=&quot;近似运行时间&quot;&gt;近似运行时间&lt;/h3&gt;
&lt;p&gt;一般来说，我们只需要关注运行频率最高的那些语句，比如内循环里的那些。例如书中ThreeSum算法里，if语句的执行次数为:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;N(N-1)(N-2)/6 = N^3/6 - N^2 / 2 + N/3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一般我们只保留多项式的首项(最高阶的一项)，因为它们的增长速度要远远快于其他项。我们用~来忽略较小的项:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~N^3/6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;用a来表示常数系数，即:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~aN^3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;定义：~f(N)表示所有当n趋近于无穷时，~f(N)/f(N)趋近于1的函数。g(N)~f(N)表示g(N)/f(N)随着N的增大而趋近于1。

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;设T(N)为ThreeSum处理N个整数的运行时间，根据前文的数学模型有T(N)~aN^3。&lt;/p&gt;

&lt;h3 id=&quot;成本模型&quot;&gt;成本模型&lt;/h3&gt;
&lt;p&gt;3-sum的成本模型：在研究解决3-sum问题的算法时，我们记录的是数组的访问次数(访问数组元素的次数，无论读写)。&lt;/p&gt;

&lt;p&gt;在这个成本模型下，3-sum的暴力算法使用了~N^3/2次数组访问来计算N个整数中和为0的整数三元组的数量。(if执行了~N^3/6次，每次3次数组访问)&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;
&lt;p&gt;对大多数程序，得到其运行时间的数学模型所需的步骤为:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;确定&lt;strong&gt;输入&lt;/strong&gt;模型，定义问题的&lt;strong&gt;规模&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;识别&lt;strong&gt;内循环&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;根据内循环中的操作，确定&lt;strong&gt;成本模型&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;对于给定的输入，判断这些操作的执行频率&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;例子：&lt;/p&gt;

&lt;h3 id=&quot;二分查找&quot;&gt;二分查找&lt;/h3&gt;
&lt;p&gt;输入模型是大小为N的数组a[]，内循环是一个while循环中的所有语句，成本模型是比较操作(比较两个数组元素的值)。&lt;/p&gt;

&lt;h3 id=&quot;白名单&quot;&gt;白名单&lt;/h3&gt;
&lt;p&gt;输入模型是白名单的大小N和由标准输入得到的M个整数，且我们假设M远大于N，内循环是一个while循环中的所有语句，成本模型是比较操作(承自二分查找)。&lt;/p&gt;

&lt;p&gt;二分查找所需的比较次数最多为lgN+1。根据以下因素，白名单问题计算所需时间的增长数量级最多为MlgN:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果N很小，输入、输出可能会成为主要成本&lt;/li&gt;
  &lt;li&gt;比较的次数取决于输入：在~M和~MlgN之间，取决于标准输入中有多少个整数在白名单内，以及二分查找要多久才能找出它们(一般为~MlgN)&lt;/li&gt;
  &lt;li&gt;Arrays.sort()使用归并排序，其时间增长数量级为NlogN。由于M远大于N，因此Arrays.sort()的成本可以忽略。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;增长数量级的分类&quot;&gt;增长数量级的分类&lt;/h2&gt;
&lt;p&gt;常见的增长数量级为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;常数级别          O(1)&lt;/li&gt;
  &lt;li&gt;对数级别          O(logN)&lt;/li&gt;
  &lt;li&gt;线性级别          O(N)&lt;/li&gt;
  &lt;li&gt;线性对数级别      O(NlogN)&lt;/li&gt;
  &lt;li&gt;平方级别          O(N^2)&lt;/li&gt;
  &lt;li&gt;立方级别          O(N^3)&lt;/li&gt;
  &lt;li&gt;指数级别          O(2^N)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;倍率实验&quot;&gt;倍率实验&lt;/h2&gt;
&lt;p&gt;此方法可以简单有效地预测任意程序的性能，并判断它们的运行时间大致的增长数量级：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;开发一个输入生成器来产生实际情况下的各种可能的输入(比如随机数)&lt;/li&gt;
  &lt;li&gt;计算每次实验和上一次的运行时间的比值&lt;/li&gt;
  &lt;li&gt;反复运行直到该比值趋近于极限2^b&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此方法对比值没有极限的算法无效，但它仍然适用于许多程序，我们可以得出以下结论:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;它们的运行时间的增长数量级为N^b。&lt;/li&gt;
  &lt;li&gt;要预测一个程序的运行时间，将上次观察得到的运行时间乘以2^b并将N加倍，如此反复。&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;倍率定理: 如果T(N)~a(N^b)lgN，那么T(2N)/T(N)~2^b。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在有性能压力的情况下，应该对算法进行倍率实验。我们可以用以下方式对程序的运行时间的增长数量级作出假设，并预测它的性能:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;评估它解决大型问题的可行性：得出算法的时间增长数量级后，我们可以预测输入翻倍后所需的运行时间。&lt;/li&gt;
  &lt;li&gt;评估使用更快计算机产生的价值：一般来说，若新计算机比老的快x倍，那么运行时间也将变为原来的x分之一。若程序本来需要运行几天，更换计算机也许只能将其缩短到一天。请根据实际情况考虑这是否值得。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;注意事项&quot;&gt;注意事项&lt;/h2&gt;
&lt;h3 id=&quot;大常数&quot;&gt;大常数&lt;/h3&gt;
&lt;p&gt;在首项近似中，一般会忽略低级项中的常数系数，但这可能是错的。若常数很大，是有可能对性能造成影响的。我们要对可能的大常数保持敏感。&lt;/p&gt;

&lt;h3 id=&quot;非决定性的内循环&quot;&gt;非决定性的内循环&lt;/h3&gt;
&lt;p&gt;内循环是决定性因素的假设不一定是正确的。错误的成本模型可能无法得到真正的内循环，问题的规模N也许大到一定程度。有些程序在内循环之外也有大量指令要考虑。换句话说，成本模型可能还需要改进。&lt;/p&gt;

&lt;h3 id=&quot;指令时间&quot;&gt;指令时间&lt;/h3&gt;
&lt;p&gt;每条指令执行所需的时间总是相同的假设并不总是正确的。例如，由于缓存技术，访问大数组的若干并不相邻的元素所需的时间可能会被相邻元素要长。&lt;/p&gt;

&lt;h3 id=&quot;系统因素&quot;&gt;系统因素&lt;/h3&gt;
&lt;p&gt;计算机中其他程序也可能抢占资源。原则上来说，此时系统中运行的其他程序应该是可以忽略的或可以控制的。&lt;/p&gt;

&lt;h3 id=&quot;不分伯仲&quot;&gt;不分伯仲&lt;/h3&gt;
&lt;p&gt;在比较两个程序时，常常出现其中一个在某些场景中更快而在另一些场景中更慢。&lt;/p&gt;

&lt;h3 id=&quot;对输入的强烈依赖&quot;&gt;对输入的强烈依赖&lt;/h3&gt;
&lt;p&gt;在研究程序运行时间的增长数量级时，我们首先作出的几个假设之一就是运行时间应该与输入无关。当假设不满足的时候，我们可能无法得到一致的结果。&lt;/p&gt;

&lt;h3 id=&quot;多个问题参量&quot;&gt;多个问题参量&lt;/h3&gt;
&lt;p&gt;有可能出现多个影响结果的参量。例如，白名单算法有两个参量：输入规模M、白名单大小N。&lt;/p&gt;</content><author><name></name></author><category term="算法分析" /><summary type="html">科学的研究方法</summary></entry><entry><title type="html">平摊分析</title><link href="https://phantomwolf.github.io//%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/amortized-analysis/" rel="alternate" type="text/html" title="平摊分析" /><published>2018-03-27T10:47:00+08:00</published><updated>2018-03-27T10:47:00+08:00</updated><id>https://phantomwolf.github.io//%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/amortized-analysis</id><content type="html" xml:base="https://phantomwolf.github.io//%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90/amortized-analysis/">&lt;h2 id=&quot;为什么需要平摊分析&quot;&gt;为什么需要平摊分析&lt;/h2&gt;
&lt;p&gt;请看以下例子。假设一个栈(stack)支持以下操作：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;PUSH(S, x): 将对象x压入栈S&lt;/li&gt;
  &lt;li&gt;POP(S): 弹出S的栈顶元素，并将其返回&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PUSH和POP操作的代价都是O(1)。现在我们新增一种操作MULTIPOP：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;MULTIPOP(S, k): 弹出栈S的k个栈顶对象&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其伪代码为：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MULTIPOP(S, k)
    while not STACK-EMPTY(S) and k != 0
        do POP(S)
           k = k - 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可见MULTIPOP(S, k)调用了k次POP(S)，其代价为O(k)。&lt;/p&gt;

&lt;p&gt;现在来分析一个由n个PUSH、POP、MULTIPOP操作所构成的序列，其作用于一个初始为空的栈。序列中一次MULTIPOP的代价为O(n)，因为栈的大小至多为n。最坏情况下，每个操作都是MULTIPOP，共O(n)个MULTIPOP操作，因此总代价是O(n^2)。虽然这一分析是正确的，但通过单独地考虑每个操作的最坏情况代价而得到的O(n^2)结论却是不够紧确的。这时我们就需要平摊分析。&lt;/p&gt;

&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;
&lt;p&gt;在平摊分析中，执行一系列数据结构操作所需要的时间是通过对执行的所有操作求平均而得出的。平摊分析可以证明，在一系列操作中，通过对所有的操作求平均之后，即使其中单一的操作具有较大的代价，平均代价还是很小的。&lt;/p&gt;

&lt;p&gt;平摊分析与平均情况分析的区别在与，前者不涉及概率，保证在最坏情况下，每个操作具有平均性能。&lt;/p&gt;

&lt;p&gt;常用的有3种方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;聚集方法(aggregate method)&lt;/li&gt;
  &lt;li&gt;记账方法(accounting method)&lt;/li&gt;
  &lt;li&gt;势能方法(potential method)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;聚集方法&quot;&gt;聚集方法&lt;/h3&gt;
&lt;p&gt;在聚集分析中，要证明对所有的n，由n个操作所构成的序列的总时间在最坏情况下为T(n)。因此，最坏情况下每个操作的平均代价(平摊代价amortized cost)为T(n)/n。请注意平摊代价对每个操作都是成立的，即使序列中存在几种不同类型的操作时也一样。&lt;/p&gt;

&lt;h3 id=&quot;记账方法&quot;&gt;记账方法&lt;/h3&gt;
&lt;p&gt;在记账方法中，我们对不同的操作赋予不同的平摊代价(amortized cost)。当平摊代价超过实际代价时，两者的差值就被当作存款(credit)，存款可以在以后用于补偿那些平摊代价低于实际代价的操作。平摊代价可以看作两部分：实际代价+存款。&lt;/p&gt;

&lt;p&gt;所有操作的平摊代价的总和，必须大于等于实际代价的总和，这样才能保证存款不为负数，才能用廉价操作的存款来补偿昂贵操作的代价。平摊代价的总和，应该是实际代价总和的一个上界。&lt;/p&gt;

&lt;h4 id=&quot;multipop-stack&quot;&gt;MULTIPOP stack&lt;/h4&gt;
&lt;p&gt;用记账方法来分析MULTIPOP stack。栈的各操作的实际代价为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;PUSH      1&lt;/li&gt;
  &lt;li&gt;POP       1&lt;/li&gt;
  &lt;li&gt;MULTIPOP  min(k, s), k为MULTIPOP的参数，s为stack里的元素数量&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其中k为MULTIPOP的一个参数，s为调用该操作时栈的大小。现在对它们赋予平摊代价：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;PUSH      2&lt;/li&gt;
  &lt;li&gt;POP       0&lt;/li&gt;
  &lt;li&gt;MULTIPOP  0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;每次PUSH元素时，平摊代价为2，除去支付PUSH的实际代价1以外，还剩下1作为存款(credit)放在元素上。任何时候，栈内元素上都有数量为1的存款。当执行POP操作时，其平摊代价为0，因为只需要用之前的存款来支付实际代价即可。同样，MULTIPOP的平摊代价也是0，弹出k个元素，就用这k个元素上的存款来支付实际代价即可。&lt;/p&gt;

&lt;p&gt;因为栈里每个元素都有1存款，且栈中总有非负个数的盘子，这就保证了存款的总量是非负的。这样，对任意的包含n次PUSH、POP、MULTIPOP操作的序列，总的平摊代价就是其总的实际代价的一个上界。又因为总的平摊代价为O(n)，故总的实际代价也为O(n)。&lt;/p&gt;

&lt;h3 id=&quot;势能法&quot;&gt;势能法&lt;/h3&gt;
&lt;p&gt;与记账法类似，势能方法(potential method)将预先支付的代价表示为一种“势能”或“势”，它在需要时可以释放出来，以支付后面的操作。势能是与整个数据结构而不是其中个别对象发生联系的。其工作方法如下：&lt;/p&gt;

&lt;p&gt;设D&lt;sub&gt;0&lt;/sub&gt;为初始的数据结构。我们对其执行i=1,2,3…,n个操作，c&lt;sub&gt;i&lt;/sub&gt;为第i个操作的实际代价，D&lt;sub&gt;i&lt;/sub&gt;为第i个操作后的结果，P(D&lt;sub&gt;i&lt;/sub&gt;)为D&lt;sub&gt;i&lt;/sub&gt;相联系的势能。第i个操作的平摊代价c&lt;sub&gt;i&lt;/sub&gt;‘为:&lt;/p&gt;

&lt;p&gt;c&lt;sub&gt;i&lt;/sub&gt;’ = c&lt;sub&gt;i&lt;/sub&gt; + P(D&lt;sub&gt;i&lt;/sub&gt;) - P(D&lt;sub&gt;i-1&lt;/sub&gt;)&lt;/p&gt;

&lt;p&gt;每个操作的平摊代价等于其实际代价加上由于该操作所增加的势能。n个操作的总的平摊代价为:&lt;/p&gt;

&lt;p&gt;sum(c&lt;sub&gt;i&lt;/sub&gt;’) = sum(c&lt;sub&gt;i&lt;/sub&gt;) + P(D&lt;sub&gt;n&lt;/sub&gt;) - P(D&lt;sub&gt;0&lt;/sub&gt;)&lt;/p&gt;

&lt;p&gt;如果我们能定义一个势能函数P使得P(D&lt;sub&gt;n&lt;/sub&gt;) &amp;gt;= P(D&lt;sub&gt;0&lt;/sub&gt;)，则总的平摊代价就是总的实际代价的一个上界。实际上，我们并不总是知道要执行多少个操作，即n未知，所以我们只要要求对于所有i，都有P(D&lt;sub&gt;i&lt;/sub&gt;) &amp;gt;= P(D&lt;sub&gt;0&lt;/sub&gt;)即可。为了方便起见，定义P(D&lt;sub&gt;0&lt;/sub&gt;)为0,然后证明对所有的i，都有P(D&lt;sub&gt;i&lt;/sub&gt;) &amp;gt;= 0。&lt;/p&gt;

&lt;h4 id=&quot;multipop&quot;&gt;MULTIPOP&lt;/h4&gt;
&lt;p&gt;以MULTIPOP栈为例，定义栈上的势能函数P为栈中对象的个数。开始时栈D&lt;sub&gt;0&lt;/sub&gt;为空，P(D&lt;sub&gt;0&lt;/sub&gt;) = 0。因为栈中的元素数量始终非负，故在第i个操作之后，栈D&lt;sub&gt;i&lt;/sub&gt;就具有非负的势能，且有P(D&lt;sub&gt;i&lt;/sub&gt;) &amp;gt;= 0 = P(D&lt;sub&gt;0&lt;/sub&gt;)。因此，势能函数P所表示的n个操作的平摊代价的总和就表示了实际代价总和的一个上界。&lt;/p&gt;

&lt;p&gt;一次PUSH操作的实际代价c&lt;sub&gt;i&lt;/sub&gt;为1，平摊代价为：&lt;/p&gt;

&lt;p&gt;c&lt;sub&gt;i&lt;/sub&gt;’ = c&lt;sub&gt;i&lt;/sub&gt; + P(D&lt;sub&gt;i&lt;/sub&gt;) - P(D&lt;sub&gt;i-1&lt;/sub&gt;) = 1 + 1 = 2&lt;/p&gt;

&lt;p&gt;假设栈上第i个操作为MULTIPOP(S, k)，且弹出了k’ = min(k, s)个对象，s为栈中目前元素的数量。操作的实际代价c&lt;sub&gt;i&lt;/sub&gt;为k’，平摊代价为:&lt;/p&gt;

&lt;p&gt;c&lt;sub&gt;i&lt;/sub&gt;’ = c&lt;sub&gt;i&lt;/sub&gt; + P(D&lt;sub&gt;i&lt;/sub&gt;) - P(D&lt;sub&gt;i-1&lt;/sub&gt;) = k’ - k’ = 0&lt;/p&gt;

&lt;p&gt;类似的，普通POP操作的代价也是0。&lt;/p&gt;

&lt;h4 id=&quot;二进制计数器递增1&quot;&gt;二进制计数器递增1&lt;/h4&gt;
&lt;p&gt;定义势能函数P(D&lt;sub&gt;i&lt;/sub&gt;)为计数器中1的个数。由于计数器中1的数量始终为非负，且P(D&lt;sub&gt;0&lt;/sub&gt;)为0，因此对任意i，都有P(D&lt;sub&gt;i&lt;/sub&gt;) &amp;gt;= P(D&lt;sub&gt;0&lt;/sub&gt;) = 0，可知总平摊代价为总实际代价的一个上界。&lt;/p&gt;

&lt;p&gt;定义第i次INCREMENT操作后计数器的势能为b&lt;sub&gt;i&lt;/sub&gt;，即第i次操作后计数器中1的个数。我们来计算INCREMENT操作的平摊代价。设第i次INCREMENT操作对t&lt;sub&gt;i&lt;/sub&gt;个位进行了复位(置为0)，该操作的实际代价至多是t&lt;sub&gt;i&lt;/sub&gt;+1，因为除了将t&lt;sub&gt;i&lt;/sub&gt;个位复位外，它至多将1个位设为1。如果b&lt;sub&gt;i&lt;/sub&gt;=0，那么第i个操作复位k个位，则b&lt;sub&gt;i-1&lt;/sub&gt;=t&lt;sub&gt;i&lt;/sub&gt;=k。如果b&lt;sub&gt;i&lt;/sub&gt;&amp;gt;0，则b&lt;sub&gt;i&lt;/sub&gt;=b&lt;sub&gt;i-1&lt;/sub&gt;-t&lt;sub&gt;i&lt;/sub&gt;+1。在这两种情况中，都有b&lt;sub&gt;i&lt;/sub&gt;&amp;lt;=b&lt;sub&gt;i-1&lt;/sub&gt;-t&lt;sub&gt;i&lt;/sub&gt;+1，而且势能差为：&lt;/p&gt;

&lt;p&gt;P(D&lt;sub&gt;i&lt;/sub&gt;) - P(D&lt;sub&gt;i-1&lt;/sub&gt;) &amp;lt;= 1 - t&lt;sub&gt;i&lt;/sub&gt;&lt;/p&gt;

&lt;p&gt;因此平摊代价为：&lt;/p&gt;

&lt;p&gt;c&lt;sub&gt;i&lt;/sub&gt;’ = c&lt;sub&gt;i&lt;/sub&gt; + P(D&lt;sub&gt;i&lt;/sub&gt;) - P(D&lt;sub&gt;i-1&lt;/sub&gt;) &amp;lt;= t&lt;sub&gt;i&lt;/sub&gt; + 1 + 1 - t&lt;sub&gt;i&lt;/sub&gt; = 2&lt;/p&gt;

&lt;p&gt;可见，INCREMENT操作的平摊代价为常数。因此，任意n次INCREMENT操作的总代价为O(n)。&lt;/p&gt;</content><author><name></name></author><category term="算法分析" /><summary type="html">为什么需要平摊分析 请看以下例子。假设一个栈(stack)支持以下操作：</summary></entry></feed>