数据库的select底层实现

来源:互联网 发布:java语言开发 编辑:程序博客网 时间:2024/06/18 05:47

话题:数据库中的select底层?
一、从数据库查询数据的角度,大概架构(前提:客户端需要将查询语句发送到服务器端)
1.接到语句查找sql计划缓存
如果没有—_—||
2.检查语句合法性(对sql语句语法的检查,)

3.检查语言含义(对sql语句的所包含的表名,字段名)

4.获得对象解析锁

5.核对用户权限是否有访问数据的权限

探究到着,我不禁想到,平时写sql语句的时候,就是这样的啊!
6.确定最佳执行计划(自我优化,很有限),同时将当前sql语
句与最佳执行计划保存到高速缓存

二、从select的原型考虑
select()原型主要是建立在fd_set类型的基础上的。fd_set是一组文件描述字(fd)的集合,它用一位来表示一个fd,对于fd_set类型通过下面四个宏定义来操作:
fd_set set;
FD_ZERO(&set); /将set的所有位置0,如set在内存中占8位则将set置为00000000/
FD_SET(0, &set); /* 将set的第0位置1,如set原来是00000000,则现在变为10000000,这样fd==1的文件描述字就被加进set中了 */
FD_CLR(4, &set); /*将set的第4位置0,如set原来是10001000,则现在变为10000000,这样fd==4的文件描述字就被从set中清除了
FD_ISSET(5, &set); /* 测试set的第5位是否为1,如果set原来是10000100,则返回非零,表明fd==5的文件描述字在set中;否则返回0*/

select函数的的接口:
int select(int nfds, fd_set readset, fd_set *writeset,fd_set exceptset, struct timeval *timeout);
功能:
测试指定的fd可读?可写?有异常条件待处理?
参数:
1.nfds:需要检查的文件描述字个数(即检查到fd_set的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset,writeset,exceptset中所含最大的fd为5,则nfds=6,因为fd是从0开始的)。设这个值是为提高效率,使函数不必检查fd_set的所有1024位。
2.readset:用来检查可读性的一组文件描述字。
3.writeset:用来检查可写性的一组文件描述字。
4.exceptset:用来检查是否有异常条件出现的文件描述字。
5.timeout:有三种可能:
1. timeout=NULL(阻塞:直到有一个fd位被置为1函数才返回)
2.timeout所指向的结构设为非零时间(等待固定时间:有一个fd位被置
为1或者时间耗尽,函数均返回)
3. timeout所指向的结构,时间设为0(非阻塞:函数检查完每个fd后立即返回)
返回值:
返回对应位仍然为1的fd的总数。
Remarks:
三组fd_set均将某些fd位置0,只有那些可读,可写以及有异常条件待处理的fd位 仍然为1。

使用select函数的一般过程:调用宏FD_ZERO将指定的fd_set清零—–>宏FD_SET将fd加入fd_set——>接着调用函数select测试fd_set中的所有fd,最后用宏FD_ISSET检查某个fd在函数select调用后,相应位是否仍然为1。

//单个文件描述字可读性
int isready(int fd)
{
int rc;
fd_set fds;
struct timeval tv;
FD_ZERO(&fds);
FD_SET(fd,&fds);
tv.tv_sec = tv.tv_usec = 0;
rc = select(fd+1, &fds, NULL, NULL, &tv);
if (rc < 0) //error
return -1;
return FD_ISSET(fd,&fds) ? 1 : 0;
}

应用程序调用select() 函数,系统调用陷入内核,进入到:
SYSCALL_DEFINE5 (sys_select)—-> core_sys_select —–> do_select()
SYSCALL_DEFINE5(select, int, n, fd_set __user , inp, fd_set __user , outp,
fd_set __user , exp, struct timeval __user , tvp)//n为文件描述符
{
struct timespec end_time, *to = NULL;
struct timeval tv;
int ret;
if (tvp) {
if (copy_from_user(&tv, tvp, sizeof(tv)))
return -EFAULT;
to = &end_time;
if (poll_select_set_timeout(to,
tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),

                        (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))                 return -EINVAL;   }   ret = core_sys_select(n, inp, outp, exp, to);   ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);   return ret;

}

