Redis在window下和linux下的区别

早期,redis只能运行在linux上,原因是底层调用的是epoll方法,而windows下没有该方法。除此之外,windows下也没有fork( )函数。最终为了强行能在windows上运行,使用的是select+ IOCP方式。

Epoll 是当事件资源满足时发出可处理通知消息;
IOCP 则是当事件完成时发出完成通知消息;
从应用程序的角度来看, Epoll 是同步非阻塞的;IOCP是异步操作;

简单来说,Epoll可以省去你等待的过程,但是不会帮你完成任务。
IOCP是在Epoll的基础上,还会帮你把任务完成,完成之后,让你过来拿结果,完全异步操作。

Windows只提供Select
Linux提供Select、Poll、Epoll

Select:

过程① 准备bitmap:先调用FD_ZERO函数,进行初始化,把一批文件描述符(一般是5个)初始化为0,然后调用FD_SET函数,循环设置(设置的数是随机,但是小于10),将bitmap上对应(需要监听的文件描述符)的置为1。

过程② 准备完毕后,将bitmap拷贝,从用户态拷贝至内核态。由内核去判断,哪个文件描述符有对应的数据到达。
比如下面,当有数据从客户端传输到网卡,发生软中断,就会调用DMA拷贝技术,拷贝到内核环形缓冲区,根据对应的文件描述符信息,拷贝到socket的数据接收队列。

 过程③:拷贝完之后,将对应的文件描述符标记成已就绪的状态,然后就会将全部文件描述符(包括未就绪的)全部返回给用户态(此时只返回了已就绪的个数,哪几个不知道)。

过程④:然后在用户态中,循环遍历出,是哪几个的文件描述符发生了变更。

完成后,阻塞解除。

 注:while(1)的右括号应该包括下面ret = select 以及for循环遍历全部过程。
accept函数是用来创建客户端连接的,并存放到fd数组中,连接到服务端。
这里有一个点,求出最大文件描述符max ,后面的max+1 ,这样不需要每次循环去多遍历那些没有用到的,节省性能。

select函数的参数:max+1 , 读文件描述符的集合,写文件描述符的集合,需要监听的异常事件文件描述符集合,超时时间(null:永不超时 ,0:不阻塞, >0 :具体阻塞的时间)

为什么要拷贝至内核态:内核态工作效率高。如果是用户态中的bitmap每有一个文件描述符有对应的数据到达,就去告诉内核态,那么就会有频繁的用户态和内核态上下文切换。

Select的不足:

fd_set每次都需要重新初始化,不能做到重用。
还是有频繁的用户态和内核态的拷贝,性能消耗大。
需要对文件描述符循环遍历,O(n)的时间复杂度。

Poll:

相比Select做了一个改进:定义了一个存放文件描述符信息的结构体,然后其中存放的事件的类型,是读事件还是写事件。然后将整个结构体数组拷贝到内核态。通过socket去监听....

当有消息从客户端发送到网卡,通过DMA拷贝技术到内核环形缓冲区。该过程和select一样。
只不过,这里是把对应的revents属性置为1。然后将整个结构体数组拷贝回用户态,去循环判断,当前的revents如果为1,则置为0。达到了结构体数组的复用,不用每次执行poll函数前去初始化

Epoll: linux2.6之后

accept函数:用来创建客户端连接,并连接到客户端,存放到epoll_event中,注册监听事件
epoll_ctl函数:注意参数,第二个是回调函数,每创建完一个socket连接后,就会把它的节点添加到红黑树中,这里涉及epitem结构体。当epitem结构体对应的文件描述符就绪的时候,通过回调函数,放进双向链表中。

每当我们调用 epoll_ctl 增加一个 fd 时,内核就会为我们创建出一个 epitem 实例,并且把这个实例作为红黑树的一个子节点,增加到 eventpoll 结构体中的红黑树中,对应的字段是 rbr。这之后,查找每一个 fd 上是否有事件发生都是通过红黑树上的 epitem 来操作。

epoll_wait函数:去检查双向链表中,检查就绪队列是否有元素,没有就将当前进程阻塞,然后让出cpu,如果有就绪事件的话,epoll_wait会立刻返回,不阻塞,会将已就绪的事件拷贝到数组中,并返回给用户态。

简述一下epoll的过程:当消息基于客户端传输到网卡,进而通过DMA拷贝技术,拷贝到内核环形缓冲区。有一个Socket消息接收队列,如果来了消息,会通过回调函数,将这条消息从内核环形缓冲区直接插入到epoll就绪队列,相比select和poll来说,不需要去遍历,时间复杂度从O( n) -> O(1)。

并且整个过程只需要传递一次文件描述符,即从用户态拷贝到内核态,后面即epoll_wait函数不需要拷贝文件描述符回用户态,而是直接通过回调函数,返回给用户态。

底层是红黑树加双向链表的结构。这样加快了每个文件描述符节点的增删改查的速度,时间复杂度从O(n) ->O (logN)。

Epoll多路复用的触发机制

redis采用的是水平触发,nginx采用的是边缘触发。

Window Form 应用程序是基于 Windows 操作系统 GUI 界面的应用程序,它使用了多线程技术。在 Window Form 应用程序中,主线程通常用于处理用户界面(UI)操作,而其他线程则用于执行后台任务,例如进行网络通信、文件处理等。这种方式可以避免长时间的操作阻塞 UI 线程,让应用程序更加流畅。 在 Window Form 应用程序中,创建线程的方式与标准的 C# 程序一样,可以使用 Thread 类或 ThreadPool 类来创建线程。但需要注意的是,在 UI 线程中访问 UI 控件是不安全的,必须使用 Invoke 或 BeginInvoke 方法来让 UI 线程更新 UI 控件。 例如,以下代码演示了如何在 Window Form 应用程序中创建一个后台线程来执行耗时的任务: ``` private void btnStart_Click(object sender, EventArgs e) { // 创建后台线程 Thread thread = new Thread(new ThreadStart(DoWork)); thread.Start(); } private void DoWork() { // 执行耗时的任务 // ... // 更新 UI 控件(需要使用 Invoke 或 BeginInvoke 方法) this.Invoke(new Action(() => { // 更新 UI 控件 // ... })); } ``` 在上述代码中,btnStart_Click 方法是 UI 线程中的事件处理程序,当用户点击按钮时会创建一个后台线程来执行任务。DoWork 方法是后台线程的入口点,它执行耗时的任务,并使用 Invoke 方法来更新 UI 控件。这样可以保证 UI 线程不会被阻塞,同时也可以让用户获得更好的体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值