带你了解Linux内核源代码编程规范

thbcm阅读(166)

这是一篇简短的文章,描述了描述了linux内核的首选代码风格。目的是为了分享,作为一名linux内核或者驱动开发工程师,很有必要了解这些内核开发规范。

  • 这些约定或者规范对我们阅读linux内核源码、了解设计思路有很大帮助。
  • 我们基于linux内核做开发,也要往内核里添加代码,遵守开发规范,有助于别人阅读和理解我们的代码。

linux内核代码规范约定如下:

1.强烈推荐单行的宽度为八十列。

任何一行超过八十列宽度的语句都应该拆分成多个行,除非超过八十列的部分可以提高可读性且不会隐藏信息。但是,千万不要把用户可见的字符串,比如 printk 的信息,拆分成多行,因为这样会导致使用 grep 的时候找不到这些信息。

2.关于大括号

c语言里的ifdo, while, for语句都会使用到大括号,内核代码倾向于把左括号放在行末,把右括号放在行首,并且大括号和前面的语句,以及if和后面的语句,都保留一个空格,例如:

以上红圈标注都代表一个空格。

3.关于空格

这个还是单独列出来说明一下吧,因为内核代码里用到空格的地方太多了。

Linux 内核风格的空格主要用在一些关键字上,即在关键字之后添一个空格。值得关注的例外是一些长得像函数的关键字,比如:sizeof, typeof, alignof, attribute,在 Linux 中,这些关键字的使用都会带上一对括号,比如sizeof(int)

所以在下面这些关键字后面需要添加一个空格:

if, switch, case, for, do, while

但是, sizeof, typeof, alignof, attribute 之后则不需要添加空格:

s = sizeof(struct file);

在声明指针或者返回值为指针的函数时,星号的位置应该紧靠着变量名或函数名,而不是类型名,例如:

char linux_banner; unsigned long long memparse(char ptr, char *retptr); char match_strdup(substring_t *s);

在二元操作符和三元操作符周围添加一个空格,例如:

= + – < > * / % | & ^ <= >= == != ? :

但是不要在一元操作符之后添加空格:

& * + – ~ ! sizeof typeof alignof attribute defined

4.变量命名

C 是一种简洁粗旷的语言,因此,你的命名也应该是简洁的。linux内核里的变量定义应该尽可能简单,在不产生歧义的情况下,越简单越好。可以用下划线,但是绝对不推荐使用大写字母。所以,内核里的变量和函数定义不要使用驼峰命名法。

5.函数

函数应该短小精悍,一个函数只干一件事。几百行代码组成一个函数是不被推荐的。

关键函数的前面最好留有注释。这样其他人可以快速看懂你的代码意图:

6.注释

多行注释推荐格式如下:

/*

  • To support ISA shared interrupts, we need to have one interrupt
  • handler that ensures that the IRQ line has been deasserted
  • before returning. Failing to do this will result in the IRQ
  • line being stuck active, and, since ISA irqs are edge triggered,
  • no more IRQs will be seen. */

7.推荐使用函数自注释

所谓函数自注释,就是从你的函数名就可以猜到你要干什么,比如内核的:

wait_event(), wait_event_interruptible(), wait_event_interruptible_timeout()等。

注意,写代码不只是写给现在的自己,也是写给以后的自己,也是写给其他人看的。如果你回看你一年前写的代码都很陌生,那说明你的代码规范是有问题的。

8.常量宏和枚举的命名都是大写的:

9.打印内核或者驱动信息

编写好的调试信息是一项巨大的挑战,一旦你完成了,这些信息会对远程调试产生巨大帮助

很多子系统在对应的 makefile 里都有 Kconfig 调试选项来打开 -DDEBUG,或者是在文件里定义宏 #define DEBUG。当调试信息可以被无条件打印,或者说已经编译了和调试有关的 #ifdef 段,那么 printk(KERN_DEBUG …) 就可以用来打印调试信息。

10.内联函数(inline)

Inline关键字会让编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。然而,inline 关键字的泛滥,会使内核变大,从而使整个系统运行速度变慢,因为大内核会占用更多的CPU高速缓存,同时会导致可用内存页缓存减少。想象一下,一次页缓存未命中就会导致一次磁盘寻址,这至少耗费5毫秒。5毫秒足够CPU运行很多很多的指令。

好了,以上就是我们在阅读linux内核源码的时候的一些代码规范的约定。linux源码作为世界上最规范、最严谨的代码,确实有很多值得我们学习的地方。有时候欣赏内核代码的时候总有一种赏心悦目的感觉,这可能跟它们的良好的代码规范有关系吧。 希望这个对大家有所帮助,然后对Linux感兴趣的同学可以看一下教程

Linux教程:https://www.w3cschool.cn/linux/

Linux微课:https://www.w3cschool.cn/minicourse/play/linuxcourse

Linux就该这么学:https://www.w3cschool.cn/linuxprobe/

三分钟带你了解Laravel 模型的get find first用法

thbcm阅读(215)

首先我们来了解一下Laravel 模型,Laravel中是有两种集合,一个是 BaseCollection,一个是 EloquentCollection。后者继承前者,而且对部分方法进行了重载。分清楚之间的细微区别,可以让你在编程中不迷惑。

本文从一整套的例子,从数据库建表,写模型,写控制器,到模板渲染,从一个小bug,讲到集合的深层次原因。

学习时间

首先创建数据库表,我们不使用迁移,直接上SQL

