使用cat读取和echo写内核文件节点的一些问题
span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }.cm-searching {background: #ffa; background: rgba(255, 255, 0, .4);}.cm-force-border { padding-right: .1px; }@media print { .CodeMirror div.CodeMirror-cursors {visibility: hidden;}}.cm-tab-wrap-hack:after { content: ""; }span.CodeMirror-selectedtext { background: none; }.CodeMirror-activeline-background, .CodeMirror-selected {transition: visibility 0ms 100ms;}.CodeMirror-blur .CodeMirror-activeline-background, .CodeMirror-blur .CodeMirror-selected {visibility:hidden;}.CodeMirror-blur .CodeMirror-matchingbracket {color:inherit !important;outline:none !important;text-decoration:none !important;}
-->
li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-level3 > li {list-style-type:lower-roman;}blockquote {padding:0 12px;padding:0 0.75rem;}blockquote > :first-child {margin-top:0;}blockquote > :last-child {margin-bottom:0;}img {border:0;max-width:100%;height:auto !important;margin:2px 0;}table {border-collapse:collapse;border:1px solid #bbbbbb;}td, th {padding:4px 8px;border-collapse:collapse;border:1px solid #bbbbbb;min-height:28px;word-break:break-all;box-sizing: border-box;}.wiz-hide {display:none !important;}
-->
作者
平台
概述
正文
一、read和write的介绍
二、简略的分析一下read和write系统调用的实现
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret; if (!(file->f_mode & FMODE_READ))
return -EBADF;
if (!(file->f_mode & FMODE_CAN_READ))
return -EINVAL;
if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
return -EFAULT; ret = rw_verify_area(READ, file, pos, count);
if (!ret) {
if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT;
ret = __vfs_read(file, buf, count, pos);
if (ret > ) {
fsnotify_access(file);
add_rchar(current, ret);
}
inc_syscr(current);
} return ret;
}
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret; if (!(file->f_mode & FMODE_WRITE))
return -EBADF;
if (!(file->f_mode & FMODE_CAN_WRITE))
return -EINVAL;
if (unlikely(!access_ok(VERIFY_READ, buf, count)))
return -EFAULT; ret = rw_verify_area(WRITE, file, pos, count);
if (!ret) {
if (count > MAX_RW_COUNT)
count = MAX_RW_COUNT;
file_start_write(file);
ret = __vfs_write(file, buf, count, pos);
if (ret > ) {
fsnotify_modify(file);
add_wchar(current, ret);
}
inc_syscw(current);
file_end_write(file);
} return ret;
}
这里需要关注:
三、简略分析cat和echo的实现
static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
{
int status = -;
off_t total = ;
bool continue_on_write_error = ;
ssize_t sendfile_sz;
char buffer[ * ]; // 用户空间缓冲区,4KB大小
enum { buffer_size = sizeof(buffer) }; // 每次read期望获得的字节数 sendfile_sz = ;
if (!size) {
size = ( * *); // 刚开始,如传入的size是0,这里将size设置为16MB
status = ; /* 表示一直读到文件结尾,也就是直到read返回0 */
} while () {
ssize_t rd; rd = safe_read(src_fd, buffer, buffer_size); // 这里调用的就是read, 读取4KB,rd是实际读到的字节数
if (rd < ) {
bb_perror_msg(bb_msg_read_error);
break;
}
read_ok:
if (!rd) { /* 表示读到了文件结尾,那么结束循环 */
status = ;
break;
}
/* 将读到的内容输出到dst_fd表示的文件描述符 */
if (dst_fd >= && !sendfile_sz) {
ssize_t wr = full_write(dst_fd, buffer, rd);
if (wr < rd) {
if (!continue_on_write_error) {
bb_perror_msg(bb_msg_write_error);
break;
}
dst_fd = -;
}
} total += rd; // total记录的是读到的字节数的累计值
if (status < ) { /* 如果传入的size不为0,那么status为-1,直到读到size个字节后,才会退出。如果size为0,这个条件不会满足 */
size -= rd;
if (!size) {
/* 'size' bytes copied - all done */
status = ;
break;
}
}
}
out:
return status ? - : total; // 当读完毕,status为0,这里返回累计读到的字节数
}
从上面的分析我们知道如下信息:
ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len)
{
ssize_t cc;
ssize_t total; total = ; while (len) {
cc = safe_write(fd, buf, len); if (cc < ) {
if (total) {
/* we already wrote some! */
/* user can do another write to know the error code */
return total;
}
return cc; /* write() returns -1 on failure. */
} total += cc;
buf = ((const char *)buf) + cc;
len -= cc;
} return total;
}
上面的函数很简单,可以得到如下信息:
四、实例分析
1、先看两个刷屏的例子
这个驱动在/sys/kernel/debug生成一个demo节点,支持读和写。
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
static struct dentry *demo_dir;
static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[10];
int ret, wrinten;
printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos);
wrinten = snprintf(kbuf, 10, "%s", "Hello");
ret = copy_to_user(user_buf, kbuf, wrinten+1);
if (ret != 0) {
printk(KERN_ERR "read error");
return -EIO;
}
*ppos += wrinten;
return wrinten;
}
static ssize_t demo_write (struct file *fp, const char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[10] = {0};
int ret;
printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos);
ret = copy_from_user(kbuf, user_buf, count);
if (ret) {
pr_err("%s: write error\n", __func__);
return -EIO;
}
*ppos += count;
return 0;
}
static const struct file_operations demo_fops = {
.read = demo_read,
.write = demo_write,
};
static int __init debugfs_demo_init(void)
{
int ret = 0;
demo_dir = debugfs_create_file("demo", 0444, NULL,
NULL, &demo_fops);
return ret;
}
static void __exit debugfs_demo_exit(void)
{
if (demo_dir)
debugfs_remove(demo_dir);
}
module_init(debugfs_demo_init);
module_exit(debugfs_demo_exit);
MODULE_LICENSE("GPL");
#include <linux/init.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <asm/uaccess.h> static struct dentry *demo_dir; static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[];
int ret, wrinten; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); wrinten = snprintf(kbuf, , "%s", "Hello"); ret = copy_to_user(user_buf, kbuf, wrinten+);
if (ret != ) {
printk(KERN_ERR "read error");
return -EIO;
} *ppos += wrinten; return wrinten;
} static ssize_t demo_write (struct file *fp, const char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[] = {};
int ret; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); ret = copy_from_user(kbuf, user_buf, count);
if (ret) {
pr_err("%s: write error\n", __func__);
return -EIO;
} *ppos += count; return ;
} static const struct file_operations demo_fops = {
.read = demo_read,
.write = demo_write,
}; static int __init debugfs_demo_init(void)
{
int ret = ; demo_dir = debugfs_create_file("demo", , NULL,
NULL, &demo_fops); return ret;
} static void __exit debugfs_demo_exit(void)
{
if (demo_dir)
debugfs_remove(demo_dir);
} module_init(debugfs_demo_init);
module_exit(debugfs_demo_exit);
MODULE_LICENSE("GPL");
我们先来看看运行结果:

