Linux驱动设计——字符设备驱动(一)-程序员宅基地

Linux字符设别驱动结构

cdev结构体

struct cdev {
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;
	unsigned int count;
};

 

dev_t成员定义了32位的设备号,其中12位为主设备号( 获取主设备号MAJOR(dev_t dev) ),20位为次设备号( 获取次设备号 MINOR(dev_t dev) )。 由主设备号和次设备号获得设备号( MKDEV(int major, int minor) )。

file_operations成员定义了字符设别驱动提供给虚拟文件系统的接口函数。

 

Linux 2.6 内核提供了一组函数用于操作cdev 结构体:
void cdev_init(struct cdev *, struct file_operations *);

    //cdev_init()函数用于初始化cdev 的成员,并建立cdev 和file_operations 之间的连接
struct cdev *cdev_alloc(void);

    //cdev_alloc()函数用于动态申请一个cdev 内存
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

     //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);

register_chrdev_region()函数用于已知起始设备的设备号的情况,而alloc_chrdev_region()用于设备号未知,向系统动态申请未被占用的设备号的情况,函数调用成功之后,会把得到的设备号放入第一个参数dev 中。alloc_chrdev_region()与register_chrdev_region()对比的优点在于它会自动避开设备号重复的冲突。

相反地,在调用cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region()应该被调用以释放原先申请的设备号,这个函数的原型为:

void unregister_chrdev_region(dev_t from, unsigned count);

Linux 字符设备驱动的组成

在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, 1, DEV_NAME);
15   } else {
16     alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
17   }
18
19   ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); /* 注册设备*/
20   ...
21 }
22 /*设备驱动模块卸载函数*/
23 static void _ _exit xxx_exit(void)
24 {
25   unregister_chrdev_region(xxx_dev_no, 1); /* 释放占用的设备号*/
26   cdev_del(&xxx_dev.cdev); /* 注销设备*/
27   ...
28 }

2.字符设备驱动的file_operations 结构体中成员函数

字符设备驱动读、写、I/O 控制函数模板

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

字符设备驱动的结构、字符设备驱动与字符设备以及字符设备驱动与用户空间访问该设备的程序之间的关系:

 

struct inode和struct file详解

内核中用inode结构表示具体的文件,而用file结构表示打开的文件描述符。

 inode 译成中文就是索引节点。每个存储设备或存储设备的分区(存储设备是硬盘、软盘、U盘 ... ... )被格式化为文件系统后,应该有两部份,一部份是inode,另一部份是Block,Block是用来存储数据用的。而inode呢,就是用来存储这些数据的信息,这些信息包括文件大小、属主、归属的用户组、读写权限等。inode为每个文件进行信息索引,所以就有了inode的数值。操作系统根据指令,能通过inode值最快的找到相对应的文件。 
做个比喻,比如一本书,存储设备或分区就相当于这本书,Block相当于书中的每一页,inode 就相当于这本书前面的目录,一本书有很多的内容,如果想查找某部份的内容,我们可以先查目录,通过目录能最快的找到我们想要看的内容。
当我们用ls 查看某个目录或文件时,如果加上-i 参数,就可以看到inode节点了;比如ls -li lsfile.sh ,最前面的数值就是inode信息

重要成员:

dev_t  i_rdev       设备文件的设备号
struct cdev *i_cdev   代表字符设备的数据结构

 

struct file 代表一个打开的文件描述符,它不是专门给驱动程序使用的,系统中每一个打开的文件在内核中都有一个关联的struct file。它由内核在open时创建,并传递给在文件上操作的任何函数,直到最后关闭。当文件的所有实例都关闭之后,内核释放这个数据结构。

重要成员:

void *private_data; 

loff_t f_pos;     //该文件在当前进程中的文件偏移量

private_data是Linux下连接VFS文件系统框架和不同文件/文件系统底层实现之间的一个核心数据结构,虽然它只是一个指针,但是一个指针可以解决所有问题。

因 为file是VFS框架的一个基本概念,它要支持文件操作结构,例如open/read/write/release之类的接口,甚至还有poll等,只有有了这些结构,它们才能被纳入VFS这个大家庭。但是对于不同的设备文件来说,它们只是披着文件外衣的设备,所以他要有自己特有的结构来和设备交流,而 这private_data就是这个连接的纽带。这样说可能还是比较抽象,最后是多看一些代码感受可能会深一些。

实质就是把device设备的private_data指针指向了自己定义的结构体。增加可复用性。