CREATE TABLE `about`(
`id` int(10) UNSIGNED NOT NULL,
`title` varchar(500) COLLATE utf8_unicode_ci NOT NULL,
`content` text COLLATE utf8_unicode_ci,
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

只有3列,一个是主键,一个是标题,一个是文本内容。注意数据库字段,表,均声明了utf-8编码。

然后创建模型 About,指定表名,我们此处略去,直接看控制器的一个方法:

public function index()
{
    $about = About::where('title', 'about-me')->get(); //id = 3
    return view('about', compact('about'));
}

SQL查询条件是根据标题返回所有的条目,然后将结果集通过 view 视图进行渲染。

然后重点来了,视图内这样写大家看会不会有问题!?

@section('title')
    {{$about->title}}
@stop


@section('content')
    {!! $about->content !!}
@stop 

如果不出意外,开启 debug = true 访问该页面时,你大概率会得到下面的错误提示:

Property [title] does not exist on this collection instance. 
(View: E:\laragon\www\newsite\resources\views\about.blade.php)

大家思考一下,这个故障是如何发生的?下一节我们给出解答。

正确写法

Laravel模型的get() 方法返回一个集合(EloquentCollection)。如果需要使用集合的属性,你首先需要进行遍历。像下面这样在视图文件内写:

@foreach ($collection as $object)
    {{ $object->title }}
@endforeach 

EloquentCollection 内的每一个元素,都是一个 About Model 对象。所以可以使用 $object->title 获取到title属性。

如果你的需求很简单,就是要第一个元素的标题,简写如下:

{{ $collection[0]->title }}

如果你要获取集合内的第一个元素,使用 first 方法:

{{ $collection->first() }}

深入一步

我们知道了问题来自 get() 方法,那么,如果要获取查询数据集的第一条数据,应该用哪个呢?find()或者first()

都会返回一个 About Model 对象,在视图中就可以愉快地写:

{{ $object->title }}

本文通过实际的例子,讲解了laravel模型中查询数据结果集的 find get first 的细微区别,希望能对大家有所帮助。想了解更多的同学可以看一下教程

Laravel 5中文文档:https://www.w3cschool.cn/qpmsiw/

Laravel入门到实战:https://www.w3cschool.cn/minicourse/play/laravelpre

文章参考来源:www.toutiao.com/a6855259233106002440/

一文带你深入了解Linux C Socket Api

thbcm阅读(222)

写这篇文章是为了让大家对Linux C Socket Api了解更加详细。

UNIX 环境高级编程对 Socket 通信的描述是套接字网络 IPC( 进程间通信 ) ,可以用于计算机间通信也可用于计算机内通信,管道、消息队列、信号量以及共享内存等都是属于计算机内通信的情况。

套接字Api详细介绍

1.套接字描述符

首先会先到的是文件描述符,对Linux一切皆文件的哲学又多懂了一点儿点儿。

套接字是通信端点的抽象。与应用程序使用文件描述符一样,访问套接字需要使用套接字描述符。套接字描述符在UNIX系统是用文件描述符实现的。

include <sys/socket.h>

int  socket (int domain, int type, int protocal);
返回值:成功返回文件(套接字)描述符,出错返回-1

参数 domain( 域 ) 确定通信的特性,包括地址格式。各个域都有自己的格式表示地址,表示各个域的常数都以 AF_开头,意指地址族 (address family).

参数type确定套接字的类型,进一步确定通信特征。下图给出了一些类型,但在实现中可以自由增加对其他类型的支持。

参数protocol通常是 0 ,表示按给定的域和套接字类型选择默认的协议。当对同一域和套接字类型支持多个协议时,可以使用 proticol 参数选择一个特定协议。在 A_FINET 通信域中套接字类型 SOCK_STREAM 的默认协议是 TCP( 传输控制协议 ) ; A_FINET 通信域中套接字类型 SOCK_DGRAM 的默认协议是 UDP( 用户数据报协议 ) 。

字节流(SOCK_STREAM)要求在交换数据之前,在本地套接字和远程套接字之间建立一个逻辑联系。

Tcp : 没有报文界限,提供的是字节流服务 。之前写过 Qt 传输图片的拆包与解包,原因就是如此吧。

调用socket与调用 open 类型,均可获得用于输入、输出的文件描述符。不用的时候记得 close 关闭。

2.寻址

如何确定一个目标通信进程?

进程的标识有两个部分:计算机的网络地址可以确定网络上与之想要通信的计算机

服务可以确定计算机上的特定进程。

2.1 字节序

在同一台计算机上进程间通信时,一般无需考虑字节序。

TCP/IP协议栈使用大端字节序。有关字节序大家可自行百度。

Linux系统是小端字节序。

2.2 地址格式

地址确定了特定通信域中的套接字端点,地址格式与特定的通信域相关。为使不同格式的地址能够被传入到套接字函数,地址被强转换成通用的地址结构sockaddr表示。

Linux中,sockaddr_in定义如下;

struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; };

其中成员sin_zero为填充字段,必须全部置0. 所以在网上搜到的例子有使用bzero.

我目前使用的ubuntu定义如下:

/ Structure describing an Internet socket address. / struct sockaddr_in { __SOCKADDRCOMMON (sin); in_port_t sin_port; / Port number. / struct in_addr sin_addr; / Internet address. /


    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
               __SOCKADDR_COMMON_SIZE -
               sizeof (in_port_t) -
               sizeof (struct in_addr)];
  };

还有很多关于地址查询的函数,这里就不一一列举了。

3. 将套接字与地址绑定

使用bind函数将地址绑定到一个套接字上。

include <sys/socket.h>

int bind(int  sockfd, const struct sockaddr * addr, socklen_t  len);
返回值:成功返回0,出错返回-1

参数socklen_t使用sizeof来计算就好了。

对于使用地址的一些限制:

端口号不能小于1024,除非该进程具有相应的特权(即为超级用户)。可见规则总是因人而异,计算机也是如此~

对于因特网域,如果指定IP地址为ADDR_ANY,套接字端点可以被绑定到所有的系统网络接口。

注意: linuxman命令可以查看api的详细说明,而且还有例子,也挺不错的。

4. 建立连接

1> connect

如果处理的是面向连接的网络服务(SOCK_STREAMSOCK_SEQPACKET),在开始交换数据前,需要在请求服务的进程套接字(客户端)和提供服务的进程套接字(服务器)之间建立一个连接。使用connect.

include <sys/socket.h>

int connect(int  sockfd, const struct sockaddr  *addr,  socklen_t  len);
返回值:成功返回0,出错返回-1

诶,这个参数好熟悉呀,和bind函数的参数一模一样呀~

client连接server时,由于一些原因,连接可能会失败。可以使用指数补偿的算法解决,了解一下即可。

2> listen

server调用listen来宣告可以接受连接请求:

include <sys/socket.h>

Int listen(int  sockfd, int  backlog);
返回值:成功返回0,出错返回-1

参数backlog提供了一个提示,用于表示该进程所要入队的连接请求数量。其值由系统决定,但上限由<sys/socket.h>SOMAXCONN指定。

一旦队列满,系统会拒绝多余的连接请求。

3> accept

一旦服务器调用了listen,套接字就能接收连接请求。使用函数accept获得连接请求并建立连接。

include <sys/socket.h>

Int accept(int sockfd,  struct sockaddr *restrict  addr, socklen_t *restrict  len);
返回值:成功返回文件(套接字)描述符,出错返回-1

函数accept所返回的文件描述符是套接字描述符,该描述符连接到调用connect的客户端。这个新的套接字描述符和原始套接字(sockfd)具有相同的套接字类型和地址族。传给accept的原始套接字没有关联到这个连接,而是继续保持可用状态并接受其他连接请求。

