深信服面试记录 2017

来源:互联网 发布:数据库中游标的用法 编辑:程序博客网 时间:2024/04/27 13:46

电话面了接近40分钟,对于自己简历上写的东西一定要清清楚楚明明白白,切勿模棱两可!!!

  • 1.线程的通信方式。A线程拥有互斥变量,B线程需要这个互斥变量,当A线程意外退出,B线程能否获取这个互斥变量?
  • 2.MFC中创建线程的流程、管理方式、使用那些API
  • 3.局部线程存储
  • 4.项目中使用内存映射解释,虚拟内存的原理,使用方式。直接读取磁盘和内存映射的区别。虚拟内存是否涉及IO操作。
  • 5.socket并发I/O
  • 6.图像在MFC框架中显示的流程及API
  • 7.MFC中PostMessage和depatchMessage的区别
  • 8.进程间的通信方式。匿名管道中,父子进程是如何通信的?
  • 9.MFC中不使用资源视图,如何动态创建一个窗口,使用到那些API
  • 10.vector中一开始获取头迭代器,当往尾部插入一个元素,之前的头迭代器能否使用?在超出了capacity失效情况下,如何判断这个头迭代器能否可用。

使用之前判断之前的capacity发生变化

  • 11.编程过程中使用过那些辅助调试工具。
  • 12.双缓存

——————————————————————————————————————————————

以下根据各个点给出答案

1.线程的通信方式。A线程拥有互斥变量,B线程需要这个互斥变量,当A线程意外退出,B线程能否获取这个互斥变量?

线程间的通信方式:互斥对象,信号量,事件对象,临界区。
在程序运行的时,操作系统维护了线程的信息以及该线程相关的互斥对象的信息,因此操作是系统是知道哪个线程终止了。如果某个线程得到所需要互斥对象的所有权,线程执行完毕没有释放该互斥对象的所有权就退出之后,操作系统一旦发现该线程已经终止,就会自动将该线程所拥有的互斥对象的线程ID设置为0,并将引用计数器置为0。所以B线程能获取这个互斥对象。——-《VC++深入详解》 P577


2.MFC中创建线程的流程、管理方式、使用那些API.

MFC创建线程流程及API。以互斥对象为例
1).HANDLE CreateThread(安全性,初始栈大小,线程处理函数,传递参数,线程创建标记,线程id)
2).HANDLE CreateMutex(安全性,所有权,互斥对象名称)

互斥对象包含:一个使用数量,一个线程ID,一个计数器。其中ID用于标识系统中的哪个线程当前拥有互斥都系。计数器用于指明该线程拥有互斥对象的次数。
第二参数为true,创建这个互斥对象的线程获得该对象的所有权,false不获取所有权
第三参数为NULL,创建一个匿名互斥对象。命名的互斥对象能保证应用程序只有一个实例。

3).BOOL ReleaseMutex(需要释放互斥对象的句柄)

互斥对象谁拥有谁释放

4).DWORD WaitForSingleObject(请求互斥对象的句柄,等待的时间)

线程必须主动请求互斥对象的使用权才有可能获得该所有权

5).CloseHandle(HANDLE hThread)

并没有中止新创建的线程,只是表示在主线程中对新创建的线程的引用不感兴趣


3.局部线程存储

Q&A
启动十个线程,如何记录这个10个线程的执行时间?要求能够在线程退出时打印?

很多人直观的想法是用一个全局变量表记录每个线程的执行时间。对于全局变量使用的缺点分析,这里不做讨论,很多地方都有讲解。本文介绍一种线程局部技术在多线程的应用场景。

线程局部存储,英文为Thread Local Storage,缩写为TLS。为什么要有TLS?原因在于,全局变量与函数内定义的静态变量,是各个线程都可以访问的共享变量。

在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。

如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要新的机制来实现。这就是TLS。
更多详细情况见:局部线程存储。


2017/7/27更新

4.项目中使用内存映射解释,虚拟内存的原理,使用方式。直接读取磁盘和内存映射的区别。虚拟内存是否涉及IO操作。

(1).实现内存页换出的算法:FIFO页面置换(不合理),MIN未来最远将使用(不可行,无法预知未来谁将使用),LRU最近最长一段时间没有使用(维护时间戳的方式,实现复杂度),LRU的近似实现二次机会算法(SCR,clock算法,不需要维护时间表,定时R置0)。

(2).关于为什么需要虚拟内存可以看该博客:操作系统管理内存的机制——为什么要设置虚拟内存?。 推荐书籍:《Windows核心编程》 p355

(3).虚拟内存是计算机内存系统管理的一种技术,它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间)。而实际上,它通常是被分离成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要的时候进行数据页面交换。

(4).内存映射文件三个用途:载入并运行.exe和动态链接库(DLL)文件;访问磁盘上的数据文件;同一台机器的不同进程之间共享数据。对访问磁盘上的数据文件进行详细说明。
内存映射文件允许开发人员预定一块地址空间并给区域调拨物理存储,与虚拟内存不同之处是,内存映射文件的物理存储器来自磁盘已有的文件,而不是来自系统的页交换文件。一旦把文件映射到地址空间,就可以对它进行访问就好像整个文件都被载入内存一样。不必再对文件执行I/O操作,处理大数据量的文件时,能起到重要的作用。


