关于linux的几道题,你能回答几个?--回答1~13题

1.memcmp可否用来比较结构体?strcmp和memcpy的区别?

参考:http://www.cnblogs.com/cxz2009/archive/2010/11/11/1875125.html

root@linux:/study/linuxknowledge# cat memcmptest.c

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
 
typedef struct CmpTest
{
char  a;
short b;
int   c;
}CmpTest;
 
int main(int argc, char *argv[])
{
CmpTest t1, t2;
printf("%d\n", memcmp(&t1, &t2, sizeof(CmpTest)));
 
CmpTest t3, t4;
memset(&t3, 0, sizeof(CmpTest));
memset(&t4, 0, sizeof(CmpTest));
printf("%d\n", memcmp(&t3, &t4, sizeof(CmpTest)));
 
t1.a = 'a';
t1.b = 3;
t1.c = 5;
t2.a = 'a';
t2.b = 3;
t2.c = 5;
 
printf("%d\n", memcmp(&t1, &t2, sizeof(CmpTest)));
t3.a = 'a';
t3.b = 3;
t3.c = 5;
 
t4.a = 'a';
t4.b = 3;
t4.c = 5;
printf("%d\n", memcmp(&t3, &t4, sizeof(CmpTest)));
t2 = t1;
printf("%d\n", memcmp(&t1, &t2, sizeof(CmpTest)));
 
t4 = t3;
printf("%d\n", memcmp(&t3, &t4, sizeof(CmpTest)));
 
return 0;
}

root@linux:/study/linuxknowledge# ./memcmptest 

1

0

1

0

0

0

root@linux:/study/linuxknowledge# 

也就是说:如果初始化赋初值(memset),则是可以相比较的

另外,如果使用“=”号赋值,两个结构是会相等的

但是,如果结构没有赋初值,即使各变量赋值相同,memcmp比较的结果仍不同。

另外,即使比较结果相同,也不能说明这两个结构体是相同的,只是结构体这么大块的内存中的内容是相同,因为可能成员不同。

 

2.软中断和硬中断的区别?

(1)硬中断为硬件产生中断信号,CPU相应中断信号;而软中断与硬件无关,由CPU调度。比如产生一个EXT中断或者MSI中断、Mailbox中断等,触发CPU来响应,这是硬中断。软中断一般作为中断的下半部来处理,CPU在中断处理函数的上半部处理需要尽快完成的工作,然后由内核来调度下半部的执行,这里的下半部可以使用软中断来实现,或者tasklet、workqueue来实现。

(2)软中断一般是处理I/O请求,不会中断CPU,由内核调度。而硬中断一般是响应硬件中断信号,会中断CPU,会触发内核中的中断函数。流程上,软中断从进程切换到驱动程序,而硬中断会是硬件->CPU->中断处理函数。

 

3.进程间通信的几种方式?哪种效率最高?

(1)管道PIPE和有名管道FIFO -- 比如shell的重定向

(2)信号signal -- 比如杀死某些进程kill -9,比如忽略某些进程nohup ,信号是一种软件中断

(3)消息队列 -- 相比共享内存会慢一些,缓冲区有限制,但不用加锁,适合命令等小块数据。

(4)共享内存 -- 最快的IPC方式,同一块物理内存映射到进程A、B各自的进程地址空间,可以看到对方的数据更新,需要注意同步机制,比如互斥锁、信号量。适合传输大量数据。

(5)信号量 -- PV操作,生产者与消费者示例

(6)套接字 -- socket网络编程

如下参考:http://blog.csdn.net/piaoairy219/article/details/17333691

------管道

管道的优点是不需要加锁,缺点是默认缓冲区太小,只有4K,同时只适合父子进程间通信,而且一个管道只适合单向通信,如果要双向通信需要建立两个。而且不适合多个子进程,因为消息会乱,它的发送接收机制是用read/write这种适用流的,缺点是数据本身没有边界,需要应用程序自己解释,而一般消息大多是一个固定长的消息头,和一个变长的消息体,一个子进程从管道read到消息头后,消息体可能被别的子进程接收到

------消息队列