2、对write进行修改
static ssize_t demo_write (struct file *fp, const char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[] = {};
int ret; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); ret = copy_from_user(kbuf, user_buf, count);
if (ret) {
pr_err("%s: write error\n", __func__);
return -EIO;
} *ppos += count; return count;
}
验证:
static ssize_t demo_write (struct file *fp, const char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[] = {}; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); return simple_write_to_buffer(kbuf, sizeof(kbuf), ppos, user_buf, count);
}
[ 2739.984844] user_buf: 00202340, count: 2, ppos: 0
/**
* simple_write_to_buffer - copy data from user space to the buffer
* @to: the buffer to write to
* @available: the size of the buffer
* @ppos: the current position in the buffer
* @from: the user space buffer to read from
* @count: the maximum number of bytes to read
*
* The simple_write_to_buffer() function reads up to @count bytes from the user
* space address starting at @from into the buffer @to at offset @ppos.
*
* On success, the number of bytes written is returned and the offset @ppos is
* advanced by this number, or negative value is returned on error.
**/
ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos,
const void __user *from, size_t count)
{
loff_t pos = *ppos;
size_t res; if (pos < )
return -EINVAL;
if (pos >= available || !count)
return ;
if (count > available - pos)
count = available - pos;
res = copy_from_user(to + pos, from, count);
if (res == count)
return -EFAULT;
count -= res;
*ppos = pos + count;
return count;
}
EXPORT_SYMBOL(simple_write_to_buffer);
可以看到,最后返回的是count,如果copy_from_user没都拷贝全,将来write还是会被再次调用。
3、对read进行修改
static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[];
int ret, wrinten; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); wrinten = snprintf(kbuf, , "%s", "Hello"); ret = copy_to_user(user_buf, kbuf, wrinten+);
if (ret != ) {
printk(KERN_ERR "read error");
return -EIO;
} *ppos += wrinten; return ;
}
static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
char kbuf[10];
int ret, wrinten;
printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos);
wrinten = snprintf(kbuf, 10, "%s", "Hello");
ret = copy_to_user(user_buf, kbuf, wrinten+1);
if (ret != 0) {
printk(KERN_ERR "read error");
return -EIO;
}
*ppos += wrinten;
return验证:
static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[];
int ret, wrinten; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); wrinten = snprintf(kbuf, , "%s", "Hello"); ret = copy_to_user(user_buf, kbuf, wrinten+);
if (ret != ) {
printk(KERN_ERR "read error");
return -EIO;
} *ppos += wrinten; return count;
}
验证:
static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[];
int ret, wrinten; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); wrinten = snprintf(kbuf, , "%s", "Hello"); if (clear_user(user_buf, count)) {
printk(KERN_ERR "clear error\n");
return -EIO;
} ret = copy_to_user(user_buf, kbuf, wrinten+);
if (ret != ) {
printk(KERN_ERR "read error\n");
return -EIO;
} *ppos += wrinten; return count;
}
上面的这种改动只是不会输出乱码了,但是还是会刷屏。
static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[];
int wrinten; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); wrinten = snprintf(kbuf, , "%s", "Hello"); if (clear_user(user_buf, count)) {
printk(KERN_ERR "clear error\n");
return -EIO;
} return simple_read_from_buffer(user_buf, count, ppos, kbuf, wrinten);
}
验证:
/**
* simple_read_from_buffer - copy data from the buffer to user space
* @to: the user space buffer to read to
* @count: the maximum number of bytes to read
* @ppos: the current position in the buffer
* @from: the buffer to read from
* @available: the size of the buffer
*
* The simple_read_from_buffer() function reads up to @count bytes from the
* buffer @from at offset @ppos into the user space address starting at @to.
*
* On success, the number of bytes read is returned and the offset @ppos is
* advanced by this number, or negative value is returned on error.
**/
ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos,
const void *from, size_t available)
{
loff_t pos = *ppos;
size_t ret; if (pos < )
return -EINVAL;
if (pos >= available || !count)
return ;
if (count > available - pos)
count = available - pos;
ret = copy_to_user(to, from + pos, count);
if (ret == count)
return -EFAULT;
count -= ret;
*ppos = pos + count;
return count;
}
EXPORT_SYMBOL(simple_read_from_buffer);
第一次read是ppos是0,读完毕之后,ppos变成了5。我们知道,cat不甘心,因为没有返回0,所以紧接着又调用了一次read,这次的ppos为5,上面的第23行代码生效了,available是5,所以直接返回了0,然后cat就乖乖的退出了。
static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[];
int wrinten; if (*ppos)
return ; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); wrinten = snprintf(kbuf, , "%s", "Hello"); if (clear_user(user_buf, count)) {
printk(KERN_ERR "clear error\n");
return -EIO;
} return simple_read_from_buffer(user_buf, count, ppos, kbuf, wrinten);
}
在第6行,先判断*ppos的值,我们知道第一次调用驱动read时,*ppos是0,读完毕后,*ppos会被更新,第二次*ppos便不为0.
static ssize_t demo_read(struct file *fp, char __user *user_buf, size_t count, loff_t *ppos)
{
char kbuf[];
int ret, wrinten; if (*ppos)
return ; printk(KERN_INFO "user_buf: %p, count: %d, ppos: %lld\n",
user_buf, count, *ppos); wrinten = snprintf(kbuf, , "%s", "Hello"); if (clear_user(user_buf, count)) {
printk(KERN_ERR "clear error\n");
return -EIO;
} ret = copy_to_user(user_buf, kbuf, wrinten);
if (ret != ) {
printk(KERN_ERR "copy error\n");
return -EIO;
} *ppos += wrinten; return wrinten;
}
使用cat读取和echo写内核文件节点的一些问题的更多相关文章
- 【转】忙里偷闲写的小例子---读取android根目录下的文件或文件夹
原文网址:http://www.cnblogs.com/wenjiang/p/3140055.html 最近几天真的是各种意义上的忙,忙着考试,还要忙着课程设计,手上又有外包的项目,另一边学校的项目还 ...
- 忙里偷闲写的小例子---读取android根目录下的文件或文件夹
最近几天真的是各种意义上的忙,忙着考试,还要忙着课程设计,手上又有外包的项目,另一边学校的项目还要搞,自己的东西还在文档阶段,真的是让人想死啊!! 近半个月来,C#这方面的编码比较多,android和 ...
- 如何用nfs命令烧写内核和文件系统(网络下载文件到nandflash)(未完)
使用tftp下载烧写 a.设uboot里的ip地址 set ipaddr 192.168.1.17(uboot的ip设置成同网段) set serverip 192.168.1.5(电脑本机作为服务i ...
- linux通用GPIO驱动,写GPIO文件不立即生效问题解决
Linux开发平台实现了通用GPIO的驱动,用户通过,SHell或者系统调用能控制GPIO的输出和读取其输入值.其属性文件均在/sys/class/gpio/目录下,该目录下有export和unexp ...
- Linux cat命令详解(连接文件并打印到标准输出设备上)
cat:连接文件并打印到标准输出设备上 一.命令格式: cat [-AbeEnstTuv] [--help] [--version] filename 二.参数说明: -n 或 --number:由 ...
- 荣品RP4412开发板烧写内核cannot load出错的原因
问:荣品RP4412开发板烧写必须要配置Xmanager吗? 现在我烧写内核出现这个错误是什么原因呢? 答:4412文件夹下没有zImage这个文件, 你打开4412这个文件夹. 你都拼写错了, zI ...
- PLSQL_PLSQL读和写XML文件方式(案例)
2012-05-01 Created By BaoXinjian
- bootstrap 中是通过写less文件来生成css文件,用什么工具来编写呢?
bootstrap 中是通过写less文件来生成css文件,用什么工具来编写呢? 如果用sublime的话如何实现代码保存后浏览器刷新成最新的代码样式? 或者有什么其他好用的工具? 从网上找了很多方法 ...
- Python写UTF8文件,UE、记事本打开依然乱码的问题
Python写UTF8文件,UE.记事本打开依然乱码的问题 Leave a reply 现象:使用codecs打开文件,写入UTF-8文本,正常无错误.用vim打开正常,但记事本.UE等打开乱码. 原 ...
随机推荐
- MDP安装之数据库
/usr/bin/mysqladmin -u root password 'Bic2017' mysql-community-client-5.6.28-2.el6.x86_64 mysql-comm ...
- springboot中url地址重写(urlwrite)
在日常网站访问中,会把动态地址改造成伪静态地址. 例如: 访问新闻栏目 /col/1/,这是原有地址,如果这样访问,不利于搜索引擎检索收录,同时安全性也不是很好. 改造之后: /col/1.html. ...
- 使用OpenSSL自建CA + Nginx配置HTTPS
Ubuntu 16.04(ECS),OpenSSL 1.0.2g 1 Mar 2016,Nginx 1.10.3 (Ubuntu), 浏览器:Chrome 67,Firefox 61,Edge 40 ...
- 转载 http://blog.csdn.net/dengta_snowwhite/article/details/6418384
从SDCard保存的txt文件读取中文到android系统中会出现乱码问题,如何解决这个乱码问题,网上有不少解答方法,譬如说利用String temp1 =EncodingUtils.getStrin ...
- Python 单例模式讲解
Python 单例模式讲解 本节内容: classmethod用途 单例模式方法一 类__new__方法讲解 单例模式方法二 前言: 使用单例方法的好处:对于一个类,多次实例化会产生多个对象,若使用单 ...
- Java基础87 MySQL数据约束
1.默认值 -- 创建表student1,设置address字段有默认值 create table student1 ( id int, name ), address ) default '广东省深 ...
- Mysql报Cannot load from mysql.proc. The table is probably corrupted
1548-Cannot load from mysql.proc. The table is probably corrupted http://bugs.mysql.com/bug.php?id=5 ...
- Android studio2.3.3升级3.1.2坑
原文:https://blog.csdn.net/qq_26361871/article/details/80255141 1.grade配置Error: Could not find com.and ...
- 【LOJ】#2479. 「九省联考 2018」制胡窜
题解 老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很 现在后缀树上线段树合并差点把我写死 主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了 但就是不想写 但是由于我过于老 ...
- 【LOJ】#2071. 「JSOI2016」最佳团体
题解 01分数规划,二分加树背包-- 代码 #include <bits/stdc++.h> #define enter putchar('\n') #define space putch ...