---
# System prepended metadata

title: Linux Lab 3 report
tags: [linux, linux-2022-autumn]

---

# Linux Lab 3 report
###### tags: `linux`,  `linux-2022-autumn`

## Task 1

> Requierment:
>  Write a new system call int get_CPU_number() so that a process can use it to get the number of the CPU that executes it.

![](https://i.imgur.com/xpHXax1.png =x100)

If we look close enough, we can see a variable called `unsigned int cpu` in `task_struct`

```c
struct task_struct {
    ...
        
    /* Current CPU: */
    unsigned int cpu;
    ...
```

### Result

```bash
❯ gcc test1.c && ./a.out
10
10
10
11
11
11
11
11
11
11
```

> 嘗試多跑幾次後，結果都在 0~11 之間。而我的 CPU 是 6 核 12 執行序，因此是合理的結果
> 根據執行結果我們可以發現一 process 會盡量不換去其他 cpu，猜測是因為 cache 的機制關西。

## Task 2

> Write a new system call start_to_count_number_of_process_switches() so that a process can use it to begin to count the number of process switches the process makes. Besides, write another new system call int stop_to_count_number_of_process_switches() so that a process can use it to stop to count the number of process switches the process makes and return the number of process switches the process makes. 

![](https://i.imgur.com/0K8lgPQ.png =200x)

switch_cnt 加在 `task_struct` 最後面
switch_to 去改

:::spoiler arch/x86/include/asm/switch_to.h
```c
struct task_struct *__switch_to_asm(struct task_struct *prev,
				    struct task_struct *next);
...
#define switch_to(prev, next, last)					\
do {									\
	((last) = __switch_to_asm((prev), (next)));			\
} while (0)
```
:::

:::spoiler arch/x86/entry/entry_64.S
```asm
SYM_FUNC_START(__switch_to_asm)
	/*
	 * Save callee-saved registers
	 * This must match the order in inactive_task_frame
	 */
	pushq	%rbp
	pushq	%rbx
	pushq	%r12
	pushq	%r13
	pushq	%r14
	pushq	%r15

	/* switch stack */
	movq	%rsp, TASK_threadsp(%rdi)
	movq	TASK_threadsp(%rsi), %rsp

#ifdef CONFIG_STACKPROTECTOR
	movq	TASK_stack_canary(%rsi), %rbx
	movq	%rbx, PER_CPU_VAR(fixed_percpu_data) + stack_canary_offset
#endif

	/*
	 * When switching from a shallower to a deeper call stack
	 * the RSB may either underflow or use entries populated
	 * with userspace addresses. On CPUs where those concerns
	 * exist, overwrite the RSB with entries which capture
	 * speculative execution to prevent attack.
	 */
	FILL_RETURN_BUFFER %r12, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW

	/* restore callee-saved registers */
	popq	%r15
	popq	%r14
	popq	%r13
	popq	%r12
	popq	%rbx
	popq	%rbp

	jmp	__switch_to
SYM_FUNC_END(__switch_to_asm)
```
:::

:::spoiler arch/x86/kernel/process_64.c
```c
/*
 *	switch_to(x,y) should switch tasks from x to y.
 *
 * This could still be optimized:
 * - fold all the options into a flag word and test it with a single test.
 * - could test fs/gs bitsliced
 *
 * Kprobes not supported here. Set the probe on schedule instead.
 * Function graph tracer not supported too.
 */
__visible __notrace_funcgraph struct task_struct *
__switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
    if (prev_p->switch_cnt_switch) {
        prev_p->switch_cnt += 1;
    }
    ...
```
:::


### Result

#### CPU-bound

:::spoiler code
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#define SYS_N_START 452
#define SYS_N_STOP 453

unsigned long long fib(unsigned long long n)
{
    if (n <= 1) {
        return 1;
    } else {
        return fib(n-1) + fib(n-2);
    }
}

int main()
{
    struct timespec ts;
    ts.tv_nsec = 1000;
    ts.tv_sec = 0;

    long retval = 0;
    unsigned long long b = 1;

    syscall(SYS_N_START);

    for (int i = 0; i < 10000; i++) {
        nanosleep(&ts, &ts);
        b = fib(26);
    }

    printf("%llu\n", b);
    retval = syscall(SYS_N_STOP);

    printf("\n\nnum: %ld\n", retval);

    return 0;
}
```
:::

</br>

**Result**

```bash
❯ gcc ./cpu-bound.c && ./a.out
196418

num: 10021
```

</br>

#### IO-bound

:::spoiler code
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#define SYS_N_START 452
#define SYS_N_STOP 453

int main()
{
    struct timespec ts;
    ts.tv_nsec = 1000;
    ts.tv_sec = 0;

    long retval = 0;
    int b = 1;

    syscall(SYS_N_START);

    for (int i = 0; i < 1000000; i++) {
        printf("%d", b++);
        fflush(stdout);
        nanosleep(&ts, &ts);
    }

    retval = syscall(SYS_N_STOP);

    printf("\n\nnum: %ld\n", retval);

    return 0;
}
```
:::


</br>

**Result**
```bash
❯ gcc ./io-bound.c && ./a.out
num: 1000034
```


</br>
</br>

## 問題準備

- what is switch_to, prev, next
    - what is `last` :thinking_face:
    - ![](https://i.imgur.com/N7J9keR.png)
    - `last` is last prev !!! :confetti_ball: 

- TASK_threadsp
    - what is role of sp register ??
    - THE rsp register ??
- definition of cpu_number
    - per cpu
    - run queue 中有 cpu info, 因此只要知道自己在哪個 run queue 就知道 cpu
- TLS
    - thread load storge
    - pointer to `struct pthread`
- cs, ds, es, ss, fs, gs
    - cs: code segment
    - ds: Data segment
    - es: estination segment
    - ss: stack segment
    - fs: TLS entry
    - gs: TLS entry
- switch PDA, FPU context
- on_cpu, cpu, recent_used_cpu, wake_cpu

## TODO

- project 1 重跑
    - 也許有些 vma 是 kernel 的 stack/heap

### after
number of run queue

systrace
perf


</br>
</br>

## Reference

- https://liujunming.top/2018/11/20/%E8%BF%9B%E7%A8%8B%E5%88%87%E6%8D%A2switch-to%E5%AE%8F%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0%E5%88%86%E6%9E%90/