Leo的技术分享

记录开发的点滴

什么是终端复用(terminal multiplexer)?终端复用可以让你在同一个终端同时打开不同的程序并观察输出,同时允许你方便地退出和恢复这些程序的使用。
使用tmux可以达到终端复用的目的,下图为tmux在同一个终端窗口中同时打开不同程序的一个示例:

安装

对于 Mac OS,使用简单的 brew命令即可完成tmux安装:

brew install tmux

Mac下如何安装的详细教程可以参考这个链接:Mac安装tmux教程

阅读全文 »

本来对于多说不支持 HTTPS 就不太满意,早就想换一个评论系统了。昨天上去多说的官网一看,发现多说竟然要在今年6月1号关闭了。

可能是多说没有找到赚钱的路子吧,这样的话自己只能默默祝福一下了。

多说的关闭也坚定了自己将博客的评论跟换成网易跟贴的决心。

说干就干。

阅读全文 »

一、多个源文件带来的问题

在编写c/c++测试程序时,我们习惯每次修改一处代码,然后就马上编译运行来查看运行的结果。这种编译方式对于小程序来说是没有多大问题的,可对于大型程序来说,由于包含了大量的源文件,如果每次改动一个地方都需要编译所有的源文件,这个简单的直接编译所有源文件方式对程序员来说简直是噩耗。
我们看一个例子:

1
2
3
4
5
6
7
8
9
10
// main.c
#include "a.h"

// 2.c
#include "a.h"
#include "b.h"

// 3.c
#include "b.h"
#include "c.h"

如果程序员只修改了头文件c.h,则源文件main.c2.c都无需编译,因为它们不依赖这个头文件。而对3.c来说,由于它包含了c.h,所以在头文件c.h改动后,就必须得新编译。
而如果改动了b.h可是忘记编译了2.c,那么最终的程序就可能无法正常工作。
make 工具就是为了解决上述问题而出现的,它会在必要时重新编译所有受改动影响的源文件。

阅读全文 »

epoll也是实现I/O多路复用的一种方法,为了深入了解epoll的原理,我们先来看下epoll水平触发(level trigger,LT,LT为epoll的默认工作模式)与边缘触发(edge trigger,ET)两种工作模式。

使用脉冲信号来解释LT和ET可能更加贴切。Level是指信号只需要处于水平,就一直会触发;而edge则是指信号为上升沿或者下降沿时触发。说得还有点玄乎,我们以生活中的一个例子来类比LT和ET是如何确定读操作是否就绪的。

水平触发
儿子:妈妈,我收到了500元的压岁钱。
妈妈:嗯,省着点花。
儿子:妈妈,我今天花了200元买了个变形金刚。
妈妈:以后不要乱花钱。
儿子:妈妈,我今天买了好多好吃的,还剩下100元。
妈妈:用完了这些钱,我可不会再给你钱了。
儿子:妈妈,那100元我没花,我攒起来了
妈妈:这才是明智的做法!
儿子:妈妈,那100元我还没花,我还有钱的。
妈妈:嗯,继续保持。
儿子:妈妈,我还有100元钱。
妈妈:…

接下来的情形就是没完没了了:只要儿子一直有钱,他就一直会向他的妈妈汇报。LT模式下,只要内核缓冲区中还有未读数据,就会一直返回描述符的就绪状态,即不断地唤醒应用进程。在上面的例子中,儿子是缓冲区,钱是数据,妈妈则是应用进程了解儿子的压岁钱状况(读操作)。

阅读全文 »

poll函数类似于select函数,也可以实现I/O多路复用。poll函数的声明如下:

1
2
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

第一个参数是指向一个结构数组第一个元素的指针。每个数组元素都是一个pollfd结构,用于指定测试某个给定描述符fd的条件。

1
2
3
4
5
struct pollfd {
int fd; // 需要测试的描述符
short event; // 对fd感兴趣的事件
short revents; // 发生在fd的事件:期待的事件或者异常情况发生
};

要测试的条件由events成员指定,poll函数在相应的revents成员中返回该描述符的状态。eventsrevents都由某个特定条件的一位或多位构成。下面表格列出了用于指定events标志以及测试revents标志的一些常值。

阅读全文 »

I/O多路复用模型允许我们同时等待多个套接字描述符是否就绪。Linux系统为实现I/O多路复用提供的最常见的一个函数是select函数,该函数允许进程指示内核等待多个事件中的任何一个发生,并只有在一个或多个事件发生或经历一段指定的时间后才唤醒它。
作为一个例子,我们可以调用select,告知内核仅在下列情况发生时才返回:

  • 当集合{0, 4}中任意描述符准备好读时返回
  • 当集合{1, 2, 7}中任意描述符准备好写时返回
  • 已经历了10.2秒

