面试题——蓝
1. 可重入是什么概念?
可重入代码(Reentry code)也叫纯代码(Pure code)是一种允许多个进程同时访问的代码。为了使各进程所执行的代码完全相同,故不允许任何进程对其进行修改。程序在运行过程中可以被打断,并由开始处再次执行,并且在合理的范围内(多次重入,而不造成堆栈溢出等其他问题),程序可以在被打断处继续执行,且执行结果不受影响。
(不应该有全局变量或静态变量,因为这些变量会保存某一个进程的修改。可重入代码中的变量应该都是局部变量,每次重新调用时变量重新被赋值,从而保证,每个进程对它的访问都产生同样的结果。)
void test()
{
int i;
i=2;
printf("%d\n",i );
i++;
printf("%d\n",i);
}
结果:每次都是:2 \n 3
void test()
{
static int i=2;
printf("%d\n",i );
i++;
prinft("%d\n",i);
}
结果:第一次:2 \n 3;第二次:3 \n 4
2. 如何确保代码可重入?
若一个程序或子程序可以安全的被并行执行,则称其为可重入(reentrant或re-entrant)的;即,当该子程序正在运行时,可以再次进入并执行它。
若一个函数是可重入的,则该函数:
不能含有静态(全局)非常量数据。 不能返回静态(全局)非常量数据的地址。 只能处理由调用者提供的数据。 不能依赖于单实例模式资源的锁。 不能调用不可重入的函数。 多 '用户/对象/进程优先级' 以及多进程一般会使得对可重入代码的控制变得复杂。同时,IO代码通常不是可重入的,因为他们依赖于像磁盘这样共享的、单独的资源。
3. 进程间通讯方式?线程间通讯方式?
进程间:管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
有名管道 (namedpipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
信号量(semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
消息队列( messagequeue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
信号 (sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
共享内存(shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
套接字(socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
有名管道 (namedpipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
信号量(semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
消息队列( messagequeue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
信号 (sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
共享内存(shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
套接字(socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
线程间通信可以通过下列三种方法: 1)使用全局变量实现线程间通信 2)使用消息实现线程间通信 3)使用CEvent类实现线程间通信
MFC下常用方法:
1.全局变量 注:定义全局变量时最好使用volatile来定义,以防编译器对此变量进行优化。
2.Message消息机制,常用的Message通信的接口主要有两个:PostMessage为线程向主窗口发送消息。而PostThreadMessage是任意两个线程之间的通信接口。
3.CEvent对象,是MFC中的一个对象,可以通过对CEvent的触发状态进行改变,从而实现线程间的通信和同步。
线程间同步机制:锁机制:包括互斥锁、条件变量、读写锁(或者说 互斥mutex、信号量Semaphore、临界区Critical section)
互斥锁提供了以排他方式防止数据结构被并发修改的方法。
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
信号机制(Signal):类似进程间的信号处理
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
互斥锁提供了以排他方式防止数据结构被并发修改的方法。
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
信号机制(Signal):类似进程间的信号处理
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
4. volatile关键词含义?————直接存取原始内存地址
volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
(volatile的作用: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.就是防止编译器对代码进行优化)
常用于:1)并行设备的硬件寄存器(如:状态寄存器)
2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3)多线程应用中被几个任务共享的变量
1).一个参数既可以是const还可以是volatile吗?是的。只读的状态寄存器。是volatile因为它可能被意想不到地改变,是const因为程序不应该试图去修改它。
2). 一个指针可以是volatile 吗?是的。如:当一个中断服务子程序修改一个指向一个buffer的指针时。
3). 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?
intsquare(volatileint*ptr)
{
return
*ptr**ptr;
}
*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!
5. 关键字static作用?与关键字extern的区别?
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
(本地化数据和代码范围的好处和重要性)
extern和static完全不同,static会定义一个变量,extern不能定义一个变量,extern表示的是“已经存在一个变量,但是不在当前的编译单元内,需要在其他编译单元中寻找。
文件a.c
static int i; //只在a文件中用
int j; //在工程里用
static void init(){} //只在a文件中用
void callme() //在工程中用
{static int sum;}
static int i; //只在a文件中用
int j; //在工程里用
static void init(){} //只在a文件中用
void callme() //在工程中用
{static int sum;}
上面的全局 i 变量和 init() 函数只能用在a.c文件中,全局变量sum的作用域只在callme里。
变量j和函数callme()的全局限扩充到整个工程文件。
所以可以在下面的b.c中用extern关键字调用。extern告诉编译器这个变量或者函数在其他文件里已经被定义了。
文件b.c
extern int j; //调用a文件里的
extern void callme(); //调用a文件里的
int main(){}
extern int j; //调用a文件里的
extern void callme(); //调用a文件里的
int main(){}
extern的另外用法是当C和C++混合编程时如果c++调用的是c源文件定义的函数或者变量,那么要加extern来告诉编译器用c方式命名函数:
文件A.cpp调用a.c里面的变量i和函数callme()
extern "C" //在c++文件里调用c文件中的变量
{ int j; void callme(); }
int main()
{ callme(); }
extern "C" //在c++文件里调用c文件中的变量
{ int j; void callme(); }
int main()
{ callme(); }
6. 查看当前目录下是否存在一个叫abc的文件
VB——if dir("c:\a.txt")<>"" then 存在 endif
php——<?php $filename = '/path/to/foo.txt'; if (file_exists($filename))
{ echo "The file $filename exists"; }
else { echo "The file $filename does not exist"; } ?>
Linux下 ( if access(const char* pathname, F_OK)==0) 存在 (F_OK文件是否存在 R_OK读权限 W_OK写权限 X_OK执行权限)
( if opendir(const char* pathname)==0) 存在(不存在 NULL)
find -name "$(date +%F)*" -exec mv {} ./history \;发现当前目录下有用当天日期为文件名的文件,即做MV
C++ #include <iostream> #include <fstream> using namespace std; #define FILENAME "stat.dat"
int main(){ fstream _file; _file.open(FILENAME,ios::in);
int main(){ fstream _file; _file.open(FILENAME,ios::in);
if(!_file) { cout<<FILENAME<<"没有被创建"; }
else { cout<<FILENAME<<"已经存在";} return 0; }
else { cout<<FILENAME<<"已经存在";} return 0; }
C语言 #include <io.h> #include <stdio.h> #include <stdlib.h>
void main( void ) { if( (_access( "ACCESS.C", 0 )) != -1 ) { printf( "File ACCESS.C exists " );
void main( void ) { if( (_access( "ACCESS.C", 0 )) != -1 ) { printf( "File ACCESS.C exists " );
if( (_access( "ACCESS.C", 2 )) != -1 ) printf( "File ACCESS.C has write permission " ); } }
7. 嵌入式系统常对寄存器或变量进行位操作,给定一个整形变量value,设置value的bit 3,清除value的bit 3,以上操作保持其它位不变
value |=0x01<<3 ; value &=-(0x01<<3)
8. 嵌入式系统访问某特定内存位置,32位机要求设置一绝对地址为0x000067a9的整形变量值为0xaa66,代码完成
若这个地址是代码段(方法)位置,且已知函数为void型,无参,如何调用?
1) int* p; p=0x000067a9; *p=0xaa66 2) void (*p)();声明 p=0x000067a9;定义 *p();调用