oscomp / testsuits-for-oskernel

95 stars 29 forks source link

系统调用测试用例clone.c中子线程的返回问题 #7

Closed retrhelo closed 3 years ago

retrhelo commented 3 years ago

注意到测试用例clone.c中的子线程的代码如下

static int child_func(void) {
  printf(" Child says successfully!\n");
  return 0;
}

但是作为子线程而言最后return的意义显得不那么明确,子线程应该返回何处?私以为子线程在执行结束后应该结束当前进程,以便父进程能够从wait()调用中离开。所以我认为子线程最后调用exit()系统调用而非return或许是一个更合适的写法? 下面这篇文章也有提到子线程不应该使用return作为结束代码

The thread function has to be careful not to return (ret) since there’s nowhere to return. It will fall off the stack and terminate the program with a segmentation fault. Remember that threads are just processes? It must use the exit() syscall to terminate. This won’t terminate the other threads.

shzhxh commented 3 years ago

我查询了clone()的手册,上面是这么说的:

When the fn(arg) function returns, the child process terminates. The integer returned by fn is the exit status for the child process. The child process may also terminate explicitly by calling exit(2) or after receiving a fatal signal.

手册里说,return的值即为子线程退出的状态。 由于clone创建的子线程有自己的栈,所以子线程脱离自己的栈,应该不会产生段错误。

retrhelo commented 3 years ago

是的,在仔细地分析了测试用例之后我发现这样确实是正确的。因为测试用例中使用的clone()实际上是SYS_clone系统调用的封装。在clone()调用了函数__clone,这个函数才是真正的系统调用,其行为与Linux中的fork()相似。这段代码是这样写的

# __clone(func, stack, flags, arg, ptid, tls, ctid)
#           a0,    a1,    a2,  a3,   a4,  a5,   a6
# syscall(SYS_clone, flags, stack, ptid, tls, ctid)
#                a7     a0,    a1,   a2,  a3,   a4
.global __clone
.type  __clone, %function
__clone:
    # Save func and arg to stack
    addi a1, a1, -16
    sd a0, 0(a1)
    sd a3, 8(a1)

    # Call SYS_clone
    mv a0, a2
    mv a2, a4
    mv a3, a5
    mv a4, a6
    li a7, 220 # SYS_clone
    ecall

    beqz a0, 1f
    # Parent
    ret

    # Child
1:      ld a1, 0(sp)
    ld a0, 8(sp)
    jalr a1

    # Exit
    li a7, 93 # SYS_exit
    ecall

对于子进程,其在SYS_clone调用之后会进入1:开始的分支,在这里会调用之前通过clone()传入的函数指针fn,并在fn返回后调用SYS_exit,从而结束子进程。