我相信很多朋友会认为启动就是双击一下Syncthing程序图标,随后就启动完毕了!如果这样认为,对,也不对!对,是因为的确是这样操作,启动了Syncthing;不对是因为在调试Syncthing启动过程时发现很奇怪!有哪些奇怪的地方呢?

一,Syncthing启动的奇怪问题。

1,启动时候总是运行"monitorMain(options)"函数,另外一个函数"syncthingMain(options)"从来没有运行过?

2,启动log前缀好几种,第三种log前缀不知道什么意思?也不知道包含这些前缀的log信息从哪里来的?

3,每次Syncthing启动完毕,通过任务管理器查看,都有两个正在运行的Syncthing,怎么同时有两个Syncthing进程?

下面是上述问题的各个截图:

问题1截图:

问题2截图:

问题3截图:

 二,分析和解决Syncthing启动的奇怪问题。

我用Gogland调试时候发现,Syncthing启动总是进入"monitorMain(options)"函数,另外一个函数"syncthingMain(options)"从来没有进入过,难道另外一个函数彻底没用?没用还保留干什么?先不管这些了,我先找那些log前缀在哪里吧?

(1),[monitor]前缀,最终在monitor.go文件中的"monitorMain(options)"函数中发现,位于39行处。具体代码:

  1. l.SetPrefix("[monitor] ")

(2),[start]前缀,没能在"monitorMain(options)"函数中找到!

(3),[NTUJ4]前缀,也没能在"monitorMain(options)"函数中找到!

难道这两个前缀的log信息,不是"monitorMain(options)"函数输出的?如果不是那会是哪个函数呢?经过调试发现,在"monitorMain(options)"函数中输出的最后一条log是:

  1. [monitor] 17:34:20 INFO: Starting syncthing

继续调试,最终也没有发现剩余前缀log在哪里产生?但是确实千真万确输出这些前缀的log了呀!!!  

先不管上面问题了,我们还是去看看"syncthingMain(options)"函数吧,它真的就是一个多余的函数,彻底没用?为了测试这个函数是否有用,我在这个函数最开始代码处写入:

  1. l.Infoln("这是我的log信息,是否会出现呢?")

Syncthing默认log全部是英文,我写的log信息是中文,如果这个函数被调用,那么我一眼就能看出来我写的log信息!!立即运行Syncthing,奇迹出现了,居然log信息中有我写的测试log信息!!!

"syncthingMain(options)"函数并不是没有用啊?我写的中文测试log居然在运行时输出的log中了!!其它log信息都在我写的中文log之后,难道剩余log信息都是它输出的?在这个函数中查找代码:

  1. l.SetPrefix

最终发现两处:

(1),

  1. l.SetPrefix("[start] ")

这个前缀不就是上面我没能找到的一个吗?

(2),

  1. l.SetPrefix(fmt.Sprintf("[%s] ", myID.String()[:5]))

难道这是剩余的那个log前缀?从这段代码理解,应该是取当前设备ID的前5个字符作为log前缀!  

为了验证我的判断,我把这两个log前缀都修改了

  1. l.SetPrefix("[syncthingMain][start] ")
  2. l.SetPrefix("[syncthingMain] ")

修改完毕后,运行一下Syncthing,log前缀确实全变成我修改的了,我的判断完全正确!!!

经过测试发现,包含"[start]"前缀的log,只有在Syncthing第一次运行时候出现,系统当中从来没有运行过Syncthing才出现,如果运行过,删除先前的配置文件后也会出现,因为那样再次运行Syncthing,等于从来没有在此电脑中运行过Syncthing!

到现在为止,我也不知道"syncthingMain(options)"函数是怎么运行起来的,但是可以肯定它一定是运行了,从它输出的log来判断,它应该是负责启动之后的Syncthing各项服务!!!偶然通过任务管理器发现,Syncthing运行后,居然有两个正着运行的Syncthing,怎么会有两个进程?一个使用内存小;一个使用内存稍大(和前面的进程使用内存比较)!先不管了,我先终止一下这两个进程吧,继续调试Syncthing!奇怪事情又发生了,如果终止内存稍大的Syncthing,立即会再次运行和它一样的进程,就是杀不死!!!如果我终止使用内存小的Syncthing进程,另外一个内存稍大的进程依然存在,而且Syncthing的各项功能均运行正常!!!难道这两个进程一个是监控进程;一个是服务进程?

