Go серия 6: системный вызов

Go

системный вызов

Оригинальный адрес:

GitHub.com/часто123/потяните…

концепция

Одна картинка стоит тысячи слов:

                                                                                                                   
+ - - - - - - - - - - - - - - -   User Mode   - - - - - - - - - - - - - - - - - -                                  
                                                                                 |                                 
|       Application                               syscall library                                                  
          program                                   /src/syscall                 |                                 
|                                                                                                                  
                                                                                 |                                 
|   +-------------------+                      +----------------------+                                            
    |                   |                      |Faccessat {           |          |                                 
|   |                   |                      |                      |                                            
    |                   |                      |  runtime·Syscall6 {  |          |                                 
|   |...                |                      |                      |                                            
    |syscall.Access(    |                      |    ...               |          |                                 
|   |     path, mode)   |                      |    SYSCALL ----------+----------------+                           
    |...                |                      |    ...    <----------+----------+-----+--------+                  
|   |                   |                      |    return;           |                |        |                  
    |                   |                      |  }                   |          |     |        |                  
|   |                   |                      |}                     |                |        |                  
    +-------------------+                      +----------------------+          |     |        |                  
|                                                                                      |        |                  
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +     |        ^                  
                                                                                       |        |                  
                                                                       switch to kernel mode    |                  
+ - - - - - - - - - - - - - - - - Kernel Mode - - - - - - - - - - - - - - - - - -      v        |                  
                                                                                 |     |        |                  
|        System call                                 Trap handler                      |        |                  
       service routine                                                           |     |        |                  
|    +------------------+                       +-----------------------+              |        |                  
     |sys_faccessat() <-+-----------+           |system call:   <-------+--------+-----+        |                  
|    |{                 |           |           |                       |                       |                  
     |                  |           |           |                       |        |              |                  
|    |                  |           |           |   ...                 |                       |                  
     |                  |           |           |                       |        |              |                  
|    |  ...             |           +-----------+---call sys_call_table |                  switch to user mode     
     |                  |                       |                       |        |              |                  
|    |                  |           +-----------+-> ...                 |                       |                  
     |  return error; --+-----------+           |                       |        |              |                  
|    |}                 |                       |    -------------------+----------->-----------+                  
     +------------------+                       +-----------------------+        |                                 
|                                                                                                                  
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +                                 

Вход

системный вызов имеет следующие записи, вsyscall/asm_linux_amd64.sсередина.

func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)

func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)

func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)

func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)

Реализация этих функций - вся сборка.Согласно спецификации вызова системных вызовов Linux, нам нужно только передать параметры в регистр по очереди в сборке и вызвать инструкцию SYSCALL для входа в логику обработки ядра.После системного вызова выполняется, возвращаемое значение помещается в RAX. :

RDI RSI RDX R10 R8 R9 RAX
параметр один второй параметр параметр три параметр четыре параметр пять Параметр шесть Номер системного вызова/возвратное значение

Единственная разница между Syscall и Syscall6 — входящие параметры:

// func Syscall(trap int64, a1, a2, a3 uintptr) (r1, r2, err uintptr);
TEXT ·Syscall(SB),NOSPLIT,$0-56
    CALL    runtime·entersyscall(SB)
    MOVQ    a1+8(FP), DI
    MOVQ    a2+16(FP), SI
    MOVQ    a3+24(FP), DX
    MOVQ    $0, R10
    MOVQ    $0, R8
    MOVQ    $0, R9
    MOVQ    trap+0(FP), AX    // syscall entry
    SYSCALL
    // 0xfffffffffffff001 是 linux MAX_ERRNO 取反 转无符号,http://lxr.free-electrons.com/source/include/linux/err.h#L17
    CMPQ    AX, $0xfffffffffffff001
    JLS    ok
    MOVQ    $-1, r1+32(FP)
    MOVQ    $0, r2+40(FP)
    NEGQ    AX
    MOVQ    AX, err+48(FP)
    CALL    runtime·exitsyscall(SB)
    RET
ok:
    MOVQ    AX, r1+32(FP)
    MOVQ    DX, r2+40(FP)
    MOVQ    $0, err+48(FP)
    CALL    runtime·exitsyscall(SB)
    RET

// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
TEXT ·Syscall6(SB),NOSPLIT,$0-80
    CALL    runtime·entersyscall(SB)
    MOVQ    a1+8(FP), DI
    MOVQ    a2+16(FP), SI
    MOVQ    a3+24(FP), DX
    MOVQ    a4+32(FP), R10
    MOVQ    a5+40(FP), R8
    MOVQ    a6+48(FP), R9
    MOVQ    trap+0(FP), AX    // syscall entry
    SYSCALL
    CMPQ    AX, $0xfffffffffffff001
    JLS    ok6
    MOVQ    $-1, r1+56(FP)
    MOVQ    $0, r2+64(FP)
    NEGQ    AX
    MOVQ    AX, err+72(FP)
    CALL    runtime·exitsyscall(SB)
    RET
ok6:
    MOVQ    AX, r1+56(FP)
    MOVQ    DX, r2+64(FP)
    MOVQ    $0, err+72(FP)
    CALL    runtime·exitsyscall(SB)
    RET

Между этими двумя функциями нет большой разницы, так почему бы не использовать одну? Личное предположение, все параметры функций Go передаются в стек, возможно, для экономии места в стеке. . Среда выполнения будет уведомлена перед обычной операцией системного вызова, а затем я выполню операцию системного вызова.runtime·entersyscall, который вызывается при выходеruntime·exitsyscall.

// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
    MOVQ    a1+8(FP), DI
    MOVQ    a2+16(FP), SI
    MOVQ    a3+24(FP), DX
    MOVQ    $0, R10
    MOVQ    $0, R8
    MOVQ    $0, R9
    MOVQ    trap+0(FP), AX    // syscall entry
    SYSCALL
    CMPQ    AX, $0xfffffffffffff001
    JLS    ok1
    MOVQ    $-1, r1+32(FP)
    MOVQ    $0, r2+40(FP)
    NEGQ    AX
    MOVQ    AX, err+48(FP)
    RET
ok1:
    MOVQ    AX, r1+32(FP)
    MOVQ    DX, r2+40(FP)
    MOVQ    $0, err+48(FP)
    RET

// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
    MOVQ    a1+8(FP), DI
    MOVQ    a2+16(FP), SI
    MOVQ    a3+24(FP), DX
    MOVQ    a4+32(FP), R10
    MOVQ    a5+40(FP), R8
    MOVQ    a6+48(FP), R9
    MOVQ    trap+0(FP), AX    // syscall entry
    SYSCALL
    CMPQ    AX, $0xfffffffffffff001
    JLS    ok2
    MOVQ    $-1, r1+56(FP)
    MOVQ    $0, r2+64(FP)
    NEGQ    AX
    MOVQ    AX, err+72(FP)
    RET
ok2:
    MOVQ    AX, r1+56(FP)
    MOVQ    DX, r2+64(FP)
    MOVQ    $0, err+72(FP)
    RET

Разница между RawSyscall и Syscall также очень мала, просто среда выполнения не уведомляется при входе в Syscall и выходе, поэтому среда выполнения теоретически не имеет возможности запланировать p of m of g, поэтому, если пользовательский код использует RawSyscall для It можно заблокировать другие г, выполнив некоторые блокирующие системные вызовы.Ниже приведены оригинальные слова официальной разработки:

Yes, if you call RawSyscall you may block other goroutines from running. The system monitor may start them up after a while, but I think there are cases where it won't. I would say that Go programs should always call Syscall. RawSyscall exists to make it slightly more efficient to call system calls that never block, such as getpid. But it's really an internal mechanism.

RawSyscall предназначен только для сохранения стоимости двух вызовов функций во время выполнения при выполнении тех системных вызовов, которые не должны блокироваться.

vdso

vdso можно рассматривать как специальный вызов, при использовании которого нет перехода из пользовательского режима в режим ядра в начале этой статьи со ссылкой на ссылку:

Используется для выполнения определенных системных вызовов, снижения накладных расходов на системные вызовы. Некоторые системные вызовы не передают параметры ядру, а просто запрашивают у ядра данные, такие как getTimeOfDay(), ядро ​​может записать систему в фиксированное место при обработке этой части системного вызова (Это действие обновления завершено ядром для завершения этого обновления каждый раз), а MMAP сопоставляется с пользовательским пространством. Это позволит быстрее избежать контекста традиционного системного вызова режима INT 0x80/syscall и переключения контекста пользовательского пространства.