如果不关心客户端标识,可以将addrlen设置为NULL,否则addr存放的是连接的客户端的地址。

如果没有连接请求等待处理,accept会阻塞直到有请求到来。另外server可以使用pollselect来等待一个请求的到来。

5. 数据传输

既然将套接字端点表示为文件描述符,那么只要建立连接,就可以使用readwrite来通过套接字通信。readwrite函数我几乎不用,了解一下即可。

1> send

include <sys/socket.h>

Int send(int sockfd,  const void *buf,  size_t  nbytes,  int  flags);
返回值:成功返回发送的字节数,出错返回-1

注意:如果send成功返回,并不一定并表示连接的另一端的进程接收数据。可以保证的是数据已经无误的发送到网络上。

标志我一直用的是0

2> recv

include <sys/socket.h>

int recv(int sockfd,  const void *buf,  size_t  nbytes,  int  flags);
返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,         出错返回-1

仍然一直是0

如果想定位发送者,可以使用recvfrom来得到数据发送者的源地址。

3> recvfrom

include <sys/socket.h>

int recv(int sockfd,  void *restrict buf, size_t len, int flag, 
struct sockaddr *restrict  addr, 
socklen_t *restrict  len);
返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,         出错返回-1

因为可以获得发送者的地址,recvfrom通常用于无连接套接字。否则,recvfrom等同于recv

以上就是关于 Linux C Socket Api的详解了,希望对大家有所帮助,对Linux感兴趣的同学可以看一下教程:

Linux教程:https://www.w3cschool.cn/linux/

Linux微课:https://www.w3cschool.cn/minicourse/play/linuxcourse

Linux就该这么学:https://www.w3cschool.cn/linuxprobe/

你知道C语言是如何处理fseek()和ftell()这两个I/O随机访问数吗?

thbcm阅读(193)

这篇文章中将会讨论到:fseek()ftell()函数的工作原理、如何使用二进制流、如何让程序可移植。

有了fseek()函数,便可把文件看作是数组,在fopen()打开的文件中直接移动到任意字节处。我们创建一个程序reverse.c演示fseek()ftell()的用法。注意,fseek()有3个参数,返回int类型的值;ftell()函数返回一个long类型的值,表示文件中的当前位置。

/* reverse.c -- displays a file in reverse order */
#include <stdio.h>
#include <stdlib.h>
#define CNTL_Z '032'   /* eof marker in DOS text files */
#define SLEN 81
int main(void)
{
    char file[SLEN];
    char ch;
    FILE *fp;
    long count, last;


    puts("Enter the name of the file to be processed:");
    scanf("%80s", file);
    if ((fp = fopen(file,"rb")) == NULL)
    {                               /* read-only mode   */
        printf("reverse can't open %sn", file);
        exit(EXIT_FAILURE);
    }


    fseek(fp, 0L, SEEK_END);        /* go to end of file */
    last = ftell(fp);
    for (count = 1L; count <= last; count++)
    {
        fseek(fp, -count, SEEK_END); /* go backward      */
        ch = getc(fp);
        if (ch != CNTL_Z && ch != 'r')  /* MS-DOS files */
            putchar(ch);
    }
    putchar('n');
    fclose(fp);


    return 0;
}

下面是对一个文件的输出:

Enter the name of the file to be processed: Cluv .C ni eno naht ylevol erom margorp a ees reven llahs I taht kniht I

该程序使用二进制模式,以便处理MS-DOS文本和UNIX文件。但是,在使用其他格式文本文件的环境中可能无法正常工作。

如果通过命令行环境运行该程序,待处理文件要和可执行文件在同一个目录(或文件夹)中。如果在IDE中运行该程序,具体查找方案序因实现而异。例如,默认情况下,Microsoft Visual Studio 2012在源代码所在的目录中查找,而Xcode 4.6则在可执行文件所在的目录中查找。

1 fseek()和ftell()的工作原理

fseek()的第1个参数是FILE指针,指向待查找的文件,fopen()应该已打开该文件。

fseek()的第2个参数是偏移量(offset)。该参数表示从起始点开始要移动的距离(参见表13.3列出的起始点模式)。该参数必须是一个long类型的值,可以为正(前移)、负(后移)或0(保持不动)。

fseek()的第3个参数是模式,该参数确定起始点。根据ANSI标准,在stdio.h头文件中规定了几个表示模式的明示常量(manifest constant),如表13.3所示。

旧的实现可能缺少这些定义,可以使用数值0L、1L、2L分别表示这3种模式。L后缀表明其值是long类型。或者,实现可能把这些明示常量定义在别的头文件中。如果不确定,请查阅实现的使用手册或在线帮助。

下面是调用fseek()函数的一些示例,fp是一个文件指针:

fseek(fp, 0L, SEEK_SET);   // go to the beginning of the file
fseek(fp, 10L, SEEK_SET);  // go 10 bytes into the file
fseek(fp, 2L, SEEK_CUR);   // advance 2 bytes from the current position
fseek(fp, 0L, SEEK_END);   // go to the end of the file
fseek(fp, -10L, SEEK_END); // back up 10 bytes from the end of the file

对于这些调用还有一些限制,我们稍后再讨论。

如果一切正常,fseek()的返回值为0;如果出现错误(如试图移动的距离超出文件的范围),其返回值为-1。

ftell()函数的返回类型是long,它返回的是参数指向文件的当前位置距文件开始处的字节数。ANSI-C把它定义在stdio.h中。在最初实现的UNIX中,ftell()通过返回距文件开始处的字节数来确定文件的位置。文件的第1个字节到文件开始处的距离是0,以此类推。ANSI C规定,该定义适用于以二进制模式打开的文件,以文本模式打开文件的情况不同。这也是程序reverse.c以二进制模式打开文件的原因。

下面,我们来分析程序reverse.c中的基本要素。首先,下面的语句:

 fseek(fp, 0L, SEEK_END);

把当前位置设置为距文件末尾0字节偏移量。也就是说,该语句把当前位置设置在文件结尾。下一条语句:

last = ftell(fp);

把从文件开始处到文件结尾的字节数赋给last。然后是一个for循环:

for (count = 1L; count <= last; count++)
{
  fseek(fp, -count, SEEK_END);    /* go backward */
     ch = getc(fp);
 }

第1轮迭代,把程序定位到文件结尾的第1个字符(即,文件的最后一个字符)。然后,程序打印该字符。下一轮迭代把程序定位到前一个字符,并打印该字符。重复这一过程直至到达文件的第1个字符,并打印。

2 二进制模式和文本模式

