断断续续学驱动,好不容易有空,做了段字符驱动的例子。主要还是跟书上学习在此记录下来,以后说不定能回过头来温故知新。

首先上驱动源码 gmem.c

  1. /*************************************************************************
  2. > File Name: gmem.c
  3. > Author: hailin.ma
  4. > Mail: mhl2018@126.com
  5. > Created Time: Fri 18 Dec 2015 05:08:51 PM CST
  6. ************************************************************************/
  7.  
  8. #include <linux/cdev.h>
  9. #include <linux/init.h>
  10. #include <linux/module.h>
  11. #include <linux/fs.h>
  12. #include <linux/mm.h>
  13. #include <linux/types.h>
  14. #include <linux/sched.h>
  15. #include <asm/io.h>
  16. #include <asm/system.h>
  17. #include <asm/uaccess.h>
  18.  
  19. #define GMEM_SIZE 0X1000
  20. #define GMEM_CLEAR 0XFF
  21. #define GMEN_MAJOR 250
  22.  
  23. static int gmem_major = GMEN_MAJOR;
  24.  
  25. typedef struct gmem_dev{
  26. struct cdev cdev;
  27. unsigned char mem[GMEM_SIZE];
  28. }*GMEM_DEVP;
  29.  
  30. GMEM_DEVP gmem_devp = NULL;
  31.  
  32. static ssize_t gmem_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
  33. {
  34. unsigned long p = *ppos;
  35. int ret = 0;
  36.  
  37. GMEM_DEVP dev = filp->private_data;
  38. if(p >= GMEM_SIZE)
  39. return 0;
  40. if(count > GMEM_SIZE - p)
  41. count = GMEM_SIZE - p;
  42.  
  43. if(copy_to_user(buf,(void*)(dev->mem + p),count))
  44. ret = -EFAULT;
  45. else{
  46. *ppos += count;
  47. ret = count;
  48. printk(KERN_INFO"read %d byte(s) from %d\n",count,p);
  49. }
  50.  
  51. return ret;
  52. }
  53.  
  54. static ssize_t gmem_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)
  55. {
  56. unsigned long p = *ppos;
  57. int ret = 0;
  58.  
  59. GMEM_DEVP dev = filp->private_data;
  60. if(p >= GMEM_SIZE)
  61. return 0;
  62.  
  63. if(p > GMEM_SIZE - p)
  64. count = GMEM_SIZE - p;
  65.  
  66. if(copy_from_user(dev->mem + p,buf,count))
  67. ret = -EFAULT;
  68. else{
  69. *ppos += count;
  70. ret = count;
  71.  
  72. printk(KERN_INFO"written %d byte(s) from %d\n",count,p);
  73. }
  74. }
  75.  
  76. static loff_t gmem_llseek(struct file *filp,loff_t offset,int orig)
  77. {
  78. loff_t ret;
  79. switch(orig){
  80. case 0:
  81. if(offset < 0){
  82. ret = -EINVAL;
  83. break;
  84. }
  85. if((unsigned int)offset > GMEM_SIZE){
  86. ret = -EINVAL;
  87. break;
  88. }
  89. filp->f_pos = (unsigned int)offset;
  90. ret = filp->f_pos;
  91. break;
  92. case 1:
  93. if((filp->f_pos + offset) > GMEM_SIZE){
  94. ret = -EINVAL;
  95. break;
  96. }
  97. if((filp->f_pos + offset) < 0){
  98. ret = -EINVAL;
  99. break;
  100. }
  101. filp->f_pos += offset;
  102. ret = filp->f_pos;
  103. break;
  104. default:
  105. ret = -EINVAL;
  106. }
  107. return ret;
  108. }
  109.  
  110. static int gmem_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg)
  111. {
  112. GMEM_DEVP dev = filp->private_data;
  113. switch(cmd){
  114. case GMEM_CLEAR:
  115. memset(dev->mem,0,GMEM_SIZE);
  116. printk(KERN_INFO"gmem is clear!\n");
  117. break;
  118. default:
  119. return -EINVAL;
  120. }
  121. return 0;
  122. }
  123.  
  124. int gmem_open(struct inode *inode,struct file *filep)
  125. {
  126. filep->private_data = gmem_devp;
  127. return 0;
  128. }
  129.  
  130. static const struct file_operations gmem_fops = {
  131. .owner = THIS_MODULE,
  132. .open = gmem_open,
  133. .llseek = gmem_llseek,
  134. .read = gmem_read,
  135. .write = gmem_write,
  136. .ioctl = gmem_ioctl,
  137. };
  138.  
  139. void gmem_setup_cdev()
  140. {
  141. int err;
  142. dev_t devno = MKDEV(gmem_major,0);
  143. cdev_init(&gmem_devp->cdev,&gmem_fops);
  144. gmem_devp->cdev.owner = THIS_MODULE;
  145. err = cdev_add(&gmem_devp->cdev,devno,1);
  146. if(err){
  147. printk(KERN_NOTICE"Error %d adding gmem",err);
  148. }
  149. }
  150.  
  151. static int __init gmem_init(void)
  152. {
  153. int result;
  154. printk(KERN_INFO"Init gmem\n");
  155.  
  156. dev_t devno = MKDEV(gmem_major,0); //通过MKDEV宏生成设备号
  157. if(gmem_major){
  158. result = register_chrdev_region(devno,1,"gmem");
  159. }
  160. else{
  161. result = alloc_chrdev_region(&devno,0,1,"gmem");
  162. gmem_major = MAJOR(devno);
  163. }
  164. if(result < 0){
  165. return result;
  166. }
  167.  
  168. gmem_devp = kmalloc(sizeof(struct gmem_dev),GFP_KERNEL);
  169. if(!gmem_devp){
  170. result = - ENOMEM;
  171. goto fail_malloc;
  172. }
  173.  
  174. memset(gmem_devp,0,sizeof(struct gmem_dev));
  175.  
  176. gmem_setup_cdev();
  177. return 0;
  178.  
  179. fail_malloc:
  180. unregister_chrdev_region(devno,0);
  181. return result;
  182. }
  183.  
  184. static void __exit gmem_exit(void)
  185. {
  186. printk(KERN_INFO"exit gmem\n");
  187. }
  188.  
  189. module_init(gmem_init);
  190. module_exit(gmem_exit);
  191. module_param(gmem_major,int,S_IRUGO);
  192.  
  193. MODULE_AUTHOR("malth <malth0988@163.com>");
  194. MODULE_LICENSE("Dual BSD/GPL");
  195. MODULE_DESCRIPTION("globle memory module");
  196. MODULE_ALIAS("globle memory");