(1),监控进程:使用内存小,负责监控服务进程运行状况。

(2),服务进程:使用内存稍大,为用户提供Syncthing各项服务。

我们还是认真来看看源代码吧!

首先看monitor.go文件中的"monitorMain(options)"函数,经过调试我最终确定,是这个函数启动和监控Syncthing的服务进程!!!

(1),启动Syncthing的服务进程:在"monitorMain(options)"函数中,有一个for的死循环,在这个for代码前面部分,有一段是启动Syncthing的服务进程:

  1. cmd := exec.Command(args[0], args[1:]...)
  2.  
  3. stderr, err := cmd.StderrPipe()
  4. if err != nil {
  5. l.Fatalln("stderr:", err)
  6. }
  7.  
  8. stdout, err := cmd.StdoutPipe()
  9. if err != nil {
  10. l.Fatalln("stdout:", err)
  11. }
  12.  
  13. l.Infoln("Starting syncthing")
  14. err = cmd.Start()
  15. if err != nil {
  16. l.Fatalln(err)
  17. }

(2),监控Syncthing的服务进程:上面那个for死循环,如果毫无节制,那系统肯定受不了啊?其实它是隔一段时间才执行一次的!看这个for循环最后一行的代码!

  1. time.Sleep(1 * time.Second)

这行代码是休眠指定的时间,目前是一秒钟!!难怪Syncthing的服务进程被杀死,马上就会有另外一个Syncthing的服务进程运行起来,这样的设计,我一辈子也杀不死这个Syncthing的服务进程啊!!!!通过调整这个休眠时间测试,最终确定,这个地方就是控制监控Syncthing的服务进程的间隔时间代码!!!!  

到此,我可以肯定了,Syncthing启动时候,会先后启动两个进程:

(1),监控进程:

  • 最早运行。
  • 调用"monitorMain(options)"函数。
  • 使用内存小。
  • 负责监控服务进程运行状况。

(2),服务进程:使用内存稍大,为用户提供Syncthing各项服务。

  • 被监控进程启动。
  • 调用"syncthingMain(options)"函数。
  • 使用内存稍大。
  • 为用户提供Syncthing的各项服务。  

继续调试"syncthingMain(options)"函数,证实我的判断是正确的,到此Syncthing的启动就算搞明白了!!

以后再调试Syncthing,不需要按照现在逻辑运行了,注释掉main.go中main函数末尾代码:

  1. if innerProcess || options.noRestart {
  2. syncthingMain(options)
  3. } else {
  4. monitorMain(options)
  5. }

把上面代码替换成:

  1. syncthingMain(options)

这样我们就可以去掉监控进程,直接调试Syncthing服务进程了!!

最后还有一个问题,为什么Syncthing的监控进程里显示Syncthing服务进程输出的log?这是用GO语言的channel来完成的!!!

三,我调试时候,加入的注释的代码。

注释方式:采用TODO代码方式,这样便于在Gogland的TODO里面立即看到我自己加入的注释或修改代码!

注释格式:

TODO:[sunylat][begin或end]注释或修改描述信息
[begin或end]为可选项,表示需要相互协作才能工作代码的开始和结束处。
这个TODO可以用"//"和"/**/"两种注释方式包含。

