算法岗位之操作系统部分面试准备2

  1. 讲述一下互斥锁(mutex)机制,以及互斥锁和读写锁的区别
  • 互斥锁和读写锁定义:
    互斥锁:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。
    读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。

  • 互斥锁和读写锁的区别:
    1)读写锁区分读者和写者,而互斥锁不区分
    2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。

  • Linux的4种锁机制:

  1. 互斥锁:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒
  2. 读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。
  3. 自旋锁:spinlock,在任何时刻同样只能有一个线程访问对象。但是当获取锁操作失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下会极大的提高效率。但如果加锁时间过长,则会非常浪费CPU资源。
  4. RCU:即read-copy-update,在修改数据时,首先需要读取数据,然后生成一个副本,对副本进行修改。修改完成后,再将老数据update成新的数据。使用RCU时,读者几乎不需要同步开销,既不需要获得锁,也不使用原子指令,不会导致锁竞争,因此就不用考虑死锁问题了。而对于写者的同步开销较大,它需要复制被修改的数据,还必须使用锁机制同步并行其它写者的修改操作。在有大量读操作,少量写操作的情况下效率非常高。
  1. 说一说进程状态转换图,动态就绪,静态就绪,动态阻塞,静态阻塞
  • 进程的五种基本状态:
    1)创建状态:进程正在被创建
    2)就绪状态:进程被加入到就绪队列中等待CPU调度运行
    3)执行状态:进程正在被运行
    4)等待阻塞状态:进程因为某种原因,比如等待I/O,等待设备,而暂时不能运行。
    5)终止状态:进程运行完毕

  • 交换技术
    当多个进程竞争内存资源时,会造成内存资源紧张,并且,如果此时没有就绪进程,处理机会空闲,I/0速度比处理机速度慢得多,可能出现全部进程阻塞等待I/O。
    针对以上问题,提出了两种解决方法:
    1)交换技术:换出一部分进程到外存,腾出内存空间。
    2)虚拟存储技术:每个进程只能装入一部分程序和数据。
    在交换技术上,将内存暂时不能运行的进程,或者暂时不用的数据和程序,换出到外存,来腾出足够的内存空间,把已经具备运行条件的进程,或进程所需的数据和程序换入到内存。
    从而出现了进程的挂起状态:进程被交换到外存,进程状态就成为了挂起状态。

  • 活动阻塞,静止阻塞,活动就绪,静止就绪
    1)活动阻塞:进程在内存,但是由于某种原因被阻塞了。
    2)静止阻塞:进程在外存,同时被某种原因阻塞了。
    3)活动就绪:进程在内存,处于就绪状态,只要给CPU和调度就可以直接运行。
    4)静止就绪:进程在外存,处于就绪状态,只要调度到内存,给CPU和调度就可以运行。
    从而出现了:
    活动就绪 —— 静止就绪 (内存不够,调到外存)
    活动阻塞 —— 静止阻塞 (内存不够,调到外存)
    执行 —— 静止就绪 (时间片用完)

  1. A a = new A; a->i = 10;在内核中的内存分配上发生了什么?*
  • A *a:a是一个局部变量,类型为指针,故而操作系统在程序栈区开辟4/8字节的空间(0x000m),分配给指针a。
  • new A:通过new动态的在堆区申请类A大小的空间(0x000n)。
  • a = new A:将指针a的内存区域填入栈中类A申请到的地址的地址。即*(0x000m)=0x000n。
  • a->i:先找到指针a的地址0x000m,通过a的值0x000n和i在类a中偏移offset,得到a->i的地址0x000n + offset,进行*(0x000n + offset) = 10的赋值操作,即内存0x000n + offset的值是10。
  1. 给你一个类,里面有static,virtual,之类的,来说一说这个类的内存分布
    1、static修饰符
    1)static修饰成员变量
    对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当做是类的成员,无论这个类被定义了多少个,静态数据成员都只有一份拷贝,为该类型的所有对象所共享(包括其派生类)。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新。
    因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以它不属于特定的类对象,在没有产生类对象前就可以使用。
    2)static修饰成员函数
    与普通的成员函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上来说,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,只能调用其他的静态成员函数。
    Static修饰的成员函数,在代码区分配内存。

2、C++继承和虚函数

C++多态分为静态多态和动态多态。静态多态是通过重载和模板技术实现,在编译的时候确定。动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行的时候确定。