程序不算长,200行左右,下面进行具体分析。

在 Linux 2.6 内核中,使用 cdev 结构体描述一个字符设备, cdev 结构体的定义:

struct cdev {
  struct kobject kobj;      /* 内嵌的 kobject 对象 */
  struct module *owner;      /*所属模块*/
  struct file_operations *ops;   /*文件操作结构体*/
  struct list_head list;
  dev_t dev;       /*设备号*/
  unsigned int count;
};
cdev 结构体的 dev_t 成员定义了设备号,为 32 位,其中 12 位主设备号, 20 位次设备号。使
用下列宏可以从 dev_t 获得主设备号和次设备号:
MAJOR(dev_t dev)
MINOR(dev_t dev)
而使用下列宏则可以通过主设备号和次设备号生成 dev_t:
MKDEV(int major, int minor)
cdev 结构体的另一个重要成员 file_operations , 定义了字符设备驱动提供给虚拟文件系统的接口函数。

Linux 2.6 内核提供了一组函数用于操作 cdev 结构体:
void cdev_init(struct cdev *, struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

cdev_init()函数用于初始化 cdev 的成员,并建立 cdev 和 file_operations 之间的连接,其源代
码:

  1. void cdev_init(struct cdev *cdev, struct file_operations *fops)
  2. {
  3. memset(cdev, 0, sizeof *cdev);
  4. INIT_LIST_HEAD(&cdev->list);
  5. kobject_init(&cdev->kobj, &ktype_cdev_default);
  6. cdev->ops = fops; /*将传入的文件操作结构体指针赋值给 cdev 的 ops*/
  7. }

cdev_alloc()函数用于动态申请一个 cdev 内存,其源代码:

  1. struct cdev *cdev_alloc(void)
  2. {
  3. struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
  4. if (p) {
  5. INIT_LIST_HEAD(&p->list);
  6. kobject_init(&p->kobj, &ktype_cdev_dynamic);
  7. }
  8. return p;
  9. }

cdev_add()函数和 cdev_del()函数分别向系统添加和删除一个 cdev,完成字符设备的注册和注
销。对 cdev_add()的调用通常发生在字符设备驱动模块加载函数中,而对 cdev_del()函数的调用则
通常发生在字符设备驱动模块卸载函数中。

在调用 cdev_add()函数向系统注册字符设备之前,应首先调用 register_chrdev_region()或
alloc_chrdev_region()函数向系统申请设备号,这两个函数的原型为:
int register_chrdev_region(dev_t from, unsigned count, const char *name);    //用于已知起始设备的设备号的情况
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);    //用于设备号未知,向系统动态申请未被占用的设备号的情况