(1),monitor.go文件中的"monitorMain(options)"函数:

  1. /*
  2. TODO:[sunylat] 运行和监控提供真正服务syncthing的入口函数
  3. */
  4. func monitorMain(runtimeOptions RuntimeOptions) {
  5. os.Setenv("STMONITORED", "yes")
  6. l.SetPrefix("[monitor] ")
  7.  
  8. var dst io.Writer = os.Stdout
  9.  
  10. logFile := runtimeOptions.logFile
  11. if logFile != "-" {
  12. var fileDst io.Writer = newAutoclosedFile(logFile, logFileAutoCloseDelay, logFileMaxOpenTime)
  13.  
  14. if runtime.GOOS == "windows" {
  15. // Translate line breaks to Windows standard
  16. fileDst = osutil.ReplacingWriter{
  17. Writer: fileDst,
  18. From: '\n',
  19. To: []byte{'\r', '\n'},
  20. }
  21. }
  22.  
  23. // Log to both stdout and file.
  24. dst = io.MultiWriter(dst, fileDst)
  25.  
  26. l.Infof(`Log output saved to file "%s"`, logFile)
  27. }
  28.  
  29. args := os.Args
  30. var restarts [countRestarts]time.Time
  31.  
  32. stopSign := make(chan os.Signal, 1)
  33. sigTerm := syscall.Signal(15)
  34. signal.Notify(stopSign, os.Interrupt, sigTerm)
  35. restartSign := make(chan os.Signal, 1)
  36. sigHup := syscall.Signal(1)
  37. signal.Notify(restartSign, sigHup)
  38.  
  39. /*
  40. TODO:[sunylat] 这是个无限循环,负责监控提供服务syncthing运行状况,同时负责处理程序的退出等事件处理,
  41. 监控间隔时间由这个for循环最后面的休眠时间决定
  42. */
  43. for {
  44. if t := time.Since(restarts[0]); t < loopThreshold {
  45. l.Warnf("%d restarts in %v; not retrying further", countRestarts, t)
  46. os.Exit(exitError)
  47. }
  48.  
  49. copy(restarts[0:], restarts[1:])
  50. restarts[len(restarts)-1] = time.Now()
  51.  
  52. //TODO:[sunylat] [begin] 启动提供服务的syncthing ----------------------
  53. cmd := exec.Command(args[0], args[1:]...)
  54.  
  55. stderr, err := cmd.StderrPipe()
  56. if err != nil {
  57. l.Fatalln("stderr:", err)
  58. }
  59.  
  60. stdout, err := cmd.StdoutPipe()
  61. if err != nil {
  62. l.Fatalln("stdout:", err)
  63. }
  64.  
  65. l.Infoln("Starting syncthing")
  66.  
  67. //TODO:[sunylat] 这条语句真正执行启动提供服务的syncthing
  68. err = cmd.Start()
  69. if err != nil {
  70. l.Fatalln(err)
  71. }
  72. //TODO:[sunylat] [end] 启动提供服务的syncthing --------------------------
  73.  
  74. // Let the next child process know that this is not the first time
  75. // it's starting up.
  76. os.Setenv("STRESTART", "yes")
  77.  
  78. stdoutMut.Lock()
  79. stdoutFirstLines = make([]string, 0, 10)
  80. stdoutLastLines = make([]string, 0, 50)
  81. stdoutMut.Unlock()
  82.  
  83. wg := sync.NewWaitGroup()
  84.  
  85. wg.Add(1)
  86. go func() {
  87. copyStderr(stderr, dst)
  88. wg.Done()
  89. }()
  90.  
  91. wg.Add(1)
  92. go func() {
  93. copyStdout(stdout, dst)
  94. wg.Done()
  95. }()
  96.  
  97. exit := make(chan error)
  98.  
  99. go func() {
  100. wg.Wait()
  101. exit <- cmd.Wait()
  102. }()
  103.  
  104. select {
  105. case s := <-stopSign:
  106. l.Infof("Signal %d received; exiting", s)
  107. cmd.Process.Kill()
  108. <-exit
  109. return
  110.  
  111. case s := <-restartSign:
  112. l.Infof("Signal %d received; restarting", s)
  113. cmd.Process.Signal(sigHup)
  114. err = <-exit
  115.  
  116. case err = <-exit:
  117. if err == nil {
  118. // Successful exit indicates an intentional shutdown
  119. return
  120. } else if exiterr, ok := err.(*exec.ExitError); ok {
  121. if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
  122. switch status.ExitStatus() {
  123. case exitUpgrading:
  124. // Restart the monitor process to release the .old
  125. // binary as part of the upgrade process.
  126. l.Infoln("Restarting monitor...")
  127. os.Setenv("STNORESTART", "")
  128.  
  129. if err = restartMonitor(args); err != nil {
  130. l.Warnln("Restart:", err)
  131. }
  132.  
  133. return
  134. }
  135. }
  136. }
  137. }
  138.  
  139. l.Infoln("Syncthing exited:", err)
  140.  
  141. //TODO:[sunylat] 这个休眠时间决定了监控的间隔时间!
  142. time.Sleep(1 * time.Second)
  143. }
  144. }

