[转帖]关于Linux下的icotl函数

 

关于Linux下的icotl函数

 

最近接触android开发,因为有时间所以就关注了下android的源码,在跟踪源码过程中到最后都会遇到icotl函数,虽然在Symbian中曾经遇到过RSocketicotl函数,但是当时没有细究,今天有时间就搜索了下,原来这个函数是跟驱动相关的。下面这篇文章在很多博客网站都能看到,到底是谁写的就不细究了,但是他让我了解了这个函数的由来。下面就是转帖。

 

我这里说的ioctl函数是在驱动程序里的,因为我不知道还有没有别的场合用到了ioctl 所以就规定了我们讨论的范围。为什么要写篇文章呢,是因为我前一阵子被ioctl给搞混了,这几天才弄明白它,于是在这里清理一下头脑。

一、 什么是ioctl

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下:

int ioctl(int fd, ind cmd, )

其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。

ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。

二、 ioctl的必要性

如果不用ioctl的话,也可以实现对设备I/O通道的控制,但那就是蛮拧了。例如,我们可以在驱动程序中实现write的时候检查一下是否有特殊约定的数据流通过,如果有的话,那么后面就跟着控制命令(一般在socket编程中常常这样做)。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员自己也会头昏眼花的。

所以,我们就使用ioctl来实现控制的功能。要记住,用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。

三、 ioctl如何实现

这是一个很麻烦的问题,我是能省则省。要说清楚它,没有四五千字是不行的,所以我这里是不可能把它说得非常清楚了,不过如果有读者对用户程序怎么和驱动程序联系起来感兴趣的话,可以看我前一阵子写的《write的奥秘》。读者只要把write换成ioctl,就知道用户程序的ioctl是怎么和驱动程序中的ioctl实现联系在一起的了。

我这里说一个大概思路,因为我觉得《Linux设备驱动程序》这本书已经说的非常清楚了,但是得化一些时间来看。

在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的,这里也没法说。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。

命令码的组织是有一些讲究的,因为我们一定要做到命令和设备是一一对应的,这样才不会将正确的命令发给错误的设备,或者是把错误的命令发给正确的设备,或者是把错误的命令发给错误的设备。这些错误都会导致不可预料的事情发生,而当程序员发现了这些奇怪的事情的时候,再来调试程序查找错误,那将是非常困难的事情。

所以在Linux核心中是这样定义一个命令码的:

设备类型

序列号

方向

数据尺寸

8 bit

8 bit

2 bit

8~14bit

这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。

这些宏我就不在这里解释了,具体的形式请读者察看Linux核心源代码中的和,文件里给除了这些宏完整的定义。这里我只多说一个地方,那就是"幻数"

幻数是一个字母,数据长度也是8,所以就用一个特定的字母来标明设备类型,这和用一个数字是一样的,只是更加利于记忆和理解。就是这样,再没有更复杂的了。

更多的说了也没有,读者还是看一看源代码吧,推荐各位阅读《Linux 设备驱动程序》所带源代码中的short一例,因为它比较短小,功能比较简单,可以看明白ioctl的功能和细节。

四、 cmd参数如何得出

这里确实要说一说,cmd参数在用户程序端由一些宏根据设备类型、序列号、传送方向、数据尺寸等生成,这个整数通过系统调用传递到内核中的驱动程序,再由驱动程序使用解码宏从这个整数中得到设备的类型、序列号、传送方向、数据尺寸等信息,然后通过switch{case}结构进行相应的操作。

要透彻理解,只能是通过阅读源代码,我这篇文章实际上只是一个引子。Cmd参数的组织还是比较复杂的,我认为要搞熟它还是得花不少时间的,但是这是值得的,驱动程序中最难的是对中断的理解。

五、 小结

ioctl其实没有什么很难的东西需要理解,关键是理解cmd命令码是怎么在用户程序里生成并在驱动程序里解析的,程序员最主要的工作量在switch{case}结构中,因为对设备的I/O控制都是通过这一部分的代码实现的。

 

通过上述转帖,大概知道了ioctl的来龙去脉,如果能来几个实例可能相对来说更能让人接受些,这不也有人找了些例子,具体如下例转帖所述。

linux系统ioctl使用示例

These were writed and collected by kf701,

you can use and modify them but NO WARRANTY.

Contact with me : kf_701@21cn.com