函数调用成功之后,会把得到的设备号放入第一个参数 dev 中。 alloc_chrdev_region()与 register_chrdev_region()对比的优点在于它会自动
避开设备号重复的冲突。
相反地,在调用 cdev_del()函数从系统注销字符设备之后, unregister_chrdev_region()应该被调
用以释放原先申请的设备号,这个函数的原型为:
void unregister_chrdev_region(dev_t from, unsigned count);

 file_operations 结构体中的成员函数是字符设备驱动程序设计的主体内容,这些函数实际会在
应用程序进行 Linux 的 open()、 write()、 read()、 close()等系统调用时最终被调用。 file_operations
结构体目前已经比较庞大,它的定义:

  1. struct file_operations {
  2. struct module *owner;
  3. /* 拥有该结构的模块的指针,一般为 THIS_MODULES */
  4. loff_t(*llseek)(struct file *, loff_t, int);
  5. /* 用来修改文件当前的读写位置 */
  6. ssize_t(*read)(struct file *, char _ _user *, size_t, loff_t*);
  7. /* 从设备中同步读取数据 */
  8. ssize_t(*write)(struct file *, const char _ _user *, size_t, loff_t*);
  9. /* 向设备发送数据*/
  10. ssize_t(*aio_read)(struct kiocb *, char _ _user *, size_t, loff_t);
  11. /* 初始化一个异步的读取操作*/
  12. ssize_t(*aio_write)(struct kiocb *, const char _ _user *, size_t, loff_t);
  13. /* 初始化一个异步的写入操作*/
  14. int(*readdir)(struct file *, void *, filldir_t);
  15. /* 仅用于读取目录,对于设备文件,该字段为 NULL */
  16. unsigned int(*poll)(struct file *, struct poll_table_struct*);
  17.  
  18. /* 轮询函数,判断目前是否可以进行非阻塞的读取或写入*/
  19. int(*ioctl)(struct inode *, struct file *, unsigned int, unsigned long);
  20. /* 执行设备 I/O 控制命令*/
  21. long(*unlocked_ioctl)(struct file *, unsigned int, unsigned long);
  22. /* 不使用 BLK 的文件系统,将使用此种函数指针代替 ioctl */
  23. long(*compat_ioctl)(struct file *, unsigned int, unsigned long);
  24. /* 在 64 位系统上, 32 位的 ioctl 调用,将使用此函数指针代替*/
  25. int(*mmap)(struct file *, struct vm_area_struct*);
  26. /* 用于请求将设备内存映射到进程地址空间*/
  27. int(*open)(struct inode *, struct file*);
  28. /* 打开 */
  29. int(*flush)(struct file*);
  30. int(*release)(struct inode *, struct file*);
  31. /* 关闭*/
  32. int (*fsync) (struct file *, struct dentry *, int datasync);
  33. /* 刷新待处理的数据*/
  34. int(*aio_fsync)(struct kiocb *, int datasync);
  35. /* 异步 fsync */
  36. int(*fasync)(int, struct file *, int);
  37. /* 通知设备 FASYNC 标志发生变化*/
  38. int(*lock)(struct file *, int, struct file_lock*);
  39. ssize_t(*sendpage)(struct file *, struct page *, int, size_t, loff_t *, int);
  40. /* 通常为 NULL */
  41. unsigned long(*get_unmapped_area)(struct file *,unsigned long, nsigned long,
  42. unsigned long, unsigned long);
  43. /* 在当前进程地址空间找到一个未映射的内存段 */
  44. int(*check_flags)(int);
  45. /* 允许模块检查传递给 fcntl(F_SETEL...)调用的标志 */
  46. int(*dir_notify)(struct file *filp, unsigned long arg);
  47. /* 对文件系统有效,驱动程序不必实现*/
  48. int(*flock)(struct file *, int, struct file_lock*);
  49. ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t,
  50. unsigned int); /* 由 VFS 调用,将管道数据粘接到文件 */
  51. ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t,
  52. unsigned int); /* 由 VFS 调用,将文件数据粘接到管道 */
  53. int (*setlease)(struct file *, long, struct file_lock **);
  54. };

