内核线程、用户线程与select/poll/epoll之间的关系?
前提:
内核线程是在“开机”时刻就已经创建的; 第一个内核线程pid=2由0进程创建, 1号进程是最原始的用户线程;
从 ps aux
命令上看 带中括号的[],如:[kthreadd]
就是内核线程;
系统调用是陷入内核的方式之一, 一般发送一个signal
陷入内核就需要一次cpu 切换, 同步出内核时也需要一次cpu切换, 即一次完整的内核调用需要cpu切换两次;
多路复用-select
调用select()函数,
分析select函数的参数
int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
maxfd : 一定是描述符集合是最大描述符fd+1;
readset|writeset|exceptset: 分别是需要监听的读、写、异常
描述符集合;
timeout: 如果为null则表示select会无限等待事件发生;
注意点:
- 以readset为例,每次select后,如果有事件则对应描述符为1(前提是设置了监听-入参时设置成1了), 没有则为设置成0; 下一轮如果还要监听相应描述符则应该重新设置监听(入参);
- maxfd这个有一个最大1024的限制;
多路复用select/poll等如果不等可读或可写描述符处理完,又紧接着执行一遍select/或poll, 这个时候返回的结果中,刚才的还未处理完读或写操作的描述符,还会在这次结果中吗? 如果在那么多个线程同时处理一个描述符会有问题, 如果不在又与多路复用的这个函数功能不相符;
简单的说就是:多次执行select/poll如果不对描述符处理,上次的结果中可处理的描述符还会在下次中出现吗?
epoll
三大函数:
epoll_create();
epoll_ctl();
epoll_wait();
epoll改进了接口设计,避免了用户态-内核态频繁的数据拷贝;
level-triggerrd (条件触发) VS edge-triggered (边缘触发);
如何理解Socket?
先得看socket在linux上的数据结构
如何从源码的角度来理解阻塞与非阻塞?
分析:
处理一个socket过程中哪些环节是内核线程处理,哪些是用户线程处理;
内核空间,用户空间,用户态, 内核态, 内核线程, 用户线程;
操作线程把虚拟地址空间分成两部分: 内核空间,用户空间;
就32位系统来说, 0-3G是用户空间, 3-4G是内核空间;
![](https://img.haomeiwen.com/i1020417/9296742544af8ac8.png)
用户线程即可以运行在用户空间也可以运行在内核空间, 运行在用户空间是管定义成用户态, 同理运行在内核空间则叫内核态;
区分内核空间和用户空间本质上是要提高操作系统的稳定性及可用性。
而内核线程只能运行在内核空间,同时内核线程是由内核自己创建的线程,也叫做守护线程(deamon), 主要从事于:
- 线程按周期性间隔运行,检测特定资源的使用,在用量超出或者低于预置的限制时采取行动;
- 在线程启动后则一直等待,直到内核线程请求执行某一特定的操作。
用户线程从用户态变成内核态主要操作是:系统调用, 软中断,硬中断;
![](https://img.haomeiwen.com/i1020417/c63c6d9dd13460c3.png)
对于一个进程来讲,从用户空间进入内核空间并最终返回到用户空间,这个过程是十分复杂的。举个例子,比如我们经常接触的概念 “堆栈”,其实进程在内核态和用户态各有一个堆栈。运行在用户空间时进程使用的是用户空间中的堆栈,而运行在内核空间时,进程使用的是内核空间中的堆栈。所以说,Linux 中每个进程有两个栈,分别用于用户态和内核态。
参考
强烈推荐:内核空间,用户空间, 内存映射
强烈推荐: 用户级线程和内核级线程,你分得清吗?
强烈推荐:linux epoll原理分析
网友评论