我们设计的程序reverse.cUNIXMS-DOS环境下都可以运行。UNIX只有一种文件格式,所以不需要进行特殊的转换。然而MS-DOS要格外注意。许多MS-DOS编辑器都用Ctrl+Z标记文本文件的结尾。以文本模式打开这样的文件时,C能识别这个作为文件结尾标记的字符。但是,以二进制模式打开相同的文件时,Ctrl+Z字符被看作是文件中的一个字符,而实际的文件结尾符在该字符的后面。文件结尾符可能紧跟在Ctrl+Z字符后面,或者文件中可能用空字符填充,使该文件的大小是256的倍数。在DOS环境下不会打印空字符,程序reverse.c中就包含了防止打印Ctrl+Z字符的代码。

二进制模式和文本模式的另一个不同之处是:MS-DOS\r\n组合表示文本文件换行。以文本模式打开相同的文件时,C程序把\r\n“看成”\n。但是,以二进制模式打开该文件时,程序能看见这两个字符。因此,程序reverse.c中还包含了不打印\r的代码。通常,UNIX文本文件既没有Ctrl+Z,也没有\r,所以这部分代码不会影响大部分UNIX文本文件。

ftell()函数在文本模式和二进制模式中的工作方式不同。许多系统的文本文件格式与UNIX的模型有很大不同,导致从文件开始处统计的字节数成为一个毫无意义的值。ANSI C规定,对于文本模式,ftell()返回的值可以作为fseek()的第2个参数。对于MS-DOSftell()返回的值把\r\n当作一个字节计数。

3 可移植性

理论上,fseek()ftell()应该符合UNIX模型。但是,不同系统存在着差异,有时确实无法做到与UNIX模型一致。因此,ANSI对这些函数降低了要求。下面是一些限制。

  • 在二进制模式中,实现不必支持SEEK_END模式。因此无法保证程序清单13.4的可移植性。移植性更高的方法是逐字节读取整个文件直到文件末尾。C预处理器的条件编译指令(第16章介绍)提供了一种系统方法来处理这种情况。
  • 在文本模式中,只有以下调用能保证其相应的行为。

不过,许多常见的环境都支持更多的行为。

4 getpos()和fsetpos()函数

fseek()ftell()潜在的问题是,它们都把文件大小限制在long类型能表示的范围内。也许20亿字节看起来相当大,但是随着存储设备的容量迅猛增长,文件也越来越大。鉴于此,ANSI C新增了两个处理较大文件的新定位函数:fgetpos()fsetpos()。这两个函数不使用long类型的值表示位置,它们使用一种新类型:fpos_t(代表file positiontype,文件定位类型)。fpos_t类型不是基本类型,它根据其他类型来定义。fpos_t类型的变量或数据对象可以在文件中指定一个位置,它不能是数组类型,除此之外,没有其他限制。实现可以提供一个满足特殊平台要求的类型,例如,fpos_t可以实现为结构。

ANSI-C定义了如何使用fpos_t类型。fgetpos()函数的原型如下:

nt fgetpos(FILE * restrict stream, fpos_t * restrict pos);

调用该函数时,它把fpos_t类型的值放在pos指向的位置上,该值描述了文件中的当前位置距文件开头的字节数。如果成功,fgetpos()函数返回0;如果失败,返回非0。

fsetpos()函数的原型如下:

: int fsetpos(FILE stream, const fpos_t pos);

调用该函数时,使用pos指向位置上的fpos_t类型值来设置文件指针指向偏移该值后指定的位置。如果成功,fsetpos()函数返回0;如果失败,则返回非0。fpos_t类型的值应通过之前调用fgetpos()获得。

以上就是关于C语言处理I/O的两个随机访问函数:fseek()ftell()的相关信息了,希望对大家有所帮助。想了解更多的知识,大家可以看一下教程

C教程:https://www.w3cschool.cn/c/

文章参考来源:www.toutiao.com/a6855410553498632708

Apache-HTTP和Nginx哪家引擎比较强

thbcm阅读(183)

本文分别介绍了Apache-HTTPNginx这两个引擎,然后对比一下他们的差别。有什么不足之处欢迎大家补充。

HTTP中间件

当我们在浏览器中输入一个网页链接后,浏览器基于HTTP(s)传输协议向相应的服务器发送一个请求,服务器收到相应的请求后经过处理,返回相应的信息给浏览器,然后由浏览器解析http中的内容,以网页的形式表现出来。

服务器负责接收请求,并在处理之后返回相应的数据,而其中又可以细分为处理http连接的服务部分和执行服务内容的应用部分(WordPress使用PHP生成需要的页面,就属于应用部分)

而不论应用部分执行的是何种应用,处理http连接的部分几乎是相同的,所以出现了专门处理http连接的中间件,目前最常见的是ApacheNginx

Apache

正式名称是“Apache HTTP Server”,是一款开源的HTTP服务器中间件,诞生于1995年,曾经是HTTP服务领域的龙头老大,拥有大量的用户和丰富的社区资源。Apache的一大优点就是方便与WordPress等CMS软件进行集成,只需要简单的设定就能搭建一个基于CMS的网站。

Apache的内部处理模型

内部构造方面,Apache采用多进程的方式,每有一个连接就会为这个连接开辟一个进程,专门用于处理这个连接上的请求,直到连接结束。这样做的好处是:

  • 来自不同客户端的连接会立刻得到相应且互不干扰,而且不会因为某一个服务占用了较长的时间而使其它的连接得不到响应。

但是缺点也是显而易见的:

  • 当同时访问数比较多的时候,Apache会建立大量的进程,占用过多的内存资源。
  • 大量线程间的调度也会造成CPU处理能力的大量浪费。

由此产生了被称为C10K的难题,C即客户端(Client),10K是指1万,即不论服务器的性能和网络带宽有多高,Apache都难以同时处理1万个以上的连接。

Nginx

读作Engine-X,和Apache一样也是用于HTTP服务的开源中间件,诞生于2004年。NginxApache的历史要短,但是正因为是后来者,Nginx吸取了Apache的教训,在设计初期就考虑到了处理大量连接时的效率问题,解决了诸如C10K等随着互联网规模壮大而产生的难题。

Nginx的内部处理模型

Nginx采用了非阻塞IO和异步消息驱动的方式,即在称作worker的线程中使用循环来处理队列中的连接请求。而根据硬件的情况,可以设定多个worker线程,充分利用CPU的核心资源。

  • 解决了处理大量连接时消耗内存过多,调度效率低下的问题,同时还能充分的利用所有的CPU核心。在相同硬件下处理并发连接的能力是Apache的10到100倍。