// func gettimeofday(tv *Timeval) (err uintptr)
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
    MOVQ    tv+0(FP), DI
    MOVQ    $0, SI
    MOVQ    runtime·__vdso_gettimeofday_sym(SB), AX
    CALL    AX

    CMPQ    AX, $0xfffffffffffff001
    JLS    ok7
    NEGQ    AX
    MOVQ    AX, err+8(FP)
    RET
ok7:
    MOVQ    $0, err+8(FP)
    RET

Система управления вызовом

Первый — это файл определения системного вызова:

/syscall/syscall_linux.go

Системные вызовы можно разделить на три категории:

  1. блокировка системного вызова
  2. неблокирующий системный вызов
  3. завернутый системный вызов

Вызов системы блокировки определяется следующим образом:

//sys   Madvise(b []byte, advice int) (err error)

Неблокирующий системный вызов:

//sysnb    EpollCreate(size int) (fd int, err error)

Затем на основе этих комментариев сценарий mksyscall.pl создает соответствующую реализацию для конкретной платформы. mksyscall.pl — это Perl-скрипт, и заинтересованные студенты могут просмотреть его самостоятельно, поэтому я не буду здесь вдаваться в подробности.

Взгляните на сгенерированные результаты блокирующих и неблокирующих системных вызовов:

func Madvise(b []byte, advice int) (err error) {
    var _p0 unsafe.Pointer
    if len(b) > 0 {
        _p0 = unsafe.Pointer(&b[0])
    } else {
        _p0 = unsafe.Pointer(&_zero)
    }
    _, _, e1 := Syscall(SYS_MADVISE, uintptr(_p0), uintptr(len(b)), uintptr(advice))
    if e1 != 0 {
        err = errnoErr(e1)
    }
    return
}

func EpollCreate(size int) (fd int, err error) {
    r0, _, e1 := RawSyscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0)
    fd = int(r0)
    if e1 != 0 {
        err = errnoErr(e1)
    }
    return
}

Очевидно, системный вызов с пометкой sys использует Syscall или Syscall6, а системный вызов с пометкой sysnb использует RawSyscall или RawSyscall6.

Как насчет обернутого системного вызова?

func Rename(oldpath string, newpath string) (err error) {
    return Renameat(_AT_FDCWD, oldpath, _AT_FDCWD, newpath)
}

Может быть имя системного вызова не очень, или параметров слишком много, поэтому просто заворачиваем. Ничего особенного.

Syscall во время выполнения

В дополнение к упомянутым выше блокирующим, неблокирующим и обернутым системным вызовам, существуют также некоторые низкоуровневые системные вызовы, определенные в среде выполнения, которые не доступны пользователю.

Библиотека системных вызовов, предоставляемая пользователю, при использовании заставит goroutine и p войти в состояния Gsyscall и Psyscall соответственно. Но эти системные вызовы, инкапсулированные самой средой выполнения, не будут вызывать entersyscall и exitsyscall независимо от того, блокируются они или нет. Хотя это системный вызов «низкого уровня»,
Но суть системного вызова, доступного пользователю, одна и та же. Эти коды находятся вruntime/sys_linux_amd64.s, чтобы привести конкретный пример:

TEXT runtime·write(SB),NOSPLIT,$0-28
    MOVQ    fd+0(FP), DI
    MOVQ    p+8(FP), SI
    MOVL    n+16(FP), DX
    MOVL    $SYS_write, AX
    SYSCALL
    CMPQ    AX, $0xfffffffffffff001
    JLS    2(PC)
    MOVL    $-1, AX
    MOVL    AX, ret+24(FP)
    RET

TEXT runtime·read(SB),NOSPLIT,$0-28
    MOVL    fd+0(FP), DI
    MOVQ    p+8(FP), SI
    MOVL    n+16(FP), DX
    MOVL    $SYS_read, AX
    SYSCALL
    CMPQ    AX, $0xfffffffffffff001
    JLS    2(PC)
    MOVL    $-1, AX
    MOVL    AX, ret+24(FP)
    RET

Вот список всех системных вызовов, определенных средой выполнения дополнительно:

#define SYS_read        0
#define SYS_write        1
#define SYS_open        2
#define SYS_close        3
#define SYS_mmap        9
#define SYS_munmap        11
#define SYS_brk         12
#define SYS_rt_sigaction    13
#define SYS_rt_sigprocmask    14
#define SYS_rt_sigreturn    15
#define SYS_access        21
#define SYS_sched_yield     24
#define SYS_mincore        27
#define SYS_madvise        28
#define SYS_setittimer        38
#define SYS_getpid        39
#define SYS_socket        41
#define SYS_connect        42
#define SYS_clone        56
#define SYS_exit        60
#define SYS_kill        62
#define SYS_fcntl        72
#define SYS_getrlimit        97
#define SYS_sigaltstack     131
#define SYS_arch_prctl        158
#define SYS_gettid        186
#define SYS_tkill        200
#define SYS_futex        202
#define SYS_sched_getaffinity    204
#define SYS_epoll_create    213
#define SYS_exit_group        231
#define SYS_epoll_wait        232
#define SYS_epoll_ctl        233
#define SYS_pselect6        270
#define SYS_epoll_create1    291

Теоретически эти системные вызовы не будут удалены планировщиком во время выполнения, поэтому горутина будет продолжать выполняться после успешного выполнения, в отличие от пользовательской горутины, если p будет удалена, она войдет в очередь ожидания.

И интерактивное расписание

Если он хочет и взаимодействовать с отправкой, он сообщил мне, что дружелюбно с помощью Syscall: Eventerscall, я ухожу с: Exitsyscall.

Таким образом, взаимодействие здесь относится к взаимодействию между кодом пользователя и планировщиком при использовании библиотеки системных вызовов.Системный вызов в среде выполнения не следует этому процессу..

entersyscall

// syscall 库和 cgo 调用的标准入口
//go:nosplit
func entersyscall() {
    reentersyscall(getcallerpc(), getcallersp())
}

//go:nosplit
func reentersyscall(pc, sp uintptr) {
    _g_ := getg()

    // 需要禁止 g 的抢占
    _g_.m.locks++

    // entersyscall 中不能调用任何会导致栈增长/分裂的函数
    _g_.stackguard0 = stackPreempt
    // 设置 throwsplit,在 newstack 中,如果发现 throwsplit 是 true
    // 会直接 crash
    // 下面的代码是 newstack 里的
    // if thisg.m.curg.throwsplit {
    //     throw("runtime: stack split at bad time")
    // }
    _g_.throwsplit = true

    // Leave SP around for GC and traceback.
    // 保存现场,在 syscall 之后会依据这些数据恢复现场
    save(pc, sp)
    _g_.syscallsp = sp
    _g_.syscallpc = pc
    casgstatus(_g_, _Grunning, _Gsyscall)
    if _g_.syscallsp < _g_.stack.lo || _g_.stack.hi < _g_.syscallsp {
        systemstack(func() {
            print("entersyscall inconsistent ", hex(_g_.syscallsp), " [", hex(_g_.stack.lo), ",", hex(_g_.stack.hi), "]\n")
            throw("entersyscall")
        })
    }

    if atomic.Load(&sched.sysmonwait) != 0 {
        systemstack(entersyscall_sysmon)
        save(pc, sp)
    }

    if _g_.m.p.ptr().runSafePointFn != 0 {
        // runSafePointFn may stack split if run on this stack
        systemstack(runSafePointFn)
        save(pc, sp)
    }

    _g_.m.syscalltick = _g_.m.p.ptr().syscalltick
    _g_.sysblocktraced = true
    _g_.m.mcache = nil
    _g_.m.p.ptr().m = 0
    atomic.Store(&_g_.m.p.ptr().status, _Psyscall)
    if sched.gcwaiting != 0 {
        systemstack(entersyscall_gcwait)
        save(pc, sp)
    }

    _g_.m.locks--
}

Видно, что G, входящий в системный вызов, точно не будет вытеснен.

exitsyscall

// g 已经退出了 syscall
// 需要准备让 g 在 cpu 上重新运行
// 这个函数只会在 syscall 库中被调用,在 runtime 里用的 low-level syscall
// 不会用到
// 不能有 write barrier,因为 P 可能已经被偷走了
//go:nosplit
//go:nowritebarrierrec
func exitsyscall(dummy int32) {
    _g_ := getg()

    _g_.m.locks++ // see comment in entersyscall
    if getcallersp(unsafe.Pointer(&dummy)) > _g_.syscallsp {
        // throw calls print which may try to grow the stack,
        // but throwsplit == true so the stack can not be grown;
        // use systemstack to avoid that possible problem.
        systemstack(func() {
            throw("exitsyscall: syscall frame is no longer valid")
        })
    }

    _g_.waitsince = 0
    oldp := _g_.m.p.ptr()
    if exitsyscallfast() {
        if _g_.m.mcache == nil {
            systemstack(func() {
                throw("lost mcache")
            })
        }
        // 目前有 p,可以运行
        _g_.m.p.ptr().syscalltick++
        // 把 g 的状态修改回 running
        casgstatus(_g_, _Gsyscall, _Grunning)

        // 垃圾收集未在运行(因为我们这段逻辑在执行)
        // 所以清理掉 syscallsp 是安全的
        _g_.syscallsp = 0
        _g_.m.locks--
        if _g_.preempt {
            // 防止在 newstack 中清理掉 preemption 标记
            _g_.stackguard0 = stackPreempt
        } else {
            // 否则恢复在 entersyscall/entersyscallblock 中破坏掉的正常的 _StackGuard
            _g_.stackguard0 = _g_.stack.lo + _StackGuard
        }
        _g_.throwsplit = false
        return
    }

    _g_.sysexitticks = 0
    _g_.m.locks--

    // 调用 scheduler
    mcall(exitsyscall0)

    if _g_.m.mcache == nil {
        systemstack(func() {
            throw("lost mcache")
        })
    }

    // 调度器返回了,所以我们可以清理掉在 syscall 期间为垃圾收集器
    // 准备的 syscallsp 信息了
    // 需要一直等待到 gosched 返回,我们不确定垃圾收集器是不是在运行
    _g_.syscallsp = 0
    _g_.m.p.ptr().syscalltick++
    _g_.throwsplit = false
}

Здесь также называются exitsyscallfastrfast и exitsyscall0.

exitsyscallfast

//go:nosplit
func exitsyscallfast() bool {
    _g_ := getg()

    // Freezetheworld sets stopwait but does not retake P's.
    if sched.stopwait == freezeStopWait {
        _g_.m.mcache = nil
        _g_.m.p = 0
        return false
    }

    // Try to re-acquire the last P.
    if _g_.m.p != 0 && _g_.m.p.ptr().status == _Psyscall && atomic.Cas(&_g_.m.p.ptr().status, _Psyscall, _Prunning) {
        // There's a cpu for us, so we can run.
        exitsyscallfast_reacquired()
        return true
    }

    // Try to get any other idle P.
    oldp := _g_.m.p.ptr()
    _g_.m.mcache = nil
    _g_.m.p = 0
    if sched.pidle != 0 {
        var ok bool
        systemstack(func() {
            ok = exitsyscallfast_pidle()
        })
        if ok {
            return true
        }
    }
    return false
}

Короче говоря, попробуйте получить P, чтобы выполнить логику после Syscall. Если для нас нет P, то перейдите к exitsyscall0.

mcall(exitsyscall0)

Когда вызывается exitsyscall0, он переключается на стек g0.

exitsyscall0

// 在 exitsyscallfast 中吃瘪了,没办法,慢慢来
// 把 g 的状态设置成 runnable,先进 runq 等着
//go:nowritebarrierrec
func exitsyscall0(gp *g) {
    _g_ := getg()

    casgstatus(gp, _Gsyscall, _Grunnable)
    dropg()
    lock(&sched.lock)
    _p_ := pidleget()
    if _p_ == nil {
        // 如果 P 被人偷跑了
        globrunqput(gp)
    } else if atomic.Load(&sched.sysmonwait) != 0 {
        atomic.Store(&sched.sysmonwait, 0)
        notewakeup(&sched.sysmonnote)
    }
    unlock(&sched.lock)
    if _p_ != nil {
        // 如果现在还有 p,那就用这个 p 执行
        acquirep(_p_)
        execute(gp, false) // Never returns.
    }
    if _g_.m.lockedg != 0 {
        // 设置了 LockOsThread 的 g 的特殊逻辑
        stoplockedm()
        execute(gp, false) // Never returns.
    }
    stopm()
    schedule() // Never returns.
}

