【实验目的】
实验目的:熟悉Linux下驱动程序设计
     编译内核
实验要求:在Linux系统下,编译内核,并在该内核下完成实验;
     自主设计驱动程序,完成驱动程序的安装

【实验内容】
1.编译内核,构造内核源码树
2.ubantu14.04 32位下编写hello world程序以及加载驱动
3.ubantu14.04第二个memory驱动程序
4.ubantu14.04第三个使用文件私有数据的globalmem的设备驱动
5.Linux设备驱动中的阻塞与非阻塞I/O

【实验环境】(含主要设计设备、器材、软件等)
Pc电脑一台

【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等)

1.编译内核,构造内核源码树(itc-centos)

(1)编译指令
①指导书上的指令

uname -r  //查看内核版本
ls /usr/src  //输出:linux-headers-3.13.0-32,linux-headers-3.13.0-32-generic
apt-cache search linux-source  //查看一下可一下载的源码包
sudo apt-get install linux-source-3.13.0  //下载完成后,在/usr/sr下会有压缩包,然后解压
make oldconfig  //开始配置内核 选择最快的原版的配置方式,menuconfig , xconfig也行
make bzImage  //执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux
make modules和make modules_install

②自己查阅的资料
  以一个不是root用户的户口,创建一个以~/rpmbuild为基础的目录树:

[user@host] $ mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SPRMS}
[user@host] $ echo ‘%_topdir % (echo $HOME) / rpmbuild’ > ~/.rpmmacros

  以一个不是root用户的普通户口,执行以下指令来安装源代码组件:

[user@host] $ rpm -i http://vault.centos.org/7.7.1908/updates/Source/SPackages
/kernel-3.10.0-1062.9.1.e17.src.rpm 2>&1 | grep -v exist

解压及预备源代码文件:

[user@host] $ cd ~/rpmbuild/SPECS
[user@host SPECS] $ rpmbuild -bp --target = $(uname -m) kernel.spec

  其中,$(uname -m)这条命令,可以将目标结构设置为你现有的内核的结构,一般来说这是可行的,因为多数人需要以i686或x86_64为目标。

(2)编译过程截图

            图1 编译内核过程

(3)编译过程注意事项
  如果这一步中没有建立内核源码树,按下面步骤进行,虽然能够生成hello.ko,但执行sudo insmod hello.ko后,执行lsmod会没反应,导致系统报告问题,会导致下次开机或重启时有问题,若启动不了,可以进入recovery模式,执行fsck,开机时做嵌入式linux开发一般在PC机上编译好了,下到板子上去运行,板子上的linux内核和PC机上的linux版本很多时候都是不一样的,比如pc机上的是linux2.6,板子上的是linux3.1,这个时候就要下linux3.1的内核,用它编译的驱动模块在板子上才能加载上,不然会出错。
  在执行最后一条指令make modules和make modules_install时,执行结束之后,会在/lib/modules下生成新的目录/lib/modules/3.13.0-32-generic/,但若由于主机本身内核版本就为3.13.0-32-generic,所以/lib/modules/3.13.0-32-generic/本身就存在,此时这两条指令make modules和make modules_install就不需要执行了。

2.ubantu14.04 32位下编写hello world程序以及加载驱动

(1)实验步骤
①编写hello. c程序并写Makefile文件;
②之后通过make指令,生成hello.ko等其他文件;
③执行sudo insmod hello.ko,在lsmod即可验证模块是否已装载,最后rmmod移出模块;
④通过cat /var/log/syslog | grep world来观察其输出。

(2)实验过程截图

          图2 hello.c代码

          图3 对于Makefile内容

          图4 执行make指令

          图5 执行lsmod指令

          图6 运行结果

(3)实验过程注意事项

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

  这句是Makefile的规则:这里的$(MAKE)就相当于make,-C 选项的作用是指将当前工作目录转移到你所指定的位置。“M=”选项的作用是,当用户需要以某个内核为基础编译一个外部模块的话,需要在make modules 命令中加入“M=dir”,程序会自动到你所指定的dir目录中查找模块源码,将其编译,生成ko文件。
  一定要实现查看电脑Linux的内核版本,机房不同电脑的内核版本可能不一样,要事先查好之后并且在Makefile文件中版本号要改。
  用printk,内核会根据日志级别,可能把消息打印到当前控制台上,这个控制台通常是一个字符模式的终端、一个串口打印机或是一个并口打印机。这些消息正常输出的前提是──日志输出级别小于console_loglevel(在内核中数字越小优先级越高)。