下面我们对 file_operations 结构体中的主要成员进行分析。
llseek()函数用来修改一个文件的当前读写位置,并将新位置返回,在出错时,这个函数返回一个负值。
read()函数用来从设备中读取数据,成功时函数返回读取的字节数,出错时返回一个负值。
write()函数向设备发送数据,成功时该函数返回写入的字节数。如果此函数未被实现,当用户进行 write()系统调用时,将得到-EINVAL 返回值。
readdir()函数仅用于目录,设备节点不需要实现它。
ioctl()提供设备相关控制命令的实现(既不是读操作也不是写操作),当调用成功时,返回给调用程序一个非负值。
mmap()函数将设备内存映射到进程内存中,如果设备驱动未实现此函数,用户进行 mmap()系统调用时将获得-ENODEV 返回值。这个函数对于帧缓冲等设备特别有意义。
当用户空间调用 Linux API 函数 open()打开设备文件时,设备驱动的 open()函数最终被调用。
驱动程序可以不实现这个函数,在这种情况下,设备的打开操作永远成功。与 open()函数对应的是 release()函数。
poll()函数一般用于询问设备是否可被非阻塞地立即读写。当询问的条件未触发时,用户空间进行 select()和 poll()系统调用将引起进程的阻塞。
aio_read()和 aio_write()函数分别对与文件描述符对应的设备进行异步读、写操作。设备实现这两个函数后,用户空间可以对该设备文件描述符调用 aio_read()、 aio_write()等系统调用进行读写。

Linux字符设备驱动的组成

1.字符设备驱动模块加载与卸载函数
在字符设备驱动模块加载函数中应该实现设备号的申请和 cdev 的注册,而在卸载函数中应实现设备号的释放和 cdev 的注销。

  1. /* 设备结构体*/
  2. struct xxx_dev_t {
  3. struct cdev cdev;
  4. ...
  5. } xxx_dev;
  6. /* 设备驱动模块加载函数
  7. static int _ _init xxx_init(void)
  8. {
  9. ...
  10. cdev_init(&xxx_dev.cdev, &xxx_fops); /* 初始化 cdev */
  11. xxx_dev.cdev.owner = THIS_MODULE;
  12. /* 获取字符设备号*/
  13. if (xxx_major) {
  14. register_chrdev_region(xxx_dev_no, , DEV_NAME);
  15. } else {
  16. alloc_chrdev_region(&xxx_dev_no, , , DEV_NAME);
  17. }
  18.  
  19. ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, ); /* 注册设备*/
  20. ...
  21. }
  22. /*设备驱动模块卸载函数*/
  23. static void _ _exit xxx_exit(void)
  24. {
  25. unregister_chrdev_region(xxx_dev_no, ); /* 释放占用的设备号*/
  26. cdev_del(&xxx_dev.cdev); /* 注销设备*/
  27. ...
  28. }

2.字符设备驱动的 file_operations 结构体中成员函数
file_operations 结构体中成员函数是字符设备驱动与内核的接口,是用户空间对 Linux 进行系
统调用最终的落实者。大多数字符设备驱动会实现 read()、 write()和 ioctl()函数,常见的字符设备
驱动的这 3 个函数的形式如

  1. /* 读设备*/
  2. ssize_t xxx_read(struct file *filp, char __user *buf, size_t count,loff_t*f_pos)
  3. {
  4. ...
  5. copy_to_user(buf, ..., ...);
  6. ...
  7. }
  8. /* 写设备*/
  9. ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
  10. {
  11. ...
  12. copy_from_user(..., buf, ...);
  13. ...
  14. }
  15. /* ioctl 函数 */
  16. int xxx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,unsigned long arg)
  17. {
  18. ...
  19. switch (cmd) {
  20. case XXX_CMD1:
  21. ...
  22. break;
  23. case XXX_CMD2:
  24. ...
  25. break;
  26. default:
  27. /* 不能支持的命令 */
  28. return - ENOTTY;
  29. }
  30. return ;
  31. }