下面是一些使用private_data的文件:

1、tty设备
static ssize_t tty_read(struct file * file, char __user * buf, size_t count, 
            loff_t *ppos)
{
    int i;
    struct tty_struct * tty;
    struct inode *inode;
    struct tty_ldisc *ld;

    tty = (struct tty_struct *)file->private_data;
2、tun/tap设备
static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
                unsigned long count, loff_t pos)
{
    struct file *file = iocb->ki_filp;
    struct tun_struct *tun = file->private_data;
3、套接口文件
static ssize_t do_sock_read(struct msghdr *msg, struct kiocb *iocb,
        struct file *file, const struct iovec *iov,
        unsigned long nr_segs)
{
    struct socket *sock = file->private_data;
    size_t size = 0;
4、epoll文件
static int ep_eventpoll_close(struct inode *inode, struct file *file)
{
    struct eventpoll *ep = file->private_data;
5、shm文件
long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
{
……
    file->private_data = sfd;

 

struct inode {
struct hlist_node    i_hash;    //哈希表 
struct list_head    i_list;    //索引节点链表 
struct list_head    i_sb_list;  
struct list_head    i_dentry;   //目录项链表
unsigned long        i_ino;    //节点号
atomic_t        i_count;      //引用计数
unsigned int        i_nlink;    //硬链接数
uid_t            i_uid;        //使用id
gid_t            i_gid;       //使用者id组
dev_t            i_rdev;   //该成员表示设备文件的inode结构,它包含了真正的设备编号。
u64            i_version;
loff_t            i_size;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t        i_size_seqcount;
#endif
struct timespec        i_atime;
struct timespec        i_mtime;
struct timespec        i_ctime;
unsigned int        i_blkbits;
blkcnt_t        i_blocks;      //文件的块数
unsigned short          i_bytes;  //使用的字节数
umode_t            i_mode;
spinlock_t        i_lock;    /* i_blocks, i_bytes, maybe i_size */
struct mutex        i_mutex;
struct rw_semaphore    i_alloc_sem;
const struct inode_operations    *i_op;
const struct file_operations    *i_fop;    /* former ->i_op->default_file_ops */
struct super_block    *i_sb;
struct file_lock    *i_flock;
struct address_space    *i_mapping;
struct address_space    i_data;
#ifdef CONFIG_QUOTA
struct dquot        *i_dquot[MAXQUOTAS];
#endif
struct list_head    i_devices;
union {
struct pipe_inode_info    *i_pipe;
struct block_device    *i_bdev;
 struct cdev        *i_cdev; //该成员表示字符设备的内核的 内部结构。当inode指向一个字符设备文件时,该成员包含了指向struct cdev结构的指针,其中cdev结构是字符设备结构体。
};
int            i_cindex;

__u32            i_generation;

#ifdef CONFIG_DNOTIFY
unsigned long        i_dnotify_mask; /* Directory notify events */
struct dnotify_struct    *i_dnotify; /* for directory notifications */
#endif

#ifdef CONFIG_INOTIFY
struct list_head    inotify_watches; /* watches on this inode */
struct mutex        inotify_mutex;    /* protects the watches list */
#endif

unsigned long        i_state;
unsigned long        dirtied_when;    /* jiffies of first dirtying */

unsigned int        i_flags;

atomic_t        i_writecount;
#ifdef CONFIG_SECURITY
void            *i_security;
#endif
void            *i_private; /* fs or device private pointer */
};

  

struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head    fu_list;    //文件对象链接指针
struct rcu_head     fu_rcuhead;  //RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制
} f_u;
struct path        f_path;      //包含dentry和mnt两个成员,用于确定文件路径
#define f_dentry    f_path.dentry   //该成员是对应的 目录结构 。
#define f_vfsmnt    f_path.mnt
const struct file_operations    *f_op;  //该操作 是定义文件关联的操作的。内核在执行open时对这个 指针赋值。 
atomic_long_t        f_count;
 unsigned int         f_flags;  //该成员是文件标志。 
mode_t            f_mode;    
loff_t            f_pos;     //该文件在当前进程中的文件偏移量
struct fown_struct    f_owner;
unsigned int        f_uid, f_gid;
struct file_ra_state    f_ra;

u64            f_version;
#ifdef CONFIG_SECURITY
void            *f_security;
#endif
/* needed for tty driver, and maybe others */
void            *private_data;//该成员是系统调用时保存状态信息非常有用的资源。 

#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head    f_ep_links;
spinlock_t        f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space    *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};

 

文件操作函数解析

按照惯例, file_operations结构或者指向这类结构的指针称为fops (或者是与此相关的其他叫法) .这个结构中的每一个字段都必须指向驱动程序中实现特定操作的函数,对于不支持的操作,对应的字段可置为NULL 值. 对各个函数而言,如果对应字段被赋为NULL 指针,那么内核的具体处理行为是不尽相同的,本节后面的列表会列出这些差异。

在通读file_operations 方法的清单肘,我们会注意到许多参数包含有__user 字符串,它其实是一种形式的文挡而已, 表明指针是一个用户空间地址,因此不能被直接引用. 对通常的编译来讲, __user 没有任何效果,但是可由外部检查软件使用,用来寻找对用户空间地址的错误使用。

 

struct module *owner

第一个 file_operations 成员根本不是一个操作; 它是一个指向拥有这个结构的模块的指针. 这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 THIS_MODULE, 一个在 <linux/module.h> 中定义的宏.

 

loff_t (*llseek) (struct file *, loff_t, int);

参数:打开的文件指针,偏移量,起始地址(SEEK_SET/SEEK_CUR/SEEK_END)

返回值:新的读写地址

llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述).

 

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

参数:设备文件指针,数据输出目标地址,读取字节数,相对偏移地址(默认为0)

返回值:读取字节数

对应的应用层的read函数:int read(int fd, const void *buf, size_t length);

用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型).

