今天一个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,代码:
#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) 操作方式与无名信号量相同