0%

操作系统and编译随记(2)

Linux操作系统

Linux Kernel

  • picture 1
  • 初始化完了之后就会变成一个中断(系统调用)的处理工具
  • systemd是所有进程的根进程

    构件最小的Linux系统

  • initramfs:
    • picture 2

      系统启动的第一个程序init

  • busybox是Linux中所有工具的集合打包,可以变成ls, vi等等(使用busybox [程序名])即可
    • 所有工具打包在一个二进制文件里
    • 功能较全面的工具包
    • picture 3
    • 如何实现不需要手动打busybox就使用所有命令?
      for cmd in $($BB --list); do
      $BB ln -s $BB /bin/$cmd
      done
      mkdir -p /tmp
      mkdir -p /proc && mount -t proc none /proc
      mkdir -p /sys && mount -t sysfs none /sys
      mknod /dev/tty c 4 1
      setsid /bin/sh </dev/tty >/dev/tty 2>&1
  • 循环是将busybox支持的所有命令,以这个命令的名字作为文件名,创建一个符号链接到/bin下,这样的话就可以实现命令行直接使用,实际上文件都是busybox
    • busy如何知道是哪个命令调用的自己?
      • 每个命令行调用传入的第一个参数都是调用的名称
  • 下面是挂载各种目录
  • proc有所有进程的信息
  • setsid:
    • 在新的会话中运行程序,也就是setsid命令 子进程从父进程继承了:SessionID、进程组ID和打开的终端。子进程如果要脱离这些,代码中可通过调用setsid来实现。,而命令行或脚本中可以通过使用命令setsid来运行程序实现。setsid帮助一个进程脱离从父进程继承而来的已打开的终端、隶属进程组和隶属的会话。
    • 给Linux内核传参rdinit=,即可修改启动的进程是init还是别的
  • init执行后会调用pivot_root函数
  • picture 4
  • /usr/sbin/init
    • picture 5
  • 需要在此之前挂在好根文件系统,将需要放入内核的模块(比如驱动)放入并且启动,例如网卡驱动
  • 创建完成之后再调换root,切换到systemd,进入正常的启动流程
  • picture 6
    • 第一句话是创建一个节点,主设备号是8次设备号是0,是磁盘
    • 第二句创建一个/newroot文件目录
    • 第三句将磁盘挂载到/newroot
    • 第四句switch root
  • systemd执行完后续的初始化之后,再将系统的权限交给窗口管理器
  • 窗口管理器完成用户界面的显示之后,将权限交给用户

    进程地址空间

    pmap

  • pmap [进程号]得到进程地址空间中的内容
  • 可以用gdb将一个程序暂停,使用info inferiors得到进程号,然后使用pmap得到空间内容
    • picture 7
  • 应用程序的内存是段组成的(带权限的连续的内存块)
    • picture 8
    • 第一个4k只读,第二个4k可读可执行
    • 16k只读

      cat /proc/进程号/maps

  • picture 9
  • 实际上pmap是通过读取/proc/进程号/maps实现的

操作系统API

  • 操作系统是API+对象

  • API+对象构成了内核

  • 但是用户不太可能跟内核直接交互,所以需要一个shell方便交互

  • bash中的``$()很多时候可以互换

  • bash中可以使用<将命令行的输入重定向

    • picture 10
  • vim支持将某一个程序的输出直接作为文件编辑,-的意思是标准输入

    • picture 11
  • 使用vim <(ls)这句命令可以将ls的输出传递给vim

  • ls | vim -效果类似

  • shell支持程序前后台切换

    • 可以使用jobs命令查看
    • 使用fg %序号将这个程序切换回前台
    • 使用Ctrl Z将一个现在在前台的程序转移到后台并且暂停
  • shell(或者bash)可以用-x选项在执行脚本文件的时候将脚本的执行过程打印出来,否则不容易调试

    shell实现管道

  • 查看运行的进程打开的文件描述符

    • picture 12
  • 文件描述符

    • 0 —— stdin (标准输入)
    • 1 —— stdout (标准输出)
    • 2 —— stderr (标准错误)
// Linux port of xv6-riscv shell (no libc)

#include "lib.h"
#include <fcntl.h>

// Parsed command representation
enum { EXEC = 1, REDIR, PIPE, LIST, BACK };

#define MAXARGS 10
#define NULL ((void *)0)

struct cmd {
int type;
};

struct execcmd {
int type;
char *argv[MAXARGS], *eargv[MAXARGS];
};

struct redircmd {
int type, fd, mode;
char *file, *efile;
struct cmd* cmd;
};

struct pipecmd {
int type;
struct cmd *left, *right;
};

struct listcmd {
int type;
struct cmd *left, *right;
};

struct backcmd {
int type;
struct cmd* cmd;
};

struct cmd* parsecmd(char*);

// Execute cmd. Never returns.
void runcmd(struct cmd* cmd) {
int p[2];
struct backcmd* bcmd;
struct execcmd* ecmd;
struct listcmd* lcmd;
struct pipecmd* pcmd;
struct redircmd* rcmd;

if (cmd == 0) syscall(SYS_exit, 1);

switch (cmd->type) {
case EXEC:
ecmd = (struct execcmd*)cmd;
if (ecmd->argv[0] == 0) syscall(SYS_exit, 1);
syscall(SYS_execve, ecmd->argv[0], ecmd->argv, NULL);
print("fail to exec ", ecmd->argv[0], "\n", NULL);
break;

case REDIR:
rcmd = (struct redircmd*)cmd;
syscall(SYS_close, rcmd->fd);
if (syscall(SYS_open, rcmd->file, rcmd->mode, 0644) < 0) {
print("fail to open ", rcmd->file, "\n", NULL);
syscall(SYS_exit, 1);
}
runcmd(rcmd->cmd);
break;

case LIST:
lcmd = (struct listcmd*)cmd;
if (syscall(SYS_fork) == 0) runcmd(lcmd->left);
syscall(SYS_wait4, -1, 0, 0, 0);
runcmd(lcmd->right);
break;

case PIPE:
pcmd = (struct pipecmd*)cmd;
assert(syscall(SYS_pipe, p) >= 0);
if (syscall(SYS_fork) == 0) {
syscall(SYS_close, 1);
syscall(SYS_dup, p[1]);
syscall(SYS_close, p[0]);
syscall(SYS_close, p[1]);
runcmd(pcmd->left);
}
if (syscall(SYS_fork) == 0) {
syscall(SYS_close, 0);
syscall(SYS_dup, p[0]);
syscall(SYS_close, p[0]);
syscall(SYS_close, p[1]);
runcmd(pcmd->right);
}
syscall(SYS_close, p[0]);
syscall(SYS_close, p[1]);
syscall(SYS_wait4, -1, 0, 0, 0);
syscall(SYS_wait4, -1, 0, 0, 0);
break;

case BACK:
bcmd = (struct backcmd*)cmd;
if (syscall(SYS_fork) == 0) runcmd(bcmd->cmd);
break;

default:
assert(0);
}
syscall(SYS_exit, 0);
}

int getcmd(char* buf, int nbuf) {
print("(sh-xv6) > ", NULL);
for (int i = 0; i < nbuf; i++) buf[i] = '\0';

while (nbuf-- > 1) {
int nread = syscall(SYS_read, 0, buf, 1);
if (nread <= 0) return -1;
if (*(buf++) == '\n') break;
}
return 0;
}

void _start() {
static char buf[100];

// Read and run input commands.
while (getcmd(buf, sizeof(buf)) >= 0) {
if (buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ') {
// Chdir must be called by the parent, not the child.
buf[strlen(buf) - 1] = 0; // chop \n
if (syscall(SYS_chdir, buf + 3) < 0)
print("cannot cd ", buf + 3, "\n", NULL);
continue;
}
if (syscall(SYS_fork) == 0) runcmd(parsecmd(buf));
syscall(SYS_wait4, -1, 0, 0, 0);
}
syscall(SYS_exit, 0);
}

// Constructors

struct cmd* execcmd(void) {
struct execcmd* cmd;

cmd = zalloc(sizeof(*cmd));
cmd->type = EXEC;
return (struct cmd*)cmd;
}

struct cmd* redircmd(struct cmd* subcmd, char* file, char* efile, int mode,
int fd) {
struct redircmd* cmd;

cmd = zalloc(sizeof(*cmd));
cmd->type = REDIR;
cmd->cmd = subcmd;
cmd->file = file;
cmd->efile = efile;
cmd->mode = mode;
cmd->fd = fd;
return (struct cmd*)cmd;
}

struct cmd* pipecmd(struct cmd* left, struct cmd* right) {
struct pipecmd* cmd;

cmd = zalloc(sizeof(*cmd));
cmd->type = PIPE;
cmd->left = left;
cmd->right = right;
return (struct cmd*)cmd;
}

struct cmd* listcmd(struct cmd* left, struct cmd* right) {
struct listcmd* cmd;

cmd = zalloc(sizeof(*cmd));
cmd->type = LIST;
cmd->left = left;
cmd->right = right;
return (struct cmd*)cmd;
}

struct cmd* backcmd(struct cmd* subcmd) {
struct backcmd* cmd;

cmd = zalloc(sizeof(*cmd));
cmd->type = BACK;
cmd->cmd = subcmd;
return (struct cmd*)cmd;
}

// Parsing

char whitespace[] = " \t\r\n\v";
char symbols[] = "<|>&;()";

