【八股03.26】

【八股03.26】

1.什么是进程?什么是线程?进程和线程的区别?进程和线程的使用场景?

什么是进程?

进程是程序运行的一个实例,是资源分配的最小单位,每个进程都有自己独立的地址空间。


什么是线程?

线程是进程中的一个执行流,是CPU调度的最小单位,同一个进程中的所有线程共享进程的地址空间,不过每个线程都有自己的栈和寄存器等。


进程和线程的区别?

1.进程是资源分配的基本单位,线程是CPU调度的基本单位。
2.同一个进程中的所有线程共享本进程的地址空间,不同的进程有独立的地址空间。
3.一个进程崩溃后不会对其它进程造成影响,但是一个线程崩溃,会导致整个进程崩溃,即同一个进程中的其它线程页会崩溃。
4.线程间的切换开销比进程间的切换开销更小。


进程和线程的使用场景?

1.需要频繁创建和销毁时优先使用线程(进程的创建和销毁开销过大)
2.需要进行大量计算时使用线程(大量计算通常需要很多cpu,会频繁进行切换,进程切换的代价太大)
3.强相关性的任务使用线程,弱相关性的任务使用进程。(考虑从进程间通信比线程间通信复杂的角度解释)
4.可能扩展到多机分布时使用进程,多核分布的话使用线程就行了。

2.进程间通信?线程间通信?

进程间通信的方式?

1.管道:通常指匿名管道,通过系统调用pipe建立,不过只适用于父进程与子进程之间的通信。

2.FIFO:有名管道,通过系统调用mkfifo创建,即使不是父子进程也可以通过FIFO进行通信,它是有路径名的,以一种特殊的形式存在于文件系统中。

3.消息队列:在内核中。

4.共享内存:不同的进程共享一个给定的存储区进行通信,通常结合信号量一起使用。

5.信号:一种异步的通信方式,事先注册回调函数,当接收到信号时执行对应的回调函数,也可以设置忽略信号。

6.信号量:通过PV操作可以实现进程间的同步与互斥。

7.socket:通常用于不同主机之间的通信。


线程间通信的方式?

(不同进程中的线程间通信相当于进行进程间通信吧?)

1.互斥锁:让不同线程互斥地访问某个共享资源,防止并发访问导致的数据竞争。

2.信号量:信号量通常表示资源的个数,通过PV操作实现线程间的同步。(再解释一下PV操作)
PV操作:当线程需要获取资源时,进行P操作,如果资源个数为0就会阻塞;当线程释放资源时,进行V操作,资源个数加1。

3.条件变量(与互斥锁搭配使用):当某个条件成立时,线程会挂起直到另一个线程唤醒它。

3.进程和线程的切换

进程的切换:
先将当前进程的上下文信息(比如堆,栈,寄存器等)保存到PCB进程控制块中,修改PCB的信息,比如将运行状态改为就绪或阻塞,再将该PCB加入对应的阻塞队列或就绪队列。然后调度新进程,从新进程的PCB中加载它的上下文信息,让PC指向新进程的代码,开始执行新进程。

线程的切换:
如果是不同的进程中的线程间切换,那相当于进行进程间切换。
如果是同一个进程中的线程间切换,那比进程间的切换开销小很多,因为他们共享进程的地址空间,只要保存线程的私有数据如栈和寄存器即可,然后加载新调度的线程的上下文信息,就可以开始执行新线程了。

4.const与函数重载?

1.const用于成员函数,在函数尾部加const可以实现重载。

2.const用于非成员函数,在函数参数加const,并且参数是指针或引用类型时可以实现重载。

void fun(int *p) { // 或者int &r
    cout << "fun() called" << endl;
}

void fun(const int *p) { // 或者const int &r
    cout << "fun() const called" << endl;
}

但是fun(int i)fun(const int i)不可以重载,因为这两个都是修改不了i的,按值传递得到的是拷贝后的副本。