设备驱动的读函数中, filp 是文件结构体指针, buf 是用户空间内存的地址,该地址在内核空间不能直接读写, count 是要读的字节数, f_pos 是读的位置相对于文件开头的偏移。
设备驱动的写函数中, filp 是文件结构体指针, buf 是用户空间内存的地址,该地址在内核空间不能直接读写, count 是要写的字节数, f_pos 是写的位置相对于文件开头的偏移。
由于内核空间与用户空间的内存不能直接互访,因此借助了函数 copy_from_user()完成用户空间到内核空间的拷贝,以及 copy_to_user()完成内核空间到用户空间的拷贝。
完成内核空间和用户空间内存拷贝的 copy_from_user()和 copy_to_user()的原型分别为:
unsigned long copy_from_user(void *to, const void _ _user *from, unsigned long count);
unsigned long copy_to_user(void _ _user *to, const void *from, unsigned long count);
上述函数均返回不能被复制的字节数,因此,如果完全复制成功,返回值为 0。
如果要复制的内存是简单类型,如 char、 int、 long 等,则可以使用简单的 put_user()和 get_user(),如:

  1. int val; // 内核空间整型变量
  2. ...
  3. get_user(val, (int *) arg); // 用户→内核, arg 是用户空间的地址
  4.  
  5. ...
  6. put_user(val, (int *) arg); // 内核→用户, arg 是用户空间的地址

读和写函数中的_ _user 是一个宏,表明其后的指针指向用户空间,这个宏定义为:

  1. #ifdef _ _CHECKER_ _
  2. # define _ _user _ _attribute_ _((noderef, address_space()))
  3. #else
  4. # define _ _user
  5. #endif

I/O 控制函数的 cmd 参数为事先定义的 I/O 控制命令,而 arg 为对应于该命令的参数。例如对于串行设备,如果 SET_BAUDRATE 是一道设置波特率的命令,那后面的 arg 就应该是
波特率值。
在字符设备驱动中,需要定义一个 file_operations 的实例,并将具体设备驱动的函数赋值给file_operations 的成员,如代码所示。

  1. struct file_operations xxx_fops = {
  2. .owner = THIS_MODULE,
  3. .read = xxx_read,
  4. .write = xxx_write,
  5. .ioctl = xxx_ioctl,
  6. ...
  7. };

xxx_fops 在 cdev_init(&xxx_dev.cdev, &xxx_fops)的语句中被建立与 cdev 的连接。

分析完了基本的函数,下面贴上Makefile文件:

  1. #CFLAGS = -g
  2. KVERS = $(shell uname -r)
  3.  
  4. obj-m += gmem.o
  5. mem-objs := gmem.o
  6.  
  7. all:
  8. make -C /lib/modules/$(KVERS)/build M=$(PWD) modules
  9. @rm *.o
  10.  
  11. clean:
  12. make -C /lib/modules/$(KVERS)/build M=$(PWD) clean
  13. @rm -f *.markers *.order

make之后生成gmem.ko文件,insmod gmem.ko gmem_major=303 加载驱动,303是指定主设备号, mknod /dev/gmem c 303 0 创建字符设备,主设备号303,次设备号0。

echo hello gmem! > /dev/gmem

cat /devgmem

通过上面两条命令可以向gmem写入数据和查看数据,这个方法比较方便,另外就是用open,read来查看了:

  1. #include <stdio.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4.  
  5. int main()
  6. {
  7. int fd;
  8. char* pathname = "/dev/gmem";
  9.  
  10. fd = open(pathname,O_RDWR);
  11. if(fd == -)
  12. {
  13. printf("open error");
  14. }
  15.  
  16. char wrbuf[] = "gmem is good!\n";
  17. write(fd,wrbuf,sizeof(wrbuf));
  18.  
  19. close(fd);
  20.  
  21. return ;
  22. }

上面代码简单的用文件来打开设备,然后向其中写入数据,程序可以继续拓展 read,ioctl之类的,时间原因,本文就更新到这里,后续可能会补充完善。