int gettoken(char** ps, char* es, char** q, char** eq) {
char* s;
int ret;

s = *ps;
while (s < es && strchr(whitespace, *s)) s++;
if (q) *q = s;
ret = *s;
switch (*s) {
case 0:
break;
case '|': case '(': case ')': case ';': case '&': case '<':
s++;
break;
case '>':
s++;
if (*s == '>') {
ret = '+'; s++;
}
break;
default:
ret = 'a';
while (s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) s++;
break;
}
if (eq) *eq = s;

while (s < es && strchr(whitespace, *s)) s++;
*ps = s;
return ret;
}

int peek(char** ps, char* es, char* toks) {
char* s;

s = *ps;
while (s < es && strchr(whitespace, *s)) s++;
*ps = s;
return *s && strchr(toks, *s);
}

struct cmd* parseline(char**, char*);
struct cmd* parsepipe(char**, char*);
struct cmd* parseexec(char**, char*);
struct cmd* nulterminate(struct cmd*);

struct cmd* parsecmd(char* s) {
char* es;
struct cmd* cmd;

es = s + strlen(s);
cmd = parseline(&s, es);
peek(&s, es, "");
assert(s == es);
nulterminate(cmd);
return cmd;
}

struct cmd* parseline(char** ps, char* es) {
struct cmd* cmd;

cmd = parsepipe(ps, es);
while (peek(ps, es, "&")) {
gettoken(ps, es, 0, 0);
cmd = backcmd(cmd);
}
if (peek(ps, es, ";")) {
gettoken(ps, es, 0, 0);
cmd = listcmd(cmd, parseline(ps, es));
}
return cmd;
}

struct cmd* parsepipe(char** ps, char* es) {
struct cmd* cmd;

cmd = parseexec(ps, es);
if (peek(ps, es, "|")) {
gettoken(ps, es, 0, 0);
cmd = pipecmd(cmd, parsepipe(ps, es));
}
return cmd;
}

struct cmd* parseredirs(struct cmd* cmd, char** ps, char* es) {
int tok;
char *q, *eq;

while (peek(ps, es, "<>")) {
tok = gettoken(ps, es, 0, 0);
assert(gettoken(ps, es, &q, &eq) == 'a');
switch (tok) {
case '<':
cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
break;
case '>':
cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREAT | O_TRUNC, 1);
break;
case '+': // >>
cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREAT, 1);
break;
}
}
return cmd;
}

struct cmd* parseblock(char** ps, char* es) {
struct cmd* cmd;

assert(peek(ps, es, "("));
gettoken(ps, es, 0, 0);
cmd = parseline(ps, es);
assert(peek(ps, es, ")"));
gettoken(ps, es, 0, 0);
cmd = parseredirs(cmd, ps, es);
return cmd;
}

struct cmd* parseexec(char** ps, char* es) {
char *q, *eq;
int tok, argc;
struct execcmd* cmd;
struct cmd* ret;

if (peek(ps, es, "(")) return parseblock(ps, es);

ret = execcmd();
cmd = (struct execcmd*)ret;

argc = 0;
ret = parseredirs(ret, ps, es);
while (!peek(ps, es, "|)&;")) {
if ((tok = gettoken(ps, es, &q, &eq)) == 0) break;
assert(tok == 'a');
cmd->argv[argc] = q;
cmd->eargv[argc] = eq;
assert(++argc < MAXARGS);
ret = parseredirs(ret, ps, es);
}
cmd->argv[argc] = 0;
cmd->eargv[argc] = 0;
return ret;
}

// NUL-terminate all the counted strings.
struct cmd* nulterminate(struct cmd* cmd) {
int i;
struct backcmd* bcmd;
struct execcmd* ecmd;
struct listcmd* lcmd;
struct pipecmd* pcmd;
struct redircmd* rcmd;

if (cmd == 0) return 0;

switch (cmd->type) {
case EXEC:
ecmd = (struct execcmd*)cmd;
for (i = 0; ecmd->argv[i]; i++) *ecmd->eargv[i] = 0;
break;

case REDIR:
rcmd = (struct redircmd*)cmd;
nulterminate(rcmd->cmd);
*rcmd->efile = 0;
break;

case PIPE:
pcmd = (struct pipecmd*)cmd;
nulterminate(pcmd->left);
nulterminate(pcmd->right);
break;

case LIST:
lcmd = (struct listcmd*)cmd;
nulterminate(lcmd->left);
nulterminate(lcmd->right);
break;

case BACK:
bcmd = (struct backcmd*)cmd;
nulterminate(bcmd->cmd);
break;
}
return cmd;
}
  • 实际上的操作是使用一系列的syscall实现创建一个管道,将标准输出指向管道的输入,将标准输入指向管道的输出等等,从而将两个不同的进程通过管道连接起来
  • 管道里的一切都是文本
  • 上文中的p是指针(文件描述符)