涉及到C语言、操作系统和思考题。
- 多线程和多进程?
一个程序至少有一个进程,一个进程至少有一个线程。线程共享进程的内存空间,创建线程花费比进程小,线程是操作cpu的基本单元。线程有自己的私有空间,一般是寄存器和栈,用来存放自己的局部变量,线程间通信通过共享的全局变量通信即可。进程的优点是隔离度好,但进程的信息资源共享麻烦。线程是进程的执行单元,有利于争夺cpu等分时资源,实现并发多任务,好处是,性能优越,但需要解决好线程安全,即对所有的资源冲突解决好,一般通过加锁的方式保证保证线程对共享资源的访问安全。线程间相互影响,出问题的几率大,对于频繁启动,大量的执行单元,用多线程较好,在程序员的角度看,只有线程安全,没有进程安全。
fork()是创建子进程的函数,它返回两次(2个进程),子进程中返回0;父进程中返回子进程的pid;出现错误,返回负值。
并发:两个或多个事件在同一时间间隔发生。宏观上,多个程序是同时进行的,但是单cpu核心同一时刻只能执行一个程序,微观上,程序是分时交替执行的。
参考:多进程和多线程区别、多进程和多线成总结、Linux中的fork()
2.tcp/ip 3次握手?
TCP是面向连接的。计算机间通信先要建立连接,然后才能通信,最后通信结束要拆除连接,通信是可靠的。UDP在网络层是面向非连接的,不可靠的。TCP建立连接需要3次握手,拆除连接需要4次握手。
3次握手:服务器必须做好接受外来的连接,通过调用socket、bind和listen函数,称为被动打开。
1):客户端打开connect连接,向服务器发送SYN(synchronize)分节(表示同步syn=j),它告诉服务器,客户即将发送初始序列号,SYN不带数据,只包含ip头部,tcp头部,可能tcp选项。客户端正在SYN_SEND状态,等待服务器确认。
2):服务器必须确认客户的SYN分节后,发送本身SYN分节(syn=k),它包含同一个连接中服务器的初始序列号,同时发送ACK(j+1),也就是同时发送SYN+ACK包,服务器处于SYN_RECV状态。
3):客户端必须确认服务器发送的SYN后,并发送确认ACK包(k+1),此包发送完毕,服务器和客户端进入Established状态。至此,3次握手完毕,tcp建立连接。
TCP连接终止:4次握手:
1):某个应用程序首先调用close,我们称客户端为主动关闭(active close),发送FIN字节(M),表示数据发送完毕。
2):服务器接收到Fin字节,成为被动关闭,它的接收也作为文件结束符。发送ACK(M+1),此客户程序进入等待关闭序列。
3):当服务器调用close关闭客户的套接口后,服务器向客户程序发送FIN(N)
4):客户程序接收到FIN后,发送ACK(N+1)确认。
参考:TCP/IP3次握手过程
3.斯密斯夫妇握手问题
史密斯夫妇邀请另外四对夫妇就餐,已知他们每个人都不和自己握手,不和自己的配偶握手,且不和同一个人握手一次以上。在大家见面握手寒暄后,史密斯问大家握手了几次,每个人的答案都不一样。问:史密斯太太握手几次?
参考:史密斯夫妇握手智力题
4.你的面前有两扇门,一边通向死亡,一边通向生存,你想要生存,你面前站了俩个人,一个只说真话,一个只说谎话,你有一次机会询问两个人中的一个人一个问题,找到生存之门。
参考:真假问题
5.一个矩形蛋糕,蛋糕内部有一块矩形的空洞。只用一刀,如何将蛋糕切成大小相等的两块?
过两矩形的中心划一条线,注意平分矩形的线都过矩形中心。沿着该线切开。
6.实现malloc()?
一个简单版本的动态申请空间和释放空间,来自K&R的5.4节。
#include<stdio.h> #define ALLOCSIZE 1000 //定义可分配的空间大小 static char allocbuf[ALLOCSIZE]; //定义可分配的空间 static char *allocp=allocbuf; //将空闲指针初始化 char * allocc(int n) { if(allocbuf+ALLOCSIZE-allocp>=n) //如果可分配的空间够用 { allocp=allocp+n; //分配空间,将空闲指针后移n位 return allocp-n; //返回分配的首地址 } else return 0; //内存不够,返回NULL((char *)0) } void afree(char *p) { if(p>=allocbuf && p<allocbuf+ALLOCSIZE) //如果是allocc分配的内存 allocp=p; //将空闲指针指向它 }
或者看复杂点的malloc()实现:malloc()和free()实现与分析
数学知识:
有趣的谜题: