Linux操作系统常见面试题(持续更新)

1.熟悉命令netstat tcpdump ipcs ipcrm

netstat:检查网络状态,tcpdump:截获数据包,ipcs:检查共享内存,ipcrm:解除共享内存

2.共享内存段被映射进进程空间之后,存在于进程空间的什么位置?共享内存段最大限制是多少?

将一块内存映射到两个或者多个进程地址空间。通过指针访问该共享内存区。一般通过mmap(提供了一种机制,让用户程序直接访问设备内存)将文件映射到进程地址共享区。

存在于进程数据段,最大限制是0x2000000Byte(32MB)

3.进程内存空间分布情况

Linux操作系统常见面试题(持续更新)_第1张图片
程序段:程序段为程序代码在内存中的映射.一个程序可以在内存中多有个副本
初始化过的数据:在程序运行值初已经对变量进行初始化的
未初始化过的数据:在程序运行初未对变量进行初始化的数据
栈(stack):存储局部,临时变量,在程序块开始时自动分配内存,结束时自动释放内存.存储函数的返回指针.
堆(heap):存储动态内存分配,需要程序员手工分配,手工释放.

4.ELF是什么?其大小与程序中全局变量的是否初始化有什么关系(注意未初始化的数据放在bss段)

可执行连接格式(用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件)。.bss段是放未初始化或者初始化为0的全局变量;.data段是存放初始了且不为零的全局变量。bss段中的值全部赋为了0。程序中就不存在没有初始化的全局变量了,你不初始化,bss段也会帮你初始化为0。

5.动态链接和静态链接的区别?

动态链接是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在可执行文件运行时再装入;而静态链接是把所有的代码和数据都复制到本模块中,运行时就不再需要库了

6.32位系统一个进程最多有多少堆内存

32位意味着4G的寻址空间,Linux把它分为两部分:最高的1G(虚拟地址从0xC0000000到0xffffffff)用做内核本身,成为“系统空间”,而较低的3G字节(从0x00000000到0xbffffff)用作各进程的“用户空间”。每个进程可以使用的用户空间是3G。虽然各个进程拥有其自己的3G用户空间,系统空间却由所有的进程共享。从具体进程的角度看,则每个进程都拥有4G的虚拟空间,较低的3G为自己的用户空间,最高的1G为所有进程以及内核共享的系统空间。实际上有人做过测试也就2G左右。

7.写一个c程序辨别系统是64位 or 32位

void* number = 0; printf("%d\n",sizeof(&number));

输出8就是64位 输出4就是32位的 根据逻辑地址判断的

8.写一个c程序辨别系统是大端or小端字节序

union{ 
	short value; 
	char a[sizeof(short)];
}test;

test.value= 0x0102;

if((test.a[0] == 1) && (test.a[1] == 2)) 
	cout << "big"<<endl; 
else 
	cout << "little"  << endl;

9.信号:列出常见的信号,信号怎么处理?

1).进程终止的信号
2).跟踪进程的信号
3).与进程例外事件相关的信号等

对于信号的处理或者执行相关的操作进行处理或者直接忽略

10.i++ 是否原子操作?并解释为什么?

原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。则其肯定不是原子操作,i++做了三次指令操作,两次内存访问,第一次,从内存中读取i变量的值到CPU的寄存器,第二次在寄存器中的i自增1,第三次将寄存器中的值写入内存。这三次指令操作中任意两次如果同时执行的话,都会造成结果的差异性。
在多核CPU的复杂内存操作中,处理器提供了总线锁缓存锁两个机制来保证原子性,可以通过处理器提供的很多LOCK前缀的指令来实现。

11.说出你所知道的各类linux系统的各类同步机制(重点),什么是死锁?如何避免死锁(每个技术面试官必问)

1)原子操作
2)信号量(其实就是互斥锁也就是锁的机制)
3)读写信号量(就是读写锁)
4)自旋锁
5)内核锁
6)顺序锁

死锁就是几个进程申请资源,出现了循环等待的情况!

避免死锁的方法:

1)资源是互斥的
2)不可抢占
3)占有且申请
4)循环等待

12、exit() _exit()的区别?

13、如何实现守护进程?

1)创建子进程,父进程退出

2)在子进程中创建新会话

3)改变当前目录为根目

4)重设文件权限掩码

5)关闭文件描述符

6)守护进程退出处理

当用户需要外部停止守护进程运行时,往往会使用 kill命令停止该守护进程。所以,守护进程中需要编码来实现kill发出的signal信号处理,达到进程的正常退出。

