海康威视嵌入式软件工程师的题目只包含两个部分选择题和简答题,也就是说,没有编程题!
照样是C语言基础的题量最多了,又因为是BSP方向,所以关于Linux操作系统和驱动方面的题目可能页会比较多一点,但是就题目的总体难度而言,也并不是特别难。但如果对Linux、驱动方面不是很了解的话,估计会很吃亏的。
64、24
64、64
16、64
16、16
KEY:B
解答:9大内部排序的时间复杂度和空间复杂度如下表所示:
分类 | 方法 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
最好 | 最坏 | 平均 | ||||
交换排序 | 交换排序 | O(n) | O() | O() | O(1) | 稳定 |
冒泡排序 | O(n) | O() | O() | O(1) | 稳定 | |
快速排序 | O() |
O() | O() | O() | 不稳定 | |
插入排序 | 直接插入排序 | O(n) | O() | O() | O(1) | 稳定 |
希尔排序 | O() | O() | O(1) | 不稳定 | ||
选择排序 | 简单选择排序 | O() | O() | O() | O(1) | 不稳定 |
堆排序 | O() | O() | O() | O(1) | 不稳定 | |
其他 | 归并排序 | O() | O() | O() | O(n) | 稳定 |
计数排序 | O(d(r+n)) | O(d(r+n)) | O(d(r+n)) | O(r) | 稳定 |
int a[15] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
int *p = &a[5];
int b = p[7];
14
13
11
12
KEY:B
#define MAX_DATA_SIZE (10)
int main()
{
unsigned int i = 0;
unsigned char data[MAX_DATA_SIZE] = { 1,2,3,4,5,6,7,8,9,0 };
memcpy(&data[2], data, MAX_DATA_SIZE / 2);
return 0;
}
1234567890
1212121890
1212345890
其他结果都不对
KEY:D
解答:由于本题中data数组里直接放的是数字,直接强制转化为char类型,按照ASCII码进行查询,显然不会是这些数字了。也就是说,如果改成以下的定义:
unsigned char data[MAX_DATA_SIZE] = { '1','2','3','4','5','6','7','8','9','0' };
那么答案就是C了。
modprobe
delmod
unmod
rmmod
KEY:D
n、nlogn、nlogn
nlogn、nlogn、n^2
nlogn、nlogn、nlogn
n、nlogn、n^2
KEY:B
互斥锁的加锁和解锁必须由同一线程分别对应使用
互斥锁只能为0/1
互斥锁的解锁过程不需要关心顺序,只需要成对使用即可
互斥锁用于线程的互斥
KEY:C
解答:互斥量和信号量的区别:
这里区分一下互斥和同步:
参考文章:线程同步(互斥锁与信号量的作用与区别)。
find
grep
cat
其他都不对
KEY:A
解答:Linux的find命令用来在指定目录下查找文件,可以使用-name选项来完成:
find /home -name "*.txt" //不忽略大小写
find /home -iname "*.txt" //忽略大小写
调度
协同
互斥
同步
KEY:D
运行进程要等待某一事件发生
运行进程的时间片用完
有新进程进入就绪状态
运行进程出错
KEY:C
解答:运行进程时间片用完,进程运行出错,运行进程阻塞(也就是等待某一事件发生)都会使操作系统选择新进程,但有新进程进入就绪状态不会影响其他进程状态变化。
进程
程序
线程
指令
KEY:A
int main()
{
int i = 10;
switch(i) {
case 9:i++;
case 10:i++;
case 11:i++;
default:i++;
}
printf("result= %d\n", i);
return 0;
}
12
13
11
14
KEY:B
int a[2][3]=((0,1,2),(3,4,5));
int a[2][3]={{0,1,2},{3,4,5}};
int a[2][3]={{0,1},{2,3},{4,5}};
int a[2][3]={0,1,2,3,4};
KEY:B
#define CALC(x,y) x*y+x
int main()
{
int a = CALC(2 * 3, 3 + 2);
printf("result= %d\n", a);
return 0;
}
26
30
24
36
KEY:A
void swap(int *a, int *b) {
int *t;
t = a;
a = b;
b = t;
}
int main()
{
int x = 1, y = 2;
int *p = &x, *q = &y;
swap(p, q);
printf("%d %d\n", *p, *q);
return 0;
}
1 1
2 2
1 2
2 1
KEY:C
解答:本题的swap函数是将形参中的a、b两个指针指向的地址交换了,实参p、q并没有变化。可以比对一下:
int x = 1, y = 2;
swap(&x, &y); //没有效果
void swap(int *a, int *b) {
int *t;
t = a;
a = b;
b = t;
}
swap(&x, &y); //有效果
void swap(int *a, int *b) {
int t;
t = *a;
*a = *b;
*b = t;
}
char test[8] = { 0x01,0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
int *test_p = (int*)test;
0x12345678
0x1234567C
0x12345679
0x1234567A
KEY:B
int n = 0;
int f = 17;
n = 4 * f % 3 + 1;
3
9
2
0
KEY:A
flag|=4
flag^=4
flag&=~4
flag|=~4
KEY:C
#define TEST(a,b) a*b
int main()
{
int a = TEST(1 + 2, 3);
printf("result= %d\n", a);
return 0;
}
2
3
7
9
KEY:C
队列空间缓冲由进程申请,栈的空间缓冲由OS分配
堆总是一棵完全二叉树
堆的数据类型和队列相似
队列先进先出,栈后进先出
KEY:B
char test[10]; strcpy(test,"test");
char test[10]; test="test";
char test[]={'t','e','s','t'};
char test[10]={'t','e','s','t'};
KEY:B
tasklet
工作队列
软中断
双向链表
KEY:ABC
解答:上半部是不能中断的,仅仅是响应中断;下半部是可以中断的。对于适时要求高的,必须放在上半部。下半部的实现主要是通过软中断、tasklet、工作队列来实现的。
上半部的功能是响应中断。当中断发生时,它就把设备驱动程序中中断处理例程的下半部挂到设备的下半部执行队列中去,然后继续等待新的中断到来。这样一来,上半部的执行速度就会很快,它就可以接受更多它负责的设备所产生的中断了。上半部之所以快,是因为它是完全屏蔽中断的,如果它没有执行完,其他中断就不能及时地处理,只能等到这个中断处理程序执行完毕以后。所以要尽可能多的对设备产生的中断进行服务和处理,中断处理程序就一定要快。
下半部的功能是处理比较复杂的过程。下半部和上半部最大的区别是可中断,而上半部却不可中断。下半部几乎完成了中断处理程序所有的事情,因为上半部只是将下半部排到了它们所负责的设备中断的处理队列中去,然后就不做其它的处理了。下半部所负责的工作一般是查看设备以获得产生中断的事件信息,并根据这些信息(一般通过读设备上的寄存器得来)进行相应的处理。下半部是可中断的,所以在运行期间,如果其它设备产生了中断,这个下半部可以暂时的中断掉,等到那个设备的上半部运行完了,再回头运行这个下半部。
下面对实现中断下半部工作的3种机制进行总结,便于在实际使用中决定使用哪种机制:
下半部机制 | 上下文 | 复杂度 | 执行性能 | 顺序执行保障 |
软中断 | 中断 | 高(需要自己确保软中断的执行顺序及锁机制) | 好(全部自己实现,便于调优) | 没有 |
tasklet | 中断 | 中(提供了简单的接口来使用软中断) | 中 | 同类型不能同时执行 |
工作队列 | 进程 | 低(在进程上下文中运行,与写用户程序差不多) | 差 | 没有(和进程上下文一样被调度) |
参考文章:如何理解中断的上半部和下半部。
int fun(int a[], short *ptr, int &c){
...
}
数组传递
指针传递
引用传递
值传递
KEY:BC
在运行时可以动态添加
物理空间不连续,空间开销更大
查找元素不需要顺序查找
可在任意节点位置插入元素
KEY:ABD
取余运算符%>加运算符+
逻辑与运算符&&>赋值运算符=
数组下标[]>自增运算符++
左移运算符<<>大于运算符>
KEY:ABCD
缺页中断
时钟中断
命令解释
进程调度
KEY:ABD
解答:缺页中断和时钟中断都属于中断,在内核态执行。进程调度属于系统调用,在内核态执行,命令解释程序属于命令接口,在用户态执行。
(1)insmod和modprobe都可以用来加载驱动模块,请描述这两者的差异。
(2)请给出三种以上Linux驱动模块加载失败的可能原因。
解答:modprobe和insmod类似,都是用来动态加载驱动模块的,区别在于modprobe可以解决load module时的依赖关系,它是通过/lib/modules/#uname -r/modules.dep(.bb)文件来查找依赖关系的;而insmod不能解决依赖问题。
也就是说,如果你确定你要加载的驱动模块不依赖其他驱动模块的话,既可以insmod也可以modprobe,当然insmod可以在任何目录下执行,更方便一些。而如果你要加载的驱动模块还依赖其他ko驱动模块的话,就只能将模块拷贝到上述的特定目录,depmod后再modprobe。
解答:Linux内核的五大模块:进程调度模块、内存管理模块、文件系统模块、进程间通信模块、网络接口模块。