使用内存映射文件步骤
1).创建或打开一个文件内核对象,该对象标识了我们想要用作内存映射文件的那个磁盘文件。
HANDLE CreateFile();
2).创建一个文件映射内核对象来告诉系统文件的大小以及打算如何访问文件
HANDLE CreateFileMapping();
3).告诉系统把文件映射对象的部分或全部映射到进程的地址空间中。
PVOID MapViewOfFile();

当完成对内存映射文件的使用时,必须执行下面的这些步骤将它清除:
4).告诉系统从你的进程的地址空间中撤销文件映射内核对象的映象
(UnmapViewOfFile函数)
5).关闭文件映射内核对象
(CloseHandle函数,第2)步创建的对象)
6).关闭文件内核对象
(CloseHandle函数,第1)步创建的对象)


6.图像在MFC框架中显示的流程及API

CImage 是VC.NET中定义的一种MFC/ATL共享类,也是ATL的一种工具类,它提供增强型的(DDB和DIB)位图支持,可以装入、显示、转换和保存多种格式的图像文件,包括BMP、GIF、JPG、PNG、TIF等。CImage是一个独立的类,没有基类。(CImage类是基于GDI+的,从VC.net起引进,VC 6.0中没有。)
ATL (Active Template Library,活动模板库)是一套基于模板的 C++ 类,用以简化小而快的 COM 对象的编写。
为了在MFC程序中使用CImage类,必须包含ATL的图像头文件atlimage.h:(在VS08 SP1中不用包含)MFC显示图片。


7.MFC中PostMessage和depatchMessage的区别

  • 1).PostMessage和SendMessage的区别

    PostMessage只把消息放入队列,不管其他程序是否处理都返回,然后继续执行,这是个异步消息投放函数,PostMessage的返回值表示PostMessage函数执行是否正确。

    SendMessage必须等待其他程序处理消息完了之后才返回,继续执行,这是个同步消息投放函数。SendMessage的返回值表示其他程序处理消息后的返回值。

    如果在同一个线程内,PostMessage发送消息时,消息要先放入线程的消息队列,然后通过消息循环Dispatch到目标窗口。SendMessage发送消息时,系统直接调用目标窗口的消息处理程序,并将结果返回。SendMessage在同一线程中发送消息并不入线程消息队列。

    如果在不同线程内。SendMessage发送消息到目标窗口所属的线程的消息队列,然后发送消息的线程等待(事实上,他应该还在做一些监测工作,比如监视QS_SENDMESSAGE标志),直到目标窗口处理完并且结果返回,发送消息的线程才继续运行。这是SendMessage的一般情况,事实上,处理过程要复杂的多。比如,当发送消息的线程监测到有别的窗口SendMessage一个消息到来时,他直接调用窗口处理过程(重入),并将处理结果返回(这个过程不需要消息循环中GetMessage等的支持)。

    PostMessage 和SendMessage的区别主要在于是否等待其他程序消息处理。PostMessage只是把消息放入队列,不管其他程序是否处理都返回,然后继续执行;而SendMessage必须等待其他程序处理消息后才返回,继续执行。
    这里写图片描述

  • 2).DepathchMessage
    这里写图片描述

引用块内容


8.进程间的通信方式。匿名管道中,父子进程是如何通信的?

进程的通信方式:匿名管道,命名管道,信号量,消息队列,信号,共享内存,套接字Socket.
匿名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。只能实现本地机器上两个进程间的通信,而不能实现跨网络的通信。
创建匿名管道:BOOL CreatePipe(返回读句柄返回写句柄安全结构体指针管道缓冲区大小)
第三个参数:当为NULL,表示函数所返回的句柄不能被子进程所继承。匿名管道只能在父子进程之间进行通信,子进程如果想要想获得匿名管道的句柄,只能从父进程继承而来。当一个子进程从其父进程继承了匿名管道的句柄之后,这两个进程就可以通过该句柄进行通信了。所以第三个结构体参数的第三个成员很关键,为TRUE返回的句柄能够被新进程继承。
启动一个进程,可以调用BOOL CreateProcess();
第五个参数: 该参数用来指定父进程随后创建的子进程是否能够继承父进程的对象句柄。为TRUE,那么父进程的每个可继承的打开句柄都能被子进程继承。
总结:在父进程中创建匿名管道,返回该管道的读写句柄,然后调用CreateProcess函数启动子进程,并且将子进程的标准输入或标准输出句柄设置为匿名管道的读、写句柄,相当于将该管道的读写句柄作上一个标记,传递给子进程,然后在子进程中得到自己的标准输入输出句柄时,相当于得到了匿名管道的读写句柄。——《vc++深入理解》p639


9.MFC中不使用资源视图,如何动态创建一个窗口,使用到那些API

WimMain函数的定义
创建一个窗口
1).设计一个窗口类
2).注册窗口类
3).创建窗口
4).显示及更新窗口
进行消息循环
编写窗口过程函数。
这里写图片描述

原创粉丝点击