文件名为mymsg.c参考kmsg.c,printk.c
目的:想定义1个缓冲区如mulog_buf,在读函数里面把缓冲区里面的数据给/proc/mymsg,应用程序通过cat /proc/mymsg就会进入到内核态,发现是虚拟文件系统的文件,找到它的file_operation结构体,调用读函数,在读函数里面调用copy_to_user
#define MYLOG_BUF_LEN 1024
//全局指针
struct proc_dir_entry *myentry;
//缓冲区大小为1024字节
static char mylog_buf[MYLOG_BUF_LEN];
static char tmp_buf[MYLOG_BUF_LEN];
static int mylog_r = 0;
static int mylog_r_for_read = 0;
static int mylog_w = 0;
//声明1个等待队列头
static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);
/*环形缓冲区函数:空、写、读、满*/
//空函数
static int is_mylog_empty(void){
//读写位置相等
return (mylog_r == mylog_w);}
static int is_mylog_empty_for_read(void)
{
return (mylog_r_for_read == mylog_w);
}
//满函数
static int is_mylog_full(void)
{
//(写位置+1)%长度=读位置
return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);}
//写入1个数据
static void mylog_putc(char c)
{
//如果缓冲区满了
if (is_mylog_full()){
/* 丢弃一个数据 */扔掉的数据是读指针指向的数据,读指针向移移
mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;
if ((mylog_r_for_read + 1) % MYLOG_BUF_LEN == mylog_r)//1:15分钟视频有解析
{
mylog_r_for_read = mylog_r;//更新读指针
}
}
//写数据
mylog_buf[mylog_w] = c;
mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;//写指针向后移1位
/* 唤醒等待数据的进程 */
wake_up_interruptible(&mymsg_waitq); /* 唤醒休眠的进程 */
}
//取数据
static int mylog_getc(char *p)
{
//如果缓冲区为空,返回0
if (is_mylog_empty()){
return 0;
}
*p = mylog_buf[mylog_r];//非空取出数据
mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;//读指针向后移
return 1;
}
//读取字符
static int mylog_getc_for_read(char *p)
{
//如果缓冲区里面没有数据
if (is_mylog_empty_for_read()){
return 0;
}
*p = mylog_buf[mylog_r_for_read];
//读位置往后移
mylog_r_for_read = (mylog_r_for_read + 1) % MYLOG_BUF_LEN;return 1;
}
//把打印信息输出到mylog_buf里面
int myprintk(const char *fmt, ...)
{
va_list args;
int i;
int j;
va_start(args, fmt);
//把fmt里面的信息输出到tmp_buf里面,i是tmp_buf中数据量的大小
i = vsnprintf(tmp_buf, INT_MAX, fmt, args);va_end(args);
//把临时缓冲区的数据放到mylog_buf环形缓冲区
for (j = 0; j < i; j++)
mylog_putc(tmp_buf[j]);
return i;
}
//读函数
如果应用程序以非阻塞方式打开我们在proc下创建的文件mymsg,
static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
int error = 0;
int i = 0;
char c;
/* 把mylog_buf的数据copy_to_user, return */
//文件以非阻塞方式打开,并且环形缓冲区是空的就立刻返回
if ((file->f_flags & O_NONBLOCK) && is_mylog_empty_for_read())return -EAGAIN;
加打印语句看卡在哪里
//printk("%s %d\n", __FUNCTION__, __LINE__);//打印哪一个函数,哪一行
//printk("count = %d\n", count);//打印需要读的数据数
//printk("mylog_r = %d\n", mylog_r);//读位置
//printk("mylog_w = %d\n", mylog_w);//写位置
//等待环形缓冲区非空 ,mymsg_waitq参数是队列,休眠时,(写数据时)以后吧你唤醒,到队列里找到进程唤醒,所以进程先放到1个队列。
error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());
条件不成立就会休眠
//环形后再次打印
//printk("%s %d\n", __FUNCTION__, __LINE__);//printk("count = %d\n", count);
//printk("mylog_r = %d\n", mylog_r);
//printk("mylog_w = %d\n", mylog_w);
/* copy_to_user */吧数据拷贝到用户空间
//如果没有错误且获得字符串,i表示读到的数据数,还没有达到count值,先获得数据存在c中
mylog_getc_for_read
error = __put_user(c, buf);//把数据拷贝到用户空间
buf++;//用户空间的buf++
i++;
}
if (!error)//如果一直没有错误
error = i;//返回读到的数据个数
return error;
}
static int mymsg_open(struct inode *inode, struct file *file)
{
//保存读变量的初始值,为了能重复打印信息
mylog_r_for_read = mylog_r;return 0;
}
//file_operation结构体(里面有打开函数和读函数)
const struct file_operations proc_mymsg_operations = {
.open = mymsg_open,
.read = mymsg_read,
};
//入口函数
创建了proc下面的1个条目(文件mymsg),以后读写这个文件的时候就会调用到proc_mymsg_operations这个结构体,调用里面的读函数
static int mymsg_init(void){ //第1个参数文件名字是mymsg,第2个参数是属性(只读),第3个参数是parent,表示proc这个根目录里面
myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root);
if (myentry)
myentry->proc_fops = &proc_mymsg_operations;
return 0;
}
//出口函数
static void mymsg_exit(void)
{
remove_proc_entry("mymsg", &proc_root);//删除创建的文件
}
module_init(mymsg_init);
module_exit(mymsg_exit);
//myprintk函数要导出来给其他驱动调用
EXPORT_SYMBOL(myprintk);
//添加GPL 协议
MODULE_LICENSE("GPL");
#################################################
#################################################
另外1个驱动程序调用myprintk函数打印信息(用first_drv.c)
调用时声明是1个外部函数
在open函数里面调用myprintk函数打印信息