腾讯一面

进程间的通信方式

进程间有\(6\)种通信方式

  1. 管道(pipe)及有名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信
  2. 信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的
  3. 消息队列(message queue):消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息
  4. 共享内存(shared memory):它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量(也就是一个进行在未完成写操作之前,另一个进程不能读取)
  5. 信号量(semaphore):它是一个计数器,信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据(而信号量一般常用于保护一段代码,使其每次只被一个执行线程运行)。对于二值信号量(0-1),信号量大于0时候,可以对进程操作,等于0时要等待,知道信号量大于0
  6. 套接字(socket):一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛

僵尸进程和孤儿进程

僵尸进程:一个父进程利用fork创建子进程,如果子进程退出,而父进程没有利用wait或者waitpid来获取子进程的状态信息,那么子进程的状态描述符依然保存在系统中

在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。这个僵尸进程需要它的父进程来为它收尸,如果他的父进程没有处理这个僵尸进程的措施,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程
僵尸进程解决

  • 调用wait函数,进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止
  • 当子进程终止时,内核就会向它的父进程发送一个SIGCHLD信号, 当父进程接收到SIGCHLD信号后就应该调用wait或waitpid函数对子进程进行善后处理,释放子进程占用的资源

孤儿进程:一个父进程退出, 而它的一个或几个子进程仍然还在运行,那么这些子进程就会变成孤儿进程,孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集的工作

子进程死亡需要父进程来处理,那么意味着正常的进程应该是子进程先于父进程死亡。当父进程先于子进程死亡时,子进程死亡时没父进程处理,这个死亡的子进程就是孤儿进程。
但孤儿进程与僵尸进程不同的是,由于父进程已经死亡,系统会帮助父进程回收处理孤儿进程。所以孤儿进程实际上是不占用资源的,因为它终究是被系统回收了。不会像僵尸进程那样占用ID,损害运行系统

TCP为啥需要3次握手

从TCP全双工的角度回答,都需要知道对方的序列号TCP 为什么三次握手而不是两次握手tcp为什么是三次握手

static_assert

static_assert这个关键字,用来做编译期间的断言,因此叫做静态断言。
语法:static_assert(常量表达式,提示字符串)—-如果第一个参数常量表达式的值为真(true或者非零值),那么static_assert不做任何事情,就像它不存在一样,否则会产生一条编译错误,错误位置就是该static_assert语句所在行,错误提示就是第二个参数提示字符串
编译器在遇到一个static_assert语句时,通常立刻将其第一个参数作为常量表达式进行演算,但如果该常量表达式依赖于某些模板参数,则延迟到模板实例化时再进行演算,这就让检查模板参数成为了可能
static_assert的断言表达式的结果必须是在编译时期可以计算的表达式,即必须是常量表达式。如果使用变量,则会导致错误

wirte和fwrite

fwrite: 带缓冲区;write: 不带缓冲区

std::move

函数原型

1
2
3
4
5
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
}

程序中\(\dfrac{1}{0}\)是在编译期出错还是在运行时候出错

在linux系统g++编译器测试:编译可以通过,运行时报错