程序1:检测接口的 inet_addr,netmask,broad_addr

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <errno.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <sys/ioctl.h>

#include <net/if.h>

 

static void usage()

{

       printf("usage : ipconfig interface \n");

       exit(0);

}

int main(int argc,char **argv)

{

       struct sockaddr_in *addr;

       struct ifreq ifr;

       char *name,*address;

       int sockfd;

      

       if(argc != 2)

              usage();

       else

              name = argv[1];

 

       sockfd = socket(AF_INET,SOCK_DGRAM,0);

       strncpy(ifr.ifr_name,name,IFNAMSIZ-1);

      

       if(ioctl(sockfd,SIOCGIFADDR,&ifr) == -1)

              perror("ioctl error"),exit(1);

      

       addr = (struct sockaddr_in *)&(ifr.ifr_addr);

       address = inet_ntoa(addr->sin_addr);

       printf("inet addr: %s ",address);

      

       if(ioctl(sockfd,SIOCGIFBRDADDR,&ifr) == -1)

              perror("ioctl error"),exit(1);

      

       addr = (struct sockaddr_in *)&ifr.ifr_broadaddr;

       address = inet_ntoa(addr->sin_addr);

       printf("broad addr: %s ",address);

      

       if(ioctl(sockfd,SIOCGIFNETMASK,&ifr) == -1)

                     perror("ioctl error"),exit(1);

      

       addr = (struct sockaddr_in *)&ifr.ifr_addr;

       address = inet_ntoa(addr->sin_addr);

       printf("inet mask: %s ",address);

       printf("\n");

       exit(0);

}

 

程序2:检查接口的物理连接是否正常

#include <stdio.h>

#include <string.h>

#include <errno.h>

#include <fcntl.h>

#include <getopt.h>

#include <sys/socket.h>

#include <sys/ioctl.h>

#include <net/if.h>

#include <stdlib.h>

#include <unistd.h>

typedef unsigned short u16;

typedef unsigned int u32;

typedef unsigned char u8;

#include <linux/ethtool.h>

#include <linux/sockios.h>

int detect_mii(int skfd, char *ifname)

{

       struct ifreq ifr;

       u16 *data, mii_val;

       unsigned phy_id;

       /* Get the vitals from the interface. */

       strncpy(ifr.ifr_name, ifname, IFNAMSIZ);

       if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0)

       {

              fprintf(stderr, "SIOCGMIIPHY on %s failed: %s\n", ifname,

              strerror(errno));

              (void) close(skfd);

              return 2;

       }

 

       data = (u16 *)(&ifr.ifr_data);

       phy_id = data[0];

       data[1] = 1;

       if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0)

       {

              fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name,

              strerror(errno));

              return 2;

       }

 

       mii_val = data[3];

       return(((mii_val & 0x0016) == 0x0004) ? 0 : 1);

}

 

int detect_ethtool(int skfd, char *ifname)

{

       struct ifreq ifr;

       struct ethtool_value edata;

       memset(&ifr, 0, sizeof(ifr));

       edata.cmd = ETHTOOL_GLINK;

       strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1);

       ifr.ifr_data = (char *) &edata;

       if (ioctl(skfd, SIOCETHTOOL, &ifr) == -1)

       {

              printf("ETHTOOL_GLINK failed: %s\n", strerror(errno));

              return 2;

       }

       return (edata.data ? 0 : 1);

}

 

int main(int argc, char **argv)

{

       int skfd = -1;

       char *ifname;

       int retval;

       if( argv[1] )

              ifname = argv[1];

       else

              ifname = "eth0";

       /* Open a socket. */

       if (( skfd = socket( AF_INET, SOCK_DGRAM, 0 ) ) < 0 )

       {

              printf("socket error\n");

              exit(-1);

       }

       retval = detect_ethtool(skfd, ifname);

       if (retval == 2)

              retval = detect_mii(skfd, ifname);

       close(skfd);

       if (retval == 2)

              printf("Could not determine status\n");

       if (retval == 1)

              printf("Link down\n");

       if (retval == 0)

              printf("Link up\n");

       return retval;

}

 

程序3:更简单一点测试物理连接

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

#include <net/if.h>

#include <linux/sockios.h>

#include <sys/ioctl.h>

#define LINKTEST_GLINK 0x0000000a

struct linktest_value

{

       unsigned int    cmd;

       unsigned int    data;

};

 

