1 进程和线程的区别
进程是操作系统分配资源的最小单位,进程就是一个正在运行的程序以及程序所需要的资源,包括内存资源、文件资源以及系统资源。
那为什么要引入线程呢,原因是如果是单线程的进程在运行过程中需要等待某些资源的到来,那么此时CPU将空转,CPU资源就被白白浪费了。为了避免这种情况就引入了线程,一个进程下可以有多个线程,这多个线程可以各司其职,当其中一个线程等待资源时,可以调度另外一个线程来使用CPU,通过这样的方式可以来提高CPU利用率。
除此之外,线程只有栈、程序计数器和寄存器等资源,所以创建、销毁一个线程以及线程的上下文切换都比进程的开销要小。进程与进程之间互相独立、互不干扰,但是线程与线程之间需要考虑线程安全问题,所以线程访问共享内存时需要进行同步。
2 协程
协程是一个用户级的轻量级的线程,在某个子程序内部跳转到另外一个子程序运行,在恰当的时机又跳转到原来的子程序内部运行。
协程有两个优点:
1 协程上下文的开销小;
2 不需要锁机制来同步,效率高,并发性比较好。
3 进程的状态有哪几种
进程的状态主要是三种:运行态、就绪态、阻塞态,其中运行态和就绪态可以互相转换。
1 运行态的进程时间片消耗完就会进入就绪态;
2 就绪态的进程被调度会进入运行态;
3 运行态的进程等待某些资源就会进入阻塞态;
4 阻塞态的进程资源到达就进入就绪态。
4 进程调度算法
非抢占式的调度算法
1 先来先服务算法:不公平
2 短进程优先:会导致长进程饥饿,不公平
抢占式的调度算法
1 最短剩余时间优先
2 时间片轮转
3 优先级
4 多级反馈队列:结合了时间片和优先级的调度算法,设置多个队列存放就绪的进程,不同队列分配不同的时间片,时间片越小优先级越高。如果一个进程被调度仍然没有执行完成,那么放到下一个时间片更大的队列中去。通过这样的方式可以使一个长进程在较小的周期内执行完成。
5 Linux下创建进程的方法
Linux下通过fork()系统调用来创建新的进程,通过fork的返回值来判断在子进程内还是在父进程内,返回值为0,代表在子进程内,返回值大于0代表在父进程内,返回值即为子进程的PID。
fork()创建出来的子进程是父进程的一份拷贝,如果fork()之后立马exec(),让子进程放入内存运行,那么之前的拷贝就是多余的,所以这里涉及到了写时复制。
写时复制 (copy on write)
在fork之后,子进程没有进行写操作,那么子进程可以共享父进程的地址空间,等到进行写操作,才去进行相关数据的复制。通过这种方式来延迟拷贝动作或者说是避免不必要的拷贝,来提高效率。
6 僵尸进程、孤儿进程
僵尸进程是子进程没有被正确销毁,会导致系统资源无法正常释放,是有危害的;孤儿进程是子进程还没结束,父进程先结束,这时候孤儿进程会被Init进程所接管,没有危害。
一个子进程结束以后,需要被父进程通过wait()系统调用来接收子进程的信息后,才能释放相关的系统资源,比如PID号。如果系统中的僵尸进程过多,就会导致无法创建新的进程。
7 进程间通信的方法
进程间通信的方法主要有管道、消息队列、共享内存、socket。
1 管道分为匿名管道和有名管道
匿名管道主要用于具有亲缘关系的进程通信,通过pipe系统调用创建管道,一端读一端写,管道的本质就是一块缓存区。
有名管道可以用于不具有亲缘关系的进程通信,类似于读写文件。
2 消息队列
3 共享内存
共享内存的实现机制是通过mmap将普通文件或资源映射到不同进程的地址空间中去,这样我们就可以像访问内存一样去访问共享的普通文件。
共享内存如何同步
主要是通过POSIX有名信号量,来对共享内存进行同步。通过sem_open来创建有名信号量。
4 不同主机上的进程通信使用socket
为什么要有虚拟内存
在早期,呈现给编程人员的存储模型就是简单的物理内存。在这种情况下,想在内存中通时运行两个程序是行不通的。如果一个程序在某个位置写入一个新的值,将会擦掉第二个程序存放在相同位置的所有内存,所以同时运行两个程序是行不通的,这两个程序会立刻崩溃。
所以有了一种存储器抽象:地址空间。
虚拟内存技术的作用:
1 使多个进程有自己独立的内存空间,相互独立,互不干扰。
2 可以处理内存超载,也就是可以使得大型程序运行在有限的物理内存中,允许程序在只有一部分被调入内存的情况下运行。
3 使用分页技术,使得每个进程都可以被分割成很多块,一方面便于管理,另一方面比起分段式可以减少内存碎片,效率也更高。