操作系统面试题 | 小林coding

操作系统面试题 | 小林coding 用户态和内核态用户态和内核态的区别内核态Kernel Mode在内核态下CPU可以执行所有的指令和访问所有的硬件资源。执行特权指令。用户态User Mode在用户态下CPU只能执行部分指令集无法直接访问硬件资源。执行用户指令。指令集MOV EAX, [0x1000]。汇编后的或者编译后的代码进程管理线程和进程的区别是什么本质区别进程是操作系统资源分配的基本单位而线程是任务调度和执行的基本单位源码中的全局变量属于进程这个全局变量就可以是 进程的硬件呀线程是执行的基本单位。因为底下这句代码pthread_create(thread1,NULL,task,(void*)数据加载);//task就是这个线程执行的任务cpu就分给这个任务进程线程协程的区别是什么协程不会不学了你说到进程是分配资源的基本单位那么这个资源指的是什么虚拟内存每个进程都会有一个虚拟内存虚拟地址再 》 物理地址。父子进程都会即使有相同 的虚拟内存但虚拟地址》物理地址不同文件描述符 父子进程都会有相同的fd但是是两个完全一样的并不是一个进程切换和线程切换的区别进程切换 换的是内存线程切换 换的是 线程带着的寄存器寄存器是小内存嘛进程上下文有哪些首先明白 cpu上下文 CPU 寄存器cache和程序计数器PC根据cpu寄存器中存储的【任务】把 CPU 上下文切换分成进程上下文切换、线程上下文切换和中断上下文切换。所以 进程的上下文切换就是把 进程所管的用户内存空间的内存换了一下内核空间换了一下。而切换的东西都记在PCB中进程间通讯有哪些方式在我们的用户内存空间之外还有 内核空间内核空间中有 进程间通信的 帮手匿名管道父子进程间拿 一个fd 交流内核空间提供一段内核空间内存当【缓冲区】【缓冲区】是为了 缓冲父子进程间时机不匹配 一个想do一个不想do时 就放在【缓冲区】intpfd[2];// pfd[0] 读端, pfd[1] 写端charbuf[1024];read(pfd[0],buf,sizeof(buf));命名管道两进程 约定一个有名字的管道管道也是文件类型的一种交流一下内核空间提供一段内核空间内存当【管道】constchar*fifo_name/tmp/my_fifo;// 创建命名管道如果已存在mkfifo会失败所以这里忽略错误mkfifo(fifo_name,0666);// 权限 0666消息队列有一个meg的结构体有了原子性不再和管道一样是二进制的流会先将消息写在用户内存空间中再 复制到 内核空间中的消息队列上strcpy(msg_send.mtext,Hello Message Queue!);msgsnd(msqid,msg_send,sizeof(msg_send.mtext),0);// 注意长度计算共享内存开辟的共享内存就在 用户内存空间中不在内核空间中通过页表 将两个进程不同的 虚拟地址 》相同的 物理地址所以就有了 进程间的 同步问题信号在命令行使用 kill -9 1234命令就是 shell 进程通过 kill系统调用向 PID 为 1234 的进程发送了一个 SIGKILL信号要求它强制终止。线程间通讯有什么方式原子操作原子操作只能保护一个变量比如 std::atomic count; count;。它只能保证这一个数字的加减是绝对安全的不能被拆分的。锁机制保护的是一段复杂的代码块临界区原子操作纯硬件级依赖 CPU 的特定指令机制OS 软件级也有一部分原子操作但是也依赖操作系统的调度。原子操作遇到另一个原子操作时。是“忙等”的线程一直是运行态。锁机制会在os的调度下从运行态》阻塞态。互斥锁如果进线遇到互斥主动放弃CPU从运行态》阻塞态。与自旋锁的区别当我上锁时其他人都不能来读。与读写锁的区别// 共享资源intticket_count100;void*sell_ticket(void*arg){intthread_id*(int*)arg;while(1){// 访问共享资源前加锁 pthread_mutex_lock(mutex);// 1. 加锁// --- 临界区开始 (被锁保护) ---if(ticket_count0){usleep(50000);// 模拟耗时操作// --- 临界区结束 ---pthread_mutex_unlock(mutex);// 2. 释放锁}returnNULL;}pthread_create(t1,NULL,sell_ticket,id1);pthread_create(t2,NULL,sell_ticket,id2);自旋锁通过TAS或者CAS指令实现的。如果进线遇到互斥线程会一直循环尝试获取锁一直处于运行态。条件变量互斥锁是线程间互斥的机制条件变量则是同步机制。写出条件变量得知道这几个点。unique_lock lock(mtx);只有这个智能锁有mtx的所有权在这个【有参构造函数】中会自动给对象lock上锁。cv.wait(lock)时又会自动给对象lock解锁。阻塞时必须是这个模板。while(条件不满足){cv.wait(lock);}#includeiostream#includethread#includemutex#includecondition_variableusingnamespacestd;mutex mtx;// 听你的搞两个条件变量泾渭分明condition_variable cv_odd;condition_variable cv_even;intcount_num1;constintMAX_NUM100;// // 奇数线程// voidprintOdd(){while(count_numMAX_NUM){unique_lockmutexlock(mtx);// 1. 我在奇数专属的床上睡觉while(count_num%20count_numMAX_NUM){cv_odd.wait(lock);}if(count_numMAX_NUM)break;cout奇数线程: count_numendl;count_num;// 2. 核心绝杀我不随便叫人了我精准地按响偶数线程床头的闹钟cv_even.notify_one();}}// // 偶数线程// voidprintEven(){while(count_numMAX_NUM){unique_lockmutexlock(mtx);// 1. 我在偶数专属的床上睡觉while(count_num%2!0count_numMAX_NUM){cv_even.wait(lock);}cout偶数线程: count_numendl;count_num;// 2. 核心绝杀精准按响奇数线程床头的闹钟cv_odd.notify_one();}}intmain(){threadt1(printOdd);threadt2(printEven);t1.join();t2.join();cout打印完毕endl;return0;}利用信号量实现的读写锁读写锁有一个独享所有权的写锁和一把共享所有权的读锁多个读者可以同时进行读写者必须互斥只允许一个写者写也不能读者写者同时进行写锁读锁锁自旋锁是什么应用在哪些场景自旋锁就是 CPU 提供的 CAS 函数Compare And Swap当锁住的代码执行时间很短就不应该用互斥锁而应该选用自旋锁互斥锁VS自旋锁互斥锁 当cpu上有上锁的进程a时进程b调用了PCB恢复了现场试了一下发现不行就又回到阻塞态了。自旋锁 当cpu上有上锁的进程a时进程b调用了PCB恢复了现场试了一下发现不行还会一直在Run态一直试。好处是不用频繁调用PCB恢复现场。乐观锁和悲观锁有什么区别悲观锁 以防有人害我像MySQL中的不管有没有人用我先用【记录锁】锁住。SELECT*FROMaccountsWHEREid1FORUPDATE;-- 锁定数据乐观锁就像自旋锁一样。即使忙等我也只用等一会会。介绍一下brkmmapmmap函数是内存映射函数。在文件映射区域偷一块内存。网络 i/o你了解过哪些io模型你了解io吗io过程是指 数据在内存进程的用户空间 buffer与外部设备之间如socket buffer内核空间硬盘的传输过程readwrite函数是io函数阻塞客户端进程因为某些事故 卡在了代码的一处阻塞IO模型intmain(){// 1. 创建套接字if((socksocket(AF_INET,SOCK_STREAM,0))0){perror(Socket creation error);exit(EXIT_FAILURE);}// 2. 连接服务器printf(Connecting to server...\n);if(connect(sock,(structsockaddr*)serv_addr,sizeof(serv_addr))0){perror(Connection Failed);exit(EXIT_FAILURE);}// 3. 发送数据printf(Sending message: %s\n,hello);send(sock,hello,strlen(hello),0);//阻塞IO// 这个 read() 调用会阻塞客户端进程intvalreadread(sock,buffer,BUFFER_SIZE);// 4. 关闭套接字close(sock);printf(Client closed\n);return0;}非阻塞IO模型非阻塞将一个fd一块内核空间 设置为非阻塞状态。进程在io时 如果read不到数据到buffer中就会1稍后轮询 2IO复用IO复用使用了epoll等机制将该fd注册到epoll上。epoll此时像一个小秘书一旦fd发生变化fd满了可读/fd空了可写epoll就会通知进程快来IO。这个小秘书还管上百个fd这就是复用。select、poll、epoll 的区别是什么select将需要监视的fd放到文件描述符集合中数据结构是位图。一次次遍历这个位图标记为是否可读。需要处理读写时需要再遍历第二遍。poll--------------------------------------------------------------------用户空间中是用数组内核中是用链表事件驱动机制事件驱动一有事就触发某事 vs select的轮询机制一直问我能不能do。事件驱动通过回调函数实现。比如epoll中epoll监视epfd一旦sockfd的内存中有变化就会给到events数组中。一有事件就放到events数组中。epollepoll_create( )创建树epoll_ctl( ):将sockfd添加到树上在树上的都会被监视触发1LT模式sockfd的内存中有数据就会一直往evets数组中给epoll_wait( )几秒钟就会读一次就读到他。2ET模式。sockfd的内存中只有状态变化时才会往events数组中给epoll_wait( )一段时间就会统计events数组中的个数。代码加强理解触发模式LT。假设客户端发送了 1000 字节数据服务器每次读取 500 字节。// 第一次发现一个sockfd中有数据。立马触发。epoll_wait返回1nepoll_wait(epoll_fd,events,10,1000);// 返回1个事件read(sockfd,buf,500);// 服务端读取500字节// 第二次发现sockfd中还有数据。立马触发。epoll_wait返回1nepoll_wait(epoll_fd,events,10,1000);// 再次返回1个事件还有500字节可读read(sockfd,buf,500);// 服务端读取剩余500字节// 第三次sockfd中没有数据。epoll_wait返回0nepoll_wait(epoll_fd,events,10,1000);// 无事件阻塞或超时redisnginxnetty 是依赖什么做的这么高性能Reactor模式1.对于高性能服务器的一种开发模型。2.[反应堆]》【对事件的反应堆】 》【一有事件Reactor就会有反应】惊群现象当多个进程/线程在等待同一个资源如网络连接、锁、信号量时当资源可用事件发生系统会唤醒所有等待者但最终只有一个进程/线程能获得该资源其他进程/线程白忙一场后重新进入等待状态。..惊群现象发生的典型场景网络服务器中的 accept() 惊群经典案例当一个新连接到达时​所有4个子进程都会从 accept() 阻塞中唤醒【阻塞态】但只有一个进程能成功接受这个连接其他3个进程扑了个空【一瞬间的就绪态】需要重新进入【阻塞状】.(都上了一遍cpu运转了一会发现不合适又下来了------cpu上下文切换)解决方案 使用互斥锁保护 accept()传统方案用上了锁不还是会有惊群现象吗是的还是会有从大惊群变成了小惊群。