static void usage(const char * pname)

{

       fprintf(stderr, "usage: %s <device>\n", pname);

       fprintf(stderr, "returns: \n");

       fprintf(stderr, "\t 0: link detected\n");

       fprintf(stderr, "\t%d: %s\n", ENODEV, strerror(ENODEV));

       fprintf(stderr, "\t%d: %s\n", ENONET, strerror(ENONET));

       fprintf(stderr, "\t%d: %s\n", EOPNOTSUPP, strerror(EOPNOTSUPP));

       exit(EXIT_FAILURE);

}

 

static int linktest(const char * devname)

{

       struct ifreq ifr;

       struct linktest_value edata;

       int fd;

       /* setup our control structures. */

       memset(&ifr, 0, sizeof(ifr));

       strcpy(ifr.ifr_name, devname);

       /* open control socket. */

       fd=socket(AF_INET, SOCK_DGRAM, 0);

       if(fd < 0 )

       {

              return -ECOMM;

       }

      

       errno=0;

       edata.cmd = LINKTEST_GLINK;

       ifr.ifr_data = (caddr_t)&edata;

       if(!ioctl(fd, SIOCETHTOOL, &ifr))

       {

              if(edata.data)

              {

                     fprintf(stdout, "link detected on %s\n", devname);

                     return 0;

              }

              else

              {

                     errno=ENONET;

              }

       }

 

       perror("linktest");

       return errno;

}

 

int   main(int argc, char *argv[])

{

       if(argc != 2)

       {

              usage(argv[0]);

       }

       return linktest(argv[1]);

}

 

程序4:调节音量

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/ioctl.h>

#include <sys/soundcard.h>

#include <stdio.h>

#include <unistd.h>

#include <math.h>

#include <string.h>

#include <stdlib.h>

#define? BASE_VALUE 257

int main(int argc,char *argv[])

{

       int mixer_fd=0;

       char *names[SOUND_MIXER_NRDEVICES]=SOUND_DEVICE_LABELS;

       int value,i;

       printf("\nusage:%s dev_no.[0..24] value[0..100]\n\n",argv[0]);

       printf("eg. %s 0 100\n",argv[0]);

       printf(" will change the volume to MAX volume.\n\n");

       printf("The dev_no. are as below:\n");

       for (i=0;i<SOUND_MIXER_NRDEVICES;i++)

       {

              if (i%3==0) printf("\n");

              printf("%s:%d\t\t",names[i],i);

       }

 

       printf("\n\n");

       if (argc<3)

              exit(1);

       if ((mixer_fd = open("/dev/mixer",O_RDWR)))

       {

              printf("Mixer opened successfully,working...\n");

              value=BASE_VALUE*atoi(argv[2]);

              if (ioctl(mixer_fd,MIXER_WRITE(atoi(argv[1])),&value)==0)

                     printf("successfully.....");

              else

                     printf("unsuccessfully.....");

              printf("done.\n");

       }

       else

              printf("can't open /dev/mixer error....\n");

       exit(0);

}

原文例子代码前面有段文字,太过冗长了,我也没去看,因为看了下第一篇转帖和例子代码,差不离就满足我对icotl函数理解的需要的,但是转帖还是要转完整,顺便把原文的长篇文转帖如下,以后有需要也可以来看看

一般的说,,用户空间的IOCTL系统调用如下所示: ioctl(int fd, int command, (char *) argstruct)因为这个调用拥有与网络相关的代码,所以文件描述符号fd就是socket()系统调用所返回的,command参数可以是 /usr/include/linux/sockios.h头文件中的任何一个,这些个命令根据它可以解决的问题所涉及的方面被分为多种的类型.

比如:

