面试记录
2014-07-04 19:59
253 查看
今天一个it培训公司,我投的软件研发岗,经理过来面试,问了好多问题,有些没有接触过,有些知道的不知道怎么表达,不过知道了流程和自己的弱点,这是值得学习的。结果客服说,“你想提升自己吗?。。。。”,这我才懂了,这是变相的招学生参加培训,加了招聘的噱头。不过,我都学到了些东东。
c++的指针和引用的区别:
我当时说的不全;
1)指针是个变量,只不过这个 变量存储的是地址,指向内存的地址;引用时别名 2)可以有const指针,但没有const引用3)指针可以有多级,但引用只有一级(int **p;合法 而 int
&&a是不合法的)4)指针可以为NULL,但引用在定义的时候必须初始化5)指针的值在初始化后可以改变,引用的值不能改变6)sizeof(引用)得到是所引用对象的大小,sizeof(指针)是指针的大小7)指针和引用的自增(++)的意义不同
2.指针和引用作为函数参数进行传递时的区别。
用指针传递参数,可以实现对实参进行改变的目的,是因为传递过来的是参数的地址.可以改变指针所指对象的值,但没法改变指针的值
。其实传递的实参的一份拷贝
用引用作为函数的参数进行传递
在讲引用作为函数参数进行传递时,实质上传递的是实参本身,即传递进来的不是实参的一个拷贝,因此修改其实是实参的修改,不仅节约时间,而且可以节约空间
fork与vfork的区别(参考http://blog.sina.com.cn/s/blog_3d97f50f0100n0vx.html)
当时没有想到会考这么细,fork记得,vfork忘了
进程在内存中3部分数据分为数据段(存放程序的全局变量,常数以及动态数据分配的数据空间),堆栈段(子程序的返回地址,子程序的参数,程序的局部变量),代码段(存放程序代码的数据)
fork返回子进程的进程号
区别如下:
1 fork():子进行拷贝父进程的数据段、堆栈段,共享父进程的代码段
vfork()子进程与父进程共享数据段,堆栈段
2 fork()父子进程的执行次序不确定 vfork保证子进程先进行
在调用exec或exit之前与父进程数据共享的,在它调用exec或exit之后父进程可能被调度运行
3: vfork保证子进程先运行,在它调用exec或exit之后父进程可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步,则会导致死锁
为什么会有vfork,因为以前的fork很傻,当它创建一个子进程时,将会创建一个新的地址空间,并拷贝父进程的资源,而往往在子进程中会执行exec调用,这样,前面的拷贝工作就白费力气了,这种情况下,聪明的人就想出了vfork,它产生的子进程刚开始暂时与父进程共享地址空间(其实线程的概念了),因为这时候子进程在父进程的地址空间中运行,所以子进行不能进行写操作,并且儿子“霸占”着老子的房子的时候,要委屈老子一下,让他在外面歇着(阻塞),一旦儿子执行了exec或者exit后,相当于儿子买了自己的房子,这时候相当分家了。
对于区别1,代码:
if(pid==0) 结尾加_exit(0),结果发现父进程的cnt=2,且子进程比父进程先进行,到执行exit或exec后父进程才执行;
扩展:exec函数
如何实现进程间同步与互斥 线程间如何通信?(参考http://www.2cto.com/os/201303/198943.html http://www.jb51.net/article/39621.htm)
线程间的同步和互斥,linux主要为我们提供了几种线程间同步互斥的机制,本文主要介绍互斥锁,条件变量和信号量。互斥锁和条件变量包含在pthread线程库中,需要包含<pthread.h>文件。而是用信号量时需要包含<semaphore.h>
1互斥锁
类型声明:pthread_mutex_t mutex
对互斥锁的初始化:
程序在使用pthread_mutex_t之前需要先对其进行初始化,对静态分配的pthread_mutex_t变量来说,只要将PTHREA_MUTEX_INITIALIZER赋给变量就行了,语句如下:
static pathread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER
对于动态分配或没有默认互斥属性的互斥变量来说,要调用pthread_mutex_init函数来执行初始化工作。
函数声明如下:int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutex_attr_t *attr)
mutex是指向一个互斥量的指针,attr是指向一个属性结构体的指针,为NULL时使用默认属性。
需要注意的是pthread_mutex_init函数只能恰好执行一次,重复执行的结果是未定义的
对互斥量的操作:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
对互斥量的销毁:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
例子如下:
有时我们可能需要让线程在某个条件满足之前一直等待,在未使用条件变量之前,你可能忙等待,如
下列代码: while(x!=y)。 引入条件变量的概念是线程在某个条件下不满足时进入挂起状态
类型声明:pthread_cond_t cond;
对条件变量的初始化: 程序在使用pthread_cond_t变量之前必须对其进行初始化。对于静态分配的pthread_cond_t变量来说,只要PTAHD_CONT_INITIALIZEER赋给变量就是了,语句如下: pthread_cond_t cond=PTHEAD_COND_INITIALIZER;
对于动态分配的或没有默认属性的变量来说,就要调用pthread_cond_init函数来初始化,函数声明如下
int pthread_cond_init(pthead_cond_t *cond,const *pthread_cond_attr_t *attr);
对条件变量的操作:
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);//使线程阻塞于某个条件
int pthread_cond_signal(pthread_cond_t *cond);//唤醒某个阻塞在cond上的线程
int pthead_cond_destory(pthread_cond_t *cond);//对条件变量销毁
使用方法:
条件变量时与断言或条件的测试一同调用的,以此实现条件变量关联到某个断言上去。通常线程会对一个条件测试,如果失败,就会调用pthread_cond_wait
函数将线程阻塞。实例代码如下:
pthread_mutex_lock(&mutex);
while(a<b)
pthretad_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
调用线程应该在它测试或调用pthread_cond_wait之前获得一个互斥量,以避免在测试条件变量的时候有其他线程改写条件中变量的值
pthread_cond_wait函数中第二个参数是一个互斥类型的指针,线程在调用pthread_cond_wait后会隐式的原子释放mutex互斥量并阻塞,允许其他线程获得互斥量并修改
断言中的变量。当线程成功的从pthread_cond_wait中返回时,它就再次拥有了互斥量,并且不用显示的重新获得互斥量
当其他线程修改了断言中变量的值后可以调用pthread_cond_signal函数来唤醒一个等待在某个断言成真的线程。也可以使用pthread_cond_broadcast
(pthread_cond_t *)函数来唤醒所有等待在某个条件变量上的线程
在修改断言中出现的任一变量之前要获得互斥量,代码
pthread_mutex_lock(&mutex);
a++;
pthread_cond_signal(&cond);
pthread_cond_unlock(&mutex);
信号量
信号量是一个整型变量,它带有两个原子wait和signal。wait 操作还可以被称为down,P操作。signal操作还可以被称为up、V、post操作
S>0的时候,说明它空闲,所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程进入睡眠队列,等待被唤醒。
通过信号量来控制线程按某种顺序执行的方法:
1 线程1中a先于线程2中b执行 [S开始为0] Process 1: a; signal(&S) process 2:wait(&S),b;
2 线程1中的a与线程2中的b交替执行[S,Q]初始化为1
Process1: Process 2:
while(1) while(1)
{wait(&S); {wait(&Q);
a; b;
signal(&Q) signal(&S)}
}
具体代码如下:
如果想Process2先执行,可以使得Q(1) ,S(0) Process1先执行,可以使得S(1) Q(0) 交替执行
需要注意的是,为了避免申请多个资源发生死锁,应按照相同的顺序申请资源,这里公司面试的时候经常问到。
linux中信号量有无名信号量和命名信号量之分,无名信号量可用于线程之间的同步和互斥,命名信号量可用于进程间的通信,命名信号量与命名管道相似,以文件的形式存于磁盘
无名信号量:
类型声明: sem_t sem;
初始化: int sem_init(sem_t *sem,int pshared,int value) 参数pshared为0,表示信号量只能由初始化这个信号量的进程中的线程使用
value表示要将sem初始化的值。value不能为负
操作:
int sem_post(sem_t *sem);//signal操作
int sem_wait(sem_t *sem);//wait操作
销毁: int sem_destroy(sem_t *sem);
命名信号量:
类型声明 sem_t sem;
初始化: sem_t *sem_open(const char *name,int oflag,...) 参数oflag用来确定创建信号量,还是仅仅由函数对其访问。若oflag中的O_CREAT比特被设置,则需要另设两个参数,mode_t mode为文件权限,unsigned value为信号量值
关闭:
int sem_close(sem_t *sem);
删除:
int sem_unlink(sem_t *sem) 操作方式与无名信号量相同
c++的指针和引用的区别:
我当时说的不全;
1)指针是个变量,只不过这个 变量存储的是地址,指向内存的地址;引用时别名 2)可以有const指针,但没有const引用3)指针可以有多级,但引用只有一级(int **p;合法 而 int
&&a是不合法的)4)指针可以为NULL,但引用在定义的时候必须初始化5)指针的值在初始化后可以改变,引用的值不能改变6)sizeof(引用)得到是所引用对象的大小,sizeof(指针)是指针的大小7)指针和引用的自增(++)的意义不同
2.指针和引用作为函数参数进行传递时的区别。
用指针传递参数,可以实现对实参进行改变的目的,是因为传递过来的是参数的地址.可以改变指针所指对象的值,但没法改变指针的值
。其实传递的实参的一份拷贝
用引用作为函数的参数进行传递
在讲引用作为函数参数进行传递时,实质上传递的是实参本身,即传递进来的不是实参的一个拷贝,因此修改其实是实参的修改,不仅节约时间,而且可以节约空间
fork与vfork的区别(参考http://blog.sina.com.cn/s/blog_3d97f50f0100n0vx.html)
当时没有想到会考这么细,fork记得,vfork忘了
进程在内存中3部分数据分为数据段(存放程序的全局变量,常数以及动态数据分配的数据空间),堆栈段(子程序的返回地址,子程序的参数,程序的局部变量),代码段(存放程序代码的数据)
fork返回子进程的进程号
区别如下:
1 fork():子进行拷贝父进程的数据段、堆栈段,共享父进程的代码段
vfork()子进程与父进程共享数据段,堆栈段
2 fork()父子进程的执行次序不确定 vfork保证子进程先进行
在调用exec或exit之前与父进程数据共享的,在它调用exec或exit之后父进程可能被调度运行
3: vfork保证子进程先运行,在它调用exec或exit之后父进程可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步,则会导致死锁
为什么会有vfork,因为以前的fork很傻,当它创建一个子进程时,将会创建一个新的地址空间,并拷贝父进程的资源,而往往在子进程中会执行exec调用,这样,前面的拷贝工作就白费力气了,这种情况下,聪明的人就想出了vfork,它产生的子进程刚开始暂时与父进程共享地址空间(其实线程的概念了),因为这时候子进程在父进程的地址空间中运行,所以子进行不能进行写操作,并且儿子“霸占”着老子的房子的时候,要委屈老子一下,让他在外面歇着(阻塞),一旦儿子执行了exec或者exit后,相当于儿子买了自己的房子,这时候相当分家了。
对于区别1,代码:
#include <sys/types.h> #include <unistd.h> #include <stdio.h> int main() { pid_t pid;
<span style="white-space:pre"> </span>int cnt=0; pid=fork(); if(pid<0) { printf("error in fork\n"); }else if(pid==0){ cnt++; printf("i'm the child process ,ID is %d cnt=%d\n",getpid(),cnt); }else { cnt++; printf("i am the parent ,ID is %d cnt=%d \n",getpid(),cnt); } }结果cnt都是为1,如果fork改为vfork(),else
if(pid==0) 结尾加_exit(0),结果发现父进程的cnt=2,且子进程比父进程先进行,到执行exit或exec后父进程才执行;
扩展:exec函数
如何实现进程间同步与互斥 线程间如何通信?(参考http://www.2cto.com/os/201303/198943.html http://www.jb51.net/article/39621.htm)
线程间的同步和互斥,linux主要为我们提供了几种线程间同步互斥的机制,本文主要介绍互斥锁,条件变量和信号量。互斥锁和条件变量包含在pthread线程库中,需要包含<pthread.h>文件。而是用信号量时需要包含<semaphore.h>
1互斥锁
类型声明:pthread_mutex_t mutex
对互斥锁的初始化:
程序在使用pthread_mutex_t之前需要先对其进行初始化,对静态分配的pthread_mutex_t变量来说,只要将PTHREA_MUTEX_INITIALIZER赋给变量就行了,语句如下:
static pathread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER
对于动态分配或没有默认互斥属性的互斥变量来说,要调用pthread_mutex_init函数来执行初始化工作。
函数声明如下:int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutex_attr_t *attr)
mutex是指向一个互斥量的指针,attr是指向一个属性结构体的指针,为NULL时使用默认属性。
需要注意的是pthread_mutex_init函数只能恰好执行一次,重复执行的结果是未定义的
对互斥量的操作:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
对互斥量的销毁:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
例子如下:
#include <stdio.h> #include <pthread.h> pthread_mutex_t Device_mutex; int count=0; void pthread_func1() { while(1) { pthread_mutex_lock(&Device_mutex); printf("thread 1: %d \n",count); pthread_mutex_unlock(&Device_mutex); count++; sleep(1); } } void pthread_func2() { while(1) { pthread_mutex_lock(&Device_mutex); printf("thread2: %d\n",count); pthread_mutex_unlock(&Device_mutex); count++; sleep(1); } } int main() { pthread_t thread1,thread2; pthread_mutex_init(&Device_mutex,NULL); if(pthread_create(&thread1,NULL,(void*)pthread_func1,NULL)==-1) { printf("create THREAD error \n"); exit(1); } if(pthread_create(&thread2,NULL,(void*)pthread_func2,NULL)==-1) { printf("create thread error \n"); exit(1); } sleep(1); pthread_join(thread1,NULL); pthread_join(thread2,NULL); pthread_mutex_destroy(&Device_mutex); return 0; }条件变量
有时我们可能需要让线程在某个条件满足之前一直等待,在未使用条件变量之前,你可能忙等待,如
下列代码: while(x!=y)。 引入条件变量的概念是线程在某个条件下不满足时进入挂起状态
类型声明:pthread_cond_t cond;
对条件变量的初始化: 程序在使用pthread_cond_t变量之前必须对其进行初始化。对于静态分配的pthread_cond_t变量来说,只要PTAHD_CONT_INITIALIZEER赋给变量就是了,语句如下: pthread_cond_t cond=PTHEAD_COND_INITIALIZER;
对于动态分配的或没有默认属性的变量来说,就要调用pthread_cond_init函数来初始化,函数声明如下
int pthread_cond_init(pthead_cond_t *cond,const *pthread_cond_attr_t *attr);
对条件变量的操作:
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);//使线程阻塞于某个条件
int pthread_cond_signal(pthread_cond_t *cond);//唤醒某个阻塞在cond上的线程
int pthead_cond_destory(pthread_cond_t *cond);//对条件变量销毁
使用方法:
条件变量时与断言或条件的测试一同调用的,以此实现条件变量关联到某个断言上去。通常线程会对一个条件测试,如果失败,就会调用pthread_cond_wait
函数将线程阻塞。实例代码如下:
pthread_mutex_lock(&mutex);
while(a<b)
pthretad_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
调用线程应该在它测试或调用pthread_cond_wait之前获得一个互斥量,以避免在测试条件变量的时候有其他线程改写条件中变量的值
pthread_cond_wait函数中第二个参数是一个互斥类型的指针,线程在调用pthread_cond_wait后会隐式的原子释放mutex互斥量并阻塞,允许其他线程获得互斥量并修改
断言中的变量。当线程成功的从pthread_cond_wait中返回时,它就再次拥有了互斥量,并且不用显示的重新获得互斥量
当其他线程修改了断言中变量的值后可以调用pthread_cond_signal函数来唤醒一个等待在某个断言成真的线程。也可以使用pthread_cond_broadcast
(pthread_cond_t *)函数来唤醒所有等待在某个条件变量上的线程
在修改断言中出现的任一变量之前要获得互斥量,代码
pthread_mutex_lock(&mutex);
a++;
pthread_cond_signal(&cond);
pthread_cond_unlock(&mutex);
#include <stdio.h> #include <pthread.h> pthread_mutex_t Device_mutex; pthread_cond_t cond; int count=0; /*print 3*k elems*/ void pthread_func1() { while(1) { pthread_mutex_lock(&Device_mutex); if(count%3!=0) pthread_cond_wait(&cond,&Device_mutex); printf("thread 1: %d \n",count); pthread_mutex_unlock(&Device_mutex); pthread_mutex_lock(&Device_mutex); count++; pthread_cond_signal(&cond); sleep(1); pthread_mutex_unlock(&Device_mutex); } } void pthread_func2() { while(1) { pthread_mutex_lock(&Device_mutex); if(count%3==0) pthread_cond_wait(&cond,&Device_mutex); printf("thread2: %d\n",count); pthread_mutex_unlock(&Device_mutex); pthread_mutex_lock(&Device_mutex); count++; pthread_cond_signal(&cond); sleep(1); pthread_mutex_unlock(&Device_mutex); } } int main() { pthread_t thread1,thread2; /*initial */ pthread_cond_init(&cond,NULL); pthread_mutex_init(&Device_mutex,NULL); if(pthread_create(&thread1,NULL,(void*)pthread_func1,NULL)==-1) { printf("create THREAD error \n"); exit(1); } if(pthread_create(&thread2,NULL,(void*)pthread_func2,NULL)==-1) { printf("create thread error \n"); exit(1); } sleep(1); pthread_join(thread1,NULL); pthread_join(thread2,NULL); pthread_mutex_destroy(&Device_mutex); pthread_cond_destroy(&cond); return 0; }thread1打印3的倍数 thread2打印不是3的倍数的数
信号量
信号量是一个整型变量,它带有两个原子wait和signal。wait 操作还可以被称为down,P操作。signal操作还可以被称为up、V、post操作
S>0的时候,说明它空闲,所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程进入睡眠队列,等待被唤醒。
通过信号量来控制线程按某种顺序执行的方法:
1 线程1中a先于线程2中b执行 [S开始为0] Process 1: a; signal(&S) process 2:wait(&S),b;
2 线程1中的a与线程2中的b交替执行[S,Q]初始化为1
Process1: Process 2:
while(1) while(1)
{wait(&S); {wait(&Q);
a; b;
signal(&Q) signal(&S)}
}
具体代码如下:
如果想Process2先执行,可以使得Q(1) ,S(0) Process1先执行,可以使得S(1) Q(0) 交替执行
#include <pthread.h> #include <semaphore.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> int number; sem_t sem_id; sem_t sem_id2; void *thread_one_fun(void *arg) { while(1) { sem_wait(&sem_id); printf("thread_one have the semphore\n"); number++; printf("number= %d\n",number); //sleep(1); sem_post(&sem_id2); sleep(1); } } void * thread_two_fun(void *arg){ while(1) { sem_wait(&sem_id2); number++; printf("thread_two have the semaphor \n"); printf("number= %d\n",number); //sleep(1); sem_post(&sem_id); sleep(1); } } int main(int argc,char *argv[]) { number=1; sem_init(&sem_id,0,1); sem_init(&sem_id2,0,1); pthread_t id1,id2; pthread_create(&id1,NULL,thread_one_fun,NULL); pthread_create(&id2,NULL,thread_two_fun,NULL); pthread_join(id1,NULL); pthread_join(id2,NULL); printf("main...\n"); return 0; }
需要注意的是,为了避免申请多个资源发生死锁,应按照相同的顺序申请资源,这里公司面试的时候经常问到。
linux中信号量有无名信号量和命名信号量之分,无名信号量可用于线程之间的同步和互斥,命名信号量可用于进程间的通信,命名信号量与命名管道相似,以文件的形式存于磁盘
无名信号量:
类型声明: sem_t sem;
初始化: int sem_init(sem_t *sem,int pshared,int value) 参数pshared为0,表示信号量只能由初始化这个信号量的进程中的线程使用
value表示要将sem初始化的值。value不能为负
操作:
int sem_post(sem_t *sem);//signal操作
int sem_wait(sem_t *sem);//wait操作
销毁: int sem_destroy(sem_t *sem);
命名信号量:
类型声明 sem_t sem;
初始化: sem_t *sem_open(const char *name,int oflag,...) 参数oflag用来确定创建信号量,还是仅仅由函数对其访问。若oflag中的O_CREAT比特被设置,则需要另设两个参数,mode_t mode为文件权限,unsigned value为信号量值
关闭:
int sem_close(sem_t *sem);
删除:
int sem_unlink(sem_t *sem) 操作方式与无名信号量相同
相关文章推荐
- SQL面试常见问题:查询及删除重复记录的方法
- .NET面试记录
- 亚信前端实习生面试记录
- 滴滴面试记录
- 斐讯面试记录—阻塞Socket和非阻塞Socket
- 程序员修炼之路(四)走近腾讯 走进腾讯(一个关于面试准备的记录)
- 面试记录1---前端开发
- 2017.3找工作面试记录-第二周
- 前几天参加了阿里巴巴的电话面试,把他们问的问题都记录了下来,希望对那些正准备面试的人有用。
- 2015链家网面试记录
- 13.7.31嵌入式面试经历记录
- 面试记录第八节——(listview面试)
- 记录 android 开发的一个 "面试" 问题
- Android实习生面试记录-蘑菇街、网易等
- 宇信易诚面试记录
- C++面试宝典笔试题记录
- 面试记录第十九节——(MVC)
- 面试记录五:腾讯后台研发
- 今天写的一段面试代码,留个记录
- 三星研究院实习面试记录&&搜狗面试记录