execve()関数

実行ファイルは、
– コードを含むデータ領域のファイル上のオフセット、サイズ、メモリマップ開始アドレス
– 最初に実行する命令のメモリアドレス(エントリポイント)
を持っている。

100 オフセット(基準点)
100 サイズ
300 メモリマップ開始アドレス
200 データオフセット
200 データサイズ
400 メモリマップ開始アドレス
400 エントリポイント(エントリポイントからプログラム開始)

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>

static void child(){
    char *args[] = {"/bin/echo", "hello", NULL};
    printf("I'm child! my pid is %d.\n", getpid());
    fflush(stdout);
    execve("/bin/echo", args, NULL);
    err(EXIT_FAILURE, "exec() failed");
}

static void parent(pid_t pid_c) {
    printf("I'm parent! my pid is %d and the pid of my child is %d.\n", getpid(), pid_c);
    exit(EXIT_SUCCESS);
}

int main(void) {
    pid_t ret;
    ret = fork();
    if (ret == -1)
        err(EXIT_FAILURE, "fork() failed");
    if (ret == 0) {
        child();
    } else {
        parent(ret);
    }
    err(EXIT_FAILURE, "shouldn't reach here");
}

$ ./fork
I’m parent! my pid is 314918 and the pid of my child is 314919.
I’m child! my pid is 314919.
hello

スタックマシンの場合はpushとpupだが、この場合はオフセット、サイズ、エントリポイント、メモリ(レジスタ)の番地を指定している。逆に言えば、スタックの場合は順番に気をつけなければならないが、オフセットなどを指定できる場合はより柔軟に対応できるということか。

Linuxのプロセス生成

Linuxのプロセス生成にはfork() ※clone(), execve()の2つのシステムコールがある。

同じプログラムの処理を複数のプロセスに分けるのはfork()
– プロセスを新規作成

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>

static void child(){
    printf("I'm child! my pid is %d.\n", getpid());
    exit(EXIT_SUCCESS);
}

static void parent(pid_t pid_c) {
    printf("I'm parent! my pid is %d and the pid of my child is %d.\n", getpid(), pid_c);
    exit(EXIT_SUCCESS);
}

int main(void) {
    pid_t ret;
    ret = fork();
    if (ret == -1)
        err(EXIT_FAILURE, "fork() failed");
    if (ret == 0) {
        child();
    } else {
        parent(ret);
    }
    err(EXIT_FAILURE, "shouldn't reach here");
}

$ ./fork
I’m child! my pid is 314564.
I’m parent! my pid is 314563 and the pid of my child is 314564.

システムコールのラッパー関数

システムコールはアーキテクチャ依存のアセンブリコードを使って呼び出す

$ ldd /bin/echo
linux-vdso.so.1 (0x0000ffffb992d000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffb9720000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffb98f4000)

$ ldd /usr/bin/python3
linux-vdso.so.1 (0x0000ffffb5233000)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffffb4b90000)
libexpat.so.1 => /lib/aarch64-linux-gnu/libexpat.so.1 (0x0000ffffb4b50000)
libz.so.1 => /lib/aarch64-linux-gnu/libz.so.1 (0x0000ffffb4b20000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffb4970000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffb51fa000)

pythonは内部的に標準のclibraryを使っている。libcというのがcライブラリ
OSが提供するプログラムの一例として
init, sysctl, nice, sync, touch, mkidr, grep, sort, uniq, sar, iostat, gcc, perl, python, ruby, bash などがある。

【C】システムコール呼び出しの仕組み

### C言語の場合

#include <stdio.h>

int main(void) {
    puts("hello world");
    return 0;
}

$ strace -o hello.log ./hello
hello world