但是Nginx这种方式也不是没有缺点。

  • 当服务器单核性能较差时,基于CMS的动态网站可能需要较长的时间来执行一个请求,此时来自其他客户端的请求将无法立即被执行。当CPU核心数较少,worker线程不足时会更加明显。

好在现在服务器的性能越来越强,在AMD的带领下CPU核心数也越来越多,Nginx的缺点足以被弥补,而高效的优势也愈发显现出来。

综合对比

Apache Nginx处理能力有限10-100倍是否会被复杂任务阻塞否有可能会设定难度比较简单相对复杂社区资源丰富相对较少

近年来,Nginx的市场占有率不断提高,2019年已经达到了和Apache持平的水平。而对于有极大访问量的大型网站,可以看到访问量越大,Nginx的占比也就越高。这也从侧面印证了Nginx在处理大量访问时的优越性能。

负载均衡

Nginx除了可以作为HTTP服务器使用,其强大的反向代理功能还被广泛地用作负载均衡前端服务器,逐渐取代了基于硬件的负载均衡器。

Nginx中可以配置若干个后端服务器,Nginx在收到HTTP请求之后按照一定规则(轮询,IP哈希,优先随机)等将请求转发给后端服务器,实现负载在多台服务器上的平均或加权分配。

同时作为负载均衡的前端还能缓存后端返回的数据,缓解后端服务器的压力。前端采用Nginx做负载均衡限制每个服务器的连接数,后端服务器运行Apache的模式也并不少见。

硬件负载均衡器的业界大佬F5 networks在2019年收购了Nginx,推出了包含收费服务的负载均衡解决方案Nginx+

以上是关于apachenginx的对比,希望对于刚接触apahcenginx的人有一定的帮助。使用一个产品不能糊里糊涂的使用,我们需要了解其优点和缺点,这样才能更好的使用它们。你也可以了解更多相关知识

Nginx 入门指南:https://www.w3cschool.cn/nginx/

Apache Beam 2.23.0 今日发布,更新了大数据批处理和流处理标准

thbcm阅读(185)

简介

Apache Beam 2.23.0现已发布。Apache BeamGoogle 在 2016 年 2 月份贡献给 Apache基金会的项目,主要目标是统一批处理和流处理的编程范式,为无限、乱序、web-scale 的数据集处理提供简单灵活,功能丰富以及表达能力十分强大的 SDKApache Beam项目重点在于数据处理的编程范式和接口定义,并不涉及具体执行引擎的实现,Apache Beam 希望基于 Beam 开发的数据处理程序可以执行在任意的分布式计算引擎上。

主要更新内容:

Highlights

  • Twister2 Runner(BEAM-7304)。
  • Python 3.8支持(BEAM-8494)。

I/Os

  • 添加了对 Snowflake reading 的支持(Java)(BEAM-9722)。
  • 增加了对写入 Splunk 的支持(Java)(BEAM-8596)。
  • 添加了对 assume role 的支持(Java)(BEAM-10335)。
  • 已添加一个新的可从 BigQuery 读取的 transform:apache_beam.io.gcp.bigquery.ReadFromBigQuery。此 transform 是实验性的。它通过将数据导出到 Avro 文件并读取这些文件来从 BigQuery 读取数据。它还支持通过导出到 JSON 文件来读取数据。与时间和日期相关的字段在行为上有很小的差异。
  • SnowflakeIO.write 添加 dispositions(BEAM-10343)

New Features/Improvements

更新 Snowflake JDBC 依赖关系,并将 application=beam 添加到 connection URL(BEAM-10383)。

Breaking Changes

  • 在反序列化 JSON(Java)时,RowJson.RowJsonDeserializerJsonToRowPubsubJsonTableProvider现在默认接受“implicit nulls”。以前的 null 只能用 explicit null 值表示,例如 {"foo": "bar", "baz": null},而像{"foo": "bar"} 这样的 implicit null 值则会引发异常。现在,两个 JSON 字符串默认都会产生相同的结果。可以使用用RowJson.RowJsonDeserializer#withNullBehavior来覆盖此行为。
  • 修复 Python 中GroupIntoBatches实验转换中的一个错误,该错误实际上是按键对批次进行分组的。这将更改此转换的输出类型(BEAM-6696)。

Deprecations

  • 删除 Gearpump runner。(BEAM-9999)
  • 删除 Apex 运行程序。(BEAM-9999)
  • RedisIO.readAll() 已被弃用,将在 2 个版本中删除,用户必须使用 RedisIO.readKeyPatterns() 作为替代(BEAM-9747)。

文章参考来源:https://beam.apache.org/blog/beam-2.23.0/

还在为开发API烦恼吗?一份API开发指南献上

thbcm阅读(171)

每个开发人员对API这词应该都挺熟悉的,API是软件系统之间或不同组成部分之间进行连接的约定。特别是移动应用程序和微服务架构的不断普及,API就是他们成功背后的功臣,这个时候如何设计和开发API就显得格外重要,今天这篇文章就是一份完整的API开发指南,介绍了在开发API过程中的内容、工具和最佳实践。

一、API介绍

API它的全称是Application Programming Interface——应用程序编程接口,是一组指令、标准或要求,使软件或应用程序可以利用另一应用程序、平台或设备的功能/服务来获得更好的服务。简而言之,它可以让应用程序彼此通信。例如,当我们在使用支付宝、微信APP时,都会通过API请求后台服务器上的数据,在APP上进行展示。

API是处理数据或启动两个产品或服务之间的通信的所有应用程序的基础。它使移动应用程序或平台能够与其他应用程序或平台共享其数据,并在不涉及开发人员的情况下简化用户体验。最重要的是,API消除了从头开始构建类似程序或平台的需求。您可以使用其他一些应用程序/平台中的现有应用程序。基于这些原因,应用程序开发人员和业务主管都将重点放在API开发上。

在深入研究之前,先让我们看一下使您更容易理解该概念的基本术语。

