就对朋友面试上海先锋商泰试题总结
1) 写了一个函数 int f(int a,int b,int c);请问这几个变量存储在哪里?他们存放的顺序是怎样排列的?为什么是这样的顺序?
答:存储在栈之中,存放顺序是从后往前的的顺序,
#include
void foo(int x, int y, int z)
{
}
int main(int argc, char *argv[])
{
}
运行结果:
x = 100 at [BFE28760]
y = 200 at [BFE28764]
z = 300 at [BFE28768]
C程序栈底为高地址,栈顶为低地址,因此上面的实例可以说明函数参数入栈顺序的确是从右至左的。可到底为什么呢?查了一直些文献得知,参数入栈顺序是和具体编译器实现相关的。比如,Pascal语言中参数就是从左到右入栈的,有些语言中还可以通过修饰符进行指定,如Visual C++。即然两种方式都可以,为什么C语言要选择从右至左呢?
进一步发现,Pascal语言不支持可变长参数,而C语言支持这种特色,正是这个原因使得C语言函数参数入栈顺序为从右至左。具体原因为:C方式参数入栈顺序(从右至左)的好处就是可以动态变化参数个数。通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底。除非知道参数个数,否则是无法通过栈指针的相对位移求得最左边的参数。这样就变成了左边参数的个数不确定,正好和动态参数个数的方向相反。
因此,C语言函数参数采用自右向左的入栈顺序,主要原因是为了支持可变长参数形式。换句话说,如果不支持这个特色,C语言完全和Pascal一样,采用自左向右的参数入栈方式
这儿其实还涉及到C语言中调用约定所采用的方式,下面简单的介绍一下:
__stdcall与C调用约定(__cdecl)的区别
C调用约定在返回前,要作一次堆栈平衡,也就是参数入栈了多少字节,就要弹出来多少字节.这样很安全.
有一点需要注意:stdcall调用约定如果采用了不定参数,即VARARG的话,则和C调用约定一样,要由调用者来作堆栈平衡.
(1)_stdcall是Pascal方式清理C方式压栈,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。 int f(void *p) -->> _f@4(在外部汇编语言里可以用这个名字引用这个函数)
在WIN32 API中,只有少数几个函数,如wspintf函数是采用C调用约定,其他都是stdcall
(2)C调用约定(即用__cdecl关键字说明)(The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数vararg的函数(如printf)只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。
(3)__fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。__fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。
(4)thiscall仅仅应用于"C++"成员函数。this指针存放于CX/ECX寄存器中,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
(5)naked call。 当采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。
(这些代码称作 prolog and epilog code,一般,ebp,esp的保存是必须的).
但是naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。
关键字 __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。
要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。
简单总结一下上面的几个调用方式:
调用约定
__cdecl
__stdcall
__fastcall
Thiscall
2) 判断大小端问题,写一段程序。
答:a = 0x12345678;
3) 进程和线程之间的区别?
答:①进程是资源分配的最小单位,线程是资源调度的的最小单位;②进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。③线程是进程的一个实体, 是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。④一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序 健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
4) 你最擅长什么?(技术方面)
答:c语言,linux系统编程,底层驱动
5) 进程能共享资源么?线程呢?
答:进程是不可以共享的,线程是可以共享数据段的但他仍然有自己的堆栈段
6) 为什么要进程间通信?进程间通信方式都有哪些。
答:为了提高计算机系统的效率.增强计算机系统内各种硬件的并行操作能力.操作系统要求程序结构必须适应并发处理的需要.为此引入了进程的概念。进程是操作系统的核心,所有基于多道程序设计的操作系统都建立在进程的概念之上。目前的计算机系统均提供了多任务并行环境.无论是应用程序还是系统程序.都需要针对每一个任务创建相应的进程。进程是设计和分析操作系统的有力工具。然而不同的进程之间.即使是具有家族联系的父子进程.都具有各自不同的进程映像。由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间的信息传递不可能通过变量或其它数据结构直接进行,只能通过进程间通信来完成。
并发进程之间的相互通信是实现多进程间协作和同步的常用工具.具有很强的实用性,进程通信是操作系统内核层极为重要的部分。根据进程通信时信息量大小的不同,可以将进程通信划分为两大类型:控制信息的通信和大批数据信息的通信.前者称为低级通信,后者称为高级通信。低级通信主要用于进程之间的同步、互斥、终止、挂起等等控制信息的传递,6种通信方式。
7) 进程间通信中的共享内存都有哪些函数,全部写出来。
答:int shmget(key_t key,int size,int shmflag);
8) 你会51单片机么?单片机的上的一些外围设备总线例如I^2C总线的时序图,uart的时序画出
SDA |
停止信号 |
SCL |
启动信号 |
I^2C(内部集成电路总线)用于连接微控制器及其外围设备,是具有总线仲裁和高低速设备同步功能的高性能多主机总线,直接用导线连接设备,通信时无需片选信号
I^2C总线上,两条线分别是:串行数据总线SDA线和串行时钟总线(SCL线)他们都是双向传输的
I^2C总线上的操作模式:主发送模式,主接受模式,从发送模式,从接受模式
9) Lcd驱动时所用的总线是什么?(他提示我说是RGB,但是我不知道有这种总线的)
答:这个题不知道是问什么?
10)
答:TCP报头:
IP报头:
IP协议定义了通过TCP/IP网络传输的数据的格式。IP数据报由报头和数据两部分组成。报头部分包含了目的地址和源地址,数据的类型等信息。
下面解释IP数据报格式的各字段的含义:
(1) 版本字段:记录数据报文符合协议的哪一个版本协议。版本号表示发送者、接收者和路由器对该数据的处理都要按所示的版本进行。现在的版本号是4。
(2) 首部长度:指明报头的长度。
(3) 服务类型:一共8个比特,前3个比特表示优先级,第4个比特表示要求有更低的时延,第5个比特表示要求有更高的吞吐量,第6个比特表示要求更高的可靠性,第7个比特表示选择价格更低廉的路由,最后一个比特未用。
(4) 总长度包括报头长度和数据长度,最大长度=216 = 65536字节。
(5) 标识符:用于数据分段,一个数据报在传输过程中可能分成若干段,标识符可以区分某分段属于某报文,一个数据报的所有分段具有相同的标识符。
(6) DF:该位置1时表示不分段,置0时允许分段。
(7) MF:表示后面还有一分段,除了最后一个分段,所有分段的MF置为1。
(8) 段偏移:指明此分段在当前数据报中的位置。
(9) 生存周期:限定分段生存期的计数器,当它为0时该分段被抛弃,时间单位为秒。
(10) 协议:指明此数据报属于哪一种传送过程,如TCP、UDP等。
(11) 报头校验和:只校验报头。
(12) 源端地址和目的端地址:指明源和目的方的网络编号与主机号,即IP地址。
(13) 可选字段:用于协商设定服务参数。
以太网上的数据按帧(frame)传输,每个帧的格式
以太网头(14 octet)
数据 尾部校验
目的地物理地址 源物理地址 ethernet type code data CRC
对于ethernet,物理地址,即MAC地址,都是48bit。
本文所说的所有包,除去dhcp过程的包,都是EAPOL EAP/802.1x类型,ethernet type code 用的是0x888e
而dhcp是基于IP协议的TCP/IP应用层协议。对于ipv4,ethernet type code 用的是0x0800.
ethernet的包的最大长度(以太网头+数据)是1500 octet,最小是60 octet.
11)
答:自旋锁和信号量
12)
答:信号量可能引起睡眠及调度;由于中断是必须马上处理的,没有理由在中断里进行睡眠调度,所以中断下不能用信号量
13)
答:
上半部 (紧急硬件操作) |
调度 |
下半部 (延缓的耗时操作) |
为了在中断执行时间 尽可能短和中断处理需完成大量工作之前找到一个平衡点,linux将中断处理程序分解成两个半部,顶半部和地半部,顶半部完成尽可能少的比较紧急的功能,他往往只是简单的读取寄存器中的中断状态并清除中断标志后就进行“登记中断“的工作,登记中断意味着将底半部处理程序挂到该设备的底半部执行队列中去,这样,顶半部处理程序的执行速度就会很快,可以服务更多的中断请求
14)
答:优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性
15)
16)
二面就扯会淡:
1) 自我介绍;
2) 问一下学校成绩
3) 班级排名?
4) 你学习这些知识是在什么时候开始的?
5)为什么不去考研?
6)你对windows平台和linux平台的文件系统之间有什么区别?谈一下你的看法。、