$ cat hello.log
execve(“./hello”, [“./hello”], 0xffffda929930 /* 42 vars */) = 0
brk(NULL) = 0xaaab00641000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffff9126e000
faccessat(AT_FDCWD, “/etc/ld.so.preload”, R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, “/etc/ld.so.cache”, O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, “”, {st_mode=S_IFREG|0644, st_size=48255, …}, AT_EMPTY_PATH) = 0
mmap(NULL, 48255, PROT_READ, MAP_PRIVATE, 3, 0) = 0xffff9122d000
close(3) = 0
openat(AT_FDCWD, “/lib/aarch64-linux-gnu/libc.so.6”, O_RDONLY|O_CLOEXEC) = 3
read(3, “\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\340u\2\0\0\0\0\0″…, 832) = 832
newfstatat(3, “”, {st_mode=S_IFREG|0755, st_size=1637400, …}, AT_EMPTY_PATH) = 0
mmap(NULL, 1805928, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffff91074000
mmap(0xffff91080000, 1740392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffff91080000
munmap(0xffff91074000, 49152) = 0
munmap(0xffff91229000, 15976) = 0
mprotect(0xffff91208000, 61440, PROT_NONE) = 0
mmap(0xffff91217000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x187000) = 0xffff91217000
mmap(0xffff9121d000, 48744, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffff9121d000
close(3) = 0
set_tid_address(0xffff9126ef30) = 313040
set_robust_list(0xffff9126ef40, 24) = 0
rseq(0xffff9126f600, 0x20, 0, 0xd428bc00) = 0
mprotect(0xffff91217000, 16384, PROT_READ) = 0
mprotect(0xaaaac9350000, 4096, PROT_READ) = 0
mprotect(0xffff91273000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0xffff9122d000, 48255) = 0
newfstatat(1, “”, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x3), …}, AT_EMPTY_PATH) = 0
getrandom(“\xbf\x00\x96\xce\x0e\x82\xcd\xa2”, 8, GRND_NONBLOCK) = 8
brk(NULL) = 0xaaab00641000
brk(0xaaab00662000) = 0xaaab00662000
write(1, “hello world\n”, 12) = 12
exit_group(0) = ?
+++ exited with 0 +++

色々はかれていますが、write()がシステムコールです。
write(1, “hello world\n”, 12) = 12

### Pythonの場合

#include <stdio.h>

int main(void) {
    puts("hello world");
    return 0;
}

$ strace -o hello.py python3 ./hello.py

システムコールってよく聞くけど、システムコールの理解が深まった気がする。

Average: CPU %user %nice %system %iowait %steal %idle
Average: all 1.62 0.00 1.69 0.19 0.00 96.50
Average: 0 1.56 0.00 1.79 0.11 0.00 96.54
Average: 1 1.68 0.00 1.60 0.26 0.00 96.46

ユーザモードはuser, niceで systemがカーネルモード、idelは何も動いていない状態です。

【React.js】Base64 encodeとdecodeをReactで実装する

import React, {useState} from 'react';
 
const App = () => {
    /* ↓state変数を定義 */
    const [encode_text, setText] = useState("");
    const input = {
        width: "80%",
    }
    let msg = '文字列を入力してください';
  
    return (
      <div className="App">
        <h6 class="card-subtitle mb-2 text-body-secondary">Base64 Encode</h6>
        <input
        value={encode_text}
        onChange={(event) => setText(event.target.value)} style={input} placeholder={msg}
        />
        <p>{btoa(encode_text)}</p><br />
      </div>
    );
};
export default App;

なるほど〜

【Python】スタックマシンの実装

内部でどのような処理が行われているかを表している

class PVM:
    def __init__(self):
        self.stack = []
        self.output = []

    def LOAD_NAME(self, name):
        self.stack.append(name)

    def LOAD_CONST(self, const):
        self.stack.append(const)

    def CALL(self, arg_count):
        args = [self.stack.pop() for _ in range(arg_count)]
        func = self.stack.pop()
        result = func(*args)
        if result is not None:
            self.stack.append(result)

    def POP_TOP(self):
        if self.stack:
            self.stack.pop()

    def simulate(self, instructions):
        for instr, arg in instructions:
            match instr:
                case "LOAD_NAME":
                    self.LOAD_NAME(arg)
                case "LOAD_CONST":
                    self.LOAD_CONST(arg)
                case "CALL":
                    self.CALL(arg)
                case "POP_TOP":
                    self.POP_TOP()

            self.output.append(list(self.stack))
        
        return self.output

instructions = [
    ("LOAD_NAME", print),
    ("LOAD_CONST", "Hello, World"),
    ("CALL", 1),
    ("POP_TOP", None),
]

pvm = PVM()
stack_states = pvm.simulate(instructions)
print(stack_states)

$ python3 pvm.py
Hello, World
[[], [, ‘Hello, World’], [], []]

関数と変数をスタックに格納してpopとpushで処理していく。なるほどー
そうすると、どれくらい作り込むかってところか…

【Python】hello worldの裏で起こっていること

main.py

print("Hello, World")

$ python3 main.py
Hello, World

### python実行の流れ
– ソースコード読み込み
– 字句解析
– 構文解析
– 意味解析
– バイトコードを実行(仮想マシン上)

### 字句解析/構文解析の流れ

import ast

source = """
print("Hello, World")
"""

tree = ast.parse(source)
print(ast.dump(tree))

$ python3 main.py
Module(body=[Expr(value=Call(func=Name(id=’print’, ctx=Load()), args=[Constant(value=’Hello, World’)], keywords=[]))], type_ignores=[])

### バイトコード

import ast
import dis

source = """
print("Hello, World")
"""

tree = ast.parse(source)

compiled_code = compile(tree, filename="<ast>", mode="exec")
bytecode = dis.dis(compiled_code)
print(bytecode)

$ python3 main.py
2 0 LOAD_NAME 0 (print). // stackにpush
2 LOAD_CONST 0 (‘Hello, World’) // stackにpush
4 CALL_FUNCTION 1 // print関数の呼び出し
6 POP_TOP            // stackのtopを破棄
8 LOAD_CONST 1 (None) // Noneスタックを入れてスタックから戻る
10 RETURN_VALUE
None

このバイトコードはスタックマシン上で実行されている
つまり、スタックマシンが行っているのは、意味解析が終わった最後のバイトコードの実行のところということだ。
なるほど、スタックマシンについて少し理解した。

【Rust】最大値・最小値【Algorithm】

変数を用意しておいて、最大値、最小値の値を更新していくだけですね。
ループを抜けるのは10000以上か0以下としています。

fn main() {

   let mut imax: i32 = -1;
   let mut imin: i32 = 10000;
   let mut kmax: i32 = 0;
   let mut kmin: i32 = 0;
   let mut k: i32 = 0;
   let mut i: i32 = 0;

   while(i >= 0 && i <= 9999){
      println!("4桁以内の正の整数を入力してください。");
      let mut x = String::new();
      std::io::stdin().read_line(&mut x).expect("Failed to read line");
      x = x.trim_end().to_owned();
      i = x.parse::<i32>().unwrap();
      if (0 < i && i < 10000) {
         k += 1;
         if(imax < i) {
            imax = i;
            kmax = k;
         }

         if(imin > i) {
            imin = i;
            kmin = k;
         }
      }
   }

   if (k == 0) {
      println!("入力されていません。");
   } else {
      println!("最大値 {}({}回目)", imax, kmax);
      println!("最小値 {}({}回目)", imin, kmin);
   }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.90s
Running `target/debug/basic`
4桁以内の正の整数を入力してください。
100
4桁以内の正の整数を入力してください。
20
4桁以内の正の整数を入力してください。
50
4桁以内の正の整数を入力してください。
29
4桁以内の正の整数を入力してください。
829
4桁以内の正の整数を入力してください。
2222
4桁以内の正の整数を入力してください。
2452
4桁以内の正の整数を入力してください。
11245
最大値 2452(7回目)
最小値 20(2回目)

うーむ なるほど〜

【Rust】所持金ピッタリ【Algorithm】

所持金ピッタリになる組み合わせをwhile文で全パターン調べる

fn main() {

   println!("所持金を入力してください。");
   let mut x = String::new();
   std::io::stdin().read_line(&mut x).expect("Failed to read line");
   x = x.trim_end().to_owned();
   let mut wallet: i32 = x.parse::<i32>().unwrap();
   
   let mut x = 0;
   let mut y = 0;

   while(500 * x + 300*y <= wallet) {
      while(500 * x + 300*y <= wallet) {
         if(500*x + 300*y == wallet) {
            println!("500円のケーキ:{}個", x);
            println!("300円のケーキ:{}個\n", y);
         }
         y += 1;
      }
      x += 1;
      y=0;
   }
}

Running `target/debug/basic`
所持金を入力してください。
3000
500円のケーキ:0個
300円のケーキ:10個

500円のケーキ:3個
300円のケーキ:5個

500円のケーキ:6個
300円のケーキ:0個

while文の入れ子って珍しい感じがするが、条件に当てはまるまでのループはforよりwhileの方が分かりやすい感じはしますね。

【React.js】JSXでhrefの文字列と値を連結する

文字列の連結は、{‘hoge’ + value} と言うように、{}の中で連結する

render() {
    if (this.state.data) {
      return <div>
        <table class="table">
        <thead>
        <tr>
          <th scope="col">Height</th>
          <th scope="col">Time</th>
          <th scope="col">Hash</th>
        </tr>
        </thead>
        <tbody>
          {this.state.data.map((value) => (
            <tr>
              <th scope="row"><a href={'/data/' + value.height} >{value.height}</a></th>
              <td>{value.time}</td>
              <td>{value.hash}</td>
            </tr>
          ))}
        </tbody>
        </table>
      </div>
    }
    else {
      return <div>Loading...</div>
    }
  }

リンクは以下のように設定されます
http://***/data/1

ここに辿り着くまでに結構時間かかりました… OK