二、API术语

  1. API Key:当一个API请求通过Header或参数来识别调用者时,传递到请求中的授权码就是API Key
  1. Endpoint:当一个API与另一个系统交互时,通信通道的两端被认为是Endpoint
  1. JSON:是用于API请求参数和响应主体的数据格式。
  1. GETRESTful APIHTTP方法,用于获取资源。
  1. POSTRESTful APIHTTP方法,用于创建资源。
  1. OAuth:它基本上是一个开放标准的授权框架,可以在不直接共享凭据的情况下从客户端进行访问。
  1. RESTREST(代表性状态转移)是一种编程体系结构的实现,用于提供两个设备/系统之间的通信效率。它是一个轻量级的,他是通过数据引用而不是数据副本的方式来共享数据,基于这个架构创建的系统称为“RESTful”系统,而RESTful系统中最著名的例子就是万维网。
  1. SOAPSOAP或简单对象访问协议是一种消息协议,用于在计算机网络中执行Web服务时共享结构化信息。它与XML信息集和应用程序层协议(如HTTPSMTP)一起使用,分别用于消息格式和消息协商与传输。
  1. 延迟:延迟定义为API从请求到响应的过程中所花费的总时间。
  1. 速率限制API速率限制是指定义最终用户可以访问API的速率的过程。也就是说限制用户每次可以向API发送的请求数。
  1. API限流:调节用户在特定时间段内使用API的过程称为限流。这可以用于API限制,比如,设置每天限制1000个API请求,当用户点击1001个请求时,服务器会返回429HTTP状态码,并带着“请求太多”的消息。

三、API的工作流程

假如打开一些旅游应用程序/网站来预订航班,再填写了表格——输入了出发和返回日期,城市,航班以及其他相关详细信息——并提交了。只需几秒钟,屏幕上就会显示航班清单以及价格,时间,座位可用性以及其他详细信息。

为了提供这样严格的数据,该平台向航空公司的网站发送了请求,以访问其数据库并通过API获取相关数据。网站以API形式传递给平台数据作为响应,平台将其显示在屏幕上,基本的过程如下:

在此,航班预订应用程序/平台和航空公司的网站充当端点(EndPoint),而API充当简化数据共享过程的中介。在谈论端点通信时,API有两种形式,即RESTSOAP。尽管这两种方法都能带来有效的结果,但目前移动应用开发程序更喜欢使用REST而不是SOAP,因为SOAP API繁重且依赖于平台。

下面就介绍一下如何开发API?选择哪些工具和技术?

四、开发API的工具

在开发API的过程中有许多工具和技术可以使用,下面介绍几个用于为开发人员开发API的流行工具:

  1. Apigee:它是GoogleAPI管理工具,通过重新建立API方法来帮助开发人员和企业家在数字化转型方面取得成功。
  1. APIMatic and API Transformer:提供了复杂的自动生成工具,通过API特定格式构建高质量的SDK和代码片段,并将其转换为其他规范的形式,如RAMLAPI Blueprint等等。
  1. API Science:该工具主要用于评估内部API和外部API的性能。
  1. API Serverless Architecture:该产品借助云的服务器基础架构协助移动应用程序开发人员设计、构建、发布和托管API。
  1. API Platform:这是一个适用于Web API开发的开源PHP框架。
  1. OAuth2:这是一种用于身份验证和授权API的身份管理解决方案。
  1. ClearBlade:这是一个API管理程序,用于将IOT技术融入流程中。
  1. GitHub:这是一个开源的Git存储库,用来托管代码服务,可以提交代码、发布请求,版本控制。还可以将代码保存在私有存储库中。
  1. Postman:这是一个API工具链,使开发人员能够运行、测试、记录和评估其API的性能。

五、高效API的特性

  1. 修改时间戳/按条件搜索API应该允许用户根据不同的条件(例如日期)搜索数据,并能对检索的数据进行修改(更新,编辑和删除),并能记录修改的时间戳。
  1. 分页:当数据量很大的时候,我们不希望每次都获取完整的数据列表。在这种情况下,API应该能够确定一次显示多少数据以及总页数,还应告知最终用户剩余的数据页数。
  1. 排序API应授权用户根据修改时间或其他条件对数据进行排序。
  1. JSON支持/ REST:尽量使用RESTful风格进行有效的API开发。REST API是无状态的,轻量级的。此外,JSON的语法类似于大多数编程语言的语法,这使移动应用程序开发人员可以轻松地将其解析为任何其他语言。
  1. 通过OAuth进行授权:由于API需要对外暴露,因此还需要通过OAuth进行授权-您只需单击一个按钮即可完成。

六、构建API的最佳实践

  1. 流量限制:流量限制是考虑流量溢出,并保护其免受Dos攻击的一种好习惯。
  1. 将API网关视为增强点:在设置限制规则、API 秘钥和OAuth的应用时,必须将API网关视为最佳实施点。只有正确的、合法的用户才能访问后面的数据,并能在网关这里加密消息或编辑私密消息,从而分析和管理API
  1. 允许覆盖HTTP方法:由于某些代理仅支持GETPOST方法,因此需要让RESTful API 覆盖HTTP方法,可以使用自动以HTTPX-HTTP-Method-Override
  1. 评估API和基础结构:当前,实时分析是可以实现的,但是如果API服务器存在内存泄漏、CPU耗尽或其他问题该怎么办?考虑到这种情况,可以使用一些工具来对API进行评估和排查。
  1. 文档:为API编写文档,可以使用OpenAPI的规范的格式,这样其他应用程序开发人员可以轻松的了解整个过程并利用这些信息来提供更好的用户体验。总之,良好的API文档可以减少项目实施的时间,提供API开发的效率。

以上就是一份关于API开发的指南文档了,希望对大家有所帮助。想了解更多的话,可以看一下相关文档

io.js API 中文文档:https://www.w3cschool.cn/fkcaso/

Fetch API官方文档:https://www.w3cschool.cn/fetch_api/

文章参考来源:appinventiv.com/blog/complete-guide-to-api-development/

带你认识Linux中的ELF文件

thbcm阅读(166)

Linux系统使用过程中,我们经常会看到elf32-i386ELF 64-bit LSB等字样。那么究竟ELF是什么呢?

几种常见的ELF文件

Linux下,我们经gcc编译之后生成的可执行文件属于ELF文件:

ELF是一类文件类型,而不是特指某一后缀的文件。ELF(Executable and Linkable Format,可执行与可链接格式)文件格式,在Linux下主要有如下三种文件:

  • 可执行文件(.out)Executable File,包含代码和数据,是可以直接运行的程序。其代码和数据都有固定的地址 (或相对于基地址的偏移 ),系统可根据这些地址信息把程序加载到内存执行。
  • 可重定位文件(.o文件)Relocatable File,包含基础代码和数据,但它的代码及数据都没有指定绝对地址,因此它适合于与其他目标文件链接来创建可执行文件或者共享目标文件。
  • 共享目标文件(.so)Shared Object File,也称动态库文件,包含了代码和数据,这些数据是在链接时被链接器(ld)和运行时动态链接器(ld.so.l、libc.so.l、ld-linux.so.l)使用的。

ELF格式可结构大致为:

ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。

实际上,一个文件中不一定包含全部内容,而且它们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。

readelf工具的使用

Linux下,我们可以使用readelf 命令工具可以查看ELF格式文件的一些信息。下面我们先准备一个动态链接相关的demo:

文件1(main.c)

include “test.h”


int main(void)
{
    print_hello();
    return 0;
}

文件2(test.c)

include “test.h”

void print_hello(void) { printf(“hello world\n”); }

文件3(test.h)

ifndef __TEST_H

#define __TEST_H


#include <stdio.h>


void print_hello(void);


#endif

执行相关命令生成相关文件:.out文件.o文件.so文件。如:

下面我们使用readelf命令来查看这三类文件的一些信息。readelf命令格式为:

readelf <option(s)> elf-file(s)

查看可执行文件头部信息:

查看可执行文件头部信息是,我们发现这样一个问题,头部信息中的类型竟然是共享库文件,而我们查看的是可执行文件,自相矛盾?

查了一些资料发现:gcc编译默认加了--enable-default-pie选项:

Position-Independent-ExecutableBinutilsglibcgcc的一个功能,能用来创建介于共享库和通常可执行代码之间的代码–能像共享库一样可重分配地址的程序,这种程序必须连接到Scrt1.o。标准的可执行程序需要固定的地址,并且只有被装载到这个地址时,程序才能正确执行。PIE能使程序像共享库一样在主存任何位置装载,这需要将程序编译成位置无关,并链接为ELF共享对象。

引入PIE的原因是让程序能装载在随机的地址,通常情况下,内核都在固定的地址运行,如果能改用位置无关,那攻击者就很难借助系统中的可执行码实施攻击了。类似缓冲区溢出之类的攻击将无法实施。而且这种安全提升的代价很小。

也就是说,pie这是一种保护我们可执行程序的一种手段。这里我们只是做实验,我们可以加-no-pie参数先把pie给关掉:

可以看到,类型终于对得上了。ELF头部信息还包含有Entry point address(入口地址)、Start of program headers(程序头的起始字节)、Start of section headers(节头的起始字节)等信息。

查看可重定位文件头部信息:

查看共享目标文件头部信息:

同样的,readelf 搭配其它参数可以查看ELF文件的其它信息:

objdump工具的使用

objdump工具用于显示一个或多个目标文件的信息。objdump命令格式:

objdump <option(s)> <file(s)>

可执行文件、可重定位文件与共享目标文件都属于目标文件,所以都可以使用这个命令来查看一些信息。

查看可重定位文件反汇编信息:

查看可执行文件反汇编信息:

查看共享目标文件反汇编信息:

总结

以上就是本次的分享。简单地介绍了ELF文件的一些信息,同时介绍了分析ELF文件的两个工具。ELF文件的内容很多,并且比较抽象,详细分析起来是个深坑。我们大致先进行一个简单的了解,我现在还没有这个能力或者说还没有这个需求去学习、分析这些底层的东西,之后如果深入学习时再做另外的分享。有兴趣的同学可以跟我一起学

Linux教程:https://www.w3cschool.cn/linux/

Linux微课:https://www.w3cschool.cn/minicourse/play/linuxcourse

Linux就该这么学:https://www.w3cschool.cn/linuxprobe/

JavaScript如何实现深拷贝

thbcm阅读(189)

JavaScript 开发工作中,我们经常会碰到需要进行深拷贝的情况,而且在面试中也经常会问到这个问题,那么什么是浅拷贝,什么是深拷贝?

什么是浅拷贝

关于浅拷贝的概念,我在网上看到一种说法,直接上代码。

var person = {name: "Jason", age: 18, car: {brand: "Ferrari", type: "430"}};
var person1 = person;       //他们认为这是浅拷贝

但是我个人认为,上面这个根本不涉及拷贝,只是一个简单的引用赋值。以我的理解,浅拷贝应该是不考虑对象的引用类型的属性,只对当前对象的所有成员进行拷贝,代码如下:

function copy(obj){
    var objCopy = {};
    for(var key in obj){
        objCopy[key] = obj[key];
    }
    return objCopy;
}


var person = {name: "Jason", age: 18, car: {brand: "Ferrari", type: "430"}};
var personCopy = copy(person);

上面这段代码中,person对象拥有两个基本类型的属性nameage,一个引用类型的属性car,当使用如上方法进行拷贝的时候,nameage属性会被正常的拷贝,但是car属性,只会进行引用的拷贝,这样会导致拷贝出来的对象personCopyperson会共用一个car对象。这样就是所谓的浅拷贝。

什么是深拷贝

深拷贝的就是在拷贝的时候,需要将当前要拷贝的对象内的所有引用类型的属性进行完整的拷贝,也就是说拷贝出来的对象和原对象之间没有任何数据是共享的,所有的东西都是自己独占的一份。

如何实现深拷贝

实现深拷贝需要考虑如下几个因素:

  • 传入的对象是使用对象字面量{}创建的对象还是由构造函数生成的对象
  • 如果对象是由构造函数创建出来的,那么是否要拷贝原型链上的属性
  • 如果要拷贝原型链上的属性,那么如果原型链上存在多个同名的属性,保留哪个
  • 处理循环引用的问题

第三方库实现深拷贝

jQuery的$.extend()

我们可以通过$.extend()方法来完成深复制。值得庆幸的是,我们在jQuery中可以通过添加一个参数来实现递归extend。调用$.extend(true, {}, ...)就可以实现深复制,参考下面的例子:

var x = {
    a: 1,
    b: { f: { g: 1 } },
    c: [ 1, 2, 3 ]
};


var y = $.extend({}, x),          //shallow copy
    z = $.extend(true, {}, x);    //deep copy


y.b.f === x.b.f       // true
z.b.f === x.b.f       // false

但是jQuery的这个$.extend()方法,有弊端,什么弊端呢?我们看下面的例子:

var objA = {};
var objB = {};


objA.b = objB;
objB.a = objA;


$.extend(true,{},a);


//这个时候就出现异常了
//Uncaught RangeError: Maximum call stack size exceeded(…)

也就是说,jQuery中的$.extend()并没有处理循环引用的问题。

使用JSON对象实现深拷贝

使用JSON全局对象的parsestringify方法来实现深复制也算是一个简单讨巧的方法。

function jsonClone(obj) {
    return JSON.parse(JSON.stringify(obj));
}
var clone = jsonClone({ a:1 });

然而使用这种方法会有一些隐藏的坑,它能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。

自己造轮子

下面我们给出一个简单的解决方案,当然这个方案是参考别人的方式来实现的。希望对大家有用。

