[{"data":1,"prerenderedAt":271},["ShallowReactive",2],{"tag-golang-1":3},{"title":4,"articles":5,"hot_article":56,"month_arch":119,"total":267,"cover":268},"golang - 独孤影的博客",[6,18,27,36,46],{"id":7,"title":8,"uri":9,"author":10,"tags":11,"created_at":15,"view_count":16,"content":17},94,"golang程序并发读写全局变量，导致空指针异常","concurrent-reading-and-writing-global-var-caused-nil-pointer-dereference-panic","duguying",[12,13,14],"golang","数据竞争","线程安全","2023-02-28 19:27:48",348,"\u003Cp>最近将一个golang程序由原本的单线程改为双线程处理日志解析，在生产环境节点上运行发现出现了一个空指针异常，异常信息如下：\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>[E] 2023\u002F02\u002F27 11:53:25 panic.go:838: parse error:runtime error: invalid memory address or nil pointer dereference,line:Feb 27 11:53:25 xxxxx.site nginx: [xxxxxxxxxxxxxxxxxxx] [27\u002FFeb\u002F2023:11:53:25 +0800] [https] [120.232.31.196:443] [xxxxx.map.xx.com] [39.144.41.41:37647] [200] [30.171.153.132:10000] [200] [3339597] [POST \u002Ftr?mllc HTTP\u002F1.1] [621] [152] [xxxxxxx.map.xx.com] [Dalvik\u002F2.1.0 (Linux; U; Android 10; HMA-AL00 Build\u002FHUAWEIHMA-AL00)] [-] [0.008] [0.008] [0.004] [0.008] [46795] [311532943564] [1][1:46:0:0:0:0:0] [ECDHE-RSA-AES128-GCM-SHA256] [TLSv1.2] [r][-] [-] [n] [2358837] [-1] [1677470005.398|5|51|126|-1|-1|126|126|130|-1|-1|130|134|134|134|134#200|200|8|152|120.232.31.196|30.171.153.132:10000|0|0] [POST] [\u002Ftr] [mllc] [HTTP\u002F1.1] [39.144.41.41] [-] [-] [-] [-] [-] [-] [-] [-] [-] [-] [169.254.213.29:50937] [0]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>修改程序，打印出异常栈\n\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2023\u002F02\u002Fx7s4uzea.webp\" alt=\"x7s4uzea\" \u002F>\u003C\u002Fp>\n\n\u003Cp>发现异常发生在代码133行，代码如下\n\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2023\u002F02\u002F0zhhoxbp.png\" alt=\"0zhhoxbp\" \u002F>\u003C\u002Fp>\n\n\u003Cp>很明显这里不太可能出现空指针，除非运行到这一行的时候\u003Ccode class=\"inline-code\">sl\u003C\u002Fcode>对象被置为\u003Ccode class=\"inline-code\">nil\u003C\u002Fcode>，但是我很确定这里不存在其他线程共享\u003Ccode class=\"inline-code\">sl\u003C\u002Fcode>的情况，也就是不可能被其他线程置为\u003Ccode class=\"inline-code\">nil\u003C\u002Fcode>，何况，我这里没有任何操作将sl置为\u003Ccode class=\"inline-code\">nil\u003C\u002Fcode>，百思不得其解。最后发现\u003Ccode class=\"inline-code\">key1\u003C\u002Fcode>，\u003Ccode class=\"inline-code\">key2\u003C\u002Fcode>这两个变量是全局变量，全局变量在多线程环境下会存在数据竞争问题。原本定义如下\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#ff7b72\">var\u003C\u002Fspan> (\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\turiSep \u003Cspan style=\"color:#ff7b72;font-weight:bold\">*\u003C\u002Fspan>regexp.Regexp = \u003Cspan style=\"color:#79c0ff\">nil\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\tkey1 = \u003Cspan style=\"color:#a5d6ff\">&#34;&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\tkey2 = \u003Cspan style=\"color:#a5d6ff\">&#34;&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>)\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>可以看到\u003Ccode class=\"inline-code\">key1\u003C\u002Fcode>,\u003Ccode class=\"inline-code\">key2\u003C\u002Fcode>被定义为全局变量，忘记修改了。修改之后，神奇的发现，空指针异常已经不再存在了。\u003C\u002Fp>\n\n\u003Cp>我在发现这个问题之前，在本地开发服务器上尝试重现，但是一直未能重现出来，估计是我本地qps不够高，所以难以复现，生产环境qps是3w到4w左右，这个空指针异常呈现无规律的隔几秒钟出现一次。\u003C\u002Fp>\n\n\u003Cp>golang中，双协程（绑定在双M和双cpu上）中同时读写一个共享变量导致空指针异常，这种情况我还是第一次遇见，以前遇到这种双协程读写一个共享变量的情况都是数据错乱，并没有空指针。据说这种线程安全、数据竞争导致的空指针异常在C++中也是常见的情况。所以，我在想，这里会不会是因为我将2个协程绑核了，所以在双线程绑双核的情况下更容易复现呢。\u003C\u002Fp>\n",{"id":19,"title":20,"uri":21,"author":10,"tags":22,"created_at":24,"view_count":25,"content":26},92,"为go程序的协程绑核","set-cpu-affinity-binding-for-golang-program",[12,23],"绑核","2023-02-23 10:22:23",1752,"\u003Cp>最近在公司的日志处理程序上做性能优化，用到了绑核的情况。背景是这样的，nginx进行http转发，产生日志，然后我们的程序读取日志，用lexer分词器对日志分隔字段，并且对字段进行统计聚合上报，生成监控。日志处理程序最开始是在单个goroutine里进行读取并且解析操作了，但是在核数比较多的大机器上，发现日志生成太快，解析程序处理不过来，在日志rotate的过程中会发生丢失日志的情况。于是针对这个情况进行了优化。\u003C\u002Fp>\n\n\u003Cp>用pprof发现，性能消耗最大的部分是lexer，lexer其实就是个分词器，编译器中常用的技术，逐字符读取每行日志，然后基于状态机状态标记对日志的字段进行分割，中间涉及到的状态也不算太多，主要是双引号(&ldquo;&rdquo;)作为定界符提取字符串字段，方括号([])作为定界符提取字符串字段，空字符(空格、\\t)和竖线符(|)作为分隔符分隔字段，转义符()对字符串中的字符串定界符(&rdquo;[])进行转义，总体来说，状态不算复杂，其中也针对lexer优化过尽量减少变量分配和杜绝变量逃逸，lexer实在是已经无可优化了。\u003C\u002Fp>\n\n\u003Cp>于是只好从其他方面下手，首先就是cpu切换的性能损耗。众所周知golang中没有线程的，golang中只有协程（goroutine），而防止cpu切换的性能损耗只有绑核这个方法，具体就是讲指定的核绑定到某个线程上，这样这个线程就会只在这个指定的核上运行，不会被系统切换到其他核上，这样也就不会产生切换的损耗了。但是golang程序中只有goroutine，不能直接操作线程。其实我们是有办法对goroutine进行绑核的。\u003C\u002Fp>\n\n\u003Cp>首先，使用go里面的\u003Ccode class=\"inline-code\">runtime.LockOSThread()\u003C\u002Fcode>将当前goroutine绑定到它所在的M线程，这样，这个goroutine就不会在M线程之间切换了；然后，我们可以使用cgo，调用\u003Ccode class=\"inline-code\">pthread_self\u003C\u002Fcode>获取当前协程所在M线程的线程ID，并调用\u003Ccode class=\"inline-code\">CPU_SET\u003C\u002Fcode>对这个线程ID设置cpuid绑定。具体如下\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#ff7b72\">package\u003C\u002Fspan> affinity\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u002F*\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">#define _GNU_SOURCE\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">#include &lt;sched.h&gt;\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">#include &lt;pthread.h&gt;\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">int lock_thread(int cpuid) {\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  pthread_t tid;\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  cpu_set_t cpuset;\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  tid = pthread_self();\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  CPU_ZERO(&amp;cpuset);\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  CPU_SET(cpuid, &amp;cpuset);\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  return  pthread_setaffinity_np(tid, sizeof(cpu_set_t), &amp;cpuset);\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">}\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">pthread_t current_thread_id() {\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  pthread_t tid;\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  tid = pthread_self();\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">  return tid;\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">}\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">*\u002F\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#ff7b72\">import\u003C\u002Fspan> \u003Cspan style=\"color:#a5d6ff\">&#34;C&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#ff7b72\">import\u003C\u002Fspan> (\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\t\u003Cspan style=\"color:#a5d6ff\">&#34;fmt&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\t\u003Cspan style=\"color:#a5d6ff\">&#34;runtime&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>)\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u002F\u002F SetAffinity 设置CPU绑定\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">func\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">SetAffinity\u003C\u002Fspan>(cpuID \u003Cspan style=\"color:#ff7b72\">int\u003C\u002Fspan>) (\u003Cspan style=\"color:#ff7b72\">uint64\u003C\u002Fspan>, \u003Cspan style=\"color:#ff7b72\">error\u003C\u002Fspan>) {\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\truntime.\u003Cspan style=\"color:#d2a8ff;font-weight:bold\">LockOSThread\u003C\u002Fspan>()\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\tret \u003Cspan style=\"color:#ff7b72;font-weight:bold\">:=\u003C\u002Fspan> C.\u003Cspan style=\"color:#d2a8ff;font-weight:bold\">lock_thread\u003C\u002Fspan>(C.int(cpuID))\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\ttid \u003Cspan style=\"color:#ff7b72;font-weight:bold\">:=\u003C\u002Fspan> uint64(C.\u003Cspan style=\"color:#d2a8ff;font-weight:bold\">ulong\u003C\u002Fspan>(C.\u003Cspan style=\"color:#d2a8ff;font-weight:bold\">current_thread_id\u003C\u002Fspan>()))\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\t\u003Cspan style=\"color:#ff7b72\">if\u003C\u002Fspan> ret &gt; \u003Cspan style=\"color:#a5d6ff\">0\u003C\u002Fspan> {\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\t\t\u003Cspan style=\"color:#ff7b72\">return\u003C\u002Fspan> \u003Cspan style=\"color:#a5d6ff\">0\u003C\u002Fspan>, fmt.\u003Cspan style=\"color:#d2a8ff;font-weight:bold\">Errorf\u003C\u002Fspan>(\u003Cspan style=\"color:#a5d6ff\">&#34;set cpu core affinity failed with return code %d&#34;\u003C\u002Fspan>, ret)\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\t}\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\t\u003Cspan style=\"color:#ff7b72\">return\u003C\u002Fspan> tid, \u003Cspan style=\"color:#79c0ff\">nil\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>}\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>这样一来，我们只需要在goroutine中调用\u003Ccode class=\"inline-code\">SetAffinity\u003C\u002Fcode>就可以将指定的cpuid和当前goroutine进行绑定。这样就实现了goroutine的绑定。\u003C\u002Fp>\n\n\u003Cp>我将日志处理程序改为在主协程中读取文件并且通过channel分发日志行，然后在2个goroutine执行最占cpu的lexer及后续处理，并且在这两个goroutine中绑定cpuid为1,2。\u003C\u002Fp>\n\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2023\u002F02\u002Fqdx9l7z5.webp\" alt=\"qdx9l7z5\" \u002F>\u003C\u002Fp>\n\n\u003Cp>图中可以看到，两个处理日志的goroutine绑定了1,2两个cpu，并且不会切为其他cpu，这两个cpu都在处理日志，所以cpu占用都比较高，相当于把原来一个核处理的任务分担到2个核上了。\u003C\u002Fp>\n",{"id":28,"title":29,"uri":29,"author":10,"tags":30,"created_at":33,"view_count":34,"content":35},57,"golang客户端取消http请求",[12,31,32],"cancelable","http","2017-08-14 16:39:09",3586,"\u003Cp>首先，创建带有Cancel Context的http请求\u003C\u002Fp>\u003Cpre class=\"brush:go;toolbar:false\">var&nbsp;cx&nbsp;context.Context\ncx,&nbsp;req.cancel&nbsp;=&nbsp;context.WithCancel(context.Background())\n\nif&nbsp;req.httpreq,&nbsp;err&nbsp;=&nbsp;http.NewRequest(req.method,&nbsp;req.url,&nbsp;reqbody);&nbsp;err&nbsp;!=&nbsp;nil&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;return\n}\nreq.httpreq&nbsp;=&nbsp;req.httpreq.WithContext(cx)\nreq.httpreq.Header&nbsp;=&nbsp;req.Headers\nreq.httpreq.ContentLength&nbsp;=&nbsp;reqbodyLength\u003C\u002Fpre>\u003Cp>然后，需要停止请求的时候调用req.cancel()方法\u003C\u002Fp>\u003Cpre class=\"brush:go;toolbar:false\">if&nbsp;req.cancel&nbsp;!=&nbsp;nil&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;req.cancel()\n&nbsp;&nbsp;&nbsp;&nbsp;fmt.Println(&quot;running&nbsp;cancel...&quot;)\n}\n\nreturn&nbsp;nil\u003C\u002Fpre>\u003Cp>这样才能够客户端强行主动取消正在进行的http连接。就是这么简单\u003Cbr\u002F>\u003C\u002Fp>",{"id":37,"title":38,"uri":38,"author":10,"tags":39,"created_at":43,"view_count":44,"content":45},32,"关于阿里云ESC上go语言项目编译6l: running gcc failed: Cannot allocate memory",[12,40,41,42],"编译","内存不够","swap","2015-10-28 14:06:02",4450,"\u003Cp>前段时间将自己的阿里云服务器上的系统由centos 6.5换为了ubuntu 14，其他的硬件配置都没有发生改变，将服务器上的数据恢复并且重新安装了golang的编译环境后，发现使用go build编译稍微大一点的golang项目就会报错：\u003C\u002Fp>\u003Cblockquote>\u003Cp>\u002Fusr\u002Flocal\u002Fgo\u002Fpkg\u002Ftool\u002Flinux_amd64\u002F6l: running gcc failed: Cannot allocate memory\u003C\u002Fp>\u003C\u002Fblockquote>\u003Cp>一直想不通为啥换了个系统就会报这个错，字面意思是gcc分配内存失败，应该是内存不够用，机器配置是1G内存，free -m 发现尚有400M的内存未使用，难道剩余400M的内存还不够go build命令编译代码使用？好吧，既然如此我就给它释放内存，kill掉众多的进程之后再进行go build编译，发现又可以编译了。之后发现偶尔能编译偶尔又不能编译，看样子确实是内存不够，可是为啥之前的centos系统上没有出现这种状况呢，一直不相信简单的“内存不够”就可以解释这一问题，因为之前的centos系统上是正常的，我甚至觉得可能是gcc版本的问题，猜测只有较高版本的gcc才会报这个错误。后来也曾在“golang天朝”论坛上发过帖子，并表达自己的猜测，认为不是内存不够这么简单，结果被别人鄙视不看英文……\u003C\u002Fp>\u003Cp>不想花钱升级机器硬件，难道我只有装回centos？今天执行free -m偶然间注意到了swap的数值貌似一直是空的，我思考若是我添加swap交换空间是否能解决这一问题呢，毕竟swap其实就是用硬盘空间虚拟出的内存，一个内存的缓冲区。于是就给它加了个1G的文件作为swap，居然直接就可以用go build，再也不用担心gcc对我说Cannot allocate memory了。简单的记录一下添加文件作为swap的步骤：\u003C\u002Fp>\u003Cul class=\" list-paddingleft-2\" style=\"list-style-type: disc;\">\u003Cli>\u003Cp>创建1个1GB的file\u003Cbr\u002F>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cpre class=\"brush:bash;toolbar:false\">sudo&nbsp;dd&nbsp;if=\u002Fdev\u002Fzero&nbsp;of=\u002Fmnt\u002F1GB.swap&nbsp;bs=1M&nbsp;count=1024\u003C\u002Fpre>\u003Cul class=\" list-paddingleft-2\" style=\"list-style-type: disc;\">\u003Cli>\u003Cp>格式化为Swap file\u003Cbr\u002F>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cpre class=\"brush:bash;toolbar:false\">sudo&nbsp;mkswap&nbsp;\u002Fmnt\u002F1GB.swap\u003C\u002Fpre>\u003Cul class=\" list-paddingleft-2\" style=\"list-style-type: disc;\">\u003Cli>\u003Cp>把swap file加入到系统中\u003Cbr\u002F>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cpre class=\"brush:bash;toolbar:false\">sudo&nbsp;swapon&nbsp;\u002Fmnt\u002F1GB.swap\u003C\u002Fpre>\u003Cul class=\" list-paddingleft-2\" style=\"list-style-type: disc;\">\u003Cli>\u003Cp>将swap永久添加\u003Cbr\u002F>在\u002Fect\u002Ffstab中加入新的Swap分区\u003Cbr\u002F>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cpre class=\"brush:bash;toolbar:false\">sudo&nbsp;gedit&nbsp;\u002Fetc\u002Ffstab\u003C\u002Fpre>\u003Cul class=\" list-paddingleft-2\" style=\"list-style-type: disc;\">\u003Cli>\u003Cp>在最后加入下列内容\u003Cbr\u002F>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cpre class=\"brush:bash;toolbar:false\">\u002Fmnt\u002F1GB.swap&nbsp;none&nbsp;swap&nbsp;sw&nbsp;0&nbsp;0\u003C\u002Fpre>\u003Cp>最后，free -m 命令可以看到swap的数据如下\u003C\u002Fp>\u003Cpre class=\"brush:bash;toolbar:false\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;used&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shared&nbsp;&nbsp;&nbsp;&nbsp;buffers&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cached\nMem:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;992&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;903&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;88&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;57&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;188\n-\u002F+&nbsp;buffers\u002Fcache:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;656&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;335\nSwap:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1023\u003C\u002Fpre>\u003Cp>1G的内存交换区文件已经创建。\u003Cbr\u002F>\u003C\u002Fp>",{"id":47,"title":48,"uri":48,"author":10,"tags":49,"created_at":53,"view_count":54,"content":55},8,"Go语言新手应该知道的几点",[12,50,51,52],"初学者","包导入","变量命名","2014-08-17 13:29:25",6066,"\u003Cp>1. 变量与函数命名\u003C\u002Fp>  \u003Cp>在go语言中变量与函数的命名是有着特殊的限制的，比如，一个函数functionname与函数FunctionName是有着不同的特点的。函数名第一个字母大写表示此函数可以被其他文件中的代码所调用，而函数名第一个字母小写的函数却只能在当前文件中被调用。初学者往往会发现自己正确的导入了包，但是却无法调用包里面的函数，这时候请看一下自己定义的函数名的首字母的大小写情况。因此，建议Go语言变量命名一律使用骆驼命名法(FunctionName\u002FfunctionName)，而不是蛇形命名法(function_name)。\u003C\u002Fp>  \u003Cp>2. 包导入import\u003C\u002Fp>  \u003Cp>(1) 相对路径    \u003Cbr \u002F>\u003C\u002Fp>  \r\n\u003Cpre class=\"brush:php;\">import \".\u002Fmodel\" \u002F\u002F当前文件同一目录的model目录，但是不建议这种方式来import\u003C\u002Fpre>\r\n\r\n\u003Cp>\u003C\u002Fp>\r\n\r\n\u003Cp>(2) 绝对路径 \r\n  \u003Cbr \u002F>\u003C\u002Fp>\r\n\r\n\u003Cpre class=\"brush:php;\">import \"shorturl\u002Fmodel\" \u002F\u002F加载gopath\u002Fsrc\u002Fshorturl\u002Fmodel模块\u003C\u002Fpre>\r\n\r\n\u003Cp>(3) 点操作 \r\n  \u003Cbr \u002F>\u003C\u002Fp>\r\n\r\n\u003Cpre class=\"brush:php;\">import( . \"fmt\" ) \u003C\u002Fpre>\r\n这个点操作的含义就是这个包导入之后在你调用这个包的函数时，你可以省略前缀的包名，也就是前面你调用的fmt.Println(\"hello world\")可以省略的写成Println(\"hello world\") \r\n\r\n\u003Cp>\u003C\u002Fp>\r\n\r\n\u003Cp>(4) 别名操作 \r\n  \u003Cbr \u002F>别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字 \u003C\u002Fp>\r\n\r\n\u003Cpre class=\"brush:php;\">import( f \"fmt\" ) \u002F\u002F别名操作的话调用包函数时前缀变成了我们的前缀，即f.Println(\"hello world\")\u003C\u002Fpre>\r\n\r\n\u003Cp>(5) _操作\r\n  \u003Cbr \u002F>\u003C\u002Fp>\r\n\r\n\u003Cpre class=\"brush:php;\">import ( \"database\u002Fsql\" \r\n         _ \"github.com\u002Fziutek\u002Fmymysql\u002Fgodrv\" \r\n        )\u003C\u002Fpre>\r\n_操作其实是引入该包，而不直接使用包里面的函数，而是调用了该包里面的init函数",{"title":57,"list":58},"最热文章",[59,65,71,77,83,89,95,101,107,113],{"id":60,"title":61,"uri":62,"author":10,"created_at":63,"view_count":64},28,"docker容器目录与主机目录同步","\u002Farticle\u002Fdocker容器目录与主机目录同步","2014-12-31 12:34:08",15573,{"id":66,"title":67,"uri":68,"author":10,"created_at":69,"view_count":70},21,"beego中使用fis","\u002Farticle\u002Fbeego中使用fis","2014-12-03 10:47:56",9253,{"id":72,"title":73,"uri":74,"author":10,"created_at":75,"view_count":76},20,"用github登录，oauth开发","\u002Farticle\u002F用github登录，oauth开发","2014-11-23 13:33:37",9115,{"id":78,"title":79,"uri":80,"author":10,"created_at":81,"view_count":82},18,"windows服务注册工具nssm","\u002Farticle\u002Fwindows服务注册工具nssm","2014-10-31 21:34:39",9054,{"id":84,"title":85,"uri":86,"author":10,"created_at":87,"view_count":88},49,"用latex排版中文书籍","\u002Farticle\u002F用latex排版中文书籍","2016-10-31 11:12:02",8645,{"id":90,"title":91,"uri":92,"author":10,"created_at":93,"view_count":94},19,"texlive中文书签乱码","\u002Farticle\u002Ftexlive中文书签乱码","2014-11-15 02:54:01",8466,{"id":96,"title":97,"uri":98,"author":10,"created_at":99,"view_count":100},6,"Windows上的软件包管理器chocolatey","\u002Farticle\u002FWindows上的软件包管理器chocolatey","2014-08-10 15:55:22",8012,{"id":102,"title":103,"uri":104,"author":10,"created_at":105,"view_count":106},3,"利用开源和免费资源搭建CI环境","\u002Farticle\u002F利用开源和免费资源搭建CI环境","2014-07-04 16:54:58",7740,{"id":108,"title":109,"uri":110,"author":10,"created_at":111,"view_count":112},16,"php-fpm网站目录权限配置","\u002Farticle\u002Fphp-fpm网站目录权限配置","2014-10-18 01:36:10",7276,{"id":114,"title":115,"uri":116,"author":10,"created_at":117,"view_count":118},27,"sublime text 2插件开发，外部修改文件无法重新加载","\u002Farticle\u002Fsublime text 2插件开发，外部修改文件无法重新加载","2014-12-26 20:24:13",7055,{"title":120,"list":121},"按月归档",[122,126,129,132,135,138,141,145,149,152,155,158,162,165,168,172,175,178,181,185,188,191,194,197,200,203,206,209,212,215,218,221,224,227,230,233,236,239,242,245,249,252,255,258,261,264],{"title":123,"uri":124,"view_count":125},"2026年7月","\u002Farchive\u002F2026\u002F7\u002F1","[1]",{"title":127,"uri":128,"view_count":125},"2026年1月","\u002Farchive\u002F2026\u002F1\u002F1",{"title":130,"uri":131,"view_count":125},"2025年4月","\u002Farchive\u002F2025\u002F4\u002F1",{"title":133,"uri":134,"view_count":125},"2024年6月","\u002Farchive\u002F2024\u002F6\u002F1",{"title":136,"uri":137,"view_count":125},"2024年5月","\u002Farchive\u002F2024\u002F5\u002F1",{"title":139,"uri":140,"view_count":125},"2024年4月","\u002Farchive\u002F2024\u002F4\u002F1",{"title":142,"uri":143,"view_count":144},"2023年12月","\u002Farchive\u002F2023\u002F12\u002F1","[6]",{"title":146,"uri":147,"view_count":148},"2023年11月","\u002Farchive\u002F2023\u002F11\u002F1","[2]",{"title":150,"uri":151,"view_count":125},"2023年10月","\u002Farchive\u002F2023\u002F10\u002F1",{"title":153,"uri":154,"view_count":125},"2023年7月","\u002Farchive\u002F2023\u002F7\u002F1",{"title":156,"uri":157,"view_count":148},"2023年6月","\u002Farchive\u002F2023\u002F6\u002F1",{"title":159,"uri":160,"view_count":161},"2023年5月","\u002Farchive\u002F2023\u002F5\u002F1","[3]",{"title":163,"uri":164,"view_count":148},"2023年4月","\u002Farchive\u002F2023\u002F4\u002F1",{"title":166,"uri":167,"view_count":148},"2023年3月","\u002Farchive\u002F2023\u002F3\u002F1",{"title":169,"uri":170,"view_count":171},"2023年2月","\u002Farchive\u002F2023\u002F2\u002F1","[5]",{"title":173,"uri":174,"view_count":125},"2023年1月","\u002Farchive\u002F2023\u002F1\u002F1",{"title":176,"uri":177,"view_count":148},"2022年12月","\u002Farchive\u002F2022\u002F12\u002F1",{"title":179,"uri":180,"view_count":171},"2022年11月","\u002Farchive\u002F2022\u002F11\u002F1",{"title":182,"uri":183,"view_count":184},"2022年10月","\u002Farchive\u002F2022\u002F10\u002F1","[4]",{"title":186,"uri":187,"view_count":144},"2022年9月","\u002Farchive\u002F2022\u002F9\u002F1",{"title":189,"uri":190,"view_count":171},"2022年8月","\u002Farchive\u002F2022\u002F8\u002F1",{"title":192,"uri":193,"view_count":125},"2019年7月","\u002Farchive\u002F2019\u002F7\u002F1",{"title":195,"uri":196,"view_count":125},"2019年2月","\u002Farchive\u002F2019\u002F2\u002F1",{"title":198,"uri":199,"view_count":125},"2018年9月","\u002Farchive\u002F2018\u002F9\u002F1",{"title":201,"uri":202,"view_count":125},"2018年8月","\u002Farchive\u002F2018\u002F8\u002F1",{"title":204,"uri":205,"view_count":125},"2017年11月","\u002Farchive\u002F2017\u002F11\u002F1",{"title":207,"uri":208,"view_count":125},"2017年8月","\u002Farchive\u002F2017\u002F8\u002F1",{"title":210,"uri":211,"view_count":125},"2017年5月","\u002Farchive\u002F2017\u002F5\u002F1",{"title":213,"uri":214,"view_count":125},"2017年4月","\u002Farchive\u002F2017\u002F4\u002F1",{"title":216,"uri":217,"view_count":125},"2016年11月","\u002Farchive\u002F2016\u002F11\u002F1",{"title":219,"uri":220,"view_count":125},"2016年10月","\u002Farchive\u002F2016\u002F10\u002F1",{"title":222,"uri":223,"view_count":148},"2016年9月","\u002Farchive\u002F2016\u002F9\u002F1",{"title":225,"uri":226,"view_count":125},"2016年8月","\u002Farchive\u002F2016\u002F8\u002F1",{"title":228,"uri":229,"view_count":148},"2016年7月","\u002Farchive\u002F2016\u002F7\u002F1",{"title":231,"uri":232,"view_count":171},"2016年6月","\u002Farchive\u002F2016\u002F6\u002F1",{"title":234,"uri":235,"view_count":125},"2015年12月","\u002Farchive\u002F2015\u002F12\u002F1",{"title":237,"uri":238,"view_count":125},"2015年10月","\u002Farchive\u002F2015\u002F10\u002F1",{"title":240,"uri":241,"view_count":148},"2015年2月","\u002Farchive\u002F2015\u002F2\u002F1",{"title":243,"uri":244,"view_count":125},"2015年1月","\u002Farchive\u002F2015\u002F1\u002F1",{"title":246,"uri":247,"view_count":248},"2014年12月","\u002Farchive\u002F2014\u002F12\u002F1","[8]",{"title":250,"uri":251,"view_count":148},"2014年11月","\u002Farchive\u002F2014\u002F11\u002F1",{"title":253,"uri":254,"view_count":171},"2014年10月","\u002Farchive\u002F2014\u002F10\u002F1",{"title":256,"uri":257,"view_count":161},"2014年9月","\u002Farchive\u002F2014\u002F9\u002F1",{"title":259,"uri":260,"view_count":161},"2014年8月","\u002Farchive\u002F2014\u002F8\u002F1",{"title":262,"uri":263,"view_count":161},"2014年7月","\u002Farchive\u002F2014\u002F7\u002F1",{"title":265,"uri":266,"view_count":148},"2013年9月","\u002Farchive\u002F2013\u002F9\u002F1",5,{"url":269,"title":270},"https:\u002F\u002Fimage.duguying.net\u002F2022\u002F09\u002Fij6x8o4s.webp","老家 摄于2020年秋",1790781594019]