linux下怎样运行oyrhon,第十章 LINUX驱动程序实验
10-1ADC驱动程序
10-1-1 说明
程序源代码说明:
驱动源代码所在目录
drivers/spi/
驱动程序名称
mcp3201.c
设备号
mcp3201属于杂项设备,设备自动生成
设备名
/dev/mcp3201
测试程序源代码目录
Examples/Drivers/ADC
测试程序名称
test-mcp3201.c
测试程序可执行文件名称
test-mcp3201
要写实际的驱动,就必须了解相关的硬件资源,比如用到的寄存器,物理地址,中断等,在这里,mcp3201是一个很简单的例子,它用到了如下硬件资源。
开发板上所用到的 1个ADC的硬件资源:
ADC
对应的IO 寄存器名称
对应的CPU 引脚
Mcp3201
Spi0
G16
要操作所用到的 IO口,就要设置它们所用到的寄存器,我们可以调用一些现成的函数或者宏,在我们的驱动中使用的是mcp3201_open()来进行控制的。
函数mcp3201_open()的定义在我们的驱动程序中,接下来的驱动程序代码将给出示意。
在下面的驱动代码中,你将看到调用mcp3201_open()函数来对设备实施初始化并打开,除此之外,还需要调用一些和设备驱动密切相关的基本函数,如注册设备misc_register,填写驱动函数结构file_operations,以及像Hello,Module中那样的module_init和module_exit函数等。
有些函数并不是必须的,随着你对Linux驱动开发的进一步了解和的代码,你自然明白。下面是我们为mcp3201编写的驱动代码清单。
10-1-2驱动程序清单
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineSB2F_BOARD_SPI0_BASE0xbfe80000
struct spi_device *adc;
static int mcp3201_open(struct inode *inode, struct file *file) {
//取消片选CS3
writeb(0xff, SB2F_BOARD_SPI0_BASE + REG_SPCSR);
//复位SPI控制寄存器工作
writeb(0x10, SB2F_BOARD_SPI0_BASE + REG_SPCR);
//重置状态寄存器SPSR
writeb(0xc0, SB2F_BOARD_SPI0_BASE + REG_SPSR);
//设置外部寄存器
writeb(0x02, SB2F_BOARD_SPI0_BASE + REG_SPER);
//禁用SPI FLASH读
writeb(0x30, SB2F_BOARD_SPI0_BASE + REG_SPPR);
//配置SPI时序
writeb(0xd3, SB2F_BOARD_SPI0_BASE + REG_SPCR);
//设置片选CS3
writeb(0x7f, SB2F_BOARD_SPI0_BASE + REG_SPCSR);
return 0;
}
static int mcp3201_close(struct inode *inode, struct file *file) {
//取消对CS3的片选
writeb(0xff, SB2F_BOARD_SPI0_BASE + REG_SPCSR);
return 0;
}
static ssize_t mcp3201_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
{
ssize_t retval;
unsigned char val[2] = {0x0};
unsigned char tx_buf[1] = {0x0};
unsigned char rx_buf[2] = {0};
//发送0个字节的命令到spi从机,并从spi从机中读取2字节的数据到缓冲区
retval = spi_write_then_read(adc, tx_buf, 0, rx_buf, 2);
if (retval
dev_err(&adc->dev, "error %d reading SR\n", (int)retval);
return retval;
}
//对从mcp3201读来的数据,按照其datasheet的要求取出编码
val[0] = rx_buf[1] >> 1;
val[0] |= (rx_buf[0] & 0x01) <
val[1] = (rx_buf[0] >> 1) & 0xf;
//将从mcp3201取出的数据合理编码,并传送到用户空间供用户使用
if (copy_to_user(buf, val, 2))
{
printk("Copy data to userspace error!\n");
return -EFAULT;
}
return 0;
}
static int __devinit mcp3201_probe(struct spi_device *spi)
{
struct spi_device *spi0;
spi0 = spi;
adc = spi0;
return 0;
}
static int __devexit mcp3201_remove(struct spi_device *spi)
{
writeb(0xff, SB2F_BOARD_SPI0_BASE + REG_SPCSR);
return 0;
}
static struct spi_driver mcp3201_driver = {
.driver = {
.name= "mcp3201",
.bus= &spi_bus_type,
.owner= THIS_MODULE,
},
.probe= mcp3201_probe,
.remove= __devexit_p(mcp3201_remove),
};
static const struct file_operations mcp3201_ops = {
.owner = THIS_MODULE,
.open = mcp3201_open,
.release = mcp3201_close,
.read = mcp3201_read,
};
static struct miscdevice mcp3201_miscdev = {
MISC_DYNAMIC_MINOR,
"mcp3201",
&mcp3201_ops,
};
static int mcp3201_init(void)
{
if (misc_register(&mcp3201_miscdev)) {
printk(KERN_WARNING "buzzer:Couldn't register device 10, %d.\n", 255);
return -EBUSY;
}
return spi_register_driver(&mcp3201_driver);
}
static void mcp3201_exit(void)
{
spi_unregister_driver(&mcp3201_driver);
}
module_init(mcp3201_init);
module_exit(mcp3201_exit);
MODULE_LICENSE("GPL");
10-2外部按键驱动
10-2-1 说明
程序源代码说明:
驱动源程序所在目录
driver/input/
驱动程序名
74LV165_button.c
74LV165_button.h
设备名
ls1b_buttons
测试程序源代码目录
测试程序代码名
74LV165_test.c
测试程序可执行文件名
74LV165_test
说明:按键驱动已经被编译到缺省内核中,因此不用使用insmod方式加载
开发板用到的资源:
gpio0
KEY_DATA
gpio1
KEY_EN
gpio2
KEY_SCL
按键驱动在硬件上没有接外部中断,且通过两片并行输入串行输出的移位芯片74LV165连接,因此,按键通过轮询的方式实现。应用程序直接读取/dev/ls1b_buttons设备节点来对按键值进行读取。在启动linux内核后,在根目录下执行当前目录下的可执行文件74LV165_test。
10-2-2驱动程序清单
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "74LV165_button.h"
#define BUF_MAXSIZE 16
#define TIMER_DELAY 5
static unsigned int BReadBuf = 0;
static int delay(int time){
while(--time);
return 0;
}
static void prepare_for_read(void){
int reg = 0;
/* CP =0 */
LS1B_74LV165_READ(reg, LS1B_74LV165_CP);
LS1B_74LV165_WRITE(LS1B_74LV165_CP, reg & (~(1 <
/* PL = 0 */
LS1B_74LV165_READ(reg, LS1B_74LV165_PL);
LS1B_74LV165_WRITE(LS1B_74LV165_PL, reg & ~(1 <
/* delay 100 */
delay(10000);
/* PL = 1 */
LS1B_74LV165_READ(reg, LS1B_74LV165_PL);
LS1B_74LV165_WRITE(LS1B_74LV165_PL, reg | (1 <
}
static void scanf_keyboard(void){
int reg = 0, val = 0;
int time;
delay(10);
LS1B_74LV165_READ(reg, LS1B_74LV165_DATA);
val = ((~reg) & (1 <
BReadBuf = val >> 0;
for(time = 1; time
/* delay */
delay(100);
/* CP = 1 */
LS1B_74LV165_READ(reg, LS1B_74LV165_CP);
LS1B_74LV165_WRITE(LS1B_74LV165_CP, reg | (1 <
delay(100);
LS1B_74LV165_READ(reg, LS1B_74LV165_DATA);
val = ((~reg) & (1 <
BReadBuf |= val <
/* CP = 0 */
LS1B_74LV165_READ(reg, LS1B_74LV165_CP);
LS1B_74LV165_WRITE(LS1B_74LV165_CP, reg & (~(1 <
}
}
static void button_read(void){
prepare_for_read();
/* scanf keyboard */
scanf_keyboard();
if(BReadBuf){
printk("\nBReadBuf value is 0x%08x\n",BReadBuf);
}
}
static int LS1B_74LV165_button_open(struct inode *inode, struct file *filp){
int reg;
printk("Welcome to use 74LV165 driver\n");
//enalbe pin
LS1B_74LV165_EN_GPIO(GPIO_KEY_DATA);
LS1B_74LV165_EN_GPIO(GPIO_KEY_EN);
LS1B_74LV165_EN_GPIO(GPIO_KEY_SCL);
//enable output
LS1B_74LV165_OEN_GPIO(GPIO_KEY_SCL);
LS1B_74LV165_OEN_GPIO(GPIO_KEY_EN);
LS1B_74LV165_IEN_GPIO(GPIO_KEY_DATA);
printk("init 74LV165 is done\n");
return 0;
}
static ssize_t LS1B_74LV165_button_read(struct file *filp, char __user *buf, size_t count, loff_t *oppos){
button_read();
if(BReadBuf){
copy_to_user(buf,&BReadBuf,count);
BReadBuf= 0;
delay(10000000);
return count;
}
return -EFAULT;
}
static int LS1B_74LV165_button_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){
return 0;
}
static struct file_operations ls1b_74Llv165_button_fops = {
.open = LS1B_74LV165_button_open,
.read = LS1B_74LV165_button_read,
.ioctl = LS1B_74LV165_button_ioctl,
};
static struct miscdevice ls1b_74lv165_button = {
.minor = LS1B_BUTTON_MINOR,
.name = "ls1b_buttons",
.fops = &ls1b_74Llv165_button_fops,
};
static int __init LS1B_74LV165_button_init(void){
int ret;
printk("======================button init=========================\n");
ret = misc_register(&ls1b_74lv165_button);
if(ret
printk("74LV165_button can't get major number !\n");
return ret;
}
#ifdef CONFIG_DEVFS_FS
devfs_button_dir = devfs_mk_dir(NULL,"LS1B_74LV165_button",NULL);
devfs_buttonraw = devfs_register(devfs_kbd_dir,"0raw",DEVFS_FL_DEFAULT,kbdMajor,KBDRAW_MINOR,S_IFCHR|S_IRUSR|S_IWUSR,&LS1B_74LV165_button_fops,NULL);
#endif
return 0;
}
static void __exit LS1B_74LV165_button_exit(void){
misc_deregister(&ls1b_74lv165_button);
}
module_init(LS1B_74LV165_button_init);
module_exit(LS1B_74LV165_button_exit);
其中module_init()和module_exit()函数注册了LS1B_74LV165_button_init及LS1B_74LV165_button_exit函数。当模块加载时将会调用这两个函数。
在LS1B_74LV165_button_init()函数中调用misc_register()函数注册按键驱动为misc设备。并将struct miscdevice的数据结构传递给该函数,在该结构中对minor字段及name字段进行赋值,这样该驱动会在/dev目录下创建名为ls1b_buttons的设备文件,其中主设备号为10,次设备号为minor的值,即为143。
在struct miscdevice数据结构中,还有一个fops的字段,该字段为指向struct file_operations数据结构的指针。其中包括open、read、ioctl等字段,当用户程序调用open、write、ioctl函数时,最终调用该驱动的相应函数。
在LS1B_74LV165_button_open()函数中使能芯片74LV165用到的3个gpio口,并将端口设置为输出。当用户调用read函数时,运行驱动程序的LS1B_74LV165_button_read()函数,该函数调用button_read(),读取按键编码。在button_read()函数中调用prepare_for_read()根据芯片74LV165的时序将按键信息写入芯片74LV165的移位寄存器,然后调用scanf_keyboard()函数根据芯片74LV165的时序读写移位寄存器中的按键编码。最后通过LS1B_74LV165_button_read()函数中的copy_to_user()函数将按键编码返回给用户空间。
10-3RTC驱动程序
10-3-1 说明
程序源代码说明:
驱动源代码所在目录
/driver/rtc
驱动程序名称
rtc-gs2fsb.c
该驱动的主设备号
RTC设备,设备号将自动生成
设备名
/dev/rtc0
测试程序可执行文件名称
date hwclock
RTC模块寄存器位于0xbfe64000——0xbfe67fff的16KB地址空间内,其基地址为0xbfe64000,所有寄存器位宽均为32位。详细的寄存器描述可参考loongson 1B的数据手册。
10-3-2驱动程序清单
#include
#include
#include
#include
#include
#defineRTC_TOYIM0x00
#defineRTC_TOYWLO0x04
#defineRTC_TOYWHI0x08
#defineRTC_TOYRLO0x0c
#defineRTC_TOYRHI0x10
#defineRTC_TOYMH00x14
#defineRTC_TOYMH10x18
#defineRTC_TOYMH20x1c
#defineRTC_CNTL0x20
#defineRTC_RTCIM0x40
#defineRTC_WRITE00x44
#defineRTC_READ00x48
#defineRTC_RTCMH00x4c
#defineRTC_RTCMH10x50
#defineRTC_RTCMH20x54
struct rtc_sb2f{
struct rtc_device*rtc;
void __iomem*regs;
unsigned longalarm_time;
unsigned longirq;
spinlock_tlock;
};
static int sb2f_rtc_read_register(struct rtc_sb2f *rtc, unsigned long reg)
{
int data;
data = (*(volatile unsigned int *)(rtc->regs + reg));
return data;
}
static int sb2f_rtc_write_register(struct rtc_sb2f *rtc, unsigned long reg, int data)
{
(*(volatile unsigned int *)(rtc->regs + reg)) = data;
}
static int sb2f_rtc_readtime(struct device *dev, struct rtc_time *tm)
{
struct rtc_sb2f*rtc = dev_get_drvdata(dev);
unsigned long now, now1;
now = sb2f_rtc_read_register(rtc, RTC_TOYRLO);
tm->tm_sec = ((now >> 4) & 0x3f);
tm->tm_min = ((now >> 10) & 0x3f);
tm->tm_hour = ((now >> 16) & 0x1f);
tm->tm_mday = ((now >> 21) & 0x1f);
tm->tm_mon = ((now >> 26) & 0x3f) -1;
now1 = sb2f_rtc_read_register(rtc, RTC_TOYRHI);
tm->tm_year = (now1 -1900) ;
return 0;
}
static int sb2f_rtc_settime(struct device *dev, struct rtc_time *tm)
{
struct rtc_sb2f *rtc = dev_get_drvdata(dev);
unsigned long now;
int ret;
now = ((tm->tm_sec
(tm->tm_mday
spin_lock_irq(&rtc->lock);
sb2f_rtc_write_register(rtc, RTC_TOYWLO, now);
//set year
sb2f_rtc_write_register(rtc, RTC_TOYWHI, (tm->tm_year+1900) );
spin_unlock_irq(&rtc->lock);
return 0;
}
static int sb2f_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_sb2f *rtc = dev_get_drvdata(dev);
unsigned long time;
spin_lock_irq(&rtc->lock);
time = sb2f_rtc_read_register(rtc, RTC_TOYMH0);
spin_unlock_irq(&rtc->lock);
alrm->time.tm_sec = (time & 0x3f);
alrm->time.tm_min = ((time >> 6) & 0x3f);
alrm->time.tm_hour = ((time >> 12) & 0x1f);
alrm->time.tm_mday = ((time >> 17) & 0x1f);
alrm->time.tm_mon = ((time >> 22) & 0x1f);
alrm->time.tm_year = ((time >> 26) & 0x3f);
return 0;
}
static int sb2f_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_sb2f *rtc = dev_get_drvdata(dev);
int time;
time =(( alrm->time.tm_sec & 0x3f) | ((alrm->time.tm_min & 0x3f) <
| ((alrm->time.tm_hour & 0x1f)
& 0x1f)
((alrm->time.tm_year & 0x3f) <
spin_lock_irq(&rtc->lock);
sb2f_rtc_write_register(rtc, RTC_TOYMH0, time);
spin_unlock_irq(&rtc->lock);
return 0;
}
static int sb2f_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case RTC_PIE_ON:
break;
case RTC_PIE_OFF:
break;
case RTC_UIE_ON:
break;
case RTC_UIE_OFF:
break;
case RTC_AIE_ON:
break;
case RTC_AIE_OFF:
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static irqreturn_t sb2f_rtc_interrupt(int irq, void *dev_id)
{
struct rtc_sb2f *rtc = (struct rtc_sb2f *)dev_id;/*接收申请中断时传递过来的rtc_dev参数*/
unsigned long events = 0;
spin_lock(&rtc->lock);
events = RTC_AF | RTC_IRQF;
/*节拍时间中断到来的时候,去设定RTC中节拍时间的相关信息,具体设定的方法,RTC核心部分已经在rtc_update_irq接口函数中实现,函数定义实现在interface.c中*/
rtc_update_irq(rtc->rtc, 1, events);
spin_unlock(&rtc->lock);
}
static struct rtc_class_ops sb2f_rtc_ops = {
.ioctl= sb2f_rtc_ioctl,
.read_time= sb2f_rtc_readtime,
.set_time= sb2f_rtc_settime,
.read_alarm= sb2f_rtc_readalarm,
.set_alarm= sb2f_rtc_setalarm,
};
static int __init sb2f_rtc_probe(struct platform_device *pdev)
{
struct resource *regs;/*定义一个资源,用来保存获取的RTC的资源*/
struct rtc_sb2f *rtc;
int irq = -1;
int ret;
rtc = kzalloc(sizeof(struct rtc_sb2f), GFP_KERNEL);
if (!rtc) {
dev_dbg(&pdev->dev, "out of memory\n");
return -ENOMEM;
}
/*获取RTC平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和RTC平台设备定义中的一致*/
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
dev_dbg(&pdev->dev, "no nmio resource defined\n");
ret = -ENXIO;
goto out;
}
printk("the regs->start is 0x%x", regs->start);
//在系统定义的RTC平台设备中获取TICK节拍时间中断号
irq = platform_get_irq(pdev, 0);
printk("the irq is %d\n", irq);
rtc->irq = irq;
//把I/O资源IORESOURCE_MEM起始地址到结束地址范围的 资源映射到虚拟地址ioremap定义在io.h中。
//注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作
rtc->regs = ioremap(regs->start, regs->end - regs->start + 1);
if (!rtc->regs) {
ret = -ENOMEM;
dev_dbg(&pdev->dev, "could not map i/o memory\n");
goto out;
}
//初始化自旋锁
spin_lock_init(&rtc->lock);
//next set control register
//sb2f_rtc_write_register(rtc, RTC_CNTL, 0x800);
(*(volatile int *)(0xbfe64040)) = 0x2d00;//TOY和RTC控制寄存器
//0010 1101 0000 0000
//申请中断(这个可以放到open()函数里)
//res->start 中断号(在具体的平台设备platform_device定义时赋值)
//s3c2410wdt_irq 中断处理函数IRQF_DISABLED中断处理属性 快速慢速共享
//pdev->name 这个传递给request_irq的字串用在/proc/interrupts来显示中断的拥有者
//pdev 用作共享中断线的指针 被驱动用来指向它自己的私有数据区(来标识哪个设备在中断).
ret = request_irq(irq, sb2f_rtc_interrupt, IRQF_SHARED, "rtc", rtc);
if (ret) {
dev_dbg(&pdev->dev, "could not request irq %d", irq);
goto out_iounmap;
}
//注册RTC设备
/* register RTC and exit */
/*将RTC注册为RTC设备类,RTC设备类在RTC驱动核心部分中由系统定义好的,
注意rtcops这个参数是一个结构体,该结构体的作用和里面的接口函数实现在第③步中。
rtc_device_register函数在rtc.h中定义,在drivers/rtc/class.c中实现*/
rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &sb2f_rtc_ops, THIS_MODULE);
/*里的IS_ERR(),它就是判断kthread_run()返回的指针是否有错,如果指针并不是指向最后一个page,那么没有问题,申请成功了,如果指针指向了最后一个page,那么说明实际上这不是一个有效的指针,这个指针里保存的实际上是一种错误代码.而通常很常用的方法就是先用IS_ERR()来判断是否是错误,然后如果是,那么就调用PTR_ERR()来返回这个错误代码.*/
if (IS_ERR(rtc->rtc)) {
dev_dbg(&pdev->dev, "could not register rtc device\n");
//PTR_ERR()只是返回错误代码,也就是提供一个信息给调用者,如果你只需要知道是否出错,而不在乎因为什么而出错
ret = PTR_ERR(rtc->rtc);
goto out_free_irq;
}
/*将RTC设备类的数据传递给系统平台设备。
platform_set_drvdata是定义在platform_device.h的宏,如下:
#define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data))
而dev_set_drvdata又被定义在include/linux/device.h中,如下:
static inline void dev_set_drvdata (struct device *dev, void *data){
dev->driver_data = data;
}*/
platform_set_drvdata(pdev, rtc);
/*device_init_wakeup该函数定义在pm_wakeup.h中,定义如下:
static inline void device_init_wakeup(struct device *dev, int val){
dev->power.can_wakeup = dev->power.should_wakeup = !!val;}
显然这个函数是让驱动支持电源管理的,这里只要知道,can_wakeup为1时表明这个设备可以被唤醒,设备驱动为了支持Linux中的电源管理,有责任调用device_init_wakeup()来初始化can_wakeup,而should_wakeup则是在设备的电源状态发生变化的时候被device_may_wakeup()用来测试,测试它该不该变化,因此can_wakeup表明的是一种能力,而should_wakeup表明的是有了这种能力以后去不去做某件事。好了,我们没有必要深入研究电源管理的内容了,要不就扯远了,电源管理以后再讲*/
device_init_wakeup(&pdev->dev, 1);
//头文件include/linux/device.h中所提供的宏(包括dev_printk()、dev_dbg()、dev_warn()、dev_info()和dev_err())来决定何种类型的设备访问消息需要被记录。printk()
dev_info(&pdev->dev, "SB2F RTC at %08lx irq %ld \n", (unsigned long)rtc->regs, rtc->irq);
return 0;
out_free_irq:
free_irq(irq, rtc);
out_iounmap:
iounmap(rtc->regs);
out:
kfree(rtc);
return ret;
}
/*注意:这是使用了一个__devexit。
我们还是先来讲讲这个:
在Linux内核中,使用了大量不同的宏来标记具有不同作用的函数和数据结构,
这些宏在include/linux/init.h头文件中定义,编译器通过这些宏可以把代码优化放到合适的内存位置,
以减少内存占用和提高内核效率。__devinit、__devexit就是这些宏之一,在probe()和remove()函数中
应该使用__devinit和__devexit宏。又当remove()函数使用了__devexit宏时,则在驱动结构体中一定要
使用__devexit_p宏来引用remove(),所以在第①步中就用__devexit_p来引用rtc_remove*/
static int __exit sb2f_rtc_remove(struct platform_device *pdev)
{
/*从系统平台设备中获取RTC设备类的数据*/
struct rtc_sb2f *rtc = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
free_irq(rtc->irq, rtc);
iounmap(rtc->regs);/*释放RTC虚拟地址映射空间*/
rtc_device_unregister(rtc->rtc);/*注销RTC设备类*/
kfree(rtc);/*销毁保存RTC平台设备的资源内存空间*/
platform_set_drvdata(pdev, NULL);/*清空平台设备中RTC驱动数据*/
return 0;
}
MODULE_ALIAS("platform:sb2f-rtc");
static struct platform_driver sb2f_rtc_driver = {
.remove= __exit_p(sb2f_rtc_remove),
.driver= {
.name= "sb2f-rtc",
.owner= THIS_MODULE,
},
};
static int __init sb2f_rtc_init(void)
{
/*将RTC注册成平台设备驱动*/
return platform_driver_probe(&sb2f_rtc_driver, sb2f_rtc_probe);
}
module_init(sb2f_rtc_init);
static void __exit sb2f_rtc_exit(void)
{
/*注销RTC平台设备驱动*/
platform_driver_unregister(&sb2f_rtc_driver);
}
module_exit(sb2f_rtc_exit);
MODULE_AUTHOR("");
MODULE_DESCRIPTION("Real Time clock for loongson2fsb");
MODULE_LICENSE("GPL");
linux下怎样运行oyrhon,第十章 LINUX驱动程序实验相关推荐
- linux下怎样运行oyrhon,Linux 下从头再走 GTK+-3.0 (一)
原本由于项目需求在 Linux 下学习过一段时间的 GTK+2.0 图形开发,时隔一段时间,想真正深入学习一下 GTK . 这次直接从头学习 GTK+-3.0 ,并写下博文便于日后查看,也方便新手入门 ...
- linux下怎么运行2048,如何在 Linux 中安装 2048 游戏
流行的移动益智游戏 2048 也可以在 Ubuntu 和 Linux 发行版上玩.啊!你甚至可以在 Linux 终端上玩 2048.如果你的生产率因为这个让人上瘾的游戏下降,请不要怪我. 早在 201 ...
- linux下怎样运行oyrhon,Fedora CoreOS基础操作,包括安装并运行Fedora CoreOS
如果不知从何处入手Fedora CoreOS,那就看看本文,内容有:Fedora CoreOS的理念.入门.创建配置.安装并运行Fedora CoreOS.zincati,rpm-ostree和自动更 ...
- linux下软件编译终止,[2018年最新整理]linux下编译运行程序命令大全.ppt
[2018年最新整理]linux下编译运行程序命令大全 1. 项目课题引入 2. Vi编辑器的使用方法 3. Linux中C语言程序的编辑 4. Linux中C语言程序的运行 5. 现场演示案例 课题 ...
- 解决aapt命令在Linux下无法运行的问题
解决aapt命令在Linux下无法运行的问题 参考文章: (1)解决aapt命令在Linux下无法运行的问题 (2)https://www.cnblogs.com/zhjsll/p/5961291.h ...
- Linux 下后台运行程序,查看和关闭后台运行程序(转载)
1.运行.sh文件 直接用./sh 文件就可以运行,但是如果想后台运行,即使关闭当前的终端也可以运行的话,需要nohup命令和&命令. (1)&命令 功能:加在一个命令的最后,可以把这 ...
- Linux下Fluent运行脚本及PBS脚本、Fluent TUI指令和Tecplot的一些操作
Linux下Fluent运行脚本及PBS脚本.Fluent TUI指令和Tecplot的一些操作 目录 Linux下Fluent运行脚本及PBS脚本.Fluent TUI指令和Tecplot的一些操作 ...
- samba服务器在linux下如何运行共享
samba服务器在linux下如何运行共享 1. 什么是samba Samba服务类似于windows上的共享功能,可以实现在Linux上共享文件,windows上访问,当然在Linux上也可以访问到 ...
- linux下查看进程的线程数,linux查看进程的线程数
top -H -p $PID #查看对应进程的那个线程占用CPU过高 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行 ...
最新文章
- 关于Quartz的Job 不能被注入以及SpringAop对Job失效
- 整合spring cloud云架构 - SSO单点登录之OAuth2.0登录认证(1)
- 【CSS练习】IT修真院--练习4-移动端界面
- amuse ui(web插件,js插件,css样式)?
- android studio资产目录,在Android Studio中设置单元测试的自定义资产目录
- php接收arduino向服务器发来的请求
- 织梦响应式个人博客资讯网站模板(自适应手机移动端)
- rx580网络适配器下载_通过Rx和数据绑定简化RecyclerView适配器
- 快速了解前端开发HTML的正确姿势
- [Web Chart系列之六] canvas Chart 导出图文件
- python画饼图存在的问题_python_使用matplotlib画饼状图(pie)
- 游戏开发经验分享:我所理解的打击感
- 关于Adobe软件安装失败的各类错误代码BUG汇总!!
- 把自定义encoder加入ffmpeg源码
- cookie 和session
- quickpcb添加pcb库_QuickPcb元件库下载
- 学习笔记:CentOS7学习之十六:LVM管理和ssm存储管理器使用
- 人工智能学习路线图(超详细、超全面)
- Linux之setenv()/getenv()设置/获取环境变量
- 如何用几何画板把圆奇数等分