一、操作系统导论——进程(知识点总结)

/ 操作系统导论 / 没有评论 / 512浏览

抽象:进程

1、进程的定义:操作系统为正在运行的程序提供抽象,就是所谓的进程。
2、时分共享和空分共享:

3、进程API:

4、进程创建的某些细节:

操作系统运行程序必须做的第一件事是将代码和所有静态数据(例如初始化变量)加载(load)到内存中,加载到进程的地址空间中。现代操作系统惰性(lazily)执行该过程,即仅在程序执行期间需要加载的代码或数据片段,才会加载。

test1

C程序:

5、进程状态:

test2

7、进程控制块(Process Control Block,PCB),也称进程列表:是一个包含每个进程信息的C 结构。

倒叙:进程API

1、fork()系统调用:

(1 系统调用fork()用于创建新进程。
(2 子进程不会从main()函数开始执行。而是直接从fork()系统调用返回,就好像是它自己调用了fork()。
(3 子进程并非完全拷贝父进程。

代码:

// p1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    //打印父进程pid
    printf("hello world (pid:%d)\n", (int) getpid());	
    //fork()在父进程中返回子进程pid,在子进程中返回0
    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(-1);
    } else if (rc == 0) {			
        //子进程执行
        printf("hello, I am child (pid:%d)\n", (int) getpid());
    } else {						
        // 父进程执行
        printf("hello, I am parent (pid:%d)\n", (int) getpid());
    }
    return 0;
}

执行结果:

zk@ubuntu:~/Desktop/OS/5$ gcc -o p1 p1.c
zk@ubuntu:~/Desktop/OS/5$ ./p1
hello world (pid:11990)
hello, I am parent (pid:11990)
hello, I am child (pid:11991)

可见在我的机器里,父进程先执行,子进程后执行。

2、wait()系统调用:可等待子进程执行完毕,返回父进程执行。
代码:

// p2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    //打印父进程pid
    printf("hello world (pid:%d)\n", (int) getpid());	
    //fork()在父进程中返回子进程pid,在子进程中返回0
    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(-1);
    } else if (rc == 0) {			
        //子进程执行
        printf("hello, I am child (pid:%d)\n", (int) getpid());
    } else {		
        int wc = wait(NULL);				
        // 父进程执行
        printf("hello, I am parent (pid:%d)\n", (int) getpid());
    }
    return 0;
}

执行结果:

zk@ubuntu:~/Desktop/OS/5$ gcc p2.c -o p2
zk@ubuntu:~/Desktop/OS/5$ ./p2
hello world (pid:1892)
hello, I am child (pid:1893)
hello, I am parent (pid:1892)

可见子进程先执行。

3、exec()系统调用:这个系统调用可以让子进程执行与父进程我同的程序。 代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
    //打印父进程pid
	printf("hello world (pid:%d)\n", (int) getpid());
    //fork()在父进程中返回子进程pid,在子进程中返回0
	int rc = fork();
	if (rc < 0) {
		fprintf(stderr, "fork failed\n");
		exit(1);
	} else if (rc == 0) {
        //子进程执行
		printf("hello, I am child (pid:%d)\n", (int) getpid());
		char *myargs[2];
		myargs[0] = strdup("pwd");
		myargs[1] = NULL; 
		execvp(myargs[0], myargs); 
		printf("this shouldn't print out");
	} else {
		int wc = wait(NULL);
		// 父进程执行
        printf("hello, I am parent (pid:%d)\n", (int) getpid());
	}
	return 0;
}

执行结果:

zk@ubuntu:~/Desktop/OS/5$ ./p3
hello world (pid:2024)
hello, I am child (pid:2025)
/home/zk/Desktop/OS/5
hello, I am parent (pid:2024)

exec()会从可执行程序中加载代码和静态数据,并用它覆写自己的代码段(以及静态数据),堆、栈及其他内存空间也会被重新初始化。 子进程执行exec()之后,几乎就像p3.c 从未运行过一样。对exec()的成功调用永远我会返回。

4、这种分离fork()及exec()的做法在构建UNIX shell 的时候非常有用,因为这给了shell 在fork 之后exec 之前运行代码的机会,这些代码可以谁运 行新程序前改变环境,从而让一系列有趣的功能很容易实现。
UNIX 管道也是用类似的方式实现的,但用的是pipe()系统调用。

受限直接执行

1、用户模式和内核模式:

2、操作系统回收CPU使用权的方法(当进程恶意无限循环时):利用时钟中断重新获得控制权。
3、上下文切换:操作系统为当前正在执行的进程保存一些寄存器的值(例如,到它的内核栈),并为即将执行的进程恢复一些寄存器的值(从它的内核栈)。