Linux 驱动学习笔记05--字符驱动实例,实现一个共享内存设备的驱动的更多相关文章

  1. LINUX编程学习笔记(十四) 创建进程与 父子进程内存空间

    1什么是进程:进程是一个执行中的程序 执行的程序: 代码->资源->CPU 进程有很多数据维护:进程状态/进程属性 所有进程属性采用的一个树形结构体维护 ps  -a//所有进程 ps - ...

  2. linux 驱动学习笔记01--Linux 内核的编译

    由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...

  3. IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试

    IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...

  4. linux kernel学习笔记-5内存管理_转

    void * kmalloc(size_t size, gfp_t gfp_mask); kmalloc()第一个参数是要分配的块的大小,第一个参数为分配标志,用于控制kmalloc()的行为. km ...

  5. linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxx ...

  6. Linux系统学习笔记:文件I/O

    Linux支持C语言中的标准I/O函数,同时它还提供了一套SUS标准的I/O库函数.和标准I/O不同,UNIX的I/O函数是不带缓冲的,即每个读写都调用内核中的一个系统调用.本篇总结UNIX的I/O并 ...

  7. 尚硅谷韩顺平Linux教程学习笔记

    目录 尚硅谷韩顺平Linux教程学习笔记 写在前面 虚拟机 Linux目录结构 远程登录Linux系统 vi和vim编辑器 关机.重启和用户登录注销 用户管理 实用指令 组管理和权限管理 定时任务调度 ...

  8. 【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动

    linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息,抓取政府网新闻内容

    python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI,采用Python语言编写 ...

随机推荐

  1. java sdk与jdk区别

    SDK是Software Development Kit的缩写,中文意思是"软件开发工具包".这是一个覆盖面相当广泛的名词,可以这么说:辅助开发某一类软件的相关文档.范例和工具的集 ...

  2. 无法打开登录所请求的数据库 "xxx"登录失败用户 'NT AUTHORITY\NETWORK SERVICE'

    解决:添加用户,选择NT AUTHORITY\SYSTEM登录名,选择当前数据库的架构. 勾选架构 勾选成员身份.如果不勾选,也会报异常:拒绝了对对象 'FW_ORG' (数据库 'ZW_DWSJ', ...

  3. suspendlayout

    c# this.suspendlayout有什么作用 为了防止layout事件的发生.这个事件将引发重新绘图的事件,如果介面上控件较多的话,改变控件某些属性时,由于不断地重画,性能变低,所以用susp ...

  4. 流水灯 外侧<->中间<->外侧

    /* //side<-center->side //side->center<-side //loop *// #include "reg52.h" voi ...

  5. C# MVC 微信支付之微信模板消息推送

    微信支付之微信模板消息推送                    今天我要跟大家分享的是"模板消息"的推送,这玩意呢,你说用途嘛,那还是真真的牛逼呐.原因在哪?就是因为它是依赖微信 ...

  6. Java性能调优

    一.JVM内存模型及垃圾收集算法 1.根据Java虚拟机规范,JVM将内存划分为: New(年轻代) Tenured(年老代) 永久代(Perm) 其中New和Tenured属于堆内存,堆内存会从JV ...

  7. X-Cart 学习笔记 完整目录

    如果有需要,可以直接联系博主,讨论学习 一.了解X-CART. 3 二.PHP环境搭建... 3 三.安装配置X-CART. 4 1.下载X-Cart并配置域名,映射地址... 4 2.配置X-Car ...

  8. springmvc 接受特殊类型字段的处理方法

    springmvc接受前台传入的数据时如果该字段类型无法被封装(如Date),则会出现400 Bad Request错误,解决方法如下. 1.在需要处理的字段前加上注解: @DateTimeForma ...

  9. Ibatis.net总是报:【ExecuteStoreCommand SqlParameterCollection 中已包含 SqlParameter】(转)

    今天很奇怪调用EF的ExecuteStoreCommand 出现了个这样的错误,怎么也调试不过,痛定思痛 原来 command被连着调用了而没有销毁掉   public static DataTabl ...

  10. 细说IIS异常日志 — 你必须知道的功能

    最近在跟QAD用Webservice搞接口做数据维护,搞的哥那个叫头大,遇到很多问题,系统的log4net根本就无法记录.话说QAD调我某一个接口,可能包含几百个字段,而且QAD是个产品,所以我这边提 ...