动态多态实现有几个条件:

(1) 虚函数;

(2) 一个基类的指针或引用指向派生类的对象;

基类指针在调用成员函数(虚函数)时,就会去查找该对象的虚函数表。虚函数表的地址在每个对象的首地址。查找该虚函数表中该函数的指针进行调用。

每个对象中保存的只是一个虚函数表的指针,C++内部为每一个类维持一个虚函数表,该类的对象的都指向这同一个虚函数表。

虚函数表中为什么就能准确查找相应的函数指针呢?因为在类设计的时候,虚函数表直接从基类也继承过来,如果覆盖了其中的某个虚函数,那么虚函数表的指针就会被替换,因此可以根据指针准确找到该调用哪个函数。

3、virtual修饰符

如果一个类是局部变量则该类数据存储在栈区,如果一个类是通过new/malloc动态申请的,则该类数据存储在堆区。

如果该类是virutal继承而来的子类,则该类的虚函数表指针和该类其他成员一起存储。虚函数表指针指向只读数据段中的类虚函数表,虚函数表中存放着一个个函数指针,函数指针指向代码段中的具体函数。

如果类中成员是virtual属性,会隐藏父类对应的属性。

  1. 回答一下软链接和硬链接区别
    为了解决文件共享问题,Linux引入了软链接和硬链接。除了为Linux解决文件共享使用,还带来了隐藏文件路径、增加权限安全及节省存储等好处。若1个inode(索引结点,存储文件的元信息)号对应多个文件名,则为硬链接,即硬链接就是同一个文件使用了不同的别名,使用ln创建。若文件用户数据块中存放的内容是另一个文件的路径名指向,则该文件是软连接。软连接是一个普通文件,有自己独立的inode,但是其数据块内容比较特殊。

  2. 什么是大端小端以及如何判断大端小端
    大端是指低字节存储在高地址;小端存储是指低字节存储在低地址。我们可以根据联合体来判断该系统是大端还是小端。因为联合体变量总是从低地址存储。

  3. 静态变量什么时候初始化
    静态变量存储在虚拟地址空间的数据段和bss段,C语言中其在代码执行之前初始化,属于编译期初始化。而C++中由于引入对象,对象生成必须调用构造函数,因此C++规定全局或局部静态对象当且仅当对象首次用到时进行构造

  4. 用户态和内核态区别
    用户态和内核态是操作系统的两种运行级别,两者最大的区别就是特权级不同。用户态拥有最低的特权级,内核态拥有较高的特权级。运行在用户态的程序不能直接访问操作系统内核数据结构和程序。内核态和用户态之间的转换方式主要包括:系统调用,异常和中断。

  5. 如何设计server,使得能够接收多个客户端的请求
    多线程,线程池,io复用

  6. 死循环+来连接时新建线程的方法效率有点低,怎么改进?
    提前创建好一个线程池,用生产者消费者模型,创建一个任务队列,队列作为临界资源,有了新连接,就挂载到任务队列上,队列为空所有线程睡眠。改进死循环:使用select epoll这样的技术

  7. 怎么唤醒被阻塞的socket线程?
    给阻塞时候缺少的资源

  8. 怎样确定当前线程是繁忙还是阻塞?
    使用ps命令查看

  9. 就绪状态的进程在等待什么?
    被调度使用cpu的运行权

  10. select、poll、epoll之间的区别
    select、poll、epoll之间的区别

  11. 说一说多线程的同步,锁的机制
    同步的时候用一个互斥量,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。在这种方式下,每次只有一个线程可以向前执行

  12. 两个进程访问临界区资源,会不会出现都获得自旋锁的情况?
    单核cpu,并且开了抢占可以造成这种情况。

  13. windows消息机制知道吗,请说一说
    当用户有操作(鼠标,键盘等)时,系统会将这些操作转化为消息。每个打开的进程系统都为其维护了一个消息队列,系统会将这些消息放到进程的消息队列中,而应用程序会循环从消息队列中取出来消息,完成对应的操作。

  14. C++的锁你知道几种?
    互斥锁、读写锁、自旋锁、条件变量
    条件变量是利用线程间共享全局变量进行同步的一种机制。条件变量上的基本操作有:触发条件(当条件变为 true 时);等待条件,挂起线程直到其他线程触发条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值