改变路由表(SIOCADDRT, SIOCDELRT

读取或更新ARP/RARP缓存(SIOCDARP, SIOCSRARP

一般的和网络有关的函数(SIOCGIFNAME, SIOCSIFADDR等等)

Goodies目录中包含了很多展示ioctl用法的示例程序,看这些程序的时候,注意根据ioctl的命令类型来学习具体的调用参数结构,比如:和路由表相关的IOCTLRTENTRY结构, rtentry结构是被定义在/usr/include/linux/route.h文件中的,再一个和ARP相关的ioctl调用用到的arpreq结构被定义在/usr/include/linux/if_arp.h文件之中.网络接口相关的ioctl命令最具有代表性的特征为都是以SG开头,其实就是设置或得到数据, getifinfo.c程序用这些命令去读取IP地址信息,硬件地址信息,广播地址信息,和与网络接口相关的标志.对于这些ioctl,第三个参数是一个 IFREQ结构体,这个结构体被定义在/usr/include/linux/if.h头文件中,在一些情况下,新的ioctl命令可能被需要(除了在那 个头文件中被定义的之外),比如 WAVELAN无线网卡保持着无线信号强度的信息,这些信西可能要 对用户程序有用.用户程序是怎么访问到这些信息的呢?我们的第一反应就是定义一个新的命令在sockios.h头文件中,比如 SIOCGIFWVLNSS,不幸的是,这个命令在其他的网络接口上是根本没有意义的,另外试图在其他接口上用这个名另而并非是在无线网口上用会出现违规 访问,我们需要的是定义新特性接口命令的机理。幸运的是,LINUX操作系统为此目的内置了钩子,如果你再看一下那个头文件sockios.h你会注意到 每一个设备都有一个预定义的SIOCDEVPRIVATE命令,实现它的任务就全权交给了写这个设备驱动的程序员了.根据常规约定,一个用户程序调用一个 特定的ioctl命令如下: ioctl(sockid, SIOCDEVPRIVATE, (char *) &ifr)这里ifr是一个ifreq结构体变量,它用一个和这个设备联系的接口名称填充ifrifr NAME,比如,前述的无线网卡接口名称为eth1

不失一般性,一个用户程序将同样要与内核交换命令参数和操作结果,而这些已经通过一个域ifr.ifr_data的填充而做到了,比如,这个网 卡的信号强度信息被返回到这个域当中。LINUX源代码已经包含了两个特殊设备de4x5ewrk3,他们定义和实现了特殊的ioctl命令.,这些设 备的源代码在以下的文件中:de4x5.h, de4x5.c, ewrk3.h, ewrk3.c, 他们两个设备都为在用户空间和驱动间交换数据定义了他们自己的私有结构,ioctl之前,用户程序填充了需要的数据并且将ifr.ifr_data指向 这个结构体.

我们在两个驱动中走的更远些从而进入代码前,让我们跟踪一下处理ioctl系统调用的若干步骤,,所有接口类型的ioctl请求都导致 dev_ioctl()被调用,这个ioctl仅仅是个包装,大部分的真实的操作留给了dev_ifsioc().,这个dev_ioctl()要做的唯 一一个事情就是检查调用过程是否拥有合适的许可去核发这个命令,然后dev_ifsioc()首先要做的事情之一就是得到和名字域 ifr.ifr_name中所对应的设备结构,这在一个很大的switch语块的代码后实现。

SIOCDEVPRIVATE命令和SIOCDEVPRIVATE+15的命令参数全部交给了默认操作,这些都是switch的分支语句.这里 发生的是,内核检查是否一个设备特殊的ioctl的回调已经在设备结构中被设置,这个回调是保持在设备结构中的一个函数指针。如果回调已经被设置了.内核 就会调用它.

所以,为了实现一个特殊的ioctl,需要做的就是写一个特殊ioctl的回调,然后让device结构中的do_ioctl域指向它,对于 EWK3设备,这个函数叫做ewrk3_ioctl(),对应的设备结构在ewrk3_init()中被初始化,ewrk3_ioctl()的代码清晰的 展示了ifr.ifr_data的作用 ,是为了在用户程序和驱动之间交换信息。注意,内存的这个区域有双方向交换数据的作用,例如,ewrk3驱动代码中,ifr.ifr_data最初的2 个字节被用做向驱动传递预想要的动作。同样第五个字节指向的缓冲区用于交换其他的信息。

当你浏览ewrk3_ioctl()代码的时候,记住在一个应用中用户空间的指令是无法访问内核空间的,由于这个原因 ,2个特殊的步骤提供给了驱动编写人员.他们是memcpy_tofs()memcpy_fromfs()。内核里的做法是用 memcpy_tofs() 拷贝内核数据到用户空间,类似的memcpy_fromfs()也是这样的,只是他拷贝用户数据到内核空间.。这些程序步骤是由于调用 verify_area()而被执行的,目的是确认数据访问不会违法。同样记住printk()的用法是打印调试信息,这个函数和printf()很相 ,但是它不能处理浮点数据,printf()函数在内核中是不能被使用的。由printk()产生的输出被转储到了一个目录./usr/adm /messages

 

转载于:https://www.cnblogs.com/franksunny/archive/2011/10/20/2218984.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/475454.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matplotlib - 柱状图、直方图、条形图 bar() barh() 所有用法详解

目录 基本用法 多个直方图并列显示 显示直方图上的数值 多个直方图堆叠显示 水平直方图 相较散点图和折线图&#xff0c;柱状图&#xff08;直方图、条形图&#xff09;、饼图、箱线图是另外 3 种数据分析常用的图形&#xff0c;主要用于分析数据内部的分布状态或分散状…

word里双横线怎么打_美人计 | 精致打工人秀智,教你内双怎么化

通勤妆千千万&#xff0c;大家画好才能算。国民初恋裴秀智搭档“南朋友”南柱赫&#xff0c;《启动了》这部剧让很多颜值控都纷纷沦陷了。起初奔着这两大主角看的&#xff0c;结果看着看着又被男二金宣虎圈了粉&#xff0c;在剧中裴秀智和金宣虎两小无猜的感情没能发展成爱情&a…

LeetCode 480. 滑动窗口中位数(大小堆升级版+set实现)

1. 题目 中位数是有序序列最中间的那个数。 如果序列的大小是偶数&#xff0c;则没有最中间的数&#xff1b;此时中位数是最中间的两个数的平均数。 例如&#xff1a; [2,3,4]&#xff0c;中位数是 3 [2,3]&#xff0c;中位数是 (2 3) / 2 2.5 给你一个数组 nums&#xff0…

Matplotlib - 饼图、环形图 pie() 多重饼图 subplots() 所有用法详解

目录 基本用法 饼图中突出显示某部分 环形图&#xff08;空心饼图&#xff09; 多重饼图&#xff0c;并添加分割线 相较散点图和折线图&#xff0c;柱状图、饼图、箱线图是另外 3 种数据分析常用的图形&#xff0c;主要用于分析数据内部的分布状态或分散状态。饼图主要用于…

USACO2.11 The Castle hdu1198

题意&#xff1a; 我们憨厚的USACO主人公农夫约翰(Farmer John)以无法想象的运气,在他生日那天收到了一份特别的礼物&#xff1a;一张“幸运爱尔兰”&#xff08;一种彩票&#xff09;。结果这张彩票让他获得了这次比赛唯一的奖品——坐落于爱尔兰郊外的一座梦幻般的城堡&#…

LeetCode 1072. 按列翻转得到最大值等行数(查找相同的模式,哈希计数)

1. 题目 给定由若干 0 和 1 组成的矩阵 matrix&#xff0c;从中选出任意数量的列并翻转其上的 每个 单元格。 翻转后&#xff0c;单元格的值从 0 变成 1&#xff0c;或者从 1 变为 0 。 返回经过一些翻转后&#xff0c;行上所有值都相等的最大行数。 示例 1&#xff1a; 输入…

Matplotlib - 箱线图、箱型图 boxplot () 所有用法详解

目录 基本用法 水平箱线图&#xff0c;显示均值 改变箱线图的形状&#xff08;箱体的形状notch、异常值的形状sym&#xff09; 改变箱线图的颜色&#xff08;箱体边框的颜色、箱体填充色&#xff09; 相较散点图和折线图&#xff0c;柱状图、饼图、箱线图&#xff08;箱…

【转载】三极管,场效应管 工作原理小结

三极管属于流控器件&#xff0c;即Ib控制放大Ic&#xff0c; 场效应管属于压控器件&#xff0c;即Ugs控制Id。 二者都有三个工作区域&#xff0c;即截止区&#xff0c;恒流区和可变电阻区。 Ib小于开启电流时&#xff0c;Ic不受控&#xff0c;Rce很大&#xff0c;Ic很小&#x…

educoderpython答案顺序结构程序设计_答案汇总:土木机械类+计算机类

土木机械类(点击图片查看答案)理论力学1哈工大第7版机械设计濮良贵第9版机械原理西工大第8版材料力学1刘鸿文第5版结构力学1龙驭球第4版结构力学朱慈勉第2版工程力学范钦珊第2版材料力学2孙训方第5版理论力学教程水小平机械工程控制基础杨叔子第6版自动控制原理胡寿松第6版土力…

Pyecharts - 动态地图 geo()/ map() - 安装与用法详解

目录 安装Pyecharts 安装对应的地图拓展&#xff1a; 准备数据 使用 pyecharts 模块中的 Geo 函数&#xff1a; 使用 pyecharts 模块中的 map 函数&#xff1a; 把一些地域性比较明显的数据显示在一张地图上&#xff0c;远比给别人一个 Excel 文件好得多。 Matplotlib 中…

[编程启蒙游戏] 1. 猜数字

文章目录1. 游戏前提2. 游戏目的3. python代码4. 玩一玩1. 游戏前提 儿童能认识数字能比较数字大小 2. 游戏目的 培养孩子的二分查找思维 3. python代码 # python 3.7 环境 while True:n int(input("请输入一个数来猜&#xff1a;\n"))count 1print((\n*15))g…

《记得我们有约》17集

电视剧《记得我们有约》“来杯铁马咖啡吧&#xff01;“穿梭在苗栗田野&#xff0c;骑着铁马卖咖啡的叮当-江沐云&#xff0c;天性活泼开朗&#xff0c;意志坚强的她&#xff0c;从不向命运低头&#xff0c;在民宿里担任打杂帮手&#xff0c;勤快不怕苦又带些小霸道的个性&…

对照表 - 用心整理了一份国内985/211大学名单及其一流学科

主要根据用户所填的大学名称&#xff0c;用于用户画像时对用户分类 国内985/211大学名单&#xff0c;但其中未包含港澳台的学校&#xff0c;比如香港中文大学&#xff0c;香港理工大学等。 类型地区学校名称一流学科建设名单985北京清华大学法学、政治学、马克思主义理论、数…

生命银行怎么样_银行双职工家庭现状实录

和大多数银行女一样&#xff0c;我和老公也是同在银行上班相识&#xff0c;14年结婚&#xff0c;15年9月&#xff0c;大儿子出生&#xff0c;然后婆婆跟妈妈轮流照顾小孩&#xff0c;18年1月&#xff0c;小儿子出生&#xff0c;从此便开始了歇斯里底&#xff0c;腥风血雨的生活…

LeetCode 1049. 最后一块石头的重量 II(DP)

1. 题目 有一堆石头&#xff0c;每块石头的重量都是正整数。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。 假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果如下&#xff1a; 如果 x y&#xff0c;那么两块石头都会…

特征计算 - Jaccard 相似系数与 Python 代码实现

Jaccard 相似系数又称为Jaccard相似性度量&#xff08;Jaccard系数&#xff0c;Jaccard 指数&#xff0c;Jaccard index&#xff09;。用于比较有限样本集之间的相似性与差异性。Jaccard系数值越大&#xff0c;样本相似度越高。定义为相交的大小除以样本集合的大小&#xff1a;…

LeetCode 1293. 网格中的最短路径(DP/BFS)

1. 题目 给你一个 m * n 的网格&#xff0c;其中每个单元格不是 0&#xff08;空&#xff09;就是 1&#xff08;障碍物&#xff09;。 每一步&#xff0c;您都可以在空白单元格中上、下、左、右移动。 如果您 最多 可以消除 k 个障碍物&#xff0c;请找出从左上角 (0, 0) 到…

员工奖金需要交税吗_企业为员工发福利 奖品奖金也需要缴税

原标题&#xff1a;企业为员工发福利 奖品奖金也需要缴税企业的福利待遇经常成为员工炫耀的资本&#xff0c;过生日企业准备了小礼品&#xff0c;各种节日为员工准备的物品&#xff0c;举办不容形式的年会的时候对先进工作者发放奖金、奖品及举行抽奖活动。那么&#xff0c;发放…

天气预报Dom解析(转)

view plain<span style"font-family:Arial, Verdana, sans-serif;color:#000000;"><span style"white-space: normal;"><span style"color:#000099;"> </span></span></span> DOM是用与平台无关和语言无关…

用户行为分析模型-(行为事件分析、用户留存分析、漏斗分析、行为路径分析、用户分群、点击分析)

最近有些忙&#xff0c;但是看到了很好的分析模型也要跟大家分享的&#xff0c;这篇博客有些粗糙&#xff0c;主要是po上一些链接供大家学习&#xff0c;有时间的话&#xff0c;我也会写出自己关于用户行为分析的理解的。 下面是关于用户行为分析常见的分析维度&#xff0c;有…