Jekyll Theme
2014-06-24T02:14:16-07:00
http://zhuqingcode.github.io
朱毛毛
zhuqinggooogle@gmail.com
Linux内核编程之等待队列的简单使用
2014-06-24T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/06/24/linux-kernel-program-waitqueue
<p>同上一篇《Linux内核编程之完成接口completion的简单使用》,这里对<em>等待队列</em>如何实现也不做深入解释,有兴趣的可以去看Linux源代码,主要在 </p>
<ul>
<li>include/linux/wait.h<br></li>
</ul>
<p>这个头文件里。这里只对如何使用结合demo稍作解释。下面直接给出驱动源代码: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/timer.h> // for timer_list API
#include <linux/param.h> // for HZ
#include <linux/jiffies.h> // for jiffies
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/sched.h>
DECLARE_WAIT_QUEUE_HEAD(waitqueue_demo);//定义一个等待队列头
static int waitqueue_flag = 0;//读写flag
static char waitqueue_str[16] = {0x0};
static int waitqueue_open(struct inode *inode, struct file *file) {
printk("waitqueue opened.\n");
return 0;
}
static int waitqueue_release(struct inode *inode, struct file *file) {
printk("waitqueue released.\n");
return 0;
}
static ssize_t waitqueue_read(struct file *file, char __user *buffer, size_t count, loff_t *pos) {
ssize_t ret;
printk("waitqueue read.\n");
wait_event_interruptible(waitqueue_demo, waitqueue_flag != 0);//读进程阻塞
ret = copy_to_user(buffer, waitqueue_str, count);
if (ret == 0) {
waitqueue_flag = 0;
return count;
} else {
return -EFAULT;
}
}
static ssize_t waitqueue_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) {
ssize_t ret;
printk("waitqueue write.\n");
ret = copy_from_user(waitqueue_str, buffer, count);
if (ret == 0) {
waitqueue_flag = 1;
wake_up_interruptible(&waitqueue_demo);//唤醒读进程
return count;
} else {
return -EFAULT;
}
}
static struct file_operations waitqueue_fops =
{
.owner = THIS_MODULE,
.open = waitqueue_open,
.release = waitqueue_release,
.read = waitqueue_read,
.write = waitqueue_write,
};
static struct miscdevice waitqueue_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "waitqueue",
.fops = &waitqueue_fops,
};
static int __init waitqueue_demo_init(void)
{
int ret;
printk("init waitqueue demo.\n");
ret = misc_register(&waitqueue_dev);
if (ret) {
printk("misc_register error.\n");
return ret;
}
return 0;
}
static void __exit waitqueue_demo_exit(void)
{
printk("exit waitqueue demo.\n");
misc_deregister(&waitqueue_dev);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhuqinggooogle@gmail.com");
module_init(waitqueue_demo_init);
module_exit(waitqueue_demo_exit);
</code></pre></div>
<p>同《Linux内核编程之完成接口completion的简单使用》,这里也是模拟 </p>
<ul>
<li>两个进程,一个进程写,另一个进程读。但是,必须保证在一个进程写完后,另一个进程才能读<br></li>
</ul>
<p>所以,上层demo基本上不需要修改,只需修改<em>open</em>函数里面的参数,如下: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
char waitqueue_demo[] = "waitqueue demo";
int main(int argc, char **argv) {
int fd = -1;
char buffer[16] = {0x0};
int ret;
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork error.\n");
exit(-1);
} else if (0 == pid) {
printf("----child process----\n");
fd = open("/dev/waitqueue", O_RDWR);
if (fd < 0) {
perror("open error\n");
exit(-1);
}
ret = read(fd, buffer, 16);
if (ret > 0) {
printf("%s\n", buffer);
close(fd);
exit(0);
}
close(fd);
} else if (pid > 0) {
printf("----father process----\n");
fd = open("/dev/waitqueue", O_RDWR);
if (fd < 0) {
perror("open error\n");
exit(-1);
}
printf("sleep for 10s just for delaying write operation.\n");
sleep(10);
ret = write(fd, waitqueue_demo, sizeof(waitqueue_demo));
if (ret > 0) {
printf("write ok.\n");
close(fd);
exit(0);
}
close(fd);
}
}
</code></pre></div>
<p>编译过程这里就略过了。我们直接把模块插入内核试试: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ insmod waitqueue.ko
init waitqueue demo.
</code></pre></div>
<p>再运行demo看看: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ ./demo
----father procewaitqueue opened.
ss----
sleep fowaitqueue opened.
r 10s just for dwaitqueue read.
elaying write operation.
----child process----
waitqueue write.
write ok.
waitqueue released.
/mnt/3520d $ waitqueue released.
waitqueue demo
</code></pre></div>
<p>打印为什么会错乱就不需要我解释了吧!为了更直观地说明问题,我录制了一个gif图: </p>
<p><img src="https://raw.githubusercontent.com/zhuqingcode/images4myblog/master/waitqueue.gif" alt="waitqueue"> </p>
Linux内核编程之完成接口completion的简单使用
2014-06-24T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/06/24/linux-kernel-program-completion
<p>这里对<em>完成接口completion</em>如何实现不做深入解释,有兴趣的可以去看Linux源代码,主要在 </p>
<ul>
<li>include/linux/completion.h<br></li>
</ul>
<p>这个头文件里。这里只对如何使用结合demo稍作解释。下面直接给出驱动源代码: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/timer.h> // for timer_list API
#include <linux/param.h> // for HZ
#include <linux/jiffies.h> // for jiffies
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/errno.h>
DECLARE_COMPLETION(completion_demo);//定义一个completion变量
static char completion_str[16] = {0x0};
static int completion_open(struct inode *inode, struct file *file) {
printk("completion opened.\n");
return 0;
}
static int completion_release(struct inode *inode, struct file *file) {
printk("completion released.\n");
return 0;
}
static ssize_t completion_read(struct file *file, char __user *buffer, size_t count, loff_t *pos) {
ssize_t ret;
printk("completion read.\n");
wait_for_completion_interruptible(&completion_demo);//阻塞在这儿
if (ret == 0) {
return count;
} else {
return -EFAULT;
}
}
static ssize_t completion_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) {
ssize_t ret;
printk("completion write.\n");
ret = copy_from_user(completion_str, buffer, count);
if (ret == 0) {
complete(&completion_demo);//唤醒阻塞的进程
return count;
} else {
return -EFAULT;
}
}
static struct file_operations completion_fops =
{
.owner = THIS_MODULE,
.open = completion_open,
.release = completion_release,
.read = completion_read,
.write = completion_write,
};
static struct miscdevice completion_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "completion",
.fops = &completion_fops,
};
static int __init completion_demo_init(void)
{
int ret;
printk("init completion demo.\n");
ret = misc_register(&completion_dev);
if (ret) {
printk("misc_register error.\n");
return ret;
}
return 0;
}
static void __exit completion_demo_exit(void)
{
printk("exit completion demo.\n");
misc_deregister(&completion_dev);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhuqinggooogle@gmail.com");
module_init(completion_demo_init);
module_exit(completion_demo_exit);
</code></pre></div>
<p>这里主要模拟这种情况: </p>
<ul>
<li>两个进程,一个进程写,另一个进程读。但是,必须保证在一个进程写完后,另一个进程才能读<br></li>
</ul>
<p>为此,我写了个简单的上层demo,下面是源代码: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
char completion_demo[] = "completion demo";
int main(int argc, char **argv) {
int fd = -1;
char buffer[16] = {0x0};
int ret;
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork error.\n");
exit(-1);
} else if (0 == pid) {
printf("----child process----\n");
fd = open("/dev/completion", O_RDWR);
if (fd < 0) {
perror("open error\n");
exit(-1);
}
ret = read(fd, buffer, 16);
if (ret > 0) {
printf("%s\n", buffer);
close(fd);
exit(0);
}
close(fd);
} else if (pid > 0) {
printf("----father process----\n");
fd = open("/dev/completion", O_RDWR);
if (fd < 0) {
perror("open error\n");
exit(-1);
}
printf("sleep for 10s just for delaying write operation.\n");
sleep(10);
ret = write(fd, completion_demo, sizeof(completion_demo));
if (ret > 0) {
printf("write ok.\n");
close(fd);
exit(0);
}
close(fd);
}
}
</code></pre></div>
<p>这里,父进程写,子进程读。父进程里休眠的10秒来模拟写延时的情况,子进程先读,读不到阻塞,然后父进程完成写操作后唤醒子进程。 </p>
<p>我们编译驱动demo和上层demo,下面是驱动demo的<em>Makefile</em>: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">LINUXROOT := /home/Hi3520D_SDK_V1.0.2.0/osdrv/kernel/linux-3.0.y
PWD := $(shell pwd)
obj-m := completion_demo.o
completion_demo-y += completion.o
default:
@make -C $(LINUXROOT) M=$(PWD) modules
clean:
@rm *.o *.ko -rf
@make -C $(LINUXROOT) M=$(PWD) clean
</code></pre></div>
<p>编译成功后,加载到开发板是看看: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ insmod completion_demo.ko
init completion demo.
</code></pre></div>
<p>然后再去运行上层demo: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ ./demo
----father procecompletion opened.
ss----
sleep focompletion opened.
r 10s just for dcompletion read.
elaying write operation.
----child process----
completion write.
write ok.
completion released.
/mnt/3520d $ completion released.
completion demo
</code></pre></div>
<p>看上去打印好乱,其实这是因为驱动demo里和上层demo里都有打印,打印混乱的结果。如果没有混乱应该是下面的情况: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ ./demo
----father process----
completion opened.
sleep for 10s just for delaying write operation.
----child process----
completion opened.
completion read.
completion write.
write ok.
completion released.
completion demo
/mnt/3520d $ completion released.
</code></pre></div>
如何将Bash脚本中的执行语句打印出来以起到调试作用
2014-06-24T00:00:00-07:00
http://zhuqingcode.github.io/shell/2014/06/24/linux-bash-set-x
<p>我们在调试Linux驱动的时候经常会用到<em>printk</em>函数打印一些消息到控制台,这样能帮我们判断程序是怎么执行下来的,以起到调试的作用。但是如何在<em>Bash</em>脚本中如何打印出执行的语句呢?下面举个例子做简单的介绍。 </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">linux-geek:~/3520d/osdrv/busybox # cat make-rootfs.sh
#!/bin/bash
if [ $# != 1 ];then
echo "Please input flash block size:0x10000 or 0x40000"
exit
fi
cd ./rootfs_dev/home && \
rm -rf mpp.tar.lzma &&
tar -cv mpp | lzma -z > mpp.tar.lzma && \
mv mpp ../../backup && \
cd - && \
mkfs.jffs2 -d ./rootfs_dev -l -e $1 -o jffs2-rootfs.img && \
mkfs.cramfs ./rootfs_dev cramfs-rootfs.img && \
cp jffs2-rootfs.img /tftpboot && \
cp cramfs-rootfs.img /tftpboot && \
mv ./backup/mpp ./rootfs_dev/home
</code></pre></div>
<p>假设,我们手上有上面一个简单的脚本程序,它的作用是做<em>jffs2</em>、<em>cramfs</em>的文件系统镜像。我们去执行一下试试看: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">linux-geek:~/3520d/osdrv/busybox # ./make-rootfs.sh 0x40000
mpp/
mpp/ko/
mpp/ko/hi3520D_vda.ko
mpp/ko/hi3520D_viu.ko
mpp/ko/hi3520D_vou.ko
mpp/ko/hi3520D_adec.ko
mpp/ko/hi3520D_aenc.ko
mpp/ko/jpeg.ko
mpp/ko/hi3520D_vdec.ko
mpp/ko/hi3520D_venc.ko
mpp/ko/crgctrl_8D1_hi3520D.sh
mpp/ko/hi3520D_vfmw.ko
mpp/ko/crgctrl_2X720P_hi3520D.sh
mpp/ko/crgctrl_1X1080P_hi3520D.sh
mpp/ko/hi3520D_base.ko
mpp/ko/hi3520D_group.ko
mpp/ko/hi3520D_vpss.ko
mpp/ko/pinmux_8D1_hi3520D.sh
mpp/ko/load3520D
mpp/ko/sysctl_hi3520D.sh
mpp/ko/hi3520D_chnl.ko
mpp/ko/hi3520D_ai.ko
mpp/ko/hi3520D_ao.ko
mpp/ko/hi3520D_rc.ko
mpp/ko/extdrv/
mpp/ko/extdrv/hi_ext_wdt.ko
mpp/ko/extdrv/gpioi2c.ko
mpp/ko/extdrv/hi_ir.ko
mpp/ko/extdrv/tw2968.ko
mpp/ko/extdrv/gpio.ko
mpp/ko/extdrv/rtc-isl1208.ko
mpp/ko/extdrv/wdt.ko
mpp/ko/extdrv/wifi-skw17/
mpp/ko/extdrv/wifi-skw17/ath9k_common.ko
mpp/ko/extdrv/wifi-skw17/ath9k_hw.ko
mpp/ko/extdrv/wifi-skw17/ath9k_htc.ko
mpp/ko/extdrv/wifi-skw17/compat.ko
mpp/ko/extdrv/wifi-skw17/wifi-insmod.sh
mpp/ko/extdrv/wifi-skw17/ath.ko
mpp/ko/extdrv/wifi-skw17/mac80211.ko
mpp/ko/extdrv/wifi-skw17/cfg80211.ko
mpp/ko/extdrv/wifi-skw17/ath9k.ko
mpp/ko/hi3520D_jpege.ko
mpp/ko/crgctrl_1XHD_4XD1_hi3520D.sh
mpp/ko/load3520D_socket
mpp/ko/mmz.ko
mpp/ko/hi3520D_h264e.ko
mpp/ko/vcmp.ko
mpp/ko/hiuser.ko
mpp/ko/hifb.ko
mpp/ko/hi3520D_dsu.ko
mpp/ko/hi3520D_ive.ko
mpp/ko/hi3520D_sio.ko
mpp/ko/hi3520D_tde.ko
mpp/ko/hi3520D_region.ko
mpp/ko/hi3520D_sys.ko
/root/3520d/osdrv/busybox
</code></pre></div>
<p>上述的打印主要是命令<em>tar</em>的打印。我们在脚本中加入 </p>
<ul>
<li>set -x </li>
</ul>
<p>试试看: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#!/bin/bash
set -x
if [ $# != 1 ];then
echo "Please input flash block size:0x10000 or 0x40000"
exit
fi
cd ./rootfs_dev/home && \
rm -rf mpp.tar.lzma &&
tar -cv mpp | lzma -z > mpp.tar.lzma && \
mv mpp ../../backup && \
cd - && \
mkfs.jffs2 -d ./rootfs_dev -l -e $1 -o jffs2-rootfs.img && \
mkfs.cramfs ./rootfs_dev cramfs-rootfs.img && \
cp jffs2-rootfs.img /tftpboot && \
cp cramfs-rootfs.img /tftpboot && \
mv ./backup/mpp ./rootfs_dev/home
</code></pre></div>
<p>再去执行一下看看: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">+ '[' 1 '!=' 1 ']'
+ cd ./rootfs_dev/home
+ rm -rf mpp.tar.lzma
+ tar -cv mpp
mpp/
mpp/ko/
mpp/ko/hi3520D_vda.ko
+ lzma -z
mpp/ko/hi3520D_viu.ko
mpp/ko/hi3520D_vou.ko
mpp/ko/hi3520D_adec.ko
mpp/ko/hi3520D_aenc.ko
mpp/ko/jpeg.ko
mpp/ko/hi3520D_vdec.ko
mpp/ko/hi3520D_venc.ko
mpp/ko/crgctrl_8D1_hi3520D.sh
mpp/ko/hi3520D_vfmw.ko
mpp/ko/crgctrl_2X720P_hi3520D.sh
mpp/ko/crgctrl_1X1080P_hi3520D.sh
mpp/ko/hi3520D_base.ko
mpp/ko/hi3520D_group.ko
mpp/ko/hi3520D_vpss.ko
mpp/ko/pinmux_8D1_hi3520D.sh
mpp/ko/load3520D
mpp/ko/sysctl_hi3520D.sh
mpp/ko/hi3520D_chnl.ko
mpp/ko/hi3520D_ai.ko
mpp/ko/hi3520D_ao.ko
mpp/ko/hi3520D_rc.ko
mpp/ko/extdrv/
mpp/ko/extdrv/hi_ext_wdt.ko
mpp/ko/extdrv/gpioi2c.ko
mpp/ko/extdrv/hi_ir.ko
mpp/ko/extdrv/tw2968.ko
mpp/ko/extdrv/gpio.ko
mpp/ko/extdrv/rtc-isl1208.ko
mpp/ko/extdrv/wdt.ko
mpp/ko/extdrv/wifi-skw17/
mpp/ko/extdrv/wifi-skw17/ath9k_common.ko
mpp/ko/extdrv/wifi-skw17/ath9k_hw.ko
mpp/ko/extdrv/wifi-skw17/ath9k_htc.ko
mpp/ko/extdrv/wifi-skw17/compat.ko
mpp/ko/extdrv/wifi-skw17/wifi-insmod.sh
mpp/ko/extdrv/wifi-skw17/ath.ko
mpp/ko/extdrv/wifi-skw17/mac80211.ko
mpp/ko/extdrv/wifi-skw17/cfg80211.ko
mpp/ko/extdrv/wifi-skw17/ath9k.ko
mpp/ko/hi3520D_jpege.ko
mpp/ko/crgctrl_1XHD_4XD1_hi3520D.sh
mpp/ko/load3520D_socket
mpp/ko/mmz.ko
mpp/ko/hi3520D_h264e.ko
mpp/ko/vcmp.ko
mpp/ko/hiuser.ko
mpp/ko/hifb.ko
mpp/ko/hi3520D_dsu.ko
mpp/ko/hi3520D_ive.ko
mpp/ko/hi3520D_sio.ko
mpp/ko/hi3520D_tde.ko
mpp/ko/hi3520D_region.ko
mpp/ko/hi3520D_sys.ko
+ mv mpp ../../backup
+ cd -
/root/3520d/osdrv/busybox
+ mkfs.jffs2 -d ./rootfs_dev -l -e 0x40000 -o jffs2-rootfs.img
+ mkfs.cramfs ./rootfs_dev cramfs-rootfs.img
+ cp jffs2-rootfs.img /tftpboot
+ cp cramfs-rootfs.img /tftpboot
+ mv ./backup/mpp ./rootfs_dev/home
</code></pre></div>
<p>仔细看,你会发现其中好多语句前面多了个<em>+</em>号,这正是脚本中的执行语句,说到这里你肯定就明白了只要在脚本的开头处加上个 </p>
<h3>set -x</h3>
<p>就可以打印出脚本里的执行语句即调试脚本了。 </p>
嵌入式Linux下如何利用NFSROOT启动开发板
2014-06-23T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/06/23/linux-nfsroot-boot
<p>做嵌入式开发的朋友肯定听过或者使用过NFSROOT来启动开发板,这样子有几个好处: </p>
<ul>
<li>避免了重复制作根文件系统(rootfs)镜像以及烧写文件系统</li>
<li>节省了开发时间<br></li>
</ul>
<p>利用NFSROOT来启动开发板对开发固然有好处,但是要怎么开启NFSROOT?要具备以下几个条件: </p>
<ol>
<li>宿主机(一般虚拟机下Linux系统)要开启NFS server服务<br></li>
<li>开发板使用的Linux内核要支持NFSROOT</li>
<li>开发板的网口要可以使用即网络要畅通<br></li>
</ol>
<p>以下就着重介绍上述第二点。 </p>
<p>其实,说起来也很简单,只要在内核配置中选中有关NFSROOT有关的选项,重新编译内核然后把内核镜像烧写到flash中的内核分区或者利用<em>u-boot tftp</em>下载到内存中利用<em>bootm</em>启动;另外,配置相关的u-boot启动参数就基本上OK了。接下来说说如何在内核中开启NFSROOT的功能: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">File systems--->Network File Systems--->
<*> NFS client support
[*] NFS client support for NFS version 3
[*] NFS client support for the NFSv3 ACL protocol extension
[*] NFS client support for NFS version 4
[ ] NFS client support for NFSv4.1 (EXPERIMENTAL)
[*] Root file system on NFS
</code></pre></div>
<p>这样子基本上可以了。接下来就是重新编译内核以及烧写的过程,这里不再赘述。 </p>
<p>然后,就是在<em>u-boot</em>中修改相关的启动参数,一个标准的参数如下:</p>
<p>setenv bootargs 'mem=64M console=ttyAMA0,115200 root=/dev/nfs rw nfsroot=192.168.4.165:/home/NFSROOT/hi3520d-rootfs-s-b ip=192.168.4.99:192.168.4.1:255.255.255.0 mtdparts=hi_sfc:256K(boot),256K(env),1792K(kernel),-(rootfs)' </p>
<p>特别注意这么几个参数: </p>
<ul>
<li>root=/dev/nfs rw </li>
<li>nfsroot=192.168.4.165:/home/NFSROOT/hi3520d-rootfs-s-b ip=192.168.4.99:192.168.4.1:255.255.255.0<br></li>
</ul>
<p><em>root</em>后一定要写成<em>/dev/nfs</em>,至于<em>rw</em>就随你便,不过最好是<em>rw</em>;<em>nfsroot</em>后加NFS服务器(这里是虚拟机下的Linux)的<em>ip</em>地址+roorfs目录;<em>ip</em>后是开发板将要使用的地址,如果你选用静态ip的话,你需要这么来写。</p>
<p>其余参数的具体含义就不赘述了。修改好,保存一下。最后就是启动的过程了,如果不出意外,应该可以顺利的启动起来。</p>
Linux内核编程之tasklet的简单使用
2014-06-19T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/06/19/linux-kernel-program-tasklet
<p><em>tasklet</em>主要用在中断的下半段(bh),主要用于处理一些可以延时处理的操作,这样子可以使中断ISR早点结束,把一些扫尾工作交给<em>tasklet</em>。说到底,<em>tasklet</em>也是一种延时机制,跟<em>work_queue</em>有点像。同上一篇,这里对<em>tasklet</em>的机制不做深入分析,只对如何使用稍作介绍。</p>
<p>为了能说明清楚这里给出一个demo: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h> // for timer_list API
#include <linux/param.h> // for HZ
#include <linux/jiffies.h> // for jiffies
#include <linux/interrupt.h>
static struct timer_list timer;
void tasklet_demo_handle(unsigned long data) {
printk("%s\n", __func__);
printk("%d\n", data);
}
DECLARE_TASKLET(tasklet_demo, tasklet_demo_handle, 0);
static void ISR_handler(unsigned long time) {
tasklet_schedule(&tasklet_demo);
return;
}
static int __init tasklet_demo_init(void)
{
printk("init tasklet demo.\n");
init_timer(&timer) ;
timer.function = ISR_handler;
timer.expires = jiffies + HZ * 10;
add_timer(&timer);
return 0;
}
static void __exit tasklet_demo_exit(void)
{
printk("exit tasklet demo.\n");
del_timer_sync(&timer);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhuqinggooogle@gmail.com");
module_init(tasklet_demo_init);
module_exit(tasklet_demo_exit);
</code></pre></div>
<p>这里用内核定时器模拟一个中断,在中断例程<em>ISR_handler</em>里提交一个<em>tasklet</em>,值得注意的是:</p>
<ul>
<li>DECLARE<em>TASKLET(tasklet</em>demo, tasklet<em>demo</em>handle, 0); </li>
</ul>
<p>第三个参数必须是常量,要不然编译会报类似 </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">error: initializer element is not constant
error: (near initialization for 'tasklet_demo.data')
</code></pre></div>
<p>这个错误。</p>
<p>我们编译一下,<em>insmod</em>到系统看一下: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ insmod tasklet_demo.ko
init tasklet demo.
</code></pre></div>
<p>大约过10s后,会打印: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ tasklet_demo_handle
0
</code></pre></div>
Linux如何查看USB设备的VID(Vendor ID)、PID(Product ID)
2014-06-17T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/06/17/linux-usb-devices-checkout
<p>在Linux下经常会用到USB设备,例如U盘、USB WIFI模块、3G模块(通过miniPCI-E转接板)。用到这种需要接到系统USB接口上的设备,通常你都会想“插上去能不能被Linux系统识别?”。其实,查看其能否被识别其实很简单,只需一条shell命令即可: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">cat /proc/bus/usb/devices
</code></pre></div>
<p>我们来看一下: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2
B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 1.10 Cls=09(hub ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=0000 ProdID=0000 Rev= 2.06
S: Manufacturer=Linux 2.6.16.21-0.25-default uhci_hcd
S: Product=UHCI Host Controller
S: SerialNumber=0000:00:07.2
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 0mA
I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 2 Ivl=255ms
T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 6
B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1
P: Vendor=0000 ProdID=0000 Rev= 2.06
S: Manufacturer=Linux 2.6.16.21-0.25-default ehci_hcd
S: Product=EHCI Host Controller
S: SerialNumber=0000:02:02.0
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 0mA
I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 2 Ivl=256ms
</code></pre></div>
<p>这里很明显可以清楚的看到: </p>
<ul>
<li>Vendor=? ProdID=? </li>
</ul>
<p>的字样。这就是制造商的VID(Vendor ID)、PID(Product ID)。 </p>
<p>但是,如果想查看 </p>
<ul>
<li>/proc/bus/usb/devices<br></li>
</ul>
<p>这个文件有一个前提:你必须事先挂在好了usbfs(文件系统)。要挂在其实也很简单,一条命令即可: </p>
<ul>
<li>mount -t usbfs none /proc/bus/usb<br></li>
</ul>
Linux内核编程之工作队列(work_queue)的简单使用
2014-06-13T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/06/13/linux-kernel-program-workqueue
<p>这里对Linux的工作队列(work_queue)不做深层次的挖掘,只对如何使用它以及一些简单的结构做简单地介绍。 </p>
<p>Linux源代码(3.0.8)中和工作队列(work_queue)相关的结构主要在 </p>
<ul>
<li>include/linux/workqueue.h<br></li>
</ul>
<p>这个头文件中,这里就不摘抄了。这里就直接给出例子代码,在结合稍作解释:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
static struct work_struct work;
static void work_handler(struct work_struct *data)
{
printk("just a demo for work queue.\n");
}
static int __init workqueue_init(void)
{
printk("init work queue demo.\n");
INIT_WORK(&work, work_handler);
schedule_work(&work);
return 0;
}
static void __exit workqueue_exit(void)
{
printk("exit work queue demo.\n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhuqinggooogle@gmail.com");
module_init(workqueue_init);
module_exit(workqueue_exit);
</code></pre></div>
<p>一个简单的工作队列(work_queue)的演示就完成了。我们来编译后,<em>insmod</em>到系统看看: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ insmod hi_work_queue.ko
init work queue demo
just a demo for work queue.
</code></pre></div>
<p>从系统中<em>rmmod</em>看一下: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ rmmod hi_work_queue
exit work queue demo.
</code></pre></div>
<p>如果你对Linux的工作队列(work_queue)有稍微的了解,你看到这里会提问,“我们的工作队列项提交到了哪个工作队列线程上面呢?”,这就得从 </p>
<ul>
<li>schedule_work<br></li>
</ul>
<p>函数入手。看一下其定义: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/**
* schedule_work - put work task in global workqueue
* @work: job to be done
*
* Returns zero if @work was already on the kernel-global workqueue and
* non-zero otherwise.
*
* This puts a job in the kernel-global workqueue if it was not already
* queued and leaves it in the same position on the kernel-global
* workqueue otherwise.
*/
int schedule_work(struct work_struct *work)
{
return queue_work(system_wq, work);
}
</code></pre></div>
<p>这里牵扯到 </p>
<ul>
<li>system_wq<br></li>
</ul>
<p>这个全局变量,我们来看看他到底是什么。在</p>
<ul>
<li>kernel/workqueue.c</li>
</ul>
<p>这个文件的底部给出了定义:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">system_wq = alloc_workqueue("events", 0, 0);
</code></pre></div>
<p>看到这就清楚了,刚才是把工作队列项提交了默认的工作线程<em>events</em>上的。那我们自己可以创建一个工作队列线程吗?可以把自己的工作队列项提交到上面吗?当然,可以。下面给出一个demo代码: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
static struct workqueue_struct *queue = NULL;
static struct work_struct work;
static void work_handler(struct work_struct *data)
{
printk("just a demo for work queue.\n");
}
static int __init workqueue_init(void)
{
queue = create_singlethread_workqueue("workqueue demo");
if (!queue)
return -1;
printk("init work queue demo.\n");
INIT_WORK(&work, work_handler);
queue_work("queue", &work);
return 0;
}
static void __exit workqueue_exit(void)
{
printk("exit work queue demo.\n");
destroy_workqueue(queue);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhuqinggooogle@gmail.com");
module_init(workqueue_init);
module_exit(workqueue_exit);
</code></pre></div>
<p>我们来<em>insmod</em>看一下: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">/mnt/3520d $ insmod hi_work_queue.ko
init work queue demo.
just a demo for work queue.
/mnt/3520d $
/mnt/3520d $ ps | grep "workqueue demo"
728 root 0 SW< [workqueue demo]
</code></pre></div>
<p>你会发现多了一个内核线程<em>workqueue demo</em>,这就是我们代码中自己创建的。</p>
Linux内核编程之内核定时器的简单使用
2014-06-13T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/06/13/linux-kernel-program-timer
<p>Linux内核定时器主要用在重复做一件事儿的场景。同样,这里对Linux内核定时器怎么工作的不做介绍,这里只对其使用方法做简单介绍。下面给出一个例子,并稍作解释: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#include <linux/module.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/semaphore.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/timer.h> // for timer_list API
#include <linux/param.h> // for HZ
#include <linux/jiffies.h> // for jiffies
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
static struct timer_list ext_wdt_timer;
static void ext_wdt_handler(unsigned long time) {
printk("ext_wdt works well\n");
mod_timer(&ext_wdt_timer, jiffies + msecs_to_jiffies(1000));//重要,重置内核定时器,这样子可以做到定时一秒。
return;
}
static void ext_wdt_timer_setup(unsigned char ntime) {
/*** Initialize the timer structure***/
init_timer(&ext_wdt_timer);//初始化内核定时器
ext_wdt_timer.function = ext_wdt_handler;//定时器函数,这里每隔一秒执行一次
ext_wdt_timer.expires = jiffies + HZ * ntime; //HZ = 100
add_timer(&ext_wdt_timer);
/***Initialisation ends***/
return;
}
static int __init ext_wdt_init(void) {
unsigned char ntime;
ntime = 1;//每隔一秒去做一件事儿
ext_wdt_timer_setup(ntime);
printk("init ext_wdt ok\n");
return 0;
}
static void __exit ext_wdt_exit(void) {
del_timer_sync(&ext_wdt_timer);
}
module_init(ext_wdt_init);
module_exit(ext_wdt_exit);
MODULE_AUTHOR("zhuqing@njtalent.cn");
MODULE_LICENSE("GPL");
</code></pre></div>
Linux内核数据结构之spinlock
2014-06-10T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/06/10/kernel-data-struct-spinlock
<p>从我接触Linux已经有大约四年时间了,内核源代码也尝试着看过,琢磨过。<em>Linus Benedict Torvalds</em> 也说过,学习Linux最好的方法就是 <em>read the fucking source code</em>。 但是, <em>看了忘,忘了看,看了再忘,忘了再看</em> 这个循环始终困扰着我,于是乎打算把看过的一些Linux源码中我觉得比较重要的东西想借助自己的博客记录下来,只希望能通过此举加深印象。 </p>
<p>今天就来简单记录一下自旋锁 <em>spinlock</em> 的数据类型。注意:以下代码取自Linux <em>3.0.8</em>, 如有出入, 可能是由于源代码版本的问题。 </p>
<p><em>include/linux/</em>目录下关于 <em>spinlock</em> 的头文件有6个: </p>
<blockquote>
<p>1.spinlock.h<br>
2.spinlock_api_smp.h<br>
3.spinlock_api_up.h<br>
4.spinlock_types.h<br>
5.spinlock_types_up.h<br>
6.spinlock_up.h </p>
</blockquote>
<p>初步看一下名字,第六感告诉我们这肯定和 <em>SMP</em> 和 <em>UP</em>,也就是多CPU和单CPU有关系。我们看一下 <em>spinlock_types.h</em> 中关于 <em>spinlock</em> 的定义: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">typedef struct spinlock {
union {
struct raw_spinlock rlock;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
struct {
u8 __padding[LOCK_PADSIZE];
struct lockdep_map dep_map;
};
#endif
};
} spinlock_t;
</code></pre></div>
<p>其中,牵扯到 <em>struct raw_spinlock rlock;</em>, 再来看一下它的定义: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">typedef struct raw_spinlock {
arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned int magic, owner_cpu;
void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
} raw_spinlock_t;
</code></pre></div>
<p>其中,又牵扯到 <em>arch<em>spinlock</em>t raw_lock;</em>, 如果你认真跟踪源代码,你会发现有两个地方牵扯到了其定义,也就是 <em>spinlock_types.h</em> 开头的一段代码包含进来的两个头文件:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#if defined(CONFIG_SMP)
# include <asm/spinlock_types.h>
#else
# include <linux/spinlock_types_up.h>
#endif
</code></pre></div>
<p><em>asm/spinlock_types.h</em> 跟平台有关系,这里看一下arm平台的: </p>
<p>如果是 <em>SMP</em> 系统: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">typedef struct {
volatile unsigned int lock;
} arch_spinlock_t;
</code></pre></div>
<p>如果是 <em>UP</em> 系统: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#ifdef CONFIG_DEBUG_SPINLOCK
typedef struct {
volatile unsigned int slock;
} arch_spinlock_t;
#define __ARCH_SPIN_LOCK_UNLOCKED { 1 }
#else
typedef struct { } arch_spinlock_t;
#define __ARCH_SPIN_LOCK_UNLOCKED { }
#endif
</code></pre></div>
<p>从这里看出,如果定义了 <em>CONFIG_DEBUG_SPINLOCK</em> 就是一个<em>volatile unsigned int</em>的变量;相反,则是一个空结构体。这也间接证明一个问题,<em>UP</em> 系统上不存在真正意义上的自旋锁。</p>
嵌入式Linux驱动需要加载固件(firmware)的情况
2014-05-23T00:00:00-07:00
http://zhuqingcode.github.io/linux/2014/05/23/firmware
<p>前段时间一直在板子上调试wifi模块(skwlab skw17),驱动(compat wireless)可以正常编译通过,就是加载驱动的时候,提示需要固件,但是固件总是找不到,导致wifi模块初始化失败。这个问题困扰了我好久,一直找不到原因(起初,一直以为是驱动有问题,下个四个版本的驱动,仍然如此),没办法只能换一个模块(RT3070)来调试,谁知道在调试过程中也遇到相同的问题,大致问题如下图: </p>
<p><img src="/images/firmware.jpg" alt="firmware"> </p>
<p>和 </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">rt2870usb 1-1:1.0: firmware: requesting rt2870.bin
phy0 -> rt2x00lib_request_firmware: Error - Failed to request Firmware.
</code></pre></div>
<p>顿时感觉不是驱动有问题,应该是我的Linux系统有问题。后来,google一番后,终于找到原因:busybox里面没有配置mdev。于是,果断在busybox选上mdev: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">[*] mdev
[*] Support /etc/mdev.conf
[*] Support subdirs/symlinks
[*] Support regular expressions substitutions when renaming device
[*] Support command execution at device addition/removal
[*] Support loading of firmwares
</code></pre></div>
<p>编译,后放到板子上,在启动脚本里加上: </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#start mdev
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
</code></pre></div>
<p>启动后,加载驱动,完美地解决问题。</p>