逍遥子 欢迎来到我的个人站~ https://mathslinux.github.io/ Thu, 09 Mar 2023 10:06:07 +0000 Thu, 09 Mar 2023 10:06:07 +0000 Jekyll v3.9.3 记一次 arm64 非法内存访问调试 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>最近工业软件组的小朋友在移植程序到arm64下的时候碰到了一个比较难调诡异的问题,花了几个小时帮忙调试了一下,将过程记录一下。防止以后踩坑。</p> <h1 id="问题分析">问题分析</h1> <p>程序执行到某一个分支的时候内存飞了。用gdb分析dump文件,提示非法内存访问。提示 <code class="language-plaintext highlighter-rouge">cannot access memory at address 0xxxxxxxx</code> 这种错误。由于软件代码比较庞大(几十万),没有第一时间去检查编译配置,编译日志等等(后面正好是这个地方出错!!!),只能去定位具体的函数。</p> <p>实际代码比较复杂,我做了简化: 有两个c文件,a.c 和 b.c,其中b.c有一个函数 <code class="language-plaintext highlighter-rouge">foo()</code> ,返回一个指针,a.c 中获取这个指针然后打印。</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// a.c</span> <span class="cp">#include &lt;stdio.h&gt; </span><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="kt">char</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="n">foo</span><span class="p">();</span> <span class="c1">// &lt;-- 留意这里</span> <span class="n">printf</span><span class="p">(</span><span class="s">"addr in a: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// b.c</span> <span class="cp">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; </span><span class="kt">char</span> <span class="o">*</span><span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="kt">char</span> <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span> <span class="n">printf</span><span class="p">(</span><span class="s">"addr in b: %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span> <span class="k">return</span> <span class="n">p</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gcc <span class="nt">-o</span> <span class="nb">test </span>a.c b.c <span class="o">&amp;&amp;</span> ./test addr <span class="k">in </span>b: 0x798e04e260 addr <span class="k">in </span>a: 0xffffffff8e04e260 // &lt;<span class="nt">--</span> 高4位的地址全被置1了。导致得到的指针地址非法。 </code></pre></div></div> <h1 id="排查">排查</h1> <p>用 gdb 断点跟踪,foo返回的指针是对的,但是一出 foo 函数,赋值给p就出错了(高4位置为1) 为啥?</p> <p>用反汇编查看,恶魔终于现行了。</p> <pre><code class="language-asm">foo: // cut start mov x0, 1024 bl malloc str x0, [sp, 24] ldr x0, [sp, 24] //&lt;-- 这里 x0 得到了正确的地址 ret // cut end main: // cut start bl foo //&lt;-- 调用 foo,返回值存在 x0 中 sxtw x0, w0 // &lt;-- 为毛这里还有一个符号扩展的 xstw 操作?? // cut end </code></pre> <p>我们知道无论是 x86 还是arm,编译器一般将第一个寄存器当做函数的参数和返回值。从上面的汇编分析,出错的根源在于编译器 在调用完 foo 之后对 x0 做了 sxtw 操作,将 x0 的低32位寄存器 w0 不变,高 32 位全置1。这闹的。。。</p> <p>原因很清楚了,google 了一下为什么会多出来这行汇编,根源在于对于 <code class="language-plaintext highlighter-rouge">foo()</code> 的隐式申明的函数调用,在 x86 平台下,gcc做了多余的处理,从而使得返回值满足64位的长度。至于在32位下为什么没有问题,因为32位系统下,int型和指针类型都是一样长度的。我做了验证,x86 下的情况也是一样,会多插入一条 <code class="language-plaintext highlighter-rouge">cltq</code> 扩展位数的指令。</p> <pre><code class="language-example">When C doesn't find a declaration, it assumes this implicit declaration: int f();, which means the function can receive whatever you give it, and returns an integer. If this happens to be close enough (and in case of printf, it is), then things can work. In some cases (e.g. the function actually returns a pointer, and pointers are larger than ints), it may cause real trouble. Note that this was fixed in newer C standards (C99, C11). In these standards, this is an error. However, gcc doesn't implement these standards by default, so you still get the warning. </code></pre> <p>从上可知,调用返回值的长度大于 4个字节的函数,如果没有在调用者那里申明,在 64 位系统下就一定位发生异常。比如下面的代码</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// a.c </span> <span class="cp">#include &lt;stdio.h&gt; </span><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="kt">long</span> <span class="kt">int</span> <span class="n">p</span> <span class="o">=</span> <span class="n">foo</span><span class="p">();</span> <span class="n">printf</span><span class="p">(</span><span class="s">"p in a: %ld</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// b.c</span> <span class="cp">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; </span><span class="kt">long</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="kt">long</span> <span class="n">p</span> <span class="o">=</span> <span class="mh">0x1000000001</span><span class="p">;</span> <span class="n">printf</span><span class="p">(</span><span class="s">"p in a: %ld</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">p</span><span class="p">);</span> <span class="k">return</span> <span class="n">p</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <h1 id="总结">总结</h1> <p>教训就是对研发团队来讲,一定要做好代码审查,在编译c/c++项目的时候,不要忽略任何警告!最好引入小型的 CI/CD 工具,在编译期间就把这类问题给彻底fix掉。否则会在很多低级错误上花费大量时间。</p> Tue, 01 Nov 2022 21:25:00 +0000 https://mathslinux.github.io/2022/11/Core_Dump_Experience/ https://mathslinux.github.io/2022/11/Core_Dump_Experience/ Linux_System_Program Xenomai 安装部署 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <h1 id="简介">简介</h1> <p>Xenomai 主要由三部分组成:</p> <ul> <li>cobalt: 内核空间的实时内核。</li> <li>dovetail: 类似 ADEOS的实现,管理分发中断。Liunx 5.4 以前的代码这部分工作由 ipipe处理。</li> <li>libcobalt: 用户空间的实时库。</li> </ul> <p>cobalt 和 libcobalt 随 xenomai 源码提供,dovetail 与具体的硬件架构和Linux 版本相关,以Linux补丁的形式提供。</p> <h1 id="环境准备">环境准备</h1> <p><strong>NOTE: 编译的机器和目标机器可以不在一台</strong></p> <ul> <li>ubuntu 22.04</li> <li>kernel 5.15.55</li> <li>dovetail 5.15.51-dovetail1</li> <li>xenomai v3.2.1</li> </ul> <h2 id="安装基本工具软件">安装基本工具软件:</h2> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装部署编译依赖包</span> <span class="c"># apt-get install -y build-essential libncurses-dev flex bison libssl-dev dkms libelf-dev libiberty-dev pahole \</span> devscripts debhelper autotools-dev autoconf automake libtool pkg-config libltdl-dev <span class="c"># mkdir -p ~/xenomai &amp;&amp; cd ~/xenomai # 将 ~/xenomai 作为工作目录, 个人习惯</span> </code></pre></div></div> <h2 id="准备内核源码和补丁">准备内核源码和补丁</h2> <ol> <li>准备内核源码</li> </ol> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># wget -c https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.55.tar.xz</span> <span class="c"># tar xf linux-5.15.55.tar.xz</span> </code></pre></div></div> <ol> <li>准备补丁:由于dovetail 是一个内核补丁,所以选择 dovetail 的时候需要和对应的内核版本匹配。在 <a href="https://xenomai.org/downloads/dovetail/">这里</a> 选择对应的版本,这里选择 <strong>patch-5.15.51-dovetail1.patch.bz2</strong></li> </ol> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># wget https://xenomai.org/downloads/dovetail/patch-5.15.51-dovetail1.patch.bz2</span> <span class="c"># bzip -d patch-5.15.51-dovetail1.patch.bz2</span> </code></pre></div></div> <h2 id="准备-xenomai-源码">准备 xenomai 源码</h2> <p>从 <a href="https://source.denx.de/Xenomai/xenomai">这里</a> 下载最新稳定版,这里选择 3.2.1 版本。</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># wget -c https://source.denx.de/Xenomai/xenomai/-/archive/v3.2.1/xenomai-v3.2.1.tar.bz2</span> <span class="c"># tar xf xenomai-v3.2.1.tar.bz2</span> </code></pre></div></div> <h1 id="编译安装内核">编译安装内核</h1> <h2 id="打内核补丁">打内核补丁</h2> <p>根据 xenomai 的架构,需要对linux内核打 dovetail 和 cobalt的补丁。</p> <ol> <li>打 dovetail 补丁:</li> </ol> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cd ~/xenomai/linux-5.15.55/</span> <span class="c"># patch -p1 &lt; ../patch-5.15.51-dovetail1.patch # 打 dovetail 补丁</span> </code></pre></div></div> <ol> <li>打 cobalt 补丁:</li> </ol> <p>首先生成 cobalt 补丁:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cd ~/xenomai/xenomai-v3.2.1/scripts/</span> <span class="c"># ./prepare-kernel.sh --help</span> usage: prepare-kernel <span class="nt">--linux</span><span class="o">=</span>&lt;linux-tree&gt; <span class="o">[</span><span class="nt">--dovetail</span><span class="o">=</span>&lt;dovetail-patch&gt;]|[--ipipe<span class="o">=</span>&lt;ipipe-patch&gt;] <span class="o">[</span><span class="nt">--arch</span><span class="o">=</span>&lt;<span class="nb">arch</span><span class="o">&gt;]</span> <span class="o">[</span><span class="nt">--outpatch</span><span class="o">=</span>&lt;file&gt; <span class="o">[</span><span class="nt">--filterkvers</span><span class="o">=</span>y|n] <span class="o">[</span><span class="nt">--filterarch</span><span class="o">=</span>y|n]] <span class="o">[</span><span class="nt">--forcelink</span><span class="o">]</span> <span class="o">[</span><span class="nt">--default</span><span class="o">]</span> <span class="o">[</span><span class="nt">--verbose</span><span class="o">]</span> <span class="c"># --linux 指定 linux 源码路径</span> <span class="c"># --dovetail 制定 dovetail 补丁,因为选择手动打补丁,这里忽略</span> <span class="c"># --arch 指定硬件架构,这里选择 x86_64</span> <span class="c"># --outpath 指定输出 cobalt 补丁的路径</span> <span class="c"># ./prepare-kernel.sh --linux=/root/xenomai/linux-5.15.55 --arch=x86_64 --outpatch=/root/xenomai/cobalt-core-3.2.1-1.patch</span> </code></pre></div></div> <p>应用 cobalt 补丁</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 回到 linux 打补丁</span> <span class="c"># cd ~/xenomai/linux-5.15.55/</span> <span class="c"># patch -p1 &lt; ../cobalt-core-3.2.1-1.patch</span> </code></pre></div></div> <h2 id="配置内核选项">配置内核选项</h2> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cd ~/xenomai/linux-5.15.55/</span> <span class="c"># 配置内核选项是很麻烦的,为了方便,在现在运行的 ubuntu 的内核基础上修改可以节省大连工作量,如果为了生产环境,建议手动配置所有内核选项,做出一个精简的内核。</span> <span class="c"># cp /boot/config-$(uname -r) .config</span> <span class="c"># make menuconfig</span> </code></pre></div></div> <p>默认会看到这样的警告,需要针对 xenomai 将一些内核选项配置完成之后这些警告才会消失。</p> <pre><code class="language-example">[*] Xenomai/cobalt (NEW) ---&gt; *** WARNING! Page migration (CONFIG_MIGRATION) may increase *** *** latency. *** *** WARNING! At least one of APM, CPU frequency scaling, ACPI 'processor' *** *** or CPU idle features is enabled. Any of these options may *** *** cause troubles with Xenomai. You should disable them. *** </code></pre> <p>依次进行以下修改:</p> <pre><code class="language-example">General setup --&gt; Preemption Model (Preemptible Kernel (Low-Latency Desktop)) --&gt; (X) Preemptible Kernel (Low-Latency Desktop) --&gt; 配置可抢占式内核 Processor type and features --&gt; Processor family --&gt; (X) Core 2/newer Xeon --&gt; 根据具体的处理器架构选择。 --&gt; [ ] Multi-core scheduler support --&gt; 关闭多核调度优化,后面的 CPU 自动调频依赖此项 Power management and ACPI options --&gt; ACPI (Advanced Configuration and Power Interface) Support --&gt; &lt; &gt; Processor --&gt; CPU Frequency scaling --&gt; [ ] CPU Frequency scaling --&gt; 关闭 CPU 自动调频 --&gt; CPU Idle --&gt; [ ] CPU idle PM support --&gt; 关闭 CPU idle 特性 Memory Management options --&gt; 关闭影响实时性的内存选项 --&gt; [ ] Allow for memory compaction --&gt; [ ] Transparent Hugepage Support --&gt; [ ] Contiguous Memory Allocator --&gt; [ ] Page migration </code></pre> <p><strong>NOTE: linux各个发行版默认选择大部分驱动和功能,这里最好也将这些选项去掉只保留适合目标机器的精简内核,否则编译时间和编译完成的体积会非常大。</strong> 以下是建议关闭的选项:</p> <pre><code class="language-example">[ ] Linux guest support --&gt; 作为虚拟机guest运行才需要此项 [ ] Suspend to RAM and standby --&gt; xno: 关闭休眠到内存选项, S3 state [ ] Hibernation (aka 'suspend to disk') --&gt; xno: 关闭休眠到磁盘选项 &lt; &gt; Microsoft Hyper-V client drivers --&gt; 去掉 hyperv 虚拟机驱动支持 File systems --&gt; 禁用不需要的文件系统 Device Drivers --&gt; 禁用不需要的设备驱动 </code></pre> <h2 id="编译内核">编译内核</h2> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># make -j$(nproc) bindeb-pkg # 选择直接编译打包为 deb 包.</span> </code></pre></div></div> <p>编译完成后会生成一系列 <strong>.deb</strong> 包:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># lsd -1 linux-*.deb</span> linux-headers-5.15.55_5.15.55-1_amd64.deb linux-image-5.15.55-dbg_5.15.55-1_amd64.deb linux-image-5.15.55_5.15.55-1_amd64.deb linux-libc-dev_5.15.55-1_amd64.deb </code></pre></div></div> <h2 id="安装新内核">安装新内核</h2> <p>进入目标机器,执行</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># dpkg -i linux-image-5.15.55_5.15.55-1_amd64.deb</span> </code></pre></div></div> <h1 id="编译安装-xenomai">编译安装 xenomai</h1> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cd ~/xenomai/xenomai-v3.2.1/</span> <span class="c"># debuild -us -uc # 使用debuild工具编译成 .deb 包</span> <span class="c"># dpkg -i xenomai-runtime_2.99.0_amd64.deb \</span> libxenomai1_2.99.0_amd64.deb <span class="se">\</span> libxenomai-dev_2.99.0_amd64.deb </code></pre></div></div> <h1 id="测试使用">测试使用</h1> <p>首先使用自带的 xeno-test 工具检查基本环境,简单的延迟测试等,看是否有问题。 <strong>检查 no support</strong> 选项</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xeno-test # 使用 xeno-test 工具做基本的环境测试</span> ...... arith OK cpu_affinity skipped <span class="o">(</span>no kernel support<span class="o">)</span> gdb OK ...... </code></pre></div></div> <p>手动编写一个实时demo调用xenomai 接口测试一下情况</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// gcc -o hello hello.c $(xeno-config --posix --alchemy --cflags) $(xeno-config --posix --alchemy --ldflags)</span> <span class="cp">#include &lt;stdio.h&gt; #include &lt;signal.h&gt; #include &lt;unistd.h&gt; #include &lt;xenomai/alchemy/task.h&gt; </span> <span class="n">RT_TASK</span> <span class="n">demo_task</span><span class="p">;</span> <span class="kt">void</span> <span class="nf">demo</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">)</span> <span class="p">{</span> <span class="n">RT_TASK_INFO</span> <span class="n">curtaskinfo</span><span class="p">;</span> <span class="c1">// 获取当前任务信息并打印</span> <span class="n">rt_task_inquire</span><span class="p">(</span><span class="o">&amp;</span><span class="n">demo_task</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">curtaskinfo</span><span class="p">);</span> <span class="c1">// NOTE: 实际实时应用开发中,linux系统调用不放在实时任务中,防止系统切换</span> <span class="n">printf</span><span class="p">(</span><span class="s">"Task name : %s </span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">curtaskinfo</span><span class="p">.</span><span class="n">name</span><span class="p">);</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span> <span class="kt">char</span> <span class="o">*</span><span class="n">str</span> <span class="o">=</span> <span class="s">"demo"</span><span class="p">;</span> <span class="n">printf</span><span class="p">(</span><span class="s">"start task</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span> <span class="n">rt_task_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">demo_task</span><span class="p">,</span> <span class="n">str</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="n">rt_task_start</span><span class="p">(</span><span class="o">&amp;</span><span class="n">demo_task</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">demo</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <p>如果实时内核没有运行的话,xeno-test 和测试程序分别会报以下错误:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xeno-test</span> Started child 1807: /bin/bash /usr/lib/xenomai/testsuite/xeno-test-run-wrapper /usr/bin/xeno-test ++ <span class="nb">echo </span>0 /usr/bin/xeno-test: line 91: /proc/xenomai/latency: No such file or directory <span class="c"># ./hello</span> 0<span class="s2">"000.000| BUG in low_init(): [main] Cobalt core not enabled in kernel </span></code></pre></div></div> Thu, 09 Jun 2022 18:54:00 +0000 https://mathslinux.github.io/2022/06/Install_Xenomai_On_X86/ https://mathslinux.github.io/2022/06/Install_Xenomai_On_X86/ Xenomai Go 使用 sqlite3 数据库 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>目前常用的 Go 的sqlite 的驱动有以下几个:</p> <ul> <li><a href="https://github.com/mattn/go-sqlite3">https://github.com/mattn/go-sqlite3</a> 基于 cgo实现,支持database/sql接口。</li> <li><a href="https://github.com/feyeleanor/gosqlite3">https://github.com/feyeleanor/gosqlite3</a> 基于cgo, 不支持database/sql接口。</li> <li><a href="https://github.com/phf/go-sqlite3">https://github.com/phf/go-sqlite3</a> 基于cgo,不支持database/sql接口。</li> </ul> <p>因为 mattn 的实现支持 go 的sql接口,所以选择使用这个。</p> <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span> <span class="k">import</span> <span class="p">(</span> <span class="s">"database/sql"</span> <span class="s">"fmt"</span> <span class="c">// 即使导入了 sql, 这里也要显示导入 go-sqlite3,</span> <span class="c">// 否则报错 open db err: sql: unknown driver "sqlite3" (forgotten import?)</span> <span class="n">_</span> <span class="s">"github.com/mattn/go-sqlite3"</span> <span class="p">)</span> <span class="k">const</span> <span class="n">sql_file</span> <span class="kt">string</span> <span class="o">=</span> <span class="s">"/tmp/maan.sqlite3"</span> <span class="k">const</span> <span class="n">create</span> <span class="kt">string</span> <span class="o">=</span> <span class="s">` CREATE TABLE trade ( id INTEGER NOT NULL, date DATETIME NOT NULL, code VARCHAR NOT NULL, name VARCHAR, op INTEGER, number INTEGER, price FLOAT, PRIMARY KEY (id) ); INSERT INTO trade (date, code, name, op, number, price) VALUES ('2022-06-01', '1234', '平安', 1, 100, 90.0); INSERT INTO trade (date, code, name, op, number, price) VALUES ('2022-06-02', '1234', '平安', 0, 100, 92.0); INSERT INTO trade (date, code, name, op, number, price) VALUES ('2022-06-03', '2234', '石油', 0, 4, 6.0); INSERT INTO trade (date, code, name, op, number, price) VALUES ('2022-06-05', '3234', '媒体', 1, 5, 7.0); `</span> <span class="k">func</span> <span class="n">doExec</span><span class="p">(</span><span class="n">db</span> <span class="o">*</span><span class="n">sql</span><span class="o">.</span><span class="n">DB</span><span class="p">,</span> <span class="n">sql</span> <span class="kt">string</span><span class="p">,</span> <span class="n">args</span> <span class="o">...</span><span class="k">interface</span><span class="p">{})</span> <span class="p">{</span> <span class="n">stmt</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">db</span><span class="o">.</span><span class="n">Prepare</span><span class="p">(</span><span class="n">sql</span><span class="p">)</span> <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"database error"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span> <span class="nb">panic</span><span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="p">}</span> <span class="k">defer</span> <span class="n">stmt</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span> <span class="n">stmt</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="n">args</span><span class="o">...</span><span class="p">)</span> <span class="p">}</span> <span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span> <span class="c">// open db</span> <span class="n">db</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">sql</span><span class="o">.</span><span class="n">Open</span><span class="p">(</span><span class="s">"sqlite3"</span><span class="p">,</span> <span class="n">sql_file</span><span class="p">)</span> <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"open db err:"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span> <span class="k">return</span> <span class="p">}</span> <span class="c">// lazy close db</span> <span class="k">defer</span> <span class="n">db</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span> <span class="c">// create db</span> <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">db</span><span class="o">.</span><span class="n">Exec</span><span class="p">(</span><span class="n">create</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"create table err:"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span> <span class="k">return</span> <span class="p">}</span> <span class="c">// insert row</span> <span class="n">sql</span> <span class="o">:=</span> <span class="s">`INSERT INTO trade (date, code, name, op, number, price) VALUES ('2022-06-09', '1234', '平安', 1, 120, 52.0)`</span> <span class="n">doExec</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">sql</span><span class="p">)</span> <span class="c">// delete row</span> <span class="n">sql</span> <span class="o">=</span> <span class="s">"DELETE FROM trade WHERE id=?"</span> <span class="n">doExec</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">sql</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span> <span class="c">// update row</span> <span class="n">sql</span> <span class="o">=</span> <span class="s">"UPDATE trade SET NAME=? WHERE id=2"</span> <span class="n">doExec</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">sql</span><span class="p">,</span> <span class="s">"更新"</span><span class="p">)</span> <span class="c">// query sql</span> <span class="n">rows</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">db</span><span class="o">.</span><span class="n">Query</span><span class="p">(</span><span class="s">"SELECT id, name, date, number, price FROM trade WHERE code=?"</span><span class="p">,</span> <span class="s">"1234"</span><span class="p">)</span> <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"query db err:"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span> <span class="k">return</span> <span class="p">}</span> <span class="k">defer</span> <span class="n">rows</span><span class="o">.</span><span class="n">Close</span><span class="p">()</span> <span class="k">for</span> <span class="n">rows</span><span class="o">.</span><span class="n">Next</span><span class="p">()</span> <span class="p">{</span> <span class="k">var</span> <span class="n">uid</span> <span class="kt">int</span> <span class="k">var</span> <span class="n">name</span> <span class="kt">string</span> <span class="k">var</span> <span class="n">date</span> <span class="kt">string</span> <span class="k">var</span> <span class="n">number</span> <span class="kt">string</span> <span class="k">var</span> <span class="n">price</span> <span class="kt">float32</span> <span class="n">rows</span><span class="o">.</span><span class="n">Scan</span><span class="p">(</span><span class="o">&amp;</span><span class="n">uid</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">name</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">date</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">number</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">price</span><span class="p">)</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"id: %d, name=%s, date: %s, number: %s, price: %f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">uid</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">date</span><span class="p">,</span> <span class="n">number</span><span class="p">,</span> <span class="n">price</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> Mon, 08 Nov 2021 17:54:00 +0000 https://mathslinux.github.io/2021/11/Go_Sqlite3_Driver/ https://mathslinux.github.io/2021/11/Go_Sqlite3_Driver/ Go Go 项目的静态编译方式 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>默认 go 语言是静态编译的, 比如:</p> <pre><code class="language-example">$ cat hello.go &amp;&amp; go build hello.go &amp;&amp; file hello &amp;&amp; ldd hello package main import "fmt" func main () { fmt.Println("hello") } hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped not a dynamic executable </code></pre> <p>但是对于使用了 CGO 机制直接用 C 代码的项目,默认情况下是动态链接的, 这在比如容器环境下调试非常不方便,所以最好采用静态链接的方式。在实践中,通常又分为两种方式:</p> <h1 id="自己的项目工程中使用了-cgo">自己的项目工程中使用了 CGO</h1> <p>例如以下情况:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>hello1.go <span class="o">&amp;&amp;</span> go build hello1.go <span class="o">&amp;&amp;</span> file hello1 <span class="o">&amp;&amp;</span> ldd hello1 package main //#include &lt;stdio.h&gt; import <span class="s2">"C"</span> func main<span class="o">()</span> <span class="o">{</span> C.puts<span class="o">(</span>C.CString<span class="o">(</span><span class="s2">"Hello, Cgo</span><span class="se">\n</span><span class="s2">"</span><span class="o">))</span> <span class="o">}</span> hello1: ELF 64-bit LSB executable, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked <span class="o">(</span>uses shared libs<span class="o">)</span>, <span class="k">for </span>GNU/Linux 2.6.32, BuildID[sha1]<span class="o">=</span>0cb254b1d81832db6f1685361a7e6c228a1bdae3, not stripped linux-vdso.so.1 <span class="o">=&gt;</span> <span class="o">(</span>0x00007ffe8f065000<span class="o">)</span> libpthread.so.0 <span class="o">=&gt;</span> /lib64/libpthread.so.0 <span class="o">(</span>0x00007fb96dd1e000<span class="o">)</span> libc.so.6 <span class="o">=&gt;</span> /lib64/libc.so.6 <span class="o">(</span>0x00007fb96d950000<span class="o">)</span> /lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x00007fb96df3a000<span class="o">)</span> </code></pre></div></div> <p>在容器环境下调试代码的时候,外部编译好的程序由于依赖的关系, 在容器中会运行失败</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat </span>hello1.go package main // <span class="c">#include &lt;stdio.h&gt;</span> // void hello<span class="o">()</span> <span class="o">{</span><span class="nb">printf</span><span class="o">(</span><span class="s2">"hello1</span><span class="se">\n</span><span class="s2">"</span><span class="o">)</span><span class="p">;</span><span class="o">}</span> import <span class="s2">"C"</span> func main<span class="o">()</span> <span class="o">{</span> C.hello<span class="o">()</span> <span class="o">}</span> <span class="nv">$ </span>go build <span class="nt">-o</span> /tmp/hello1 hello1.go <span class="o">&amp;&amp;</span> docker run <span class="nt">-ti</span> <span class="nt">--rm</span> <span class="nt">-v</span> /tmp:/data busybox /data/hello1 <span class="nb">exec</span> /data/hello1: no such file or directory </code></pre></div></div> <p>这个时候需要采用静态编译的方式来构建程序</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>go build <span class="nt">-o</span> /tmp/hello1 <span class="nt">-ldflags</span> <span class="s1">'-extldflags "-static"'</span> <span class="nt">-a</span> hello1.go <span class="o">&amp;&amp;</span> file hello1 hello1: ELF 64-bit LSB executable, x86-64, version 1 <span class="o">(</span>GNU/Linux<span class="o">)</span>, statically linked, <span class="k">for </span>GNU/Linux 2.6.32, BuildID[sha1]<span class="o">=</span>be8ef4888e8e1d29787b32e0a6ad8064e60a931a, not stripped <span class="nv">$ </span>docker run <span class="nt">-ti</span> <span class="nt">--rm</span> <span class="nt">-v</span> /tmp:/data busybox /data/hello1 hello1 </code></pre></div></div> <p>其中:</p> <ul> <li><code class="language-plaintext highlighter-rouge">-ldflags</code> 表示传递给 Go 链接器的参数</li> <li><code class="language-plaintext highlighter-rouge">-extldflags "-static"</code> 告诉 Go 的链接器采用静态编译该程序</li> </ul> <h1 id="导入的标准库中使用了-cgo">导入的标准库中使用了 CGO</h1> <p>目前标准库中有两个模块使用了 CGO:</p> <ul> <li><code class="language-plaintext highlighter-rouge">os/user</code> 有原生go实现和C实现。c实现的好处在于能在 LDAP等获取用户信息,一般用不到。</li> <li><code class="language-plaintext highlighter-rouge">net</code> 同样有两个实现,大部分情况下 go 的实现已经够用了。</li> </ul> <p>因此,只要将上面两个模块使用 go 原生实现就可以达到静态编译的效果,这里有两种方式实现:</p> <p>第一种,使用 <code class="language-plaintext highlighter-rouge">-tags osusergo,netgo</code> 明确使用 go 的实现</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>go build main.go <span class="o">&amp;&amp;</span> file main main: ELF 64-bit LSB executable, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked <span class="o">(</span>uses shared libs<span class="o">)</span>, not stripped <span class="nv">$ </span>go build <span class="nt">-tags</span> osusergo,netgo main.go <span class="o">&amp;&amp;</span> file main main: ELF 64-bit LSB executable, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, statically linked, not stripped </code></pre></div></div> <p>第二种方式,使用 <code class="language-plaintext highlighter-rouge">"CGO_ENABLED=0"</code> 表示禁用 CGO</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>go build main.go <span class="o">&amp;&amp;</span> file main main: ELF 64-bit LSB executable, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked <span class="o">(</span>uses shared libs<span class="o">)</span>, not stripped <span class="nv">$ CGO_ENABLED</span><span class="o">=</span>0 go build main.go <span class="o">&amp;&amp;</span> file main main: ELF 64-bit LSB executable, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, statically linked, not stripped </code></pre></div></div> <p>注意:这种方式和上面提到的自己的项目中使用了 CGO 冲突。</p> Thu, 07 Oct 2021 17:54:00 +0000 https://mathslinux.github.io/2021/10/Static_Build/ https://mathslinux.github.io/2021/10/Static_Build/ Go Glib 的事件机制 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>GLIB 的事件机制比较复杂,设计好多概念和模块,网络上相关的介绍也比较乱, 最好的理解方式就是自己动手敲一遍相关代码,然后做一个总结。</p> <h1 id="glib-事件机制介绍">GLib 事件机制介绍</h1> <p>GLib 的事件框架主要有以下组件:</p> <ul> <li><strong>GMainLoop</strong> : 管理所有可用事件源, 添加初始事件源后,会不断检查来自每个事件源的新事件并分派相关函数处理.</li> <li><strong>GMainContext</strong> : 每个时间源创建之后都需要关联到一个 GMainContext 中。</li> <li><strong>GSource</strong> : 具体的事件源对象.</li> </ul> <p>GLib 的事件分发机制流程:</p> <ul> <li>创建一个 GMainContext</li> <li>创建一个 GSource, 指定该 source 的相关元素,如准备事件,检查事件, 分发执行函数等。将需要监控的事件源,回调函数等(如果有,如文件描述符) 添加到source中,并将这个source关联到 GMainContext上。</li> <li>创建一个 GMainLoop,并将 GMainContext 关联到这个loop上。</li> <li>运行 loop。</li> </ul> <p>此后 GMainLoop 将会按照我们设定的条件周而复始的开始处理事件,如对于文件读写, 当有文件可读/可写时候,调用相关的回调函数读写等。</p> <h1 id="demo">Demo</h1> <p>下面的 demo 展示从事件创建 -&gt; 添加到context -&gt; 关联到loop,运行的基本流程</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;glib.h&gt; #include &lt;stdio.h&gt; #include &lt;string.h&gt; </span> <span class="k">typedef</span> <span class="k">struct</span> <span class="n">DemoSource</span> <span class="p">{</span> <span class="n">GSource</span> <span class="n">source</span><span class="p">;</span> <span class="kt">char</span> <span class="o">*</span><span class="n">data</span><span class="p">;</span> <span class="p">}</span> <span class="n">DemoSource</span><span class="p">;</span> <span class="cm">/** * 如果 GSourceFuncs::prepare() 返回 TRUE, 则 GSourceFuncs::dispatch 立即执行 * 如果 GSourceFuncs::prepare() 返回 FALSE: * --&gt; 等待 p_timeout 时间之后: * --&gt; 如果 GSourceFuncs::check() 返回 TRUE, 则 GSourceFuncs::dispatch 立即执行 * --&gt; 如果 GSourceFuncs::check() 返回 FALSE, 继续进入 GSourceFuncs::prepare() */</span> <span class="k">static</span> <span class="n">gboolean</span> <span class="nf">source_prepare</span><span class="p">(</span><span class="n">GSource</span> <span class="o">*</span><span class="n">source</span><span class="p">,</span> <span class="n">gint</span> <span class="o">*</span><span class="n">p_timeout</span><span class="p">)</span> <span class="p">{</span> <span class="k">static</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="o">++</span> <span class="o">%</span> <span class="mi">4</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">"%s:%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__FUNCTION__</span><span class="p">,</span> <span class="n">__LINE__</span><span class="p">);</span> <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span> <span class="p">}</span> <span class="n">printf</span><span class="p">(</span><span class="s">"%s:%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__FUNCTION__</span><span class="p">,</span> <span class="n">__LINE__</span><span class="p">);</span> <span class="o">*</span><span class="n">p_timeout</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span> <span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span> <span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span> <span class="p">}</span> <span class="k">static</span> <span class="n">gboolean</span> <span class="nf">source_check</span><span class="p">(</span><span class="n">GSource</span> <span class="o">*</span><span class="n">source</span><span class="p">)</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">"%s:%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__FUNCTION__</span><span class="p">,</span> <span class="n">__LINE__</span><span class="p">);</span> <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span> <span class="p">}</span> <span class="k">static</span> <span class="n">gboolean</span> <span class="nf">source_dispatch</span><span class="p">(</span><span class="n">GSource</span> <span class="o">*</span><span class="n">source</span><span class="p">,</span> <span class="n">GSourceFunc</span> <span class="n">callback</span><span class="p">,</span> <span class="n">gpointer</span> <span class="n">user_data</span><span class="p">)</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">"%s:%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__FUNCTION__</span><span class="p">,</span> <span class="n">__LINE__</span><span class="p">);</span> <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span> <span class="p">}</span> <span class="k">static</span> <span class="n">GSourceFuncs</span> <span class="n">source_funcs</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">prepare</span> <span class="o">=</span> <span class="n">source_prepare</span><span class="p">,</span> <span class="p">.</span><span class="n">check</span> <span class="o">=</span> <span class="n">source_check</span><span class="p">,</span> <span class="p">.</span><span class="n">dispatch</span> <span class="o">=</span> <span class="n">source_dispatch</span><span class="p">,</span> <span class="p">};</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span> <span class="kt">char</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="s">"demo source"</span><span class="p">;</span> <span class="n">GMainContext</span> <span class="o">*</span><span class="n">main_context</span><span class="p">;</span> <span class="n">GSource</span> <span class="o">*</span><span class="n">source</span> <span class="o">=</span> <span class="n">g_source_new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">source_funcs</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DemoSource</span><span class="p">));</span> <span class="n">DemoSource</span> <span class="o">*</span><span class="n">demo_source</span> <span class="o">=</span> <span class="p">(</span><span class="n">DemoSource</span> <span class="o">*</span><span class="p">)</span><span class="n">source</span><span class="p">;</span> <span class="n">demo_source</span><span class="o">-&gt;</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span> <span class="n">main_context</span> <span class="o">=</span> <span class="n">g_main_context_new</span><span class="p">();</span> <span class="n">g_source_attach</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">main_context</span><span class="p">);</span> <span class="n">g_source_unref</span><span class="p">(</span><span class="n">source</span><span class="p">);</span> <span class="n">GMainLoop</span> <span class="o">*</span><span class="n">loop</span> <span class="o">=</span> <span class="n">g_main_loop_new</span><span class="p">(</span><span class="n">main_context</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">);</span> <span class="n">g_main_loop_run</span><span class="p">(</span><span class="n">loop</span><span class="p">);</span> <span class="n">g_main_loop_unref</span><span class="p">(</span><span class="n">loop</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>对于文件事件源,类似的处理,只不过多了文件是否可读写的相关操作。</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;glib.h&gt; #include &lt;stdio.h&gt; #include &lt;string.h&gt; </span> <span class="k">typedef</span> <span class="k">struct</span> <span class="n">DemoSource</span> <span class="p">{</span> <span class="n">GSource</span> <span class="n">source</span><span class="p">;</span> <span class="n">GIOChannel</span> <span class="o">*</span><span class="n">channel</span><span class="p">;</span> <span class="n">GPollFD</span> <span class="n">fd</span><span class="p">;</span> <span class="p">}</span> <span class="n">DemoSource</span><span class="p">;</span> <span class="cm">/** * 对文件类型的source,GSourceFuncs::prepare() 返回 -1, * 然后在 GSourceFuncs::check() 中检查是否读写事件已经就绪. * 如果 check 检查到就绪,dispatch 调用callback执行。 */</span> <span class="k">static</span> <span class="n">gboolean</span> <span class="nf">source_prepare</span><span class="p">(</span><span class="n">GSource</span> <span class="o">*</span><span class="n">source</span><span class="p">,</span> <span class="n">gint</span> <span class="o">*</span><span class="n">p_timeout</span><span class="p">)</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">"%s:%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">__FUNCTION__</span><span class="p">,</span> <span class="n">__LINE__</span><span class="p">);</span> <span class="o">*</span><span class="n">p_timeout</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span> <span class="p">}</span> <span class="k">static</span> <span class="n">gboolean</span> <span class="nf">source_check</span><span class="p">(</span><span class="n">GSource</span> <span class="o">*</span><span class="n">source</span><span class="p">)</span> <span class="p">{</span> <span class="n">DemoSource</span> <span class="o">*</span><span class="n">demo_source</span> <span class="o">=</span> <span class="p">(</span><span class="n">DemoSource</span> <span class="o">*</span><span class="p">)</span><span class="n">source</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">demo_source</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">.</span><span class="n">revents</span> <span class="o">!=</span> <span class="n">demo_source</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">.</span><span class="n">events</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span> <span class="p">}</span> <span class="k">static</span> <span class="n">gboolean</span> <span class="nf">source_dispatch</span><span class="p">(</span><span class="n">GSource</span> <span class="o">*</span><span class="n">source</span><span class="p">,</span> <span class="n">GSourceFunc</span> <span class="n">callback</span><span class="p">,</span> <span class="n">gpointer</span> <span class="n">user_data</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">callback</span><span class="p">)</span> <span class="p">{</span> <span class="n">callback</span><span class="p">(</span><span class="n">source</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span> <span class="p">}</span> <span class="k">static</span> <span class="n">GSourceFuncs</span> <span class="n">source_funcs</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">prepare</span> <span class="o">=</span> <span class="n">source_prepare</span><span class="p">,</span> <span class="p">.</span><span class="n">check</span> <span class="o">=</span> <span class="n">source_check</span><span class="p">,</span> <span class="p">.</span><span class="n">dispatch</span> <span class="o">=</span> <span class="n">source_dispatch</span><span class="p">,</span> <span class="p">};</span> <span class="n">gboolean</span> <span class="nf">watch</span><span class="p">(</span><span class="n">gpointer</span> <span class="n">user_data</span><span class="p">)</span> <span class="p">{</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buf</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">gsize</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">DemoSource</span> <span class="o">*</span><span class="n">source</span> <span class="o">=</span> <span class="p">(</span><span class="n">DemoSource</span> <span class="o">*</span><span class="p">)</span><span class="n">user_data</span><span class="p">;</span> <span class="n">g_io_channel_read_line</span><span class="p">(</span><span class="n">source</span><span class="o">-&gt;</span><span class="n">channel</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">buf</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">len</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">"%s"</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span> <span class="p">}</span> <span class="n">g_free</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span> <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span> <span class="n">GMainContext</span> <span class="o">*</span><span class="n">main_context</span><span class="p">;</span> <span class="n">GSource</span> <span class="o">*</span><span class="n">source</span> <span class="o">=</span> <span class="n">g_source_new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">source_funcs</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DemoSource</span><span class="p">));</span> <span class="n">DemoSource</span> <span class="o">*</span><span class="n">demo_source</span> <span class="o">=</span> <span class="p">(</span><span class="n">DemoSource</span> <span class="o">*</span><span class="p">)</span><span class="n">source</span><span class="p">;</span> <span class="n">demo_source</span><span class="o">-&gt;</span><span class="n">channel</span> <span class="o">=</span> <span class="n">g_io_channel_new_file</span><span class="p">(</span><span class="s">"/tmp/test.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> <span class="n">demo_source</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">.</span><span class="n">fd</span> <span class="o">=</span> <span class="n">g_io_channel_unix_get_fd</span><span class="p">(</span><span class="n">demo_source</span><span class="o">-&gt;</span><span class="n">channel</span><span class="p">);</span> <span class="n">demo_source</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">.</span><span class="n">events</span> <span class="o">=</span> <span class="n">G_IO_IN</span><span class="p">;</span> <span class="n">g_source_add_poll</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">demo_source</span><span class="o">-&gt;</span><span class="n">fd</span><span class="p">);</span> <span class="n">g_source_set_callback</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">watch</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span> <span class="n">main_context</span> <span class="o">=</span> <span class="n">g_main_context_new</span><span class="p">();</span> <span class="n">g_source_attach</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">main_context</span><span class="p">);</span> <span class="n">g_source_unref</span><span class="p">(</span><span class="n">source</span><span class="p">);</span> <span class="n">GMainLoop</span> <span class="o">*</span><span class="n">loop</span> <span class="o">=</span> <span class="n">g_main_loop_new</span><span class="p">(</span><span class="n">main_context</span><span class="p">,</span> <span class="n">FALSE</span><span class="p">);</span> <span class="n">g_main_loop_run</span><span class="p">(</span><span class="n">loop</span><span class="p">);</span> <span class="n">g_main_loop_unref</span><span class="p">(</span><span class="n">loop</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>一般使用中直接使用 <code class="language-plaintext highlighter-rouge">g_io_create_watch/g_io_add_watch</code> 就可以简化相关代码</p> Wed, 15 Sep 2021 17:54:00 +0000 https://mathslinux.github.io/2021/09/GLib_Event_Loop/ https://mathslinux.github.io/2021/09/GLib_Event_Loop/ GObject K3S 快速安装体验 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>k3s是经CNCF一致性认证的Kubernetes发行版,专为物联网及边缘计算设计。架构包含server节点,agent节点。在生产环境中部署,需要部署多server节点保证高可用。这里只是为了快速体验 k3s, 采用单节点 server 和多agent节点的方式。架构如下:</p> <p><img src="/images/posts/K8S/how-it-works-k3s-revised.png" alt="" /></p> <h1 id="部署前工作">部署前工作</h1> <ul> <li>系统更新</li> <li>网络配置:包括主机名配置,时区配置,软件源等</li> <li>关闭防火墙,selinux</li> <li>关闭交换分区</li> </ul> <h1 id="部署server节点">部署server节点</h1> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. 先安装配置docker</span> <span class="o">[</span>root@k3s1 ~]# yum <span class="nb">install</span> <span class="nt">-y</span> docker-ce <span class="o">[</span>root@k3s1 ~]# systemctl <span class="nb">enable</span> <span class="nt">--now</span> docker <span class="c"># 2. 下载安装 k3s</span> <span class="o">[</span>root@k3s1 ~]# wget https://rancher-mirror.rancher.cn/k3s/k3s-install.sh <span class="c"># 2.1 默认 k3s 不适用docker,采用 containerd,这里特别指明采用 docker</span> <span class="c"># 2.2 使用 INSTALL_K3S_MIRROR=cn 环境变量制定使用国内源加速下载</span> <span class="c"># 2.3 k3s 自带 lb 插件,这里我们禁用这个插件,后面采用 metallb 的方案</span> <span class="o">[</span>root@k3s1 ~]# <span class="nv">INSTALL_K3S_MIRROR</span><span class="o">=</span>cn bash k3s-install.sh <span class="nt">--docker</span> <span class="nt">--disable</span><span class="o">=</span>traefik <span class="c"># 3. 查看 k3s 的 token, 后面部署 agent 用到</span> <span class="o">[</span>root@k3s1 ~]# <span class="nb">cat</span> /var/lib/rancher/k3s/server/node-token K1013f26620ae0297e33b4369b3e634d41a93994787d3f9ca6f3c24dbad8d898ba3::server:eb416ac93cf97f38599d9207ac3d74d6 </code></pre></div></div> <h1 id="部署-agent-节点">部署 agent 节点</h1> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. 安装配置 docker</span> <span class="o">[</span>root@k3s2 ~]# yum <span class="nb">install</span> <span class="nt">-y</span> docker-ce <span class="o">[</span>root@k3s2 ~]# systemctl <span class="nb">enable</span> <span class="nt">--now</span> docker <span class="c"># 2. 下载安装 k3s</span> <span class="o">[</span>root@k3s2 ~]# wget https://rancher-mirror.rancher.cn/k3s/k3s-install.sh <span class="c"># 2.1 默认 k3s 不适用docker,采用 containerd,这里特别指明采用 docker</span> <span class="c"># 2.2 使用 INSTALL_K3S_MIRROR=cn 环境变量制定使用国内源加速下载, SERVER_IP 是server服务器的IP地址,token通过上一步获取</span> <span class="o">[</span>root@k3s2 ~]# <span class="nv">INSTALL_K3S_MIRROR</span><span class="o">=</span>cn <span class="nv">K3S_URL</span><span class="o">=</span>https://[SERVER_IP]:6443 <span class="nv">K3S_TOKEN</span><span class="o">=[</span>TOKEN] bash k3s-install.sh <span class="nt">--docker</span> </code></pre></div></div> <h1 id="测试">测试</h1> <p>部署完成后,在server节点验证集群情况</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@k3s1 work]# kubectl get nodes NAME STATUS ROLES AGE VERSION k3s1 Ready control-plane,master 3m50s v1.23.6+k3s1 k3s2 NotReady &lt;none&gt; 9s v1.23.6+k3s1 <span class="o">[</span>root@k3s1 work]# kubectl get pods <span class="nt">-n</span> kube-system NAME READY STATUS RESTARTS AGE helm-install-traefik-zd94v 0/1 ContainerCreating 0 3m50s helm-install-traefik-crd-9qvk7 0/1 ContainerCreating 0 3m50s coredns-d76bd69b-kmnwq 1/1 Running 0 3m50s local-path-provisioner-6c79684f77-wckqm 1/1 Running 0 3m50s metrics-server-7cd5fcb6b7-m8ltf 1/1 Running 0 3m50s </code></pre></div></div> <p>部署一个nginx服务验证</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@k3s1 work]# <span class="nb">cat </span>nginx.yaml <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"=============================="</span> <span class="o">&amp;&amp;</span> <span class="nb">cat </span>srv.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:alpine ports: - containerPort: 80 <span class="o">==============================</span> apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 <span class="nb">type</span>: NodePort </code></pre></div></div> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@k3s1 work]# kubectl apply <span class="nt">-f</span> nginx.yaml <span class="o">[</span>root@k3s1 work]# kubectl apply <span class="nt">-f</span> svc.yaml <span class="o">[</span>root@k3s1 ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT<span class="o">(</span>S<span class="o">)</span> AGE kubernetes ClusterIP 10.43.0.1 &lt;none&gt; 443/TCP 16h nginx-service NodePort 10.43.50.167 &lt;none&gt; 80:31574/TCP 3m18s <span class="o">[</span>root@k3s1 ~]# curl localhost:31574 &lt;<span class="o">!</span>DOCTYPE html&gt; ...... &lt;/html&gt; </code></pre></div></div> <h1 id="部署负载均衡">部署负载均衡</h1> <p>k8s的LoadBalancer类型的Service依赖于外部的云提供的Load Balancer,metallb提供了在私有云或者裸机环境下的负载均衡方案。这里我们也采用 metallb 的方案。</p> <h2 id="安装-metallb">安装 metallb</h2> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@k3s1 metallb]# kubectl apply <span class="nt">-f</span> https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/namespace.yaml <span class="o">[</span>root@k3s1 metallb]# kubectl apply <span class="nt">-f</span> https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/metallb.yaml <span class="o">[</span>root@k3s1 metallb]# kubectl get pods <span class="nt">-n</span> metallb-system NAME READY STATUS RESTARTS AGE controller-f54fbc6f9-ppm96 1/1 Running 0 47s speaker-g7g8l 1/1 Running 0 48s speaker-h57p6 1/1 Running 0 48s </code></pre></div></div> <h2 id="配置-metallb">配置 metallb</h2> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@k3s1 metallb]# <span class="nb">cat </span>metallb-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | address-pools: - name: default protocol: layer2 addresses: <span class="c"># 可配置为下列形式或者192.168.1.0/24形式,根据预留的网段具体决定</span> <span class="c"># 可配置多个,根据实际情况配置</span> - 192.168.137.111-192.168.137.121 <span class="o">[</span>root@k3s1 metallb]# kubectl apply <span class="nt">-f</span> metallb-configmap.yaml configmap/config created </code></pre></div></div> <h2 id="测试-1">测试</h2> <p>部署一个 lb 的service</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@k3s1 metallb]# <span class="nb">cat </span>lb.yaml apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 <span class="nb">type</span>: LoadBalancer <span class="o">[</span>root@k3s1 metallb]# kubectl apply <span class="nt">-f</span> metallb-configmap.yaml configmap/config created <span class="c"># 这里我手动修改了 pod 里面nginx的内容为他的内网ip地址以便测试</span> <span class="o">[</span>root@k3s1 metallb]# curl http://192.168.137.111 10.42.1.3 <span class="o">[</span>root@k3s1 metallb]# curl http://192.168.137.111 10.42.0.22 </code></pre></div></div> <h1 id="其他">其他</h1> <p>k3s 不支持 docker 的 systemd 的 cgroupdriver, 如果有自定义docker配置,务必不要配置此项</p> Mon, 21 Jun 2021 16:35:00 +0000 https://mathslinux.github.io/2021/06/K3S_QuickStart/ https://mathslinux.github.io/2021/06/K3S_QuickStart/ K8S QT Designer 使用未显示在工具箱中控件 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>在 QT 项目开发的时候,使用 QT Designer 进行 UI 设计的时候,有时候想用的控件 不在 Designer 工具箱里面,比如 <strong>QWebEngineView</strong> 之类的控件就没有,会感到很别扭。 在 <a href="https://doc.qt.io/qt-5/designer-using-custom-widgets.html">QT 官方文档</a> 上查询了一下,可以通过 promote widget 的方式解决这个问题。</p> <p>首先,拖动一个 widget 到放置控件的地方,右键这个 widget,选择提升为</p> <p><img src="/images/posts/QT/promote_widget_1.png" alt="" /></p> <p>最后在弹出的 promote 窗口上输入该控件的描述,包括控件类名和头文件, 然后点击提升就可以了。</p> <p><img src="/images/posts/QT/promote_widget_1.png" alt="" /></p> Fri, 21 May 2021 16:35:00 +0000 https://mathslinux.github.io/2021/05/Promote_Widget/ https://mathslinux.github.io/2021/05/Promote_Widget/ QT 重启声卡服务 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>随着时间的变迁,我的老古董 Mac Air 又遇到新的问题,联系播放几小时电影之后, 或者频繁地进行快进等操作的时候,画面突然会暂停,以为是显卡什么的坏了,进行 一番排查之后,才发现罪魁祸首原来是名为 coreaudiod 的服务出现了 bug.</p> <p><img src="/images/posts/Mac/coreaudiod_bug.png" alt="" /></p> <p>找到问题之后解决就很简单了. 重启该服务即可。PS: 在 Mac 上重启很多系统服务 只需要杀死该服务即可,如 Finder 等。</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>killall <span class="nt">-9</span> coreaudiod </code></pre></div></div> Fri, 21 May 2021 09:00:00 +0000 https://mathslinux.github.io/2021/05/Restart_Audio_Service/ https://mathslinux.github.io/2021/05/Restart_Audio_Service/ Mac 使用 Jekyll 和 Github 搭建个人博客 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>晕晕乎乎了两年,准备把停更的 blog 更新一下。但是时过境迁,很多技术方法都已 经成为古董了,之前的blog是在 vps 上用 wordpress 搭建的,我在电脑上写好 emacs-org 格式的文章后,用脚本转换为 html 然后用 wordpress 的 API 发表的。 现在不想太折腾了,简单研究了一番,发现用 Jekyll 和 github 的组合最方便省心。</p> <p>大体思路为,文章还是在本地用 emacs-org 的格式来写(毕竟我用了10来年已经比较熟悉了), 然后用 pandoc 转换为基本的 markdown,最后写一个小脚本简单处理一下图片, jekyll tag 等问题。</p> <h1 id="安装-jekyll">安装 Jekyll</h1> <p>Jekyll 基于 ruby,所以首先要安装 ruby,这里选择使用 rbenv 来安装管理 ruby。</p> <p>以下操作在我的 mac 上进行:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew <span class="nb">install </span>rbenv ruby-build <span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'eval "$(rbenv init -)"'</span> <span class="o">&gt;&gt;</span> ~/.bash_profile <span class="nv">$ </span>rbenv <span class="nb">install</span> <span class="nt">-l</span> <span class="c"># 列出所有可安装的版本</span> <span class="nv">$ </span>rbenv <span class="nb">install </span>2.7.1 <span class="c"># 安装最新稳定版本</span> <span class="nv">$ </span>rbenv global 2.7.1 <span class="c"># 设置全局 ruby 版本</span> <span class="c"># 将 gem 安装的文件列入可执行文件路径中</span> <span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'export PATH="$HOME/.gem/ruby/2.7.0/bin:$PATH"'</span> <span class="o">&gt;&gt;</span> ~/.bash_profile </code></pre></div></div> <p>安装 Jekyll:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gem <span class="nb">install</span> <span class="nt">--user-install</span> bundler jekyll <span class="nv">$ </span>jekyll <span class="nt">-v</span> <span class="c"># 检查是否安装成功</span> jekyll 4.1.1 </code></pre></div></div> <h1 id="在-github-上选择模板">在 Github 上选择模板</h1> <p>这里选择 <a href="https://github.com/leopardpan/leopardpan.github.io">leopardpan 的 blog 模板</a>: 在他的 blog github 上 fork 到自己仓库,注意 仓库名要改为 username.github.io 这种名字。 <code class="language-plaintext highlighter-rouge">username</code> 是你的 github 用户名。</p> <p>克隆仓库</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone [email protected]:mathslinux/mathslinux.github.io.git <span class="c"># 克隆仓库</span> </code></pre></div></div> <p>在本地启动 blog 查看效果, 启动后在 <a href="http://127.0.0.1:4000/">http://127.0.0.1:4000/</a> 就可以查看调试了。</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>jekyll serve </code></pre></div></div> <p>也可以在 <a href="http://username.github.io/">http://username.github.io/</a> 查看。其中 username 是你的 github 用户名。 之后发表新的 blog 之后 直接 <code class="language-plaintext highlighter-rouge">git push</code> 后就是更新了。</p> <h1 id="修修改改">修修改改</h1> <p>jekyll 模板中的主要需要修改的文件:</p> <ul> <li><code class="language-plaintext highlighter-rouge">_config.yml</code>: blog 的全局配置, 包括 blog 名字等等,按照自己的实际情况修改。</li> <li><code class="language-plaintext highlighter-rouge">_includes/footer.html</code>: 修改里面的版权等信息。</li> <li>about.md: 关于自己的介绍。</li> <li>images: 该目录包含自己的头像,背景,支付宝,微信等的二维码图像等,按需修改。</li> </ul> <h1 id="发表文章">发表文章</h1> <p>发表文章很简单,只需要以 markdown 的格式写好文章,放到 <code class="language-plaintext highlighter-rouge">_posts</code> 目录下即可。 需要注意的是文件名要以 Date-Title.md 这种格式,如 <code class="language-plaintext highlighter-rouge">2017-10-06-Free_Up_Space.md</code></p> <p>文件内容和 markdown 格式没有区别。但是在文件头,需要添加一些元数据。</p> <pre><code class="language-example">--- layout: post tag: Emacs date: [2017-01-22 日 16:30] title: 在 Emacs 内部运行 Shell --- </code></pre> <ul> <li>layout: post 这行不用变。</li> <li>tag: 文章的标签。</li> <li>date: 日期。</li> <li>title: 文章的标题。</li> </ul> <h1 id="issues">Issues</h1> <ul> <li>由于国内网络的原因,安装的时候卡在下载ruby过程</li> </ul> <p>可以用科学上网的方式先把包下载到 rbenv 的缓存中, 再执行 rbenv install 操作</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir</span> <span class="nt">-p</span> ~/.rbenv/cache/ <span class="o">&amp;&amp;</span> <span class="nb">mv </span>ruby-2.7.1.tar.bz2 ~/.rbenv/cache <span class="nv">$ </span>rbenv <span class="nb">install </span>2.7.1 </code></pre></div></div> <ul> <li>如果执行 <code class="language-plaintext highlighter-rouge">jekyll serve</code> 出现依赖包的问题</li> </ul> <p>比如出现如下错误</p> <pre><code class="language-example">/bundler/spec_set.rb:86:in `block in materialize': Could not find public_suffix-4.0.5 in any of the sources (Bundler::GemNotFound) </code></pre> <p>进入 blog 主目录,删除 Gem用 bundle 自动安装依赖</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bundle <span class="nb">install</span> </code></pre></div></div> Tue, 30 Jun 2020 00:00:00 +0000 https://mathslinux.github.io/2020/06/Setup_Blog_With_Jekyll_Github/ https://mathslinux.github.io/2020/06/Setup_Blog_With_Jekyll_Github/ Others 搭建 windows 的 go vscode 开发环境 <pre><code class="language-{=org}">#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org </code></pre> <p>最近由于一些原因,只能用 windows 电脑,记录一下用windows开发go语言相关项目开发环境搭建情况。</p> <h1 id="安装配置-go">安装配置 go</h1> <h2 id="安装-go">安装 go</h2> <p>从 <a href="https://golang.google.cn/dl/">https://golang.google.cn/dl/</a> 下载windows的安装包,双击安装。结束后打开 PowerShell,输入以下指令验证</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PS C:<span class="se">\U</span>sers<span class="se">\H</span>P&gt; go <span class="nb">env</span> | Select-String <span class="s2">"gopath|goroot"</span> <span class="nb">set </span><span class="nv">GOPATH</span><span class="o">=</span>C:<span class="se">\U</span>sers<span class="se">\H</span>P<span class="se">\g</span>o <span class="nb">set </span><span class="nv">GOROOT</span><span class="o">=</span>D:<span class="se">\P</span>rogram Files<span class="se">\G</span>o </code></pre></div></div> <p>NOTE: <code class="language-plaintext highlighter-rouge">GOPATH</code> 和 <code class="language-plaintext highlighter-rouge">GOROOT</code> 环境变量已经配置了,如果不想用默认的使用以下指令自己配置</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PS C:<span class="se">\U</span>sers<span class="se">\H</span>P&gt; go <span class="nb">env</span> <span class="nt">-w</span> <span class="nv">GOPATH</span><span class="o">=[</span>YOUR-PATH] </code></pre></div></div> <h2 id="配置">配置</h2> <p>开启 go module</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>go <span class="nb">env</span> <span class="nt">-w</span> <span class="nv">GO111MODULE</span><span class="o">=</span>on </code></pre></div></div> <p>由于你懂得原因,go官方仓库国内无法访问,使用代理</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>go <span class="nb">env</span> <span class="nt">-w</span> <span class="nv">GOPROXY</span><span class="o">=</span>https://goproxy.cn,direct </code></pre></div></div> <h1 id="安装配置-vscode">安装配置 vscode</h1> <p>到 <a href="https://code.visualstudio.com/">https://code.visualstudio.com/</a> 下载安装vscode</p> <h1 id="安装配置vscode的go插件">安装配置vscode的go插件</h1> <p>打开 vs</p> <ul> <li>输入 <code class="language-plaintext highlighter-rouge">Ctrl+Shift+X</code> 然后搜索 go,安装第一个结果 <code class="language-plaintext highlighter-rouge">Rich Go language support for Visual Studio Code</code></li> <li>输入 <code class="language-plaintext highlighter-rouge">Ctrl+Shift+P</code> 然后搜索 go: install/update tools –&gt; 选择全部 –&gt; 确定, 等待 Go 工具完成更新</li> </ul> <h1 id="参考">参考</h1> <p>更多的 go proxy的配置请参考 <a href="https://github.com/goproxy/goproxy.cn/blob/master/README.zh-CN.md">https://github.com/goproxy/goproxy.cn/blob/master/README.zh-CN.md</a></p> Tue, 07 May 2019 17:54:00 +0000 https://mathslinux.github.io/2019/05/Vscode_Env/ https://mathslinux.github.io/2019/05/Vscode_Env/ Go