同理fun(int *p)fun(int *const p)也不可以重载,因为这两个都是传参的时候都是直接拷贝指针的值,也就是指向的地址,所以都不可以改变指针的指向。

5.操作系统内核态和用户态

内核态和用户态是操作系统为了保护系统资源和实现权限控制而设计的两种不同的CPU运行级别。用户态是用户程序正常执行时的状态,内核态是操作系统陷入内核后执行内核代码或响应系统调用时的状态。

与用户态相比,内核态能执行一些特权指令,能够访问内核空间以及一些受保护的系统资源。
当进行系统调用、出现异常、发生外部中断时,操作系统会由用户态切换到内核态进行处理。

6.什么是死锁?死锁的必要条件?如何解决死锁?

死锁:因为资源分配不合理,多个进程循环等待其它进程占有的资源而无限期僵持下去的局面。如果没有外力的作用,所有进程都将一直维持阻塞的状态。


死锁的必要条件?

互斥访问
不可剥夺
请求并保持
循环等待

如何解决死锁?

只要破坏死锁的4个必要条件即可避免死锁。
当进程新的资源未得到满足时,释放已经占有的资源。破坏不可剥夺条件。
资源预分配,在进程运行前将它所需要的所有资源一次性分配,破坏请求并保持条件。
采取资源有序分配法,将资源进行编号,按照一定的顺序分配资源,破坏循环等待条件。

7.孤儿进程?僵尸进程?守护进程?

孤儿进程:父进程没有调用wait函数等待子进程结束,并且父进程比子进程更早结束,此时子进程就会变为孤儿进程,系统中的init(0号进程)就会收留这个孤儿进程,从而变成init进程的子进程。

僵尸进程:父进程没有调用wait函数清理子进程占用的空间,并且子进程比父进程更早结束,此时子进程已经结束运行,但是占用的资源没有被释放,就变成了僵尸进程,此时只有等到父进程结束运行,子进程变为孤儿进程,再被init进程收留,才会被init进程清理。

守护进程:脱离终端在后台执行的程序。

8.虚拟内存?

虚拟内存是一种虚拟化的内存管理技术,它使得进程认为自身独占一片连续完整的地址空间,但实际上是分散在不同的物理内存中,甚至还有部分是在外存中,当访问到不在内存中的数据时,就要发生数据交换,比如一些页面置换之类的。
我们程序中使用的地址都是虚拟地址,操作系统会帮我们完成虚拟地址到物理地址的转换,这样更加安全,用户进程不会非法访问到其它进程的地址空间。
而且这种虚拟化也能让我们执行多个程序,如果采用的是物理地址,比如在32位linux系统下,那每个进程都要分配4G的内存空间,根本运行不了几个程序。

9.内存碎片?

内存碎片:系统中不可用的空闲内存。


内部碎片:已经被分配出去,但没有被使用的内存空间。

产生原因:因为分配的虚拟地址通常需要为4,8,16等数的倍数,当一个进程试图申请41字节空间时,可能会得到44或48这种大小的内存空间,而他只需要使用41字节,于是剩下的内存空间没有被利用,就变成了内碎片。


外部碎片:没有被分配,但是因为太小了无法分配给任何进程的内存空间。

产生原因:假如一个进程A申请了第03字节的内存空间,而后又有个进程B申请了第411字节的内存空间,然后进程A将这4个字节的内存空间释放了,若之后的进程申请的内存空间大小一直大于4字节,并且进程B一直没有归还空间,那么第0~3字节的内存空间就一直无法被使用,变成了外碎片。

10.写时拷贝

写时拷贝,就是写的时候才分配内存空间并拷贝,是一种推迟甚至避免数据拷贝的方法。fork系统调用创建子进程的时候就使用了写时拷贝,因为创建出来的子进程和父进程的地址空间应该是完全一样的,当执行读操作时其实是没必要复制的,可以共用一个地址空间,只有当进行写操作时,才会真正复制父进程的地址空间。

  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值