entersyscallblock

Зная, что могу заблокировать, я сдал p напрямую.

// 和 entersyscall 一样,就是会直接把 P 给交出去,因为知道自己是会阻塞的
//go:nosplit
func entersyscallblock(dummy int32) {
    _g_ := getg()

    _g_.m.locks++ // see comment in entersyscall
    _g_.throwsplit = true
    _g_.stackguard0 = stackPreempt // see comment in entersyscall
    _g_.m.syscalltick = _g_.m.p.ptr().syscalltick
    _g_.sysblocktraced = true
    _g_.m.p.ptr().syscalltick++

    // Leave SP around for GC and traceback.
    pc := getcallerpc()
    sp := getcallersp(unsafe.Pointer(&dummy))
    save(pc, sp)
    _g_.syscallsp = _g_.sched.sp
    _g_.syscallpc = _g_.sched.pc
    if _g_.syscallsp < _g_.stack.lo || _g_.stack.hi < _g_.syscallsp {
        sp1 := sp
        sp2 := _g_.sched.sp
        sp3 := _g_.syscallsp
        systemstack(func() {
            print("entersyscallblock inconsistent ", hex(sp1), " ", hex(sp2), " ", hex(sp3), " [", hex(_g_.stack.lo), ",", hex(_g_.stack.hi), "]\n")
            throw("entersyscallblock")
        })
    }
    casgstatus(_g_, _Grunning, _Gsyscall)
    if _g_.syscallsp < _g_.stack.lo || _g_.stack.hi < _g_.syscallsp {
        systemstack(func() {
            print("entersyscallblock inconsistent ", hex(sp), " ", hex(_g_.sched.sp), " ", hex(_g_.syscallsp), " [", hex(_g_.stack.lo), ",", hex(_g_.stack.hi), "]\n")
            throw("entersyscallblock")
        })
    }

    // 直接调用 entersyscallblock_handoff 把 p 交出来了
    systemstack(entersyscallblock_handoff)

    // Resave for traceback during blocked call.
    save(getcallerpc(), getcallersp(unsafe.Pointer(&dummy)))

    _g_.m.locks--
}

У этой функции есть только один вызывающий объект, notesleepg, поэтому я не буду здесь вдаваться в подробности.

entersyscallblock_handoff

func entersyscallblock_handoff() {
    handoffp(releasep())
}

Полегче. .

entersyscall_sysmon

func entersyscall_sysmon() {
    lock(&sched.lock)
    if atomic.Load(&sched.sysmonwait) != 0 {
        atomic.Store(&sched.sysmonwait, 0)
        notewakeup(&sched.sysmonnote)
    }
    unlock(&sched.lock)
}

entersyscall_gcwait

func entersyscall_gcwait() {
    _g_ := getg()
    _p_ := _g_.m.p.ptr()

    lock(&sched.lock)
    if sched.stopwait > 0 && atomic.Cas(&_p_.status, _Psyscall, _Pgcstop) {
        _p_.syscalltick++
        if sched.stopwait--; sched.stopwait == 0 {
            notewakeup(&sched.stopnote)
        }
    }
    unlock(&sched.lock)
}

Суммировать

Системный вызов, предоставленный пользователю, в основном уведомляет среду выполнения в форме entersyscall и exitsyscall.Когда системный вызов заблокирован, среда выполнения определяет, следует ли освободить P для другого использования M. Отмена привязки относится к отвязке между M и P. Если привязка не привязана, при возврате системного вызова этот g будет помещен в очередь выполнения runq.

При этом рантайм сохраняет свои привилегии, при выполнении собственной логики мой P не будет передан, что гарантирует, что эти системные вызовы, используемые собственным «нижним слоем» Go, могут быть обработаны сразу после возврата.

Таким образом, это тоже epollwait.Среда выполнения использует его и не может быть прервана другими.Используемый вами syscall.EpollWait, очевидно, не имеет этой привилегии.

использованная литература

  1. blog.study golang.com/2016/06/go-…

  2. the linux programming interface

  3. Еженедельная волна .IO/go - arm6…

  4. blog.CSDN.net/Ло Чжаотянь…