14、linux的任务调度机制是什么?

Linux 分实时进程普通进程,实时进程应该先于普通进程而运行。实时进程:

1) FIFO(先来先服务调度)

2) RR(时间片轮转调度)。

每个进程有两个优先级(动态优先级和实时优先级),实时优先级就是用来衡量实时进程是否值得运行的。 非实时进程有两种优先级,一种是静态优先级,另一种是动态优先级。实时进程又增加了第三种优先级,实时优先级。优先级越高,得到CPU时间的机会也就越大。

15、标准库函数和系统调用的区别?

linux系统上到下依次是用户进程->linux内核->硬件
用户进程需要发生系统调用时,内核将调用内核相关函数来实现(如sys_read(),sys_write(),sys_fork())。用户程序不能直接调用这些函数,这些函数运行在内核态,CPU 通过软中断切换到内核态开始执行内核系统调用函数。
实际上使用系统调用会影响系统的性能,在执行调用时的从用户态切换到内核态,再返回用户态会有系统开销。

用户态–>系统调用–>内核态–>返回用户态

库函数:把函数放到库里。是把一些常用到的函数编完放到一个lib文件里,供别人用。别人用的时候把它所在的文件名用#include<>加到里面就可以了。一类是c语言标准规定的库函数,一类是编译器特定的库函数。
系统调用是为了方便使用操作系统的接口,而库函数则是为了人们编程的方便。

16、系统如何将一个信号通知到进程?

内核给进程发送信号,是在进程所在的进程表项的信号域设置对应的信号的位。进程处理信号的时机就是从内核态即将返回用户态度的时候。执行用户自定义的信号处理函数的方法很巧妙。把该函数的地址放在用户栈栈顶,进程从内核返回到用户态的时候,先弹出信号处理函数地址,于是就去执行信号处理函数了,然后再弹出,才是返回进入内核时的状态。

17. fork()一子进程程后父进程的全局变量能不能使用?

fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
fork后子进程将会拥有父进程的几乎一切资源,父子进程都各自有自己的全局变量,不能通用,不同于线程。对于线程,各个线程共享全局变量。

18. 请画出socket通信连接过程

socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
Linux操作系统常见面试题(持续更新)_第2张图片
Socket通信流程:
Linux操作系统常见面试题(持续更新)_第3张图片
(加粗为服务器操作,斜体为用户操作)

服务器根据地址类型(ipv4、ipv6)、socket类型、协议创建socket
服务器为socket绑定对应的IP地址和端口号
服务器监听端口号请求,接收用户发来的连接请求,此时服务器没有打开socket

·  用户创建socket
   用户打开socket,并通过IP地址+端口号试图connect服务器的socket

  服务器接收到了用户发来的socket连接请求,被动打开socket,开始接收客户端请求,直到用户返回连接信息。这时候服务器的socket进入堵塞状态,所谓堵塞,即accept();方法一直接收到客户端返回连接信息后才返回,然后开始接收下一个用户端请求
  客户端连接成功,开始向服务器输入状态信息
  服务器accept();方法返回,连接成功
  客户端写入信息
  服务器读取信息
  客户端关闭
  服务端关闭

19. 请用socket消息队列实现“同步非阻塞”和“异步阻塞”两种模式,并指出两者的差别和优劣

http://blog.csdn.net/yongchurui/article/details/12780653

20.谈谈同步、异步、阻塞和非阻塞的概念

同步
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。最常见的例子就是 SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的 LRESULT值返回给调用者。
异步
异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。以 CAsycSocket类为例(注意,CSocket从CAsyncSocket派生,但是起功能已经由异步转化为同步),当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程立刻可以朝下运行。当连接真正建立起来以后,socket底层会发送一个消息通知该对象。这里提到执行部件和调用者通过三种途径返回结果:状态、通知和回调。可以使用哪一种依赖于执行部件的实现,除非执行部件提供多种选择,否则不受调用者控制。如果执行部件用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一种很严重的错误)。如果是使用通知的方式,效率则很高,因为执行部件几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。
阻塞
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。例如,我们在CSocket中调用Receive函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。如果主窗口和调用函数在同一个线程中,除非你在特殊的界面操作函数中调用,其实主界面还是应该可以刷新。socket接收数据的另外一个函数recv则是一个阻塞调用的例子。当socket工作在阻塞模式的时候,如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。
非阻塞
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。对象的阻塞模式和阻塞函数调用对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用特殊的函数也可以进入阻塞调用。函数select就是这样的一个例子。

你可能感兴趣的:(C++,面试)