(2),main.go中的"syncthingMain(options)"函数:

  1. /*
  2. TODO:[sunylat] 提供真正服务的syncthing入口函数
  3. */
  4. func syncthingMain(runtimeOptions RuntimeOptions) {
  5.  
  6. //l.Infoln("[syncthingMain] 这是我的测试信息!")
  7.  
  8. setupSignalHandling()
  9.  
  10. // Create a main service manager. We'll add things to this as we go along.
  11. // We want any logging it does to go through our log system.
  12.  
  13. //TODO:[sunylat] [begin] supervisor创建 ----------------------
  14. mainService := suture.New("main", suture.Spec{
  15. Log: func(line string) {
  16. l.Debugln(line)
  17. },
  18. })
  19. mainService.ServeBackground()
  20.  
  21. // Set a log prefix similar to the ID we will have later on, or early log
  22. // lines look ugly.
  23. //l.SetPrefix("[start] ")
  24.  
  25. //TODO:[sunylat] 我修改的启动时候显示的log头,加入了[syncthingMain]
  26. l.SetPrefix("[syncthingMain][start] ")
  27.  
  28. if runtimeOptions.auditEnabled {
  29. startAuditing(mainService, runtimeOptions.auditFile)
  30. }
  31.  
  32. if runtimeOptions.verbose {
  33. mainService.Add(newVerboseService())
  34. }
  35.  
  36. errors := logger.NewRecorder(l, logger.LevelWarn, maxSystemErrors, 0)
  37. systemLog := logger.NewRecorder(l, logger.LevelDebug, maxSystemLog, initialSystemLog)
  38.  
  39. // Event subscription for the API; must start early to catch the early
  40. // events. The LocalChangeDetected event might overwhelm the event
  41. // receiver in some situations so we will not subscribe to it here.
  42. apiSub := events.NewBufferedSubscription(events.Default.Subscribe(events.AllEvents&^events.LocalChangeDetected&^events.RemoteChangeDetected), 1000)
  43. diskSub := events.NewBufferedSubscription(events.Default.Subscribe(events.LocalChangeDetected|events.RemoteChangeDetected), 1000)
  44.  
  45. if len(os.Getenv("GOMAXPROCS")) == 0 {
  46. runtime.GOMAXPROCS(runtime.NumCPU())
  47. }
  48.  
  49. // Attempt to increase the limit on number of open files to the maximum
  50. // allowed, in case we have many peers. We don't really care enough to
  51. // report the error if there is one.
  52. osutil.MaximizeOpenFileLimit()
  53.  
  54. // Ensure that that we have a certificate and key.
  55. cert, err := tls.LoadX509KeyPair(locations[locCertFile], locations[locKeyFile])
  56. if err != nil {
  57. l.Infof("Generating ECDSA key and certificate for %s...", tlsDefaultCommonName)
  58. cert, err = tlsutil.NewCertificate(locations[locCertFile], locations[locKeyFile], tlsDefaultCommonName, bepRSABits)
  59. if err != nil {
  60. l.Fatalln(err)
  61. }
  62. }
  63.  
  64. myID = protocol.NewDeviceID(cert.Certificate[0])
  65.  
  66. //这里把调试信息头设置为当前设备ID的一部分
  67. //l.SetPrefix(fmt.Sprintf("[%s] ", myID.String()[:5]))
  68.  
  69. //TODO:[sunylat] 修改log头为[syncthingMain]
  70. l.SetPrefix("[syncthingMain] ")
  71.  
  72. //打印syncthing长版本
  73. l.Infoln(LongVersion)
  74. l.Infoln("My ID:", myID)
  75.  
  76. sha256.SelectAlgo()
  77. sha256.Report()
  78. perfWithWeakHash := cpuBench(3, 150*time.Millisecond, true)
  79. l.Infof("Hashing performance with weak hash is %.02f MB/s", perfWithWeakHash)
  80. perfWithoutWeakHash := cpuBench(3, 150*time.Millisecond, false)
  81. l.Infof("Hashing performance without weak hash is %.02f MB/s", perfWithoutWeakHash)
  82.  
  83. // Emit the Starting event, now that we know who we are.
  84.  
  85. events.Default.Log(events.Starting, map[string]string{
  86. "home": baseDirs["config"],
  87. "myID": myID.String(),
  88. })
  89.  
  90. cfg := loadOrCreateConfig()
  91.  
  92. if err := checkShortIDs(cfg); err != nil {
  93. l.Fatalln("Short device IDs are in conflict. Unlucky!\n Regenerate the device ID of one of the following:\n ", err)
  94. }
  95.  
  96. if len(runtimeOptions.profiler) > 0 {
  97. go func() {
  98. l.Debugln("Starting profiler on", runtimeOptions.profiler)
  99. runtime.SetBlockProfileRate(1)
  100. err := http.ListenAndServe(runtimeOptions.profiler, nil)
  101. if err != nil {
  102. l.Fatalln(err)
  103. }
  104. }()
  105. }
  106.  
  107. // The TLS configuration is used for both the listening socket and outgoing
  108. // connections.
  109.  
  110. tlsCfg := &tls.Config{
  111. Certificates: []tls.Certificate{cert},
  112. NextProtos: []string{bepProtocolName},
  113. ClientAuth: tls.RequestClientCert,
  114. SessionTicketsDisabled: true,
  115. InsecureSkipVerify: true,
  116. MinVersion: tls.VersionTLS12,
  117. CipherSuites: []uint16{
  118. 0xCCA8, // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, Go 1.8
  119. 0xCCA9, // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, Go 1.8
  120. tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
  121. tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
  122. tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  123. tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  124. tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
  125. tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
  126. tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
  127. tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
  128. },
  129. }
  130.  
  131. opts := cfg.Options()
  132.  
  133. if !opts.SymlinksEnabled {
  134. symlinks.Supported = false
  135. }
  136.  
  137. if opts.WeakHashSelectionMethod == config.WeakHashAuto {
  138. if perfWithoutWeakHash*0.8 > perfWithWeakHash {
  139. l.Infof("Weak hash disabled, as it has an unacceptable performance impact.")
  140. weakhash.Enabled = false
  141. } else {
  142. l.Infof("Weak hash enabled, as it has an acceptable performance impact.")
  143. weakhash.Enabled = true
  144. }
  145. } else if opts.WeakHashSelectionMethod == config.WeakHashNever {
  146. l.Infof("Disabling weak hash")
  147. weakhash.Enabled = false
  148. } else if opts.WeakHashSelectionMethod == config.WeakHashAlways {
  149. l.Infof("Enabling weak hash")
  150. weakhash.Enabled = true
  151. }
  152.  
  153. if (opts.MaxRecvKbps > 0 || opts.MaxSendKbps > 0) && !opts.LimitBandwidthInLan {
  154. lans, _ = osutil.GetLans()
  155. for _, lan := range opts.AlwaysLocalNets {
  156. _, ipnet, err := net.ParseCIDR(lan)
  157. if err != nil {
  158. l.Infoln("Network", lan, "is malformed:", err)
  159. continue
  160. }
  161. lans = append(lans, ipnet)
  162. }
  163.  
  164. networks := make([]string, len(lans))
  165. for i, lan := range lans {
  166. networks[i] = lan.String()
  167. }
  168. l.Infoln("Local networks:", strings.Join(networks, ", "))
  169. }
  170.  
  171. /*
  172. TODO:[sunylat] 打开本地数据库操作
  173. */
  174. //TODO:[sunylat] 获取本地数据库全路径名称
  175. dbFile := locations[locDatabase]
  176. //TODO:[sunylat] 打开本地数据库
  177. ldb, err := db.Open(dbFile)
  178.  
  179. if err != nil {
  180. l.Fatalln("Cannot open database:", err, "- Is another copy of Syncthing already running?")
  181. }
  182.  
  183. if runtimeOptions.resetDeltaIdxs {
  184. l.Infoln("Reinitializing delta index IDs")
  185. ldb.DropDeltaIndexIDs()
  186. }
  187.  
  188. protectedFiles := []string{
  189. locations[locDatabase],
  190. locations[locConfigFile],
  191. locations[locCertFile],
  192. locations[locKeyFile],
  193. }
  194.  
  195. // Remove database entries for folders that no longer exist in the config
  196. folders := cfg.Folders()
  197. for _, folder := range ldb.ListFolders() {
  198. if _, ok := folders[folder]; !ok {
  199. l.Infof("Cleaning data for dropped folder %q", folder)
  200. db.DropFolder(ldb, folder)
  201. }
  202. }
  203.  
  204. if cfg.RawCopy().OriginalVersion == 15 {
  205. // The config version 15->16 migration is about handling ignores and
  206. // delta indexes and requires that we drop existing indexes that
  207. // have been incorrectly ignore filtered.
  208. ldb.DropDeltaIndexIDs()
  209. }
  210.  
  211. //TODO:[sunylat] 创建模型
  212. m := model.NewModel(cfg, myID, myDeviceName(cfg), "syncthing", Version, ldb, protectedFiles)
  213.  
  214. if t := os.Getenv("STDEADLOCKTIMEOUT"); len(t) > 0 {
  215. it, err := strconv.Atoi(t)
  216. if err == nil {
  217. m.StartDeadlockDetector(time.Duration(it) * time.Second)
  218. }
  219. } else if !IsRelease || IsBeta {
  220. m.StartDeadlockDetector(20 * time.Minute)
  221. }
  222.  
  223. if runtimeOptions.unpaused {
  224. setPauseState(cfg, false)
  225. } else if runtimeOptions.paused {
  226. setPauseState(cfg, true)
  227. }
  228.  
  229. // Add and start folders
  230. for _, folderCfg := range cfg.Folders() {
  231. if folderCfg.Paused {
  232. continue
  233. }
  234. m.AddFolder(folderCfg)
  235. m.StartFolder(folderCfg.ID)
  236. }
  237.  
  238. mainService.Add(m)
  239.  
  240. // Start discovery
  241.  
  242. cachedDiscovery := discover.NewCachingMux()
  243. mainService.Add(cachedDiscovery)
  244.  
  245. // Start connection management
  246.  
  247. connectionsService := connections.NewService(cfg, myID, m, tlsCfg, cachedDiscovery, bepProtocolName, tlsDefaultCommonName, lans)
  248. mainService.Add(connectionsService)
  249.  
  250. if cfg.Options().GlobalAnnEnabled {
  251. for _, srv := range cfg.GlobalDiscoveryServers() {
  252. l.Infoln("Using discovery server", srv)
  253. gd, err := discover.NewGlobal(srv, cert, connectionsService)
  254. if err != nil {
  255. l.Warnln("Global discovery:", err)
  256. continue
  257. }
  258.  
  259. // Each global discovery server gets its results cached for five
  260. // minutes, and is not asked again for a minute when it's returned
  261. // unsuccessfully.
  262. cachedDiscovery.Add(gd, 5*time.Minute, time.Minute, globalDiscoveryPriority)
  263. }
  264. }
  265.  
  266. if cfg.Options().LocalAnnEnabled {
  267. // v4 broadcasts
  268. bcd, err := discover.NewLocal(myID, fmt.Sprintf(":%d", cfg.Options().LocalAnnPort), connectionsService)
  269. if err != nil {
  270. l.Warnln("IPv4 local discovery:", err)
  271. } else {
  272. cachedDiscovery.Add(bcd, 0, 0, ipv4LocalDiscoveryPriority)
  273. }
  274. // v6 multicasts
  275. mcd, err := discover.NewLocal(myID, cfg.Options().LocalAnnMCAddr, connectionsService)
  276. if err != nil {
  277. l.Warnln("IPv6 local discovery:", err)
  278. } else {
  279. cachedDiscovery.Add(mcd, 0, 0, ipv6LocalDiscoveryPriority)
  280. }
  281. }
  282.  
  283. // GUI
  284.  
  285. setupGUI(mainService, cfg, m, apiSub, diskSub, cachedDiscovery, connectionsService, errors, systemLog, runtimeOptions)
  286.  
  287. if runtimeOptions.cpuProfile {
  288. f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
  289. if err != nil {
  290. log.Fatal(err)
  291. }
  292. pprof.StartCPUProfile(f)
  293. }
  294.  
  295. for _, device := range cfg.Devices() {
  296. if len(device.Name) > 0 {
  297. l.Infof("Device %s is %q at %v", device.DeviceID, device.Name, device.Addresses)
  298. }
  299. }
  300.  
  301. // Candidate builds always run with usage reporting.
  302.  
  303. if IsCandidate {
  304. l.Infoln("Anonymous usage reporting is always enabled for candidate releases.")
  305. opts.URAccepted = usageReportVersion
  306. // Unique ID will be set and config saved below if necessary.
  307. }
  308.  
  309. if opts.URAccepted > 0 && opts.URAccepted < usageReportVersion {
  310. l.Infoln("Anonymous usage report has changed; revoking acceptance")
  311. opts.URAccepted = 0
  312. opts.URUniqueID = ""
  313. cfg.SetOptions(opts)
  314. }
  315.  
  316. if opts.URAccepted >= usageReportVersion && opts.URUniqueID == "" {
  317. // Generate and save a new unique ID if it is missing.
  318. opts.URUniqueID = rand.String(8)
  319. cfg.SetOptions(opts)
  320. cfg.Save()
  321. }
  322.  
  323. // The usageReportingManager registers itself to listen to configuration
  324. // changes, and there's nothing more we need to tell it from the outside.
  325. // Hence we don't keep the returned pointer.
  326. newUsageReportingManager(cfg, m)
  327.  
  328. if opts.RestartOnWakeup {
  329. go standbyMonitor()
  330. }
  331.  
  332. // Candidate builds should auto upgrade. Make sure the option is set,
  333. // unless we are in a build where it's disabled or the STNOUPGRADE
  334. // environment variable is set.
  335.  
  336. if IsCandidate && !upgrade.DisabledByCompilation && !noUpgradeFromEnv {
  337. l.Infoln("Automatic upgrade is always enabled for candidate releases.")
  338. if opts.AutoUpgradeIntervalH == 0 || opts.AutoUpgradeIntervalH > 24 {
  339. opts.AutoUpgradeIntervalH = 12
  340. }
  341. // We don't tweak the user's choice of upgrading to pre-releases or
  342. // not, as otherwise they cannot step off the candidate channel.
  343. }
  344.  
  345. if opts.AutoUpgradeIntervalH > 0 {
  346. if noUpgradeFromEnv {
  347. l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.")
  348. } else {
  349. go autoUpgrade(cfg)
  350. }
  351. }
  352.  
  353. events.Default.Log(events.StartupComplete, map[string]string{
  354. "myID": myID.String(),
  355. })
  356.  
  357. cleanConfigDirectory()
  358.  
  359. code := <-stop
  360.  
  361. //TODO:[sunylat] [end] supervisor结束 ----------------------
  362. mainService.Stop()
  363.  
  364. l.Infoln("Exiting")
  365.  
  366. if runtimeOptions.cpuProfile {
  367. pprof.StopCPUProfile()
  368. }
  369.  
  370. os.Exit(code)
  371. }

提示:在"syncthingMain(options)"函数使用了第三方的库,使用的代码:

  1. mainService := suture.New("main", suture.Spec{
  2. Log: func(line string) {
  3. l.Debugln(line)
  4. },
  5. })
  6. mainService.ServeBackground()

第三方库的官方地址:

https://github.com/thejerf/suture

这是用GO语言写的Erlang语言中的supervisor trees功能,应该是很有用,值得我们关注!

Erlang supervisor trees 中文参考:

http://diaocow.iteye.com/blog/1762895

  

 

Syncthing源码解析 - 启动过程的更多相关文章

  1. 分布式事务_02_2PC框架raincat源码解析-启动过程

    一.前言 上一节已经将raincat demo工程运行起来了,这一节来分析下raincat启动过程的源码 主要包括: 事务协调者启动过程 事务参与者启动过程 二.协调者启动过程 主要就是在启动类中通过 ...

  2. Symfony2源码分析——启动过程2

    文章地址:http://www.hcoding.com/?p=46 上一篇分析Symfony2框架源码,探究Symfony2如何完成一个请求的前半部分,前半部分可以理解为Symfony2框架为处理请求 ...

  3. mysql源码分析-启动过程

    mysql源码分析-启动过程 概要 # sql/mysqld.cc, 不包含psi的初始化过程 mysqld_main: // 加载my.cnf和my.cnf.d,还有命令行参数 if (load_d ...

  4. 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(下)

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/12/es-code03/ 前提 上篇文章写完了 ES 流程启动的一部分,main 方法都入 ...

  5. 渣渣菜鸡的 ElasticSearch 源码解析 —— 启动流程(上)

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/08/11/es-code02/ 前提 上篇文章写了 ElasticSearch 源码解析 -- ...

  6. quartz2.x源码分析——启动过程

    title: quartz2.x源码分析--启动过程 date: 2017-04-13 14:59:01 categories: quartz tags: [quartz, 源码分析] --- 先简单 ...

  7. Symfony2源码分析——启动过程1

    本文通过阅读分析Symfony2的源码,了解Symfony2启动过程中完成哪些工作,从阅读源码了解Symfony2框架. Symfony2的核心本质是把Request转换成Response的一个过程. ...

  8. Syncthing源码解析

    Gogland编译Syncthing 源码目录说明 Syncthing启动过程分析 在Gogland中对Syncthing的各个模块进行调试 第三方库

  9. Spring MVC源码(一) ----- 启动过程与组件初始化

    SpringMVC作为MVC框架近年来被广泛地使用,其与Mybatis和Spring的组合,也成为许多公司开发web的套装.SpringMVC继承了Spring的优点,对业务代码的非侵入性,配置的便捷 ...

