面试时间7.14 14分钟36秒
1.讲一下项目(linux文件传输)的系统调用
系统调用:操作系统提供给用户程序的一组“特殊”接口,用户程序通过这种特殊接口来获得操作系统内核获得的服务。
它相当于一个中间人,把用户进程的请求发送给内核,内核处理后将结果再返回给用户。
系统服务之所以需要通过系统调用来提供给用户空间的根本原因是为了对系统进行“保护”,因为我们知道Linux的运行空间分为内核空间和用户空间,它们各自运行在不同的级别中,逻辑上相互隔离。
软中断:它是通过软件指令触发而并非外设引发的中断。
系统调用并非直接和程序员和系统管理员打交道,它仅仅是一个通过软中断机制向内核提交请求,获得内核服务接口
socket() bind() connect() accept() send() recv() listen() 这些都是系统调用
2.讲一下项目流程?多线程,多进程用到了哪?
3.讲一下static,extern,const
static:
在C语言中:
(1)在修饰变量的时候,static修饰的静态局部变量只执行一次,而且延长了局部变量的生命周期,直到程序运行结束才释放
(2)static修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其他文件中访问,即使是extern外部声明也不可以
(3)static修饰一个函数,则这个函数的只能在本文件中调用,不能在其他文件中被调用,static修饰的局部变量存放在全局数据区的静态变量区,初始化的时候自动被初始化为0
(4)不想被释放的时候,就加static修饰。比如修饰函数中存放栈空间的数组,如果不想让这个数组在函数调用结束释放
可以用static修饰
(5)考虑到数据安全性(当程想要使用全局变量的时候应该先考虑使用static)
在C++中static关键字除了具有C中的作用还有在类中的使用
在类中,static可以用来修饰静态数据成员和静态成员方法
静态数据成员
(1)静态数据成员可以实现多个对象之间的数据共享,它是类的所有对象的共享成员,它在内存中只占一份空间,如果改变它的值,则各对象中这个数据成员的值都被改变。
(2)静态数据成员是在程序开始运行时被分配空间,到程序结束之后才释放,只要类中指定了静态数据成员,即使不定义对象,也会为静态数据成员分配空间。
(3)静态数据成员可以被初始化,但是只能在类体外进行初始化,若未对静态数据成员赋初值,则编译器会自动为其初始化为0
(4)静态数据成员既可以通过对象名引用,也可以通过类名引用。
静态成员函数
(1)静态成员函数和静态数据成员一样,他们都属于类的静态成员,而不是对象成员。
(2)非静态成员函数有this指针,而静态成员函数没有this指针。
(3)静态成员函数主要用来方位静态数据成员而不能访问非静态成员。
const:
在c语言中是常变量(c++中是常量): const 类型说明符 变量名 简单的说常变量就是其值不能改变的变量。
const int a = 5 与int const a = 5等同
类名const对象名与const类名 对象名等同
常引用: const 类型说明符 &引用名
int i = 10;
// 正确:表示不能通过该引用去修改对应的内存的内容。
const int& ri = i;
// 错误!不能这样写。
int& const rci = i;
常对象: 类名 const 对象名
常成员函数: 类名::fun(形参) const
修饰函数返回值,阻止用户修改返回值,返回值也要相应的付给一个常量或长指针
常数组: 类型说明符 const 数组名[大小]
常指针: const 类型说明符* 指针名 , 类型说明符* const 指针名
const int *m1 = new int(10);
int* const m2 = new int(20);
在上面的两个表达式中,最容易让人迷惑的是const 到底是修饰指针还是指针指向的内存区域?其实,只要知道:const 只对它左边的东西起作用,唯一的例外就是const 本身就是最左边的修饰符,那么它才会对右边的东西起作用。
根据这个规则来判断,m1 应该是常量指针(即,不能通过m1 来修改它所指向的内容。);而m2 应该是指针常量(即,不能让m2 指向其他的内存模块)。
extern:
(1)在c语言中,extern在变量或者函数前表明“此函数或变量是在别处定义的,要在此处引用”,extern声明,不是定义,即不分配存储空间
也就是说,在一个文件中定义了变量和函数, 在其他文件中要使用它们, 可以有两种方式:
1.使用头文件,然后声明它们,然后其他文件去包含头文件
2.在其他文件中直接extern
(2)告诉编译器是用c语言规则编译还是用c++规则编译
4.说一下fork进程,怎样解决僵尸进程
fork是复制进程,fork底层是通过do_fork方法传入的不同参数来实现的,首先为进程分配一个唯一标识的pid,这一步分配失败就退出复制,pid能表示的范围也就决定着系统理论上最多能运行多少个进程,接下来分配pcb,并继承父进程的pcb的值,只是将特有的信息改过来,用父进程的内核栈复制给子进程,但将eax寄存器的值强制为0,这也就是父子进程为什么沿着统一的位置开始执行,以及子进程的返回值为什么是0,接下来根据传入的do_fork的参数复制进程实体,复制的时候有写实拷贝。
vfork
clone:
解决僵尸进程:
(1).改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
(2)把父进程杀掉。父进程死后,僵尸进程成为”孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
(3)杀父进程不行的话,就尝试用 skill -t TTY 关闭相应终端,TTY是进程相应的tty号(终端号)。
(4)实在不行,重启系统吧,这也是最常用到方法之一。
5.c++学了什么,讲一下多态吧?怎样实现多态
6.说一下STL中的容器,vector和list用在哪些场景中,它俩的区别
容器:
(1)顺序容器:vector dequeue list
(2)关联容器: 有序的关联容器 (底层数据结构:红黑树)数据排好序了,非常适合范围查找
无序的关联容器 (底层数据结构:哈希表)不适合范围查找
vector适用:对象数量变化少,简单对象,随机访问元素频繁
list适用:对象数量变化大,对象复杂,插入和删除频繁
最大的区别是,list是双向的,而vector是单向的。