面试记录

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
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值