消息队列也不要加锁,默认缓冲区和单消息上限都要大一些,在我的suse10上是64K,它并不局限于父子进程间通信,只要一个相同的key,就可以让不同的进程定位到同一个消息队列上,它也可以用来给双向通信,不过稍微加个标识,可以通过消息中的type进行区分,比如一个任务分派进程,创建了若干个执行子进程,不管是父进程发送分派任务的消息,还是子进程发送任务执行的消息,都将type设置为目标进程的pid,因为msgrcv可以指定只接收消息类型为type的消息,这样就实现了子进程只接收自己的任务,父进程只接收任务结果

------共享内存

共享内存的几乎可以认为没有上限,它也是不局限与父子进程,采用跟消息队列类似的定位方式,因为内存是共享的,不存在任何单向的限制,最大的问题就是需要应用程序自己做互斥,有如下几种方案

1 只适用两个进程共享,在内存中放一个标志位,一定要声明为volatile,大家基于标志位来互斥,例如为0时第一个可以写,第二个就等待,为1时第一个等待,第二个可以写/读

2 也只适用两个进程,是用信号,大家等待不同的信号,第一个写完了发送信号2,等待信号1,第二个等待信号2,收到后读取/写入完,发送信号1,它不是用更多进程是因为虽然父进程可以向不同子进程分别发送信号,但是子进程收到信号会同时访问共享内存,产生不同子进程间的竞态条件,如果用多块共享内存,又存在子进程发送结果通知信号时,父进程收到信号后,不知道是谁发送,也意味着不知道该访问哪块共享内存,即使子进程发送不同的结果通知信号,因为等待信号的一定是阻塞的,如果某个子进程意外终止,父进程将永远阻塞下去,而不能超时处理

3 采用信号量或者msgctl自己的加锁、解锁功能,不过后者只适用于linux


4.kmalloc和vmalloc差别?

kmalloc映射的是先行区的物理内存,vmalloc映射的是非线性区的物理内存,或者说高端内存区的物理内存。因为vmalloc是映射的非线性区,所以申请大块内存时更容易成功。而kmalloc由于申请连续物理内存,由于内存空洞等原因,申请较大块的内存容易失败,可以根据伙伴系统中的大块内存使用情况来决定是否可使用kmalloc申请。

Kmalloc、get_free_pages申请的是先行区内存

Ioremap、vmalloc申请的是vmalloc区域,或者说高端内存区、非线性映射区的内存。


5. 应用层的mmap、malloc和内核态的vmalloc对内存访问的差别?

应用层的mmap用来映射文件,malloc用来动态分配内存

Mmap并不分配实际的物理空间,只是将文件映射到调用进程的虚拟地址空间。

Malloc、vmalloc分配实际的物理内存

 

6.用户态是否可以直接访问内核态内存?

一种比较常用的实现内核态和用户态进程共享内存的方法。内核态: 内核态分配内存(可用页__get_free_page),然后将分配内存的虚拟地址写到注册的proc文件系统中。用户态: 首先根据proc文件系统获得内核态分配内存的虚拟地址,将该地址转换为实际的物理地址,通过映射/dev/mem,对物理地址进行访问。另一个例子是帧缓存,也是从用户态访问内核态内存的例子。


7. PCIe中MSI中断如何触发?

通过访问MSI Capabilitiy结构中的address、data,向MSI address中写入MSI data来触发对端的MSI中断


8.怎么知道一个文件的大小?

多种方式:

(1)尾指针减去头指针:

unsigned long get_file_size(const char *path)  
{  
    unsigned long filesize = -1;  
    FILE *fp;  
    fp = fopen(path, "r");  
    if(fp == NULL)  
        return filesize;  
    fseek(fp, 0L, SEEK_END);  
    filesize = ftell(fp);  
    fclose(fp);  
    return filesize;  
}  

  

(2)读取文件属性

#include <sys/stat.h>  
  
unsigned long get_file_size(const char *path)  
{  
    unsigned long filesize = -1;      
    struct stat statbuff;  
    if(stat(path, &statbuff) < 0){  
        return filesize;  
    }else{  
        filesize = statbuff.st_size;  
    }  
    return filesize;  
}  


9.spin_lock和mutex_lock的区别?spin_lock在单核和多核模式下的区别?