在core_sys_select() 函数中调用了do_select:
int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{
ktime_t expire, *to = NULL;
struct poll_wqueues table;
poll_table *wait;
int retval, i, timed_out = 0;
unsigned long slack = 0;

     rcu_read_lock();     retval = max_select_fd(n, fds);     rcu_read_unlock();     if (retval < 0)               return retval;     n = retval;     poll_initwait(&table);//初始化结构体,主要是初始化poll_wait的回调函数为__pollwait     wait = &table.pt;     if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {               wait = NULL;               timed_out = 1;     }     if (end_time && !timed_out)               slack = estimate_accuracy(end_time);     retval = 0;     for (;;) {               unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;               inp = fds->in; outp = fds->out; exp = fds->ex;               rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;               for (i = 0; i < n; ++rinp, ++routp, ++rexp) {                        unsigned long in, out, ex, all_bits, bit = 1, mask, j;                        unsigned long res_in = 0, res_out = 0, res_ex = 0;                        const struct file_operations *f_op = NULL;                        struct file *file = NULL;                        in = *inp++; out = *outp++; ex = *exp++;                        all_bits = in | out | ex;                        if (all_bits == 0) {                                 i += __NFDBITS;                                 continue;                        }                        for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {                                 int fput_needed;                                 if (i >= n)                                           break;                                 if (!(bit & all_bits))                                           continue;                                 file = fget_light(i, &fput_needed);                                  if (file) {                                           f_op = file->f_op;                                           mask = DEFAULT_POLLMASK;                                           if (f_op && f_op->poll) {                                                     wait_key_set(wait, in, out, bit);                                                    mask = (*f_op->poll)(file, wait););//调用poll_wait处理过程,                                                    //即把驱动中等待队列头增加到poll_wqueues中的entry中,并把指向                                                    //当前里程的等待队列项增加到等待队列头中。每一个等待队列头占用一个entry                                           }                                           fput_light(file, fput_needed);                                           if ((mask & POLLIN_SET) && (in & bit)) {//如果有信号进行设置,记录,写回到对应项,设置跳出循环的retval                                                    res_in |= bit;                                                    retval++;                                                    wait = NULL;                                           }                                           if ((mask & POLLOUT_SET) && (out & bit)) {                                                    res_out |= bit;                                                    retval++;                                                    wait = NULL;                                           }                                           if ((mask & POLLEX_SET) && (ex & bit)) {                                                    res_ex |= bit;                                                    retval++;                                                    wait = NULL;                                           }                                 }                        }                        if (res_in)                                 *rinp = res_in;                        if (res_out)                                 *routp = res_out;                        if (res_ex)                                 *rexp = res_ex;                        cond_resched();//增加抢占点,调度其它进程,当前里程进入睡眠               }               wait = NULL;               if (retval || timed_out || signal_pending(current))//这里就跳出循环,需要讲一下signal_pending                        break;               if (table.error) {                        retval = table.error;                        break;               }               /*                * If this is the first loop and we have a timeout                * given, then we convert to ktime_t and set the to                * pointer to the expiry value.                */                //读取需要等待的时间,等待超时               if (end_time && !to) {                         expire = timespec_to_ktime(*end_time);                        to = &expire;               }               if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,to, slack))                       timed_out = 1;     }     poll_freewait(&table);//从等待队列头中删除poll_wait中添加的等待队列,并释放资源     return retval;//调用成功与否就看这个返回值

}

do_select大概的思想就是:当应用程序调用select() 函数, 内核就会相应调用 poll_wait(), 把当前进程添加到相应设备的等待队列上,然后将该应用程序进程设置为睡眠状态。直到该设备上的数据可以获取,然后调用wake up 唤醒该应用程序进程来获取数据。

这里写图片描述