1.1: xv6进程(xv6 process)

  • 包含用户态存储(指令,数据和堆栈),内核中存储了每一个进程的相关状态

  • xv6是分时系统

  • 进程间(对于用户看来是透明transparently,感受不到)切换使用CPU计算资源

    • 当出现进程调度时,一般挂起的进程会保存CPU寄存器的状态(执行上下文),当下次恢复运行的时候再将执行上下文恢复到CPU寄存器中

    • 内核态通过PID(process identifier)来关联进程

系统调用列表

System call

Description

nt fork()

Create a process, return child’s PID.

int exit(int status)

Terminate the current process; status reported to wait(). No return.

int wait(int *status)

Wait for a child to exit; exit status in *status; returns child PID.

int kill(int pid)

Terminate process PID. Returns 0, or -1 for error.

int getpid()

Return the current process’s PID.

int sleep(int n)

Pause for n clock ticks.

int exec(char *file, char *argv[])

Load a file and execute it with arguments; only returns if error.

char *sbrk(int n)

Grow process’s memory by n zero bytes. Returns start of new memory.

int open(char *file, int flags)

Open a file; flags indicate read/write; returns an fd (file descriptor).

int write(int fd, char *buf, int n)

Write n bytes from buf to file descriptor fd; returns n.

int read(int fd, char *buf, int n)

Read n bytes into buf; returns number read; or 0 if end of file.

int close(int fd)

Release open file fd.

int dup(int fd)

Return a new file descriptor referring to the same file as fd.

int pipe(int p[])

Create a pipe, put read/write file descriptors in p[0] and p[1].

int chdir(char *dir)

Change the current directory.

int mkdir(char *dir)

Create a new directory.

int mknod(char *file, int, int)

Create a device file.

int fstat(int fd, struct stat *st)

Place info about an open file into *st.

int link(char *file1, char *file2)

Create another name (file2) for the file file1.

int unlink(char *file)

Remove a file.

fork系统调用

  • 一个进程可以通过系统调用fork来创建新的进程,fork会创建一个调用进程内存的完整拷贝(exact copy)包括指令集合(instructions),数据和堆栈,但是他们在隔离的内存空间和寄存器里执行的,变量之间不会相互影响。

    • fork系统调用执行的结果在原始进程中和在新的进程中都有返回

      • 在原始的进程中,fork返回新的进程的PID(子进程pid)

      • 在新产生的进程中,fork运行的结果返回0

    • 原始的进程和新的进程通常也被称为父进程和子进程

    • 虽然父进程和子进程在开始时具有相同的内存内容,但是他们在隔离的内存空间和寄存器里执行的,变量之间不会相互影响

  • fork系统调用示例

int pid = fork();
//父进程调用fork系统调用创建子进程,子进程拷贝一份完整的进程memory,同时fork在父进程和子进程里均有返回。
if(pid > 0){
    printf("parent: child=%d\n", pid); //pid大于0,则为父进程满足的条件,这时输出子进程id并等待子进程执行结束,结束后返回子进程完成任务的状态输出
    pid = wait((int *) 0);
    //wait系统调用返回当前进程退出的或killed的子进程的PID,同时将退出的子进程的状态拷贝到传递给wait函数的内存地址。
    //如果调用进程的所有子进程都没有退出,wait函数将会等待直到有一个子进程退出,如果调用的进程没有子进程,wait函数会立马返回-1。  
    //如果父进程不需要关注子进程退出的状态,则可以传一个0地址给wait函数。
    printf("child %d is done\n", pid);
} 
else if(pid == 0){//pid等于0,则为子进程内执行的代码,直接输出字符串后并退出
    printf("child: exiting\n");
    exit(0); //exit系统调用使得进程停止执行并开始释放资源如内存和打开的文件等,一般传递0表示正常退出,如果传递1表示出错
} else {//否则,fork创建失败
    printf("fork error\n");
}

exec系统调用

  • exec系统调用和fork的不同在于

    • 不创建新进程,而是让当前进程运行一个不同的程序,加载的程序镜像为满足ELF(ELF, Executable and Linkable Format)格式的可执行文件,一般通过源码编译而成

    • ELF包含文本区(代码指令),数据区(全局和静态变量),动态链接区等

    • 支持动态链接和静态链接

    • 方便调试,基于section和symbol tables的丰富结构使得方便进行调试和二进制分析

    • 当调用exec后,当前进程的代码和数据都被新程序替换,但进程ID不变