asmlinkage long sys_syslog(int type, char __user * buf, int len) { return do_syslog(type, buf, len); } int do_syslog(int type, char __user *buf, int len) { unsigned long i, j, limit, count; int do_clear = 0; char c; int error = 0; error = security_syslog(type); //检查是否调用这个函数的权限 if (error) return error; switch (type) { case 0:/* 关闭日志 */ break; case 1:/* 打开日志*/ break; case 2:/*读取日志信息*/ error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { //验证是否有写的权限 error = -EFAULT; goto out; } //当log_start - log_end为0时,表示环形缓冲区无数据可读,把当前进程放入 等待队列log_wait error = wait_event_interruptible(log_wait, (log_start - log_end)); if (error) goto out; i = 0; spin_lock_irq(&logbuf_lock); while (!error && (log_start != log_end) && i < len) { c = LOG_BUF(log_start); //从环形缓冲区得到读取位置log_start log_start++; spin_unlock_irq(&logbuf_lock); error = __put_user(c,buf); //将c地址的字符传递到用户空间的buf中 buf++; i++; cond_resched(); //条件调度,让其他进程有运行时间 spin_lock_irq(&logbuf_lock); } spin_unlock_irq(&logbuf_lock); if (!error) error = i; break; case 4:/* 读/清除上一次内核消息*/ do_clear = 1; /* FALL THRU */ case 3:/*读取上一次内核消息*/ error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) //读取长度为0 goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { //验证有写权限 error = -EFAULT; goto out; } count = len; if (count > log_buf_len) count = log_buf_len; spin_lock_irq(&logbuf_lock); if (count > logged_chars) // logged_chars是上次读/清除以来产生的日志字符数 count = logged_chars; if (do_clear) logged_chars = 0; limit = log_end; /* __put_user() 可以睡眠,当__put_user睡眠时,printk()可能覆盖写正在 拷贝到用户空间的消息,因此,这些消息被反方向拷贝,将buf覆盖部分的数据重写到buf的起始位置*/ for (i = 0; i < count && !error; i++) { //读取count个字符 j = limit-1-i; if (j + log_buf_len < log_end) break; c = LOG_BUF(j); //从环形缓冲区得到读取位置j spin_unlock_irq(&logbuf_lock); //将c位置的字符传递到用户空间的buf中,如果发生错误,将发生错误的c位置给error error = __put_user(c,&buf[count-1-i]); cond_resched(); spin_lock_irq(&logbuf_lock); } spin_unlock_irq(&logbuf_lock); if (error) break; error = i; if (i != count) { //表示__put_user没有拷贝完成 int offset = count-error; /* 拷贝期间缓冲区溢出,纠正用户空间缓冲区*/ for (i = 0; i < error; i++) { if (__get_user(c,&buf[i+offset]) || __put_user(c,&buf[i])) { //将覆盖部分的数据 重写到buf的起始位置 error = -EFAULT; break; } cond_resched(); } } break; case 5:/* 清除环形缓冲区*/ logged_chars = 0; break; case 6:/*关闭向控制台输出消息*/ console_loglevel = minimum_console_loglevel; break; case 7:/*开启向控制台输出消息*/ console_loglevel = default_console_loglevel; break; case 8:/* 设置打印到控制台的日志级别*/ error = -EINVAL; if (len < 1 || len > 8) goto out; if (len < minimum_console_loglevel) len = minimum_console_loglevel; console_loglevel = len; error = 0; break; case 9:/* 得到日志消息所占缓冲区的大小*/ error = log_end - log_start; break; case 10:/*返回环形缓冲区的大小*/ error = log_buf_len; break; default: error = -EINVAL; break; } out: return error; } |