筆試部分
1.描述下面代碼中兩個(gè)static各自的含義:
static?void?func(void)
{
? ??static?unsigned?int?i;?
}
參考答案:
行1,static表示靜態(tài)函數(shù),該函數(shù)只有當(dāng)前文件的其他函數(shù)才可以調(diào)用它
行3,局部靜態(tài)變量
生存周期:從程序運(yùn)行到程序結(jié)束
作用域:只有當(dāng)前函數(shù)才可以訪問(wèn)
段位置:全局data段【不是棧區(qū)】,并且每次訪問(wèn)都會(huì)保存上一次執(zhí)行的結(jié)果
2. 寫出執(zhí)行下面代碼后變量a的值:
unsigned?int?a,b=3;
void?move(unsigned?int?*p,unsigned?int?val)
{
?p=&val; ?
? ??
}
void?main(void)
{
?a = b++;
?move(&a,b); ? ? ?
}
參考答案:3
解析:
- 行11執(zhí)行完
先將b的值賦值給a,然后b自加
- 行3,調(diào)用move后
move函數(shù)的形參p指向全局變量a,指針變量p中的值是a的地址
全局變量b的值4賦值給形參val,變量val中的值是4
行5執(zhí)行完
將val的地址賦值給指針變量p,p不再指向a,轉(zhuǎn)而指向了變量val
本題主要考察傳值和傳址的區(qū)別,這是新手最不容易理解的一個(gè)知識(shí)點(diǎn)。
3. 在32位的單片機(jī)系統(tǒng)中,下面的結(jié)構(gòu)體長(zhǎng)度是多少?
typedef?struct
{
? ? short a;
? ??char?b;
? ??char?C;
? ??int?d; ? ??
}struct1;
typedef?struct
{
? ??char?a;
? ? short b;
? ??unsigned?char?c;
? ??int?d;
}struct2;
參考答案:8/12
解析:
主要是字節(jié)對(duì)齊導(dǎo)致的問(wèn)題,struct1,struct2各成員在內(nèi)存中分布如下:
實(shí)際項(xiàng)目開發(fā)中,為了保證結(jié)構(gòu)體字節(jié)對(duì)齊,往往使用以下宏來(lái)保證數(shù)據(jù)不存在歧義。
#pragma?pack(1)
typedef?struct
{
? ??char?a;
? ? short b;
? ??unsigned?char?c;
? ??int?d;
}struct2;
#pragma
4. 請(qǐng)使用typedef定義一個(gè)數(shù)據(jù)類型func_t為指向void型函數(shù)的函數(shù)指針,再使用此數(shù)據(jù)類型定義一個(gè)指向void型函數(shù)的函數(shù)指針,并通過(guò)此指針來(lái)調(diào)用函數(shù)test。
參考答案:
#include?<stdio.h>
typedef?void?(*func_t)(int?data)?;
void?testfunc(int?data)
{
printf("yikou linux %dn",data);
}
int?main(int?argc,?char?**argv)
{
func_t?pfunc;
?pfunc = testfunc;
?pfunc(9);
}
函數(shù)名也是個(gè)地址,我們可以讓函數(shù)指針指向一個(gè)函數(shù)。
linux內(nèi)核中大量使用函數(shù)指針,各種不同的外設(shè)向子系統(tǒng)注冊(cè)操作函數(shù)集,子系統(tǒng)通過(guò)這些操作函數(shù)集對(duì)外設(shè)做不同的操作。
比如下面是字符設(shè)備操作函數(shù)集,結(jié)構(gòu)體定義:
struct?file_operations?{
?struct?module?*owner;
loff_t?(*llseek) (struct file *,?loff_t,?int);
ssize_t?(*read) (struct file *,?char?__user *,?size_t,?loff_t?*);
ssize_t?(*write) (struct file *,?constchar?__user *,?size_t,?loff_t?*);
ssize_t?(*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t?(*write_iter) (struct kiocb *, struct iov_iter *);
int?(*iterate) (struct file *, struct dir_context *);
int?(*iterate_shared) (struct file *, struct dir_context *);
unsigned?int?(*poll)?(struct file *, struct poll_table_struct *);
long?(*unlocked_ioctl) (struct file *,?unsignedint,?unsignedlong);
long?(*compat_ioctl) (struct file *,?unsignedint,?unsignedlong);
int?(*mmap) (struct file *, struct vm_area_struct *);
int?(*open) (struct inode *, struct file *);
int?(*flush) (struct file *,?fl_owner_t?id);
int?(*release) (struct inode *, struct file *);
int?(*fsync) (struct file *,?loff_t,?loff_t,?int?datasync);
int?(*fasync) (int, struct file *,?int);
int?(*lock) (struct file *,?int, struct file_lock *);
ssize_t?(*sendpage) (struct file *, struct page *,?int,?size_t,?loff_t?*,?int);
unsigned?long?(*get_unmapped_area)(struct file *,?unsigned?long,?unsigned?long,?unsigned?long,?unsigned?long);
int?(*check_flags)(int);
int?(*flock) (struct file *,?int, struct file_lock *);
ssize_t?(*splice_write)(struct pipe_inode_info *, struct file *,?loff_t?*,?size_t,?unsignedint);
ssize_t?(*splice_read)(struct file *,?loff_t?*, struct pipe_inode_info *,?size_t,?unsignedint);
int?(*setlease)(struct file *,?long, struct file_lock **,?void?**);
long?(*fallocate)(struct file *file,?int?mode,?loff_t?offset,
? ? ?loff_t?len);
void?(*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef?CONFIG_MMU
unsigned?(*mmap_capabilities)(struct file *);
#endif
ssize_t?(*copy_file_range)(struct file *,?loff_t, struct file *,
? ?loff_t,?size_t,?unsignedint);
int?(*clone_file_range)(struct file *,?loff_t, struct file *,?loff_t,
? ?u64);
ssize_t?(*dedupe_file_range)(struct file *, u64, u64, struct file *,
? ?u64);
} __randomize_layout;
5.請(qǐng)編寫宏定義實(shí)現(xiàn)以下功能:
- 1)將無(wú)符號(hào)整數(shù)a的第1位置1,同時(shí)保證其它位的值不改變;
a |=?0x1<<1;
默認(rèn)位數(shù)從0開始計(jì)。
- 2)將無(wú)符號(hào)整數(shù)b的第5位清0,同時(shí)保證其它位的值不改變;
b &=(~(0x1<<5));
- 3)計(jì)算出任意結(jié)構(gòu)體類型的常數(shù)組(如struct tt tab[])的元素個(gè)數(shù)
#define?ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
main()
{
?printf("array num:%dn",ARRAY_SIZE(tab));
}
宏定義在內(nèi)核中也頻繁的使用,來(lái)看下等待隊(duì)列宏定義:
#define?wait_event(wq_head, condition) ? ? ?
do { ? ? ? ? ?
?might_sleep(); ? ? ? ?
?if?(condition) ? ? ? ?
? break; ? ? ? ?
?__wait_event(wq_head, condition); ? ?
} while (0)
#define?__wait_event(wq_head, condition) ? ?
?(void)___wait_event(wq_head, condition, TASK_UNINTERRUPTIBLE, 0, 0,
? ? ? ?schedule())
#define?___wait_event(wq_head, condition, state, exclusive, ret, cmd) ?
({ ? ? ? ? ?
?__label__ __out; ? ? ?
?struct wait_queue_entry __wq_entry; ? ?
?long __ret = ret;?/* explicit shadow */? ?
? ? ? ? ?
?init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);
?for (;;) { ? ? ? ?
? long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);
? ? ? ? ?
if?(condition) ? ? ?
? ?break; ? ? ?
? ? ? ? ?
if?(___wait_is_interruptible(state) && __int) { ?
? ?__ret = __int; ? ? ?
? ?goto __out; ? ? ?
? } ? ? ? ?
? ? ? ? ?
? cmd; ? ? ? ?
?} ? ? ? ?
?finish_wait(&wq_head, &__wq_entry); ? ?
__out: __ret; ? ? ? ?
})
要完全看懂這段代碼,還是需要一定功底的,不光要看懂語(yǔ)法,還要了解內(nèi)核相關(guān)的其他子系統(tǒng)原理,1個(gè)月5個(gè)月1年2年????
6.請(qǐng)按照說(shuō)明實(shí)現(xiàn)下面的函數(shù):
/*功能:把十六進(jìn)制數(shù)轉(zhuǎn)換為字符,如0xA8轉(zhuǎn)換為字母A和數(shù)字8
*參數(shù):hex是待轉(zhuǎn)換的十六進(jìn)制數(shù);char1和char2是轉(zhuǎn)換后的字符的存儲(chǔ)指針
*返回值:返回0表示轉(zhuǎn)換成功,返回-1表示參數(shù)錯(cuò)誤或轉(zhuǎn)換失敗*/
unsigned?char?hex2char(unsigned?char?ch)
{
printf("ch:%dn",ch);
if?((ch >=?0) && (ch <=?9)) {
return?ch +?'0';
?}
if?((ch >=?0xa) && (ch <=?0xf)) {
return?ch -?10?+?'A';
?}
return?(unsignedchar)0xff;
}
int?hex_to_chars(unsigned?char?hex,?char?*charl,?char?*char2)
{
? ??unsignedchar?high,low;
?low = hex&0xf;
?high = (hex>>4)&0xf;
?charl[0] = hex2char(low);
?char2[0] = hex2char(high);
}
int?main(int?argc,?char?**argv)
{
unsignedchar?data =?0x18;
char?char1[10]={0};
char?char2[10]={0};
?hex_to_chars(data,char1,char2);
printf("0x%s%sn",char2,char1);
}
本題主要考察數(shù)據(jù)在內(nèi)存的形式相關(guān)知識(shí)點(diǎn),在實(shí)際應(yīng)用中可以說(shuō)非常廣,很不錯(cuò)的一道題目。
讀者可以嘗試下面一個(gè)問(wèn)題
如何將16進(jìn)制的字符串,轉(zhuǎn)換成對(duì)應(yīng)的16進(jìn)制整數(shù)?
字符串?dāng)?shù)組
char?buf[]="a8";
將字母a和8拼成0xa8,賦值給hex
unsigned?char?hex;
7. i2c編程題
請(qǐng)根據(jù)PCAxxxxx這款芯片的數(shù)據(jù)手冊(cè),編寫芯片的驅(qū)動(dòng)代碼,要求涵蓋芯片90%以上的功能:可以忽略INT(中斷)引腳的功能:可以使用標(biāo)準(zhǔn)C語(yǔ)言或偽代碼進(jìn)行編寫;I2C總線驅(qū)動(dòng)部分,可以只設(shè)計(jì)驅(qū)動(dòng)接口,不進(jìn)行具體實(shí)現(xiàn)。
參考答案,下面是基于linux的i2c驅(qū)動(dòng)架構(gòu):
#define?YIKOU_MAJOR 500
#define?YIKOU_MINOR 0
struct?yikou_device?{
struct?cdev?cdev;
struct?i2c_client?*client;
};
struct?yikou_device?*yikou;
static?int?yikou_read_byte(struct i2c_client *client,?unsigned?char?reg)
{
int?ret;
char?txbuf[1] = { reg };
char?rxbuf[1];
struct?i2c_msg?msg[2] = {
? {client->addr,?0,?1, txbuf},
? {client->addr, I2C_M_RD,?1, rxbuf}
?};
?ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if?(ret <?0) {
? printk("ret = %dn", ret);
return?ret;
?}
return?rxbuf[0];
}
static?int?yikou_write_byte(struct i2c_client *client,?unsigned?char?reg,?unsigned?char?val)
{
char?txbuf[2] = {reg, val};
struct?i2c_msg?msg[2] = {
? {client->addr,?0,?2, txbuf},
?};
?i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
return0;
}
static?long?yikou_ioctl(struct file *file,?unsigned?int?cmd,?unsigned?long?arg)
{
union?yikou_data data;
struct?i2c_client?*client?=?yikou->client;
switch(cmd) {
? ? ? ??case?CMD1:
? ? ? ? ? ? data.data1 = mpu6050_read_byte(client, REG1);
? ? ? ? ? ??break;
? ? ? ??default:
? ? ? ? ? ? printk("invalid argumentn");
? ? ? ? ? ??return?-EINVAL;
?}
if?(copy_to_user((void?*)arg, &data,?sizeof(data)))
return?-EFAULT;
returnsizeof(data);
}
struct?file_operations?yikou_fops?= {
?.unlocked_ioctl = yikou_ioctl,
? ? ......
};
static?int?yikou_probe(struct i2c_client *client,?const?struct i2c_device_id *id)
{
int?ret;
dev_t?devno = MKDEV(YIKOU_MAJOR, YIKOU_MINOR);
?printk("match OK!n");
?yikou = kzalloc(sizeof(*yikou), GFP_KERNEL);
if?(yikou ==?NULL) {
return?-ENOMEM;
?}
?yikou->client = client;
?ret = register_chrdev_region(devno,?1,?"yikou");
?cdev_init(&yikou->cdev, &yikou_fops);
?ret = cdev_add(&yikou->cdev, devno,?1);
? ......
return0;
}
static?int?yikou_remove(struct i2c_client *client)
{
?......
return0;
}
staticstruct?of_device_id?yikou_dt_match[] = {
?{.compatible =?"invensense,yikou"?},
?......
};
struct?i2c_driver?yikou_driver?= {
?.driver = {
? ......
? .of_match_table = of_match_ptr(yikou_dt_match),
?},
?.probe ? = yikou_probe,
?......
};
module_i2c_driver(yikou_driver);
其中struct i2c_msg的封裝需要參考datasheet讀寫數(shù)據(jù)時(shí)序
編寫i2c_msg信息原則如下:
- 有幾個(gè)S信號(hào),msg數(shù)組就要有幾個(gè)元素;addr為從設(shè)備地址,通過(guò)i2c總線調(diào)用注冊(cè)的probe函數(shù)的參數(shù)i2c_client傳遞下來(lái);len的長(zhǎng)度不包括S、AD、ACK、P;buf為要發(fā)送或者要讀取的DATA的內(nèi)存地址。
比如下面是某芯片寫和讀的時(shí)序:
在這里插入圖片描述
- Single-Byte Write Sequence時(shí)序只需要1個(gè)i2c_msg,len值為2,buf內(nèi)容為是RA、DATA;Single-Byte Read Sequence時(shí)序需要2個(gè)i2c_msg,len值分別都為1,第1個(gè)msg的buf是RA,第2個(gè)msg的buf緩沖區(qū)用于存取從設(shè)備發(fā)送的DATA。
在這里插入圖片描述
點(diǎn)評(píng):i2c是非常重要的一個(gè)知識(shí)點(diǎn),基本上做嵌入式,或早或晚都會(huì)接觸他。
面試部分:
1.面試官問(wèn)對(duì)公司有什么了解嗎?
2.自我介紹,講一下做的項(xiàng)目;
3.攔截網(wǎng)站怎么實(shí)現(xiàn);
4.wan/lan自適應(yīng)具體怎么實(shí)現(xiàn);
5.路由器主要承擔(dān)一個(gè)什么樣的角色,傳輸數(shù)據(jù)的過(guò)程會(huì)用到哪些協(xié)議;
6.手機(jī)連接路由器的lan口之后怎么獲取數(shù)據(jù)包;
7.應(yīng)用和驅(qū)動(dòng)哪個(gè)熟練;
8.內(nèi)核里創(chuàng)建線程(pthread_create)后怎么結(jié)束?