辅助函数:unsigned long copy_to_user(void __ user *to,const void *from,unsigned long count);

 

ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);

初始化一个异步读 -- 可能在函数返回前不结束的读操作. 如果这个方法是 NULL, 所有的操作会由 read 代替进行(同步地).

 

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

参数:设备文件指针,数据输入目标地址,写入字节数,相对偏移地址(默认为0)

返回值:输入字节数

应用层函数 int write(int fd,const void *buf, size_t length); 从buf指向的缓冲区中取length个字节写入fd文件中,返回实际写入字节数。

应用层中buf中length长数据写入到驱动层中buf中,关键是偏移量由谁指定?驱动中设置的偏移地址与应用程序中的偏移地址的关系?

就write和read在应用层中的偏移量与驱动层的偏移量是没有关系的(除了使用llseek的关联)。应用层读写函数被调用后,读写指针自动增加;而驱动中需要程序员控制。当应用层中使用lseek函数时,能够控制驱动中的读写指针的读写位置。

 1 //应用程序
 2 ...
 3 while(1)
 4 {
 5     lseek(dev_fd, 0, SEEK_SET);        //如果改成SEEK_CUR呢?
 6                                                                 //会改变驱动中loff_t *offp么?
 7     write(dev_fd,wr_buf,sizeof(wr_buf));
 8     cnt ++;
 9     if(cnt > 100)break;
10     usleep(8);
11 }
12 ...                            
View Code

发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数.

 辅助函数:unsigned long copy_from_user(void __ user *to,const void *from,unsigned long count);

关于read、write、*llseek的详细分析: http://blog.chinaunix.net/uid-25014876-id-59418.html

 

ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *);

初始化设备上的一个异步写.

 

int (*readdir) (struct file *, void *, filldir_t);

对于设备文件这个成员应当为 NULL; 它用来读取目录, 并且仅对文件系统有用.

 

unsigned int (*poll) (struct file *, struct poll_table_struct *);

poll 方法是 3 个系统调用的后端: poll, epoll, 和 select, 都用作查询对一个或多个文件描述符的读或写是否会阻塞. poll 方法应当返回一个位掩码指示是否非阻塞的读或写是可能的, 并且, 可能地, 提供给内核信息用来使调用进程睡眠直到 I/O 变为可能. 如果一个驱动的 poll 方法为 NULL, 设备假定为不阻塞地可读可写.

 

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

ioctl 系统调用提供了发出设备特定命令的方法(例如格式化软盘的一个磁道, 这不是读也不是写). 另外, 几个 ioctl 命令被内核识别而不必引用 fops 表. 如果设备不提供 ioctl 方法, 对于任何未事先定义的请求(-ENOTTY, "设备无这样的 ioctl"), 系统调用返回一个错误.

相关内容:

__IOR(type, nr,size); __IOW(type,nr,size); __IOWR(type,nr,size); 命令指定参数的 arg在用户空间和内核空间的传递:

用户空间和内核空间传递数据:get_user;put_user;copy_to_user;copy_from_user

 