也就是说,我们调用select可以告知内核我们对哪些描述符感兴趣以及等待多久时间。
select是一个复杂的函数,有许多不同的应用场景,我们将只讨论第一种场景:等待一组描述符准备好读。

1
2
3
4
5
6
7
8
9
#include <unistd.h>
#include <sys/types.h>

int select(int n, fd_set *fdset, NULL, NULL, struct timeval *timeout);

FD_ZERO(fd_set *fdset); // 将fdset初始为为空集合
FD_CLR(int fd, fd_set *fdset); // 从fdset清除fd
FD_SET(int fd, fd_set *fdset); // 将fd添加到fdset
FD_ISSET(int fd, fd_set *fdset); // fd是否存在于fdset
阅读全文 »

在多进程并发服务器的应用程序中,父进程accept一个连接,fork一个子进程,该子进程负责处理与该连接对端的客户之间的通信。
尽管多进程的编程模型中,各进程拥有独立的地址空间,减少了出错的概率,然而,fork调用却存在一些问题:

  • fork是昂贵的,fork要把父进程的内存映像复制到子进程,并在子进程中复制所有描述符,这个操作是较重量级的。
  • fork返回之后父子进程之间信息的传递需要进程间通信(IPC)机制。

线程则可以解决上述两个问题。线程有时也称为轻量级的进程,线程的创建可能比进程的创建快10-100倍。同一个进程内所有线程共享相同的全局内存,这使得线程之间易于共享信息,但伴随这种简易性而来的是线程安全问题。

线程函数

1. pthread_create 函数

我们介绍的第一个线程的函数是pthread_create,它的作用是创建一个新线程。它的定义如下:

1
2
#include <pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

这个函数的定义看起来很复杂,其实用起来很简单。
第一个参数是指向pthread_t类型的指针。线程被创建时,这个指针指向的变量将被写入一个标识符(线程ID)我们用该标识符来引用新线程。
第二个参数用于设置线程的属性,一般不需要特殊的属性,所以只需要设置该参数为NULL。
最后两个参数,分别告诉新线程将要启动执行的函数和传递给该函数的参数。
pthread_create函数在成功调用时返回0,如果失败则返回失败码。

阅读全文 »

我们来考虑有多个客户同时连接一个服务器的情况。在前面的TCP套接字编程的例子中,我们已经看到,服务器程序在接受来自客户端的一个新连接时,会创建出一个新的套接字(已连接套接字),而原先的监听套接字则继续监听后面的连接请求。如果服务器不能立刻接受后来的连接,它们将被放到队列中以等待处理。
原先的套接字仍然可用并且套接字的行为就像文件描述符,这一事实给我们提供了一种同时服务多个客户的方法。如果服务器调用fork为自己创建第二份副本,打开的套接字就将被新的子进程所继承。新的子进程可以和连接的客户进行通信,而主服务器进程可以继续接受以后的客户连接。

为了了解这是如何工作的,假设我们有两个客户端和一个服务器,服务器正在监听一个监听套接字(比如描述符3)上的连接请求。
现在假设服务器接受了客户端1的连接请求,并返回一个已连接套接字(比如描述符4),如图1所示。

图1:第一步:服务器接受客户端的连接请求

阅读全文 »

监听套接字(listening socket)和已连接套接字(connected socket)之间的区别常会使很多人感到迷惑。本文简要描述一下这两者的区别。

为了说明监听套接字与已连接套接字的区别,我们先来看一下套接字在连接中的含义。
从内核的角度来看,一个套接字就是通信的一个端点。一个连接由它两端的套接了地址唯一确定,这对套接字地址叫做套接字对(socket pair),由下列4元组来表示:

(clientip:clientport, serverip:serverport)

其中,clientip 是客户端的IP地址,clientport 是客户端的端口,serverip 是服务器的IP地址,而 serverport 是服务器的端口。


图1:套接字对4元组示意图

上图1展示了一个套接字对4元组,即一个客户端与一个服务器之间的连接。在这个示例中,客户端套接字为

阅读全文 »

概述

套接字(socket)是一种通信机制,凭借这种机制,客户与服务器的通信既可以在本地单机上进行,也可以跨网络进行。

在这里插入图片描述
图:基本的TCP客户/服务器应用程序的套接字函数

图中展示了一对TCP客户与服务器进程之间进行通信时调用套接字函数的交互情况。服务器首先启动,然后监听客户的连接。稍后客户试图连接服务器,客户连接成功后,客户给服务器发送请求,服务器处理请求,并且返回给客户一个响应。这个过程一直持续下去,直到客户关闭客户端的连接,接着服务器也关闭相应的服务器端的连接,接着服务器继续等待新的客户连接。

阅读全文 »
0%