随机推荐

  1. 如何用git命令行上传本地代码到github

    注意:安装的前提条件是配置好Git的相关环境或者安装好git.exe,此处不再重点提及 上传的步骤: 本文采用git 命令界面进行操作,先执行以下两个命令,配置用户名和email[设置用戶名和e-ma ...

  2. leetcode111

    /** * Definition for a binary tree node. * public class TreeNode { * public int val; * public TreeNo ...

  3. x264改变输出分辨率的算法<转>

    x264改变输出分辨率的算法 在某些应用场景下,x264的输入视频分辨率与接收端输出的视频分辨率不同.例如编码端摄像头采集到的YUV数据为1280x720,而接收端视频显示窗口为640x480.对于这 ...

  4. ubuntu 安装google输入法

    第五步:通常情况下,IBus图标(一个小键盘)会出现在桌面右上角的任务栏中.有时候这个图标会自行消失,可使用以下命令,找回消失的IBus图标: ibus-daemon -drx   不建议用googl ...

  5. Zabbix 监控 Cisco ASA5525 流量

    简介: Zabbix 监控 Cisco ASA5525 网络接口流量 一.Zabbix 支持 SNMP.Cisco 开启 SNMP 二.测试 shell > snmpwalk -v 2c -c ...

  6. Redis 授权操作

    [Redis 授权操作] AUTH password 通过设置配置文件中 requirepass 项的值(使用命令 CONFIG SET requirepass password ),可以使用密码来保 ...

  7. md5,原理待续

    以前项目中copy出来的 import java.security.MessageDigest; public class MD5Util { /** * @todo MD5加码 生成32位md5码 ...

  8. Laravel 文件上传失败的问题 error 7

    一个站点上传文件失败 error为7  UPLOAD_ERR_CANT_WRITE 临时文件上传不上  $_FILE打出来 Array(    [file] => Array        (  ...

  9. 基于Web Service的客户端框架搭建四:终结篇

    前言 这是这个系列的终结篇,前面3个博客介绍了一下内容: 1.使用Http Post方式调用Web Service 2.客户端框架之数据转换层 3.客户端框架之代理层 框架结构 框架是基于C#的,在V ...

  10. windows平台下spark-shell配置

    一.下载安装spark,http://spark.apache.org/,选择合适版本后下载到本地,解压,bin目录下spark-shell文件就是spark命令行交互的入口. 二.下载安装windo ...