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

抽象:进程

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

  • 时分共享:通过允许资源由一个实体使用一小段时间,然后由另一个实体使用一小段时间,如此下去,所谓的资源(例如,CPU 或网络链接)可以被许多人共享。
  • 空分共享:资源在空间上被划分给希望使用它的人,磁盘空间自然是一个空分共享资源。

3、进程API:

  • 创建(create)
  • 销毁(destroy)
  • 等待(wait)
  • 其他控制(miscellaneous control)
  • 状态(statu)

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

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

test1

C程序:

  • 使用栈存放局部变量、函数参数和返回地址。
  • 堆用于显式请求的动态分配数据。程序通过调用malloc()来请求这样的空间,并通过调用free()来明确地释放它。数据结构(如链表、散列表、树和其他有趣的数据结构)需要堆。

5、进程状态:

  • 运行(running)
  • 就绪(ready)
  • 阻塞(blocked)

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、用户模式和内核模式:

  • 用户模式(user mode)下,应用程序不能完全访问硬件资源。运行的代码会受到限制。运行时,进程不能发出I/O 请求。
  • 内核模式(kernel mode)下,操作系统可以访问机器的全部资源。
  • 操作系统还提供了陷入(trap)内核和从陷阱返回(return-from-trap)到用户模式程序的特别说明,以及一些指令。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值