3.ubantu14.04第二个memory驱动程序

(1)实验步骤
①编写c程序并写Makefile文件;
②根据设备号,从内核空间将数据取出,写入相应的设备空间内;
③通过程序创建主从设备,将数据写入;
④再通过模块功能读出,观察其输出。

(2)实验过程截图
①到包含Makefile和mydm1.c的目录下执行make,生成mydm1.ko;
②执行sudo insmod mydm1.ko;
③验证:lsmod | grep mydm1
④需要创建一个文件(该设备文件用于和设备驱动操作)
mknod /dev/fgj c 224 0 c代表字符设备 224为主设备号,0为从设备号
⑤ gcc test.c

主要代码如下:
1.mydm1.c:

int devMajor = 224; // 主设备号用于内核把文件和它的驱动链接在一起static unsigned char simple_inc = 0;
static unsigned char demoBuffer[256]; // 用于存储该驱动的数据int simple_open( struct inode *inode, struct file *filp )
{printk(" : open Success!\n");if(simple_inc>0){return -1;}simple_inc = simple_inc + 1;return 0;
}int simple_release(struct inode *inode, struct file *filp)
{printk(": release Success%d!\n",devMajor);simple_inc = simple_inc - 1;return 0;
}ssize_t simple_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{printk(": read Success%d!\n", devMajor);/*把数据复制到应用程序空间从内核空间demoBuffer中拷贝数据到用户空间buf中*/if( copy_to_user(buf,demoBuffer,count) ) {count=-EFAULT;}return count;
}ssize_t simple_write(struct file *filp, const char __user *buf,size_t count,loff_t *f_pos)
{printk(": write Success%d!\n",devMajor);/*把数据复制到内核空间*/if(copy_from_user(demoBuffer + *f_pos,buf,count)){count=-EFAULT;}return count;
}struct file_operations simple_fops={.owner=THIS_MODULE,
.read=simple_read,
.write=simple_write,
.open=simple_open,
.release=simple_release,
};/*******************************************************
MODULEROUTINE
*******************************************************/
static void __exit simple_cleanup_module(void)
{unregister_chrdev(devMajor,"mydm1");printk(" : simple_cleanup_module!\n");//return 0;
}static int __init simple_init_module(void)
{int ret;//根据设备号与设备名注册字符设备,此处设备名不一定要与模块名相同,/*register_chrdev函数用于在内核空间,把驱动和/dev下设备文件链接在一起*/ret=register_chrdev( devMajor,"mydm1",&simple_fops);if(ret<0){printk(" : Unabletoregistercharacterdevice%d!\n",devMajor);return ret;}else{printk(" : Success%d!\n",devMajor );}return 0;
}module_init(simple_init_module);
module_exit(simple_cleanup_module);

2.Makefile:

obj-m := mydm1.o
#KERNELDIR := /lib/modules/3.16.61-32-generic/build
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:rm *.o *.ko *.mod.c *.order *.symvers

3.test.c:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>void main(void)
{int fd;int i;char data[256];int retval;fd = open("/dev/fgj",O_RDWR);if(fd==-1){perror("erroropen\n");exit(-1);}printf("open/dev/fgjsuccessfully\n");//写数据retval = write(fd,"fgj",3);if(retval==-1){perror("writeerror\n");exit(-1);}printf("write successfully\n");//读数据retval=read(fd,data,3);if(retval==-1){perror("readerror\n");exit(-1);}data[retval]=0;printf("readsuccessfully:%s\n",data);//关闭设备close(fd);
}

运行结果如下:

              图7 执行make命令

              图8 执行结果

(3)实验过程注意事项
①保证/dev/fgj有读写权限 chmod 666 /dev/fgj;
②printk打印的消息若灭有在控制台上显示,可以同构dmesg命令或查看系统的日志文件cat /var/log/syslog命令看到;
③驱动编译:gcc必须用系统自带的版本,gcc -v查看当前版本, 否则insmod后,lsmod会没反应,系统直接卡死,注销也不行,只能重启;

4.ubantu14.04第三个使用文件私有数据的globalmem的设备驱动

(1)实验步骤
①编写c程序并写Makefile文件;
②执行:make ,然后sudo insmod globalmem.ko;
③验证:lsmod 可以看到该模块,mknod /dev/globalmem c 354 0, echo ‘good nihao’ > /dev/globalmem, cat /dev/globalmem可以看到输出good nihao;

(2)实验过程截图
代码如下:

#define GLOBALMEM_SIZE 0X1000 /*全局内存最大4KB*/
#define MEM_CLEAR 0x1 /*清零全局内存*/
#define GLOBALMEM_MAJOR 354static int globalmem_major = GLOBALMEM_MAJOR;/*预设的globalmem的主设备号*//*globalmem的设备结构体:包含了对应于globalmem字符设备的cdev 和 使用内存mem[GLOBALMEM_SIZE]*/
struct globalmem_dev
{struct cdev cdev;  //cdev结构体unsigned char mem[GLOBALMEM_SIZE];  //全局内存
};struct globalmem_dev *globalmem_devp;  //设备结构体指针/*文件打开函数*/
int globalmem_open(struct inode *inode,struct file *filp)
{filp->private_data = globalmem_devp; //将设备结构体指针赋值给文件私有数据指针return 0;
}/*文件释放函数*/
int globalmem_release(struct inode *inode,struct file *filp)
{return 0;
}/*设备控制函数:ioctl()函数接受的MEM_CLEAR命令,这个命令将全局内存的有效数据长度清零,对于设备不支持的命令,ioctl()函数应该返回-EINVAL*/
static long globalmem_ioctl( /*struct inode *inodep,*/struct file *filp,unsigned int cmd,unsigned long arg)
{struct globalmem_dev *dev = filp->private_data; //获得设备结构体指针switch(cmd){case  MEM_CLEAR:memset(dev->mem,0,GLOBALMEM_SIZE);printk(KERN_INFO"globalmem is set to zero\n");break;default:return -EINVAL;}return 0;
}/*读函数:读写函数主要是让设备结构体的mem[]数组与用户空间交互数据,并随着访问字节数变更返回用户的文件读写偏移位置*/
static ssize_t globalmem_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos )
{unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct globalmem_dev *dev = filp->private_data; //获得设备结构体指针if(p >= GLOBALMEM_SIZE)  //分析和获取有效的写长度{return count ? -ENXIO:0;}if(count > GLOBALMEM_SIZE - p){count = GLOBALMEM_SIZE - p;}if(copy_to_user(buf,(void *)(dev->mem+p),count))  //内核空间->用户空间{ret = -EFAULT;}else{*ppos += count;ret = count;printk(KERN_INFO"read %d bytes(s) from %ld\n",count,p);}return ret;
}/*写函数*/
static ssize_t globalmem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
{unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct globalmem_dev *dev = filp->private_data;if(p >= GLOBALMEM_SIZE)  //分析和获取有效的写长度{return count? -ENXIO:0;}if(count > GLOBALMEM_SIZE - p){count = GLOBALMEM_SIZE - p;}if(copy_from_user(dev->mem + p,buf,count)) // 用户空间->内核空间{ret = -EFAULT;}else{*ppos =+ count;ret = count;printk(KERN_INFO"written %d bytes(s) from %ld\n",count,p);}return ret;
}/*seek文件定位函数:seek()函数对文件定位的起始地址可以是文件开头(SEEK_SET,0)、当前位置(SEEK_CUR,1)、文件尾(SEEK_END,2)*/
static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig)
{loff_t ret = 0;switch(orig){case 0:   //相对文件开始位置偏移if(offset <0 ){ret = -EINVAL;break;}if((unsigned int )offset > GLOBALMEM_SIZE){ret = - EINVAL;break;}filp->f_pos = (unsigned int)offset;ret = filp->f_pos;break;case 1:   //相对文件当前位置偏移if((filp->f_pos + offset) > GLOBALMEM_SIZE){ret = -EINVAL;break;}if((filp->f_pos + offset)<0){ret = -EINVAL;break;}filp->f_pos +=offset;ret = filp->f_pos;break;default:ret = -EINVAL;break; }return ret;
}/*文件操作结构体*/
static const struct file_operations globalmem_fops=
{.owner = THIS_MODULE,.llseek = globalmem_llseek,.read = globalmem_read,.write = globalmem_write,
// .ioctl = globalmem_ioctl,.unlocked_ioctl = globalmem_ioctl,.open = globalmem_open,.release = globalmem_release,
};/*初始化并注册cdev*/
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
{int err,devno = MKDEV(globalmem_major,index);cdev_init(&dev->cdev,&globalmem_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &globalmem_fops;err = cdev_add(&dev->cdev,devno,1);if(err){printk(KERN_NOTICE"Error %d adding LED%d",err,index);}
}/*设备驱动模块加载函数*/
static int __init globalmem_init(void)
{int result;dev_t devno = MKDEV(globalmem_major,0);if(globalmem_major) //申请设备号{result = register_chrdev_region(devno,1,"globalmem");}else  //动态申请设备号{result = alloc_chrdev_region(&devno,0,1,"globalmem");globalmem_major = MAJOR(devno);}if(result < 0){return result;}globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL); //申请设备结构体的内存if(!globalmem_devp){result = -ENOMEM;goto fail_malloc;}memset(globalmem_devp,0,sizeof(struct globalmem_dev));globalmem_setup_cdev(globalmem_devp,0);return 0;fail_malloc:unregister_chrdev_region(devno,1);return result;
}/*模块卸载函数*/
static void __exit globalmem_exit(void)
{cdev_del(&globalmem_devp->cdev); //注销cdevkfree(globalmem_devp);          //释放设备结构体内存unregister_chrdev_region(MKDEV(globalmem_major,0),1); //释放设备号
}MODULE_AUTHOR("Song Baohua");
MODULE_LICENSE("Dual BSD/GPL");module_param(globalmem_major,int,S_IRUGO);module_init(globalmem_init);
module_exit(globalmem_exit);

2.Makefile:

KERNELDIR := /lib/modules/$(shell uname -r)/build
obj-m := globalmem.o
modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_installclean:make -C $(KERNELDIR) M=`pwd` modules clean  rm -rf modules.order

          图9 执行make指令

          图10 执行lsmod指令

          图11 验证结果

5.Linux设备驱动中的阻塞与非阻塞I/O

(1)实验内容
  阻塞和非阻塞I/O是设备访问的两种不同模式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式。本例子讲述了这两者的区别并实现I/O的等待队列机制,并进行了用户空间的验证。

基本概念:
1> 阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足操作条件后再进行操作。被挂起的进 程进入休眠,被从调度器移走,直到条件满足。
2> 非阻塞操作在不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直到可以进行操作。非阻塞应用程序通常使用select系统调用查询是否可以对设备进行无阻塞的访问最终会引发设备驱动中poll函数执行。

(2)实验过程截图
主要代码如下;

/*globalfifo设备结构体*/struct globalfifo_dev
{struct cdev cdev;                   //cdev结构体unsigned int current_len;           //fifo有效数据长度unsigned char mem[GLOBALFIFO_SIZE];struct semaphore sem;            //并发控制用的信号量wait_queue_head_t r_wait;           //阻塞读用的等待队列 内核双向循环链表 都可以为头wait_queue_head_t w_wait;           //阻塞写用的等待队列头
};
/*globalfifo读函数*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{int ret;struct globalfifo_dev *dev = filp->private_data;DECLARE_WAITQUEUE(wait, current);down(&dev->sem);                     /*获得信号量*/add_wait_queue(&dev->r_wait, &wait); /*加入读等待队列头 到内核*//*等待FIFO非空*/if(dev->current_len == 0){if(filp->f_flags & O_NONBLOCK){   /*如果进程为 非阻塞打开 设备文件*/ret = -EAGAIN;goto out;}__set_current_state(TASK_INTERRUPTIBLE); /*改变进程状态为睡眠*/up(&dev->sem);                           /*释放信号量*/schedule();                              /*调度其他进程执行*/if(signal_pending(current)){/*如果是因为信号唤醒*/ret = -ERESTARTSYS;goto out2;}down(&dev->sem);}
通过Up和down获取信号量以及释放信号量。
/*ioctl 设备控制函数*/
static long globalfifo_ioctl(/*struct inode *inodep,*/struct file *filp, unsigned int cmd, unsigned long arg)
{struct globalfifo_dev *dev = filp->private_data;/*获得设备结构体指针*/switch(cmd){case FIFO_CLEAR:down(&dev->sem);    /*获得信号量*/dev->current_len = 0;memset(dev->mem,0,GLOBALFIFO_SIZE);up(&dev->sem);      /*释放信号量*/printk(KERN_INFO"globalfifo is set to zero");break;default:return -EINVAL;}return 0;
}
加入轮询方式,判断FIFO空和满。
/*在驱动中的增加轮询操作*/
static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{unsigned int mask = 0;struct globalfifo_dev *dev = filp->private_data;/*获得设备结构体指针*/down(&dev->sem);poll_wait(filp, &dev->r_wait, wait);poll_wait(filp, &dev->w_wait, wait);/*fifo非空*/if(dev->current_len != 0){mask |= POLLIN | POLLRDNORM; /*标示数据可以获得*/}/*fifo 非满*/if(dev->current_len != GLOBALFIFO_SIZE){mask |= POLLOUT | POLLWRNORM ; /*标示数据可以写入*/}up(&dev->sem);return mask; /*返回驱动是否可读 或可写的 状态*/
}

            图12 运行结果1

            图13 运行结果2

【实验结果或总结】(对实验结果进行相应分析,或总结实验的心得体会,并提出实验的改进意见)

1.在编写 hello world 程序以及加载驱动的时候,module_init(hello_init); module_exit(hello_exit); 这两个是函数的入口地址和出口地址。同时Makefile文件记得更改KERNELDIR := /lib/modules/3.16.61-32-generic/build的版本号,包括32也需要改成自己电脑相匹配的。

2.$(MAKE) -C (KERNELDIR)M=(KERNELDIR) M=(KERNELDIR)M=(PWD) modules 这句是 Makefile 的规则:这里的$(MAKE)就相当于 make,-C 选项的作用是指将当前,工作目录转移到你所指定的位置。“M=”选项的作用是,当用户需要以某个内核为基础。

3.通过查阅相关资料,我明白了Makefile文件的作用,它相当于是覆写了make指令,让make指令的指向目标变为当前目录下的文件了。而sudo的作用在于,普通用户在安装时,是没有权限的,因此要通过sudo来获取超级用户权限。

4.在第二个memory驱动程序中 ret=register_chrdev( devMajor,“mydm1”,&simple_fops); register_chrdev 函数用于在内核空间,把驱动 和/dev 下设备文件链接在一起,mydm1.c :read()函数,将内核空间中的数据复制到用户空间中,从而读取出数据。在验证过程中,首先是在内核的设备区创建了对应程序的设备区,再通过测试程序往里面写入数据,最后再在用户空间中读出来。Write()函数则相反。

5.Makefile 里的KERNELDIR := /lib/modules/$(shell uname -r)/build 则是用脚本的方法获取 uname -r 即可不用对代码进行修改,动态获取内核的版本信息。

6.使用文件私有数据的globalmem的设备驱动 :用到了内核区的字符设备,将输入的字符存储到全局内存区域,再从用户空间中访问这个区域,获取到其中的数据。struct globalmem_dev { struct cdev cdev; //cdev 结构体 unsigned char mem[GLOBALMEM_SIZE]; //全局内存 }; globalmem 的设备结构体:包含了对应于 globalmem 字符设备的 cdev 和 使用内存 mem[GLOBALMEM_SIZE],其他与上一个实验类似。增加了一个ioctl函数:ioctl()函数接受的 MEM_CLEAR 命令,这个命令将全局内存的有效数据长度 清零,对于设备不支持的命令,ioctl()函数应该返回-EINVAL。,mknod /dev/globalmem c 354 0, echo ‘good nihao’ > /dev/globalmem, cat /dev/globalmem 即可验证输出 ,cat是读出,echo是写入。

  最后感谢倪福川老师以及各位同学给我的帮助,通过一学期对于操作系统理论和实验的学习,让我对操作系统加深了理解,在自己原有知识的基础上得到了更深层次的感悟,我也会把在操作系统课程之中学到的知识应用到今后的学习生活中去,思考问题也要更细致,要学会从底层去思考问题的起源,并学会从底层去解决问题。最后再一次感谢倪福川老师这一学期以来的谆谆教诲,我定当铭记于心,认真完成后续课程的学习。

【操作系统实验】设备驱动(Linux环境下)相关推荐

  1. 操作系统课程设计--在Linux环境下模拟实现简单命令解释器(C++代码)

    操作系统课程设计要求 一.设计目的 熟悉Linux编程环境,加强对Linux命令的理解及函数的运用 二.设计内容 1. 在Linux环境下模拟实现简单命令解释器. (1)要求实现的基本命令包括: pw ...

  2. Linux实验五:Linux环境下的C语言编程

    文章目录 一.实验目的: 二.实验要求 三.实验内容 1.编写一段C语言程序使其完成:父进程创建两个子进程,每个进程都在屏幕上显示自己的进程ID号. 2.上机调试下面的程序,观察运行结果,分析原因. ...

  3. Linux创建线程读取双口数据,linux环境下读写一次双口ram尽然要十几个毫秒。(附驱动代码)...

    linux环境下读写一次双口ram尽然要十几个毫秒.(附驱动代码) 我用的双口ram是IDT70V28,手册上说的读写时间应该是几个纳秒,我写了个linux驱动,然后做测试,发现读写一次的时间尽然是十 ...

  4. linux应用程序的编写实验原理,操作系统实验 1.在linux下编写一个应用程序 联合开发网 - pudn.com...

    操作系统实验 所属分类:Linux/Unix编程 开发工具:C/C++ 文件大小:1KB 下载次数:3 上传日期:2019-05-01 20:34:21 上 传 者:烟雨南风起 说明:  1.在lin ...

  5. Linux环境下使用 USB转串口驱动(二)

    minicom是linux下串口通信的软件,它的使用完全依靠键盘的操作,虽然没有"超级终端"那么易用,但是使用习惯之后读者将会体会到它的高效与便利,下面将讲解minicom的安装和 ...

  6. linux环境下调试嵌入式设备时出现Aborted、segmentation fault、卡死的问题以及关于指针使用的一点想法

    linux环境下调试一些嵌入式设备时出现Aborted.segmentation fault.卡死的问题,这些问题可能的原因为: 1.Aborted的问题,例如: # ./logUtils0322 [ ...

  7. 南工程c语言实验报告,Linux环境下-C语言编程实验报告(1)(总4页).doc

    Linux环境下-C语言编程实验报告(1)(总4页).doc 西 安 邮 电 大 学 (计算机学院)课内实验报告实验名称 Linux环境下 C语言编程 专业名称 网络工程班 级 1203班 学生姓名 ...

  8. 操作系统实验六:Linux下的C语言编程

    实验六 Linux下的C语言编程 一.实验要求 (1)熟悉Linux环境下C语言应用程序开发的基本过程: (2)熟悉基本库函数的使用: (3)具有初步的应用程序设计能力. 二.实验内容 (1)有三个程 ...

  9. Linux环境下串口号变化,自定义串口,修改串口号,绑定设备ID

    ​ 在Linux环境下有时候我们有多个外设都占用了系统环境的/dev/ttyUSBx(x表示串口号),也许代码中我们写的是x=0,但是在对USB外设重新拔插之后,我们原来的设备可能就不是x=0了,而是 ...

  10. Linux 环境下php5.6,如何正确安装微软Mssql驱动--详细教程

    Linux 环境下,如何正确安装微软Mssql驱动 近日,由于公司业务的需要,需要用到PHP连接一个客户的Mssql服务器.于是,开始一番折腾之路.Baidu与Google之后,发现,网上说的各种方法 ...

最新文章

  1. matlab文件启动位置,matlab中uigetfile()设置默认路径
  2. TeamTalk源码分析之login_server
  3. android8组windows10,微软正式开放Win10新功能:在PC端运行Android应用
  4. STM32F407 CubeMx使用定时器测量信号频率 分辨率0.001Hz
  5. Apollo进阶课程⑯丨Apollo感知之旅——感知概貌
  6. IntelliJ IDEA:文件的路径本该是”\“,却变成了”¥“
  7. Bing Maps进阶系列八:在Bing Maps中集成OpenStreetMap地图
  8. Congestion解决办法
  9. CROC 2016 - Elimination Round Mischievous Mess Makers
  10. 数据建模-经典教程笔记
  11. 什么是无线射频识别技术(RFID)
  12. linux命令不断更新
  13. 阿里Sophix热修复接入指南
  14. Rabbitmq消息发布确认机制
  15. [转] 李嘉诚的经典名言!
  16. KOG注释[Ubuntu 15.10系统]
  17. 校园导航系统之用弗洛伊德算法求加权图的最短路径
  18. ContextMenuStrip 菜单的使用技巧
  19. 怎么学习java?学习java的路线
  20. MPEG4 笔记(FTYP,MOOV,MVHD)

热门文章

  1. 评侯捷的《深入浅出MFC》和李久进的《MFC深入浅出》
  2. pk8/x509.pem转换为keystore
  3. php session超时设置,php如何设置session超时过期时间
  4. 基于微信小程序的学习网站源码
  5. Adobe Flash CS4 序列号-Adobe Dreamweaver CS4 序列号
  6. miui主题编辑器和java jdk_MIUI主题编辑器手机版下载|MIUI主题编辑器安卓版下载 v1.0.8 - 跑跑车安卓网...
  7. 图观小课堂知识点——场景编辑器
  8. 关闭windows开机浏览器自动跳转MSN
  9. 概率论与数理统计(第四版) 第二章:随机变量及其分布(第一节和第二节笔记)
  10. winrar 注册码