var clone = (function() {
    //这个方法用来获取对象的类型 返回值为字符串类型 "Object RegExp Date Array..."
    var classof = function(o) {
        if (o === null) {
            return "null";
        }
        if (o === undefined) {
            return "undefined";
        }
        // 这里的Object.prototype.toString很可能用的就是Object.prototype.constructor.name
        // 这里使用Object.prototype.toString来生成类型字符串
        var className = Object.prototype.toString.call(o).slice(8, -1);
        return className;
    };


    //这里这个变量我们用来存储已经保存过的属性,目的在于处理循环引用的问题
    var references = null;


    //遇到不同类型的对象的处理方式
    var handlers = {
        //正则表达式的处理
        'RegExp': function(reg) {
            var flags = '';
            flags += reg.global ? 'g' : '';
            flags += reg.multiline ? 'm' : '';
            flags += reg.ignoreCase ? 'i' : '';
            return new RegExp(reg.source, flags);
        },
        //时间对象处理
        'Date': function(date) {
            return new Date(+date);
        },
        //数组处理 第二个参数为是否做浅拷贝
        'Array': function(arr, shallow) {
            var newArr = [],
            i;
            for (i = 0; i < arr.length; i++) {
                if (shallow) {
                    newArr[i] = arr[i];
                } else {
                    //这里我们通过reference数组来处理循环引用问题
                    if (references.indexOf(arr[i]) !== -1) {
                        continue;
                    }
                    var handler = handlers[classof(arr[i])];
                    if (handler) {
                        references.push(arr[i]);
                        newArr[i] = handler(arr[i], false);
                    } else {
                        newArr[i] = arr[i];
                    }
                }
            }
            return newArr;
        },
        //正常对象的处理 第二个参数为是否做浅拷贝
        'Object': function(obj, shallow) {
            var newObj = {}, prop, handler;
            for (prop in obj) {
                //关于原型中属性的处理太过复杂,我们这里暂时不做处理
                //所以只对对象本身的属性做拷贝
                if (obj.hasOwnProperty(prop)) {
                    if (shallow) {
                        newObj[prop] = obj[prop];
                    } else {
                        //这里还是处理循环引用的问题
                        if (references.indexOf(obj[prop]) !== -1) {
                            continue;
                        }


                        handler = handlers[classof(obj[prop])];
                        //如果没有对应的处理方式,那么就直接复制
                        if (handler) {
                            references.push(obj[prop]);
                            newObj[prop] = handler(obj[prop], false);
                        } else {
                            newObj[prop] = obj[prop];
                        }
                    }
                }
            }
            return newObj;
        }
    };


    return function(obj, shallow) {
        //首先重置我们用来处理循环引用的这个变量
        references = [];
        //我们默认处理为浅拷贝
        shallow = shallow === undefined ? true : false;
        var handler = handlers[classof(obj)];
        return handler ? handler(obj, shallow) : obj;
    };
}());


(function() {
    //下面是一些测试代码
    var date = new Date();
    var reg = /hello word/gi;
    var obj = {
        prop: 'this ia a string',
        arr: [1, 2, 3],
        o: {
            wow: 'aha'
        }
    };
    var refer1 = {
        arr: [1, 2, 3]
    };
    var refer2 = {
        refer: refer1
    };
    refer1.refer = refer2;


    var cloneDate = clone(date, false);
    var cloneReg = clone(reg, false);
    var cloneObj = clone(obj, false);
    alert((date !== cloneDate) && (date.valueOf() === cloneDate.valueOf()));
    alert((cloneReg !== reg) && (reg.toString() === cloneReg.toString()));
    alert((obj !== cloneObj) && (obj.arr !== cloneObj.arr) && (obj.o !== cloneObj.o) && (JSON.stringify(obj) === JSON.stringify(cloneObj)));


    clone(refer2, false);
    alert("I'm not dead yet!");
    // Output:
    // true
    // true
    // true
    // I'm not dead yet!
}());

以上就是关于JavaScript拷贝的一些知识了,希望对大家有所帮助,对JavaScript有兴趣的同学可以看一下教程

JavaScript教程:https://www.w3cschool.cn/javascript/

JavaScript微课:https://www.w3cschool.cn/minicourse/play/jscourse

jQuery中的prop和attr区别在哪

thbcm阅读(153)

JQuery中,对CheckBox的操作分两个阶段,一个是JQuery1.6之前的版本,一个是1.6之后的版本

在1.6之前,我们这么做:

<input type =’checkbox’ id=’checkbox’/> <script> var isChecked = $(‘#checkbox’).attr(‘checked’); $(‘#checkbox’).attr(‘checked’,true); <script/>

但是细心的同学会发现,在jQuery1.6之后,如果还像上面这么做,那肯定会出问题: $('#checkbox').attr('checked');获取到的值并不是truefalse,而是checked或者undefined

那在1.6之后如何进行操作呢?

jQuery在之后的版本中对属性和特性进行了比较细致的区分,什么是特性呢? 特性就是像 checkedselectedIndex, tagName, nodeName, nodeType, ownerDocument, defaultChecked, 和defaultSelected等等这些。

那prop()和attr()到底有什么区别呢?

build-in属性,attributeproperty共享数据,attribute更改了会对property造成影响,反之亦然,但是两者的自定义属性是独立的数据,即使name一样,也互不影响,看起来是下面这张图,但是IE6、7没有作区分,依然共享自定义属性数据

并不是所有的attribute与对应的property名字都一致,比如刚才使用的attributeclass属性,使用property操作的时候应该是这样className t.className='active2';

对于值是true/falseproperty,类似于inputchecked attribute等,attribute取得值是HTML文档字面量值,property是取得计算结果,property改变并不影响attribute字面量,但attribute改变会一向property计算 <input id="test3" type="checkbox"/>

var t=document.getElementById(‘test3’); console.log(t.getAttribute(‘checked’));//null console.log(t.checked);//false


  t.setAttribute('checked','checked');
  console.log(t.getAttribute('checked'));//checked
  console.log(t.checked);//true


  t.checked=false;
  console.log(t.getAttribute('checked'));//checked
  console.log(t.checked);//false

对于一些和路径相关的属性,两者取得值也不尽相同,但是同样attribute取得是字面量,property取得是计算后的完整路径 <a id="test4" href="#">Click</a> js var

var t=document.getElementById(‘test4’); console.log(t.getAttribute(‘href’));//# console.log(t.href);//file:///C:Users/bsun/Desktop/ss/anonymous.html#

以上就是关于jQuery中的prop()attr()有什么区别的相关知识,希望对大家有所帮助,感兴趣的同学可以看一下教程

jQuery教程:https://www.w3cschool.cn/jquery/

jQuery微课:https://www.w3cschool.cn/minicourse/play/jquerycourse

联系我们