Spin_lock和Mutexock:(参考http://blog.csdn.net/wilsonboliu/article/details/19190861)

    互斥锁mutex_lock是sleep-waiting。 就是说当没有获得mutex时,会有上下文切换,将自己、加到忙等待队列中,直到另外一个线程释放mutex并唤醒它,而这时CPU是空闲的,可以调度别的任务处理。

    自旋锁spin lock是busy-waiting。就是说当没有可用的锁时,就一直忙等待并不停的进行锁请求,直到得到这个锁为止。这个过程中cpu始终处于忙状态,不能做别的任务。(疑问:这不死锁了么?)例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0 和Core1上。 用spin-lock,coer0上的线程就会始终占用CPU。

另外一个值得注意的细节是spin lock耗费了更多的user time。这就是因为两个线程分别运行在两个核上,大部分时间只有一个线程能拿到锁,所以另一个线程就一直在它运行的core上进行忙等待,CPU占用率一直是100%;而mutex则不同,当对锁的请求失败后上下文切换就会发生,这样就能空出一个核来进行别的运算任务了。

如何选择?

(1)Mutex适合对锁操作非常频繁的场景,并且具有更好的适应性。尽管相比spin lock它会花费更多的开销(主要是上下文切换),但是它能适合实际开发中复杂的应用场景,在保证一定性能的前提下提供更大的灵活度。
(2)spin lock的lock/unlock性能更好(花费更少的cpu指令),但是它只适应用于临界区运行时间很短的场景。而在实际软件开发中,除非程序员对自己的程序的锁操作行为非常的了解,否则使用spin lock不是一个好主意(通常一个多线程程序中对锁的操作有数以万次,如果失败的锁操作(contended lock requests)过多的话就会浪费很多的时间进行空等待)。

(3)更保险的方法或许是先(保守的)使用 Mutex,然后如果对性能还有进一步的需求,可以尝试使用spin lock进行调优。毕竟我们的程序不像Linux kernel那样对性能需求那么高(Linux Kernel最常用的锁操作是spin lock和rw lock)。

Spin_lock在单核、多核上的差异:(参考http://blog.chinaunix.net/uid-25871104-id-3052138.html)

Spin_lock是Linux内核的一种同步机制。内核代码可以通过获得spin_lock宣称对某一资源的占有,直到其释放该spin_lock;如果内核代码试图获得一个已经锁定的spin_lock,则这部分代码会一直忙等待,直到获得该spin_lock。

spin_lock在单核多核下的不同: 

Spin_lock的kernel中的实现对单核(UP),多核(SMP)有不同的处理方式。对单核来说,如果spin_lock不处于中断上下文,则spin_lock锁定的代码丢失CPU拥有权,只会在内核抢占的时候发生。所以,对于单核来说,只需要在spin_lock获得锁的时候禁止抢占,释放锁的时候开放抢占。对多核来说,存在两段代码同时在多核上执行的情况,这时候才需要一个真正的锁来宣告代码对资源的占有。


10.多进程时wait用来干什

(参考http://blog.csdn.net/wallwind/article/details/6998602)

等待子进程执行完毕:如果其所有子进程都在运行,则阻塞;如果一个子进程已经终止,正在等待的父进程获取到终止状态,则取得该子进程的终止状态立即返回;如果他没有任何子进程,则立即出错返回。


11. 进程如果卡住,如何查看卡在哪里?

(1)通过魔术键打印此时堆栈信息

(2)通过gdb打印堆栈信息

该问题没什么思路,求大神指导


12 .宕机问题如何排查?(参考http://blog.chinaunix.net/uid-25909722-id-3047986.html)

(1)查看message信息(/var/log/messages,最好重定向或配置保存到flash中,以便重启分析)

(2)魔术键查看是否系统有响应,如有响应分析魔术键盘触发的各种信息

(3)开启内核lock检测配置

这个问题也请大神指导下,思路比较有限


13.可变参数的原理?应该如何实现?(参考:http://blog.csdn.net/wooin/article/details/697106)

root@linux:/study/linuxknowledge# cat vaarg.c 

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
void simple(int i, ...)
{
va_list arg_ptr;
char *s = NULL;
 
va_start(arg_ptr, i);
s = va_arg(arg_ptr, char *);
va_end(arg_ptr);
 
printf("%d %s\n", i, s);
 
return ;
}
 
int main(int argc, char *argv[])
{
int i = 10;
simple(i, "hello ni ma!\n");
 
return 0;
}


root@linux:/study/linuxknowledge# ./vaarg 

10 hello ni ma!

 

root@linux:/study/linuxknowledge#

简单说就是用va_arg宏来实现的,va_arg宏,详情请看http://blog.csdn.net/wooin/article/details/697106

进一步的探索后面再写


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。