lowprio_linux.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // Copyright (C) 2018 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. // +build !android
  7. package osutil
  8. import (
  9. "os"
  10. "syscall"
  11. "github.com/pkg/errors"
  12. )
  13. const ioprioClassShift = 13
  14. type ioprioClass int
  15. const (
  16. ioprioClassRT ioprioClass = iota + 1
  17. ioprioClassBE
  18. ioprioClassIdle
  19. )
  20. const (
  21. ioprioWhoProcess = iota + 1
  22. ioprioWhoPGRP
  23. ioprioWhoUser
  24. )
  25. func ioprioSet(class ioprioClass, value int) error {
  26. res, _, err := syscall.Syscall(syscall.SYS_IOPRIO_SET,
  27. uintptr(ioprioWhoProcess), 0,
  28. uintptr(class)<<ioprioClassShift|uintptr(value))
  29. if res == 0 {
  30. return nil
  31. }
  32. return err
  33. }
  34. // SetLowPriority lowers the process CPU scheduling priority, and possibly
  35. // I/O priority depending on the platform and OS.
  36. func SetLowPriority() error {
  37. // Process zero is "self", niceness value 9 is something between 0
  38. // (default) and 19 (worst priority). But then, this is Linux, so of
  39. // course we get this to take care of as well:
  40. //
  41. // "C library/kernel differences
  42. //
  43. // Within the kernel, nice values are actually represented using the
  44. // range 40..1 (since negative numbers are error codes) and these are
  45. // the values employed by the setpriority() and getpriority() system
  46. // calls. The glibc wrapper functions for these system calls handle the
  47. // translations between the user-land and kernel representations of the
  48. // nice value according to the formula unice = 20 - knice. (Thus, the
  49. // kernel's 40..1 range corresponds to the range -20..19 as seen by user
  50. // space.)"
  51. const (
  52. pidSelf = 0
  53. wantNiceLevel = 20 - 9
  54. )
  55. // Remember Linux kernel nice levels are upside down.
  56. if cur, err := syscall.Getpriority(syscall.PRIO_PROCESS, 0); err == nil && cur <= wantNiceLevel {
  57. // We're done here.
  58. return nil
  59. }
  60. // Move ourselves to a new process group so that we can use the process
  61. // group variants of Setpriority etc to affect all of our threads in one
  62. // go. If this fails, bail, so that we don't affect things we shouldn't.
  63. // If we are already the leader of our own process group, do nothing.
  64. //
  65. // Oh and this is because Linux doesn't follow the POSIX threading model
  66. // where setting the niceness of the process would actually set the
  67. // niceness of the process, instead it just affects the current thread
  68. // so we need this workaround...
  69. if pgid, err := syscall.Getpgid(pidSelf); err != nil {
  70. // This error really shouldn't happen
  71. return errors.Wrap(err, "get process group")
  72. } else if pgid != os.Getpid() {
  73. // We are not process group leader. Elevate!
  74. if err := syscall.Setpgid(pidSelf, 0); err != nil {
  75. return errors.Wrap(err, "set process group")
  76. }
  77. }
  78. if err := syscall.Setpriority(syscall.PRIO_PGRP, pidSelf, wantNiceLevel); err != nil {
  79. return errors.Wrap(err, "set niceness")
  80. }
  81. // Best effort, somewhere to the end of the scale (0 through 7 being the
  82. // range).
  83. err := ioprioSet(ioprioClassBE, 5)
  84. return errors.Wrap(err, "set I/O priority") // wraps nil as nil
  85. }