int (*mmap) (struct file *, struct vm_area_struct *);

mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系统调用返回 -ENODEV.

 

int (*open) (struct inode *, struct file *); //个人理解此函数是为了建立设备文件和设备的关联(*private_data实现)。

参数:设备的索引节点,设备文件指针

尽管这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如果这个项是 NULL, 设备打开一直成功, 但是你的驱动不会得到通知.

 

int (*flush) (struct file *);

flush 操作在进程关闭它的设备文件描述符的拷贝时调用; 它应当执行(并且等待)设备的任何未完成的操作. 这个必须不要和用户查询请求的 fsync 操作混淆了. 当前, flush 在很少驱动中使用; SCSI 磁带驱动使用它, 例如, 为确保所有写的数据在设备关闭前写到磁带上. 如果 flush 为 NULL, 内核简单地忽略用户应用程序的请求.

 

int (*release) (struct inode *, struct file *);

在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL.

 

int (*fsync) (struct file *, struct dentry *, int);

这个方法是 fsync 系统调用的后端, 用户调用来刷新任何挂着的数据. 如果这个指针是 NULL, 系统调用返回 -EINVAL.

 

int (*aio_fsync)(struct kiocb *, int);

这是 fsync 方法的异步版本.

 

int (*fasync) (int, struct file *, int);

这个操作用来通知设备它的 FASYNC 标志的改变. 异步通知是一个高级的主题, 在第 6 章中描述. 这个成员可以是NULL 如果驱动不支持异步通知.

 

int (*lock) (struct file *, int, struct file_lock *);

lock 方法用来实现文件加锁; 加锁对常规文件是必不可少的特性, 但是设备驱动几乎从不实现它.

 

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

 

ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

这些方法实现发散/汇聚读和写操作. 应用程序偶尔需要做一个包含多个内存区的单个读或写操作; 这些系统调用允许它们这样做而不必对数据进行额外拷贝. 如果这些函数指针为 NULL, read 和 write 方法被调用( 可能多于一次 ).

 

ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);

这个方法实现 sendfile 系统调用的读, 使用最少的拷贝从一个文件描述符搬移数据到另一个. 例如, 它被一个需要发送文件内容到一个网络连接的 web 服务器使用. 设备驱动常常使 sendfile 为 NULL.

 

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

sendpage 是 sendfile 的另一半; 它由内核调用来发送数据, 一次一页, 到对应的文件. 设备驱动实际上不实现 sendpage.

 

unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

这个方法的目的是在进程的地址空间找一个合适的位置来映射在底层设备上的内存段中. 这个任务通常由内存管理代码进行; 这个方法存在为了使驱动能强制特殊设备可能有的任何的对齐请求. 大部分驱动可以置这个方法为 NULL.

 

int (*check_flags)(int)

这个方法允许模块检查传递给 fnctl(F_SETFL...) 调用的标志.

 

int (*dir_notify)(struct file *, unsigned long);

这个方法在应用程序使用 fcntl 来请求目录改变通知时调用. 只对文件系统有用; 驱动不需要实现 dir_notify.

 

转载于:https://www.cnblogs.com/kwseeker-bolgs/p/4363358.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30371875/article/details/97575158

智能推荐

新辰:健身会所不是大人的菜 90后创业两年净赚20万_现在做健身餐有商机吗-程序员宅基地

文章浏览阅读822次。他是90后,他还是一个大学生,他2年赚了20万,从大一开始就尝试做各种兼职,贴广告、发传单……只要学校能找到的兼职,他几乎都做过,本来就是想锻炼自己一下,没想到无意间发现了商机。他就是刘国运。一个清纯男孩,在校园里,已经赫赫有名了,又一个创业成功者诞生了,接下来,跟新辰走进他的世界吧。那么他是怎么知道做校园健身会所的呢?原来有一次,他去市区的健身会所兼职,新辰博客发现很多学校学生都在健身会_现在做健身餐有商机吗

记录一些学习的工具包:imu_tools_imu_tools 使用-程序员宅基地

文章浏览阅读1.9k次。记录一些学习的工具包1 imu_tools1.1 使用方法一1.2 使用方法二1 imu_tools通过ROS提供的相关包imu_tools进行滤波据说可以看到得到的值波动已经较小了,且静止的时候接近于01.1 使用方法一直接下载二进制文件进行安装sudo apt-get install ros-kinetic-imu-tools1.2 使用方法二下载原始文件,进行编译这里有详细说明,其实在下载好的原始文件里的readme里就写了:使用imu_tools对IMU进行滤波并可视化cat_imu_tools 使用

java.lang.IllegalArgumentException: requirement failed: Column features must be of type org.apache.s_requirement failed: column label must be of type n-程序员宅基地

文章浏览阅读2.7k次。lr训练模型报错:val model1 = lr.fit(training)java.lang.IllegalArgumentException: requirement failed: Column features must be of type org.apache.spark.ml.linalg.VectorUDT@3bfc3ba7 but was actually org.apa..._requirement failed: column label must be of type numeric but was actually of

php---杂谈_mb_regex_encoding-程序员宅基地

文章浏览阅读575次。1. 基本知识点* HTTP协议中几个状态码的含义:503 500 401 200 301302503:请求超时 500:内部服务错误,一般是php程序错误导致401:未受权访问 200 :正确响应 301::永久重定向 302:临时重定向* Include require include_once require_once的区别.include与require区别:incl_mb_regex_encoding

pycharm快捷键-程序员宅基地

文章浏览阅读128次。pycharm中这个快捷键是什么。

mac安装nvm实现node多版本控制_mackbookpro安装nvm-程序员宅基地

文章浏览阅读136次。打开~/.zshrc,根据上一步安装成功返回的信息里查找环境配置。_mackbookpro安装nvm

随便推点

[转]IIS6.0迁移至IIS7.0-程序员宅基地

文章浏览阅读272次。原文地址:http://www.splaybow.com/post/iis-6.0-7.0.html公司的项目需要迁移到IIS7的目标机器中 在此做记录原来server 2003系统 迁到2008中目标:将IIS6的所有网站包括Application Pools全部迁移至IIS7.5环境:Source:Windows Server 2003 SP2,IIS6Dest:Window..._win7 iis6.1 如何升级到iis7.0

python中shift_Pandas Shift函数的基础入门学习笔记-程序员宅基地

文章浏览阅读277次。Pandas Shift函数基础在使用Pandas的过程中,有时会遇到shift函数,今天就一起来彻底学习下。先来看看帮助文档是怎么说的:>>> import pandas>>> help(pandas.DataFrame.shift)Help on function shift in module pandas.core.frame:shift(self, p..._堆df某列的shift(2)赋值

基于Matlab的罚函数粒子群优化算法_死亡罚函数 matlab-程序员宅基地

文章浏览阅读438次,点赞2次,收藏3次。具体实现中,每个粒子都有一个位置向量、速度向量和个体最优位置向量及群体最优位置向量,其中速度向量决定了下一步的位置变化,而个体最优位置向量和群体最优位置向量则分别表示每个粒子搜索到自己的最优位置和全局最优位置。在粒子群算法中,引入罚函数可以将约束条件加入目标函数中,从而将约束条件转化为非线性约束条件,使得算法能够优化带有约束条件的问题。在实际应用中,罚函数粒子群优化算法可以有效地处理带有约束条件的优化问题,具有较好的鲁棒性和全局搜索能力,因此被广泛应用于各领域的优化问题中。_死亡罚函数 matlab

加拿大签证办理时解释信Explanation of Letter参考_explanation letter-程序员宅基地

文章浏览阅读3.6w次。加拿大签证办理时的解释信,也称肉麻信参考。Explanation of LetterMar 8th,2017Dear Canada Visa Officer, I am the applicant, WANG HONG(王洪). I am writing this letter with great pleasure. I like travelling. Du..._explanation letter

Promise.all 循环中调用接口_promise循环请求接口-程序员宅基地

文章浏览阅读2.4k次。开源商城export default { methods: { getImageBase64(images) { // 生成一个数组 let spreadList = [] images.forEach(item => { const oneApi = imageBase64({ url: item.pic }).then(res => { return res.data.code; _promise循环请求接口

Linux用户登录记录日志和相关查看命令_linux登录日志-程序员宅基地

文章浏览阅读1.8w次,点赞2次,收藏41次。Linux用户登录记录日志和相关查看命令Linux用户登录信息放在三个文件中:1  /var/run/utmp:记录当前正在登录系统的用户信息,默认由who和w记录当前登录用户的信息,uptime记录系统启动时间;[root@root log]# cat /var/run/utmp2  /var/log/wtmp:记录当前正在登录和历史登录系统的用户信息,默认由last命令查看;[root@root log]# last -n 5/var/log/wtmp3  /var/log/btmp:记_linux登录日志