sysconf_linux.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. // Copyright 2018 Tobias Klauser. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package sysconf
  5. import (
  6. "bufio"
  7. "os"
  8. "runtime"
  9. "strconv"
  10. "strings"
  11. "github.com/tklauser/numcpus"
  12. "golang.org/x/sys/unix"
  13. )
  14. const (
  15. // CLK_TCK is a constant on Linux for all architectures except alpha and ia64.
  16. // See e.g.
  17. // https://git.musl-libc.org/cgit/musl/tree/src/conf/sysconf.c#n30
  18. // https://github.com/containerd/cgroups/pull/12
  19. // https://lore.kernel.org/lkml/agtlq6$iht$1@penguin.transmeta.com/
  20. _SYSTEM_CLK_TCK = 100
  21. )
  22. func readProcFsInt64(path string, fallback int64) int64 {
  23. data, err := os.ReadFile(path)
  24. if err != nil {
  25. return fallback
  26. }
  27. i, err := strconv.ParseInt(strings.TrimRight(string(data), "\n"), 0, 64)
  28. if err != nil {
  29. return fallback
  30. }
  31. return i
  32. }
  33. // getMemPages computes mem*unit/os.Getpagesize(), but avoids overflowing int64.
  34. func getMemPages(mem uint64, unit uint32) int64 {
  35. pageSize := os.Getpagesize()
  36. for unit > 1 && pageSize > 1 {
  37. unit >>= 1
  38. pageSize >>= 1
  39. }
  40. mem *= uint64(unit)
  41. for pageSize > 1 {
  42. pageSize >>= 1
  43. mem >>= 1
  44. }
  45. return int64(mem)
  46. }
  47. func getPhysPages() int64 {
  48. var si unix.Sysinfo_t
  49. err := unix.Sysinfo(&si)
  50. if err != nil {
  51. return -1
  52. }
  53. return getMemPages(uint64(si.Totalram), si.Unit)
  54. }
  55. func getAvPhysPages() int64 {
  56. var si unix.Sysinfo_t
  57. err := unix.Sysinfo(&si)
  58. if err != nil {
  59. return -1
  60. }
  61. return getMemPages(uint64(si.Freeram), si.Unit)
  62. }
  63. func getNprocsSysfs() (int64, error) {
  64. n, err := numcpus.GetOnline()
  65. return int64(n), err
  66. }
  67. func getNprocsProcStat() (int64, error) {
  68. f, err := os.Open("/proc/stat")
  69. if err != nil {
  70. return -1, err
  71. }
  72. defer f.Close()
  73. count := int64(0)
  74. s := bufio.NewScanner(f)
  75. for s.Scan() {
  76. if line := strings.TrimSpace(s.Text()); strings.HasPrefix(line, "cpu") {
  77. cpu, _, found := strings.Cut(line, " ")
  78. if found {
  79. // skip first line with accumulated values
  80. if cpu == "cpu" {
  81. continue
  82. }
  83. _, err := strconv.ParseInt(cpu[len("cpu"):], 10, 64)
  84. if err == nil {
  85. count++
  86. }
  87. }
  88. } else {
  89. // The current format of /proc/stat has all the
  90. // cpu* lines at the beginning. Assume this
  91. // stays this way.
  92. break
  93. }
  94. }
  95. if err := s.Err(); err != nil {
  96. return -1, err
  97. }
  98. return count, nil
  99. }
  100. func getNprocs() int64 {
  101. count, err := getNprocsSysfs()
  102. if err == nil {
  103. return count
  104. }
  105. count, err = getNprocsProcStat()
  106. if err == nil {
  107. return count
  108. }
  109. // default to the value determined at runtime startup if all else fails
  110. return int64(runtime.NumCPU())
  111. }
  112. func getNprocsConf() int64 {
  113. count, err := numcpus.GetConfigured()
  114. if err == nil {
  115. return int64(count)
  116. }
  117. // TODO(tk): fall back to reading /proc/cpuinfo on legacy systems
  118. // without sysfs?
  119. return getNprocs()
  120. }
  121. func hasClock(clockid int32) bool {
  122. var res unix.Timespec
  123. if err := unix.ClockGetres(clockid, &res); err != nil {
  124. return false
  125. }
  126. return true
  127. }
  128. func sysconf(name int) (int64, error) {
  129. switch name {
  130. case SC_AIO_LISTIO_MAX:
  131. return -1, nil
  132. case SC_AIO_MAX:
  133. return -1, nil
  134. case SC_AIO_PRIO_DELTA_MAX:
  135. return _AIO_PRIO_DELTA_MAX, nil
  136. case SC_ARG_MAX:
  137. argMax := int64(_POSIX_ARG_MAX)
  138. var rlim unix.Rlimit
  139. if err := unix.Getrlimit(unix.RLIMIT_STACK, &rlim); err == nil {
  140. argMax = max(argMax, int64(rlim.Cur/4))
  141. }
  142. return argMax, nil
  143. case SC_ATEXIT_MAX:
  144. return _INT_MAX, nil
  145. case SC_CHILD_MAX:
  146. childMax := int64(-1)
  147. var rlim unix.Rlimit
  148. if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlim); err == nil && rlim.Cur != unix.RLIM_INFINITY {
  149. childMax = int64(rlim.Cur)
  150. }
  151. return childMax, nil
  152. case SC_CLK_TCK:
  153. return _SYSTEM_CLK_TCK, nil
  154. case SC_DELAYTIMER_MAX:
  155. return _DELAYTIMER_MAX, nil
  156. case SC_GETGR_R_SIZE_MAX:
  157. return _NSS_BUFLEN_GROUP, nil
  158. case SC_GETPW_R_SIZE_MAX:
  159. return _NSS_BUFLEN_PASSWD, nil
  160. case SC_MQ_OPEN_MAX:
  161. return -1, nil
  162. case SC_MQ_PRIO_MAX:
  163. return _MQ_PRIO_MAX, nil
  164. case SC_NGROUPS_MAX:
  165. return readProcFsInt64("/proc/sys/kernel/ngroups_max", _NGROUPS_MAX), nil
  166. case SC_OPEN_MAX:
  167. openMax := int64(_OPEN_MAX)
  168. var rlim unix.Rlimit
  169. if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlim); err == nil {
  170. openMax = int64(rlim.Cur)
  171. }
  172. return openMax, nil
  173. case SC_RTSIG_MAX:
  174. return _RTSIG_MAX, nil
  175. case SC_SEM_NSEMS_MAX:
  176. return -1, nil
  177. case SC_SEM_VALUE_MAX:
  178. return _SEM_VALUE_MAX, nil
  179. case SC_SIGQUEUE_MAX:
  180. var rlim unix.Rlimit
  181. if err := unix.Getrlimit(unix.RLIMIT_SIGPENDING, &rlim); err == nil {
  182. return int64(rlim.Cur), nil
  183. }
  184. return readProcFsInt64("/proc/sys/kernel/rtsig-max", _POSIX_SIGQUEUE_MAX), nil
  185. case SC_STREAM_MAX:
  186. return _STREAM_MAX, nil
  187. case SC_THREAD_DESTRUCTOR_ITERATIONS:
  188. return _POSIX_THREAD_DESTRUCTOR_ITERATIONS, nil
  189. case SC_THREAD_KEYS_MAX:
  190. return _PTHREAD_KEYS_MAX, nil
  191. case SC_THREAD_PRIO_INHERIT:
  192. return _POSIX_THREAD_PRIO_INHERIT, nil
  193. case SC_THREAD_PRIO_PROTECT:
  194. return _POSIX_THREAD_PRIO_PROTECT, nil
  195. case SC_THREAD_STACK_MIN:
  196. return _PTHREAD_STACK_MIN, nil
  197. case SC_THREAD_THREADS_MAX:
  198. return -1, nil
  199. case SC_TIMER_MAX:
  200. return -1, nil
  201. case SC_TTY_NAME_MAX:
  202. return _TTY_NAME_MAX, nil
  203. case SC_TZNAME_MAX:
  204. return -1, nil
  205. case SC_CPUTIME:
  206. if hasClock(unix.CLOCK_PROCESS_CPUTIME_ID) {
  207. return _POSIX_VERSION, nil
  208. }
  209. return -1, nil
  210. case SC_MONOTONIC_CLOCK:
  211. if hasClock(unix.CLOCK_MONOTONIC) {
  212. return _POSIX_VERSION, nil
  213. }
  214. return -1, nil
  215. case SC_SAVED_IDS:
  216. return _POSIX_SAVED_IDS, nil
  217. case SC_SPAWN:
  218. return _POSIX_SPAWN, nil
  219. case SC_SPIN_LOCKS:
  220. return _POSIX_SPIN_LOCKS, nil
  221. case SC_SPORADIC_SERVER:
  222. return _POSIX_SPORADIC_SERVER, nil
  223. case SC_SYNCHRONIZED_IO:
  224. return _POSIX_SYNCHRONIZED_IO, nil
  225. case SC_THREAD_ATTR_STACKADDR:
  226. return _POSIX_THREAD_ATTR_STACKADDR, nil
  227. case SC_THREAD_ATTR_STACKSIZE:
  228. return _POSIX_THREAD_ATTR_STACKSIZE, nil
  229. case SC_THREAD_CPUTIME:
  230. if hasClock(unix.CLOCK_THREAD_CPUTIME_ID) {
  231. return _POSIX_VERSION, nil
  232. }
  233. return -1, nil
  234. case SC_THREAD_PRIORITY_SCHEDULING:
  235. return _POSIX_THREAD_PRIORITY_SCHEDULING, nil
  236. case SC_THREAD_PROCESS_SHARED:
  237. return _POSIX_THREAD_PROCESS_SHARED, nil
  238. case SC_THREAD_SAFE_FUNCTIONS:
  239. return _POSIX_THREAD_SAFE_FUNCTIONS, nil
  240. case SC_THREAD_SPORADIC_SERVER:
  241. return _POSIX_THREAD_SPORADIC_SERVER, nil
  242. case SC_TRACE:
  243. return _POSIX_TRACE, nil
  244. case SC_TRACE_EVENT_FILTER:
  245. return _POSIX_TRACE_EVENT_FILTER, nil
  246. case SC_TRACE_EVENT_NAME_MAX:
  247. return -1, nil
  248. case SC_TRACE_INHERIT:
  249. return _POSIX_TRACE_INHERIT, nil
  250. case SC_TRACE_LOG:
  251. return _POSIX_TRACE_LOG, nil
  252. case SC_TRACE_NAME_MAX:
  253. return -1, nil
  254. case SC_TRACE_SYS_MAX:
  255. return -1, nil
  256. case SC_TRACE_USER_EVENT_MAX:
  257. return -1, nil
  258. case SC_TYPED_MEMORY_OBJECTS:
  259. return _POSIX_TYPED_MEMORY_OBJECTS, nil
  260. case SC_V7_ILP32_OFF32:
  261. return _POSIX_V7_ILP32_OFF32, nil
  262. case SC_V7_ILP32_OFFBIG:
  263. return _POSIX_V7_ILP32_OFFBIG, nil
  264. case SC_V7_LP64_OFF64:
  265. return _POSIX_V7_LP64_OFF64, nil
  266. case SC_V7_LPBIG_OFFBIG:
  267. return _POSIX_V7_LPBIG_OFFBIG, nil
  268. case SC_V6_ILP32_OFF32:
  269. return _POSIX_V6_ILP32_OFF32, nil
  270. case SC_V6_ILP32_OFFBIG:
  271. return _POSIX_V6_ILP32_OFFBIG, nil
  272. case SC_V6_LP64_OFF64:
  273. return _POSIX_V6_LP64_OFF64, nil
  274. case SC_V6_LPBIG_OFFBIG:
  275. return _POSIX_V6_LPBIG_OFFBIG, nil
  276. case SC_2_C_VERSION:
  277. return _POSIX2_C_VERSION, nil
  278. case SC_2_CHAR_TERM:
  279. return _POSIX2_CHAR_TERM, nil
  280. case SC_2_PBS,
  281. SC_2_PBS_ACCOUNTING,
  282. SC_2_PBS_CHECKPOINT,
  283. SC_2_PBS_LOCATE,
  284. SC_2_PBS_MESSAGE,
  285. SC_2_PBS_TRACK:
  286. return -1, nil
  287. case SC_2_UPE:
  288. return -1, nil
  289. case SC_XOPEN_CRYPT:
  290. // removed in glibc 2.28
  291. return -1, nil
  292. case SC_XOPEN_ENH_I18N:
  293. return _XOPEN_ENH_I18N, nil
  294. case SC_XOPEN_REALTIME:
  295. return _XOPEN_REALTIME, nil
  296. case SC_XOPEN_REALTIME_THREADS:
  297. return _XOPEN_REALTIME_THREADS, nil
  298. case SC_XOPEN_SHM:
  299. return _XOPEN_SHM, nil
  300. case SC_XOPEN_STREAMS:
  301. return -1, nil
  302. case SC_XOPEN_UNIX:
  303. return _XOPEN_UNIX, nil
  304. case SC_XOPEN_VERSION:
  305. return _XOPEN_VERSION, nil
  306. case SC_XOPEN_XCU_VERSION:
  307. return _XOPEN_XCU_VERSION, nil
  308. case SC_PHYS_PAGES:
  309. return getPhysPages(), nil
  310. case SC_AVPHYS_PAGES:
  311. return getAvPhysPages(), nil
  312. case SC_NPROCESSORS_CONF:
  313. return getNprocsConf(), nil
  314. case SC_NPROCESSORS_ONLN:
  315. return getNprocs(), nil
  316. case SC_UIO_MAXIOV: // same as _SC_IOV_MAX
  317. return _UIO_MAXIOV, nil
  318. }
  319. return sysconfGeneric(name)
  320. }