程序员的自我修养--链接、装载与库笔记:运行库
1. 入口函数和程序初始化
程序从main开始吗?:操作系统装载程序之后,首先运行的代码并不是main的第一行,而是某些别的代码,这些代码负责准备好main函数执行所需要的环境,并且负责调用main函数,这时候你才可以在main函数里放心大胆地写各种代码:申请内存、使用系统调用、触发异常、访问I/O。在main返回之后,它会记录main函数的返回值,调用atexit注册的函数,然后结束进程。
运行这些代码的函数称为入口函数或入口点(Entry Point),视平台的不同而有不同的名字。程序的入口点实际上是一个程序的初始化和结束部分,它往往是运行库的一部分。一个典型的程序运行步骤大致如下:
(1). 操作系统在创建进程后,把控制权交到了程序的入口,这个入口往往是运行库中的某个入口函数。
(2). 入口函数对运行库和程序运行环境进行初始化,包括堆、I/O、线程、全局变量构造,等等。
(3). 入口函数在完成初始化之后,调用main函数,正式开始执行程序主体部分。
(4). main函数执行完毕以后,返回到入口函数,入口函数进行清理工作,包括全局变量析构、堆销毁、关闭I/O等,然后进行系统调用结束进程。
入口函数如何实现:
GLIBC入口函数:glibc的启动过程在不同的情况下差别很大,比如静态的glibc和动态的glibc的差别,glibc用于可执行文件和用于共享库的差别,这样的差别可以组合出4种情况,这里只选取最简单的静态glibc用于可执行文件的时候作为例子。
glibc的程序入口为_start(这个入口是由ld链接器默认的链接脚本所指定的,也可以通过相关参数设定自己的入口)。_start由汇编实现,并且和平台相关。
可以从https://www.gnu.org/software/libc/sources.html 下载glibc的源码,最新的发布版本为2.30。
环境变量:是存在于系统中的一些公用数据,任何程序都可以访问。通常来说,环境变量存储的都是一些系统的公共信息,例如系统搜索路径、当前OS版本等。环境变量的格式为key=value的字符串,C语言里可以使用getenv这个函数来获取环境变量信息。
MSVC CRT入口函数:MSVC的CRT默认的入口函数名为mainCRTStartup。
运行库与I/O:IO(或I/O)的全称是Input/Output,即输入和输出。对于计算机来说,I/O代表了计算机与外界的交互,交互的对象可以是人或其它设备。而对于程序来说,I/O覆盖的范围还要宽广一些。一个程序的I/O指代了程序与外界的交互,包括文件、管道、网络、命令行、信号等。更广义地讲,I/O指代任何操作系统理解为”文件”的事务。许多操作系统,包括Linux和Windows,都将各种具有输入和输出概念的实体----包括设备、磁盘文件、命令行等----统称为文件,因此这里所说的文件是一个广义的概念。对于一个任意类型的文件,操作系统会提供一组操作函数,这包括打开文件、读文件、写文件、移动文件指针等。C语言文件操作是通过一个FILE结构的指针来进行的。在操作系统层面上,文件操作也有类似于FILE的一个概念,在Linux里,这叫做文件描述符(File Descriptor),而在Windows里,叫做句柄(Handle)(以下在没有歧义的时候统称为句柄)。用户通过某个函数打开文件以获得句柄,此后用户操作文件皆通过该句柄进行。设计这么一个句柄的原因在于句柄可以防止用户随意读写操作系统内核的文件对象。无论是Linux还是Windows,文件句柄总是和内核的文件对象相关联的,但如何关联细节用户并不可见。内核可以通过句柄来计算出内核里文件对象的地址,但此能力并不对用户开放。I/O初始化的职责:首先I/O初始化函数需要在用户空间中建立stdin、stdout、stderr及其对应的FILE结构,使得程序进入main之后可以直接使用printf、scanf等函数。
MSVC CRT的入口函数初始化:MSVC的入口函数初始化主要包含两个部分,堆初始化和I/O初始化。
系统堆初始化:MSVC的堆初始化由函数_heap_init完成,它调用HeapCreate创建一个系统堆。
MSVC的I/O初始化:主要进行了如下几个工作:建立打开文件表;如果能够继承自父进程,那么从父进程获取继承的句柄;初始化标准输入输出。
2. C/C++运行库
C语言运行库:任何一个C程序,它的背后都有一套庞大的代码来进行支撑,以使得该程序能够正常运行。这套代码至少包括入口函数,及其所依赖的函数所构成的函数集合。当然,它还理应包括各种标准库函数的实现。这样的一个代码集合称之为运行时库(Runtime Library)。而C语言的运行库,即被称为C运行库(CRT)。
一个C语言运行库大致包含了如下功能:
(1). 启动与退出:包括入口函数及入口函数所依赖的其它函数等。
(2). 标准函数:由C语言标准规定的C语言标准库所拥有的函数实现。
(3). I/O:I/O功能的封装和实现。
(4). 堆:堆的封装和实现。
(5). 语言实现:语言中一些特殊功能的实现。
(6). 调试:实现调试功能的代码。
C语言标准库:美国国家标准协会(American National Standards Institute, ANSI)在1983年成立了一个委员会,旨在对C语言进行标准化,此委员会所建立的C语言标准被称为ANSI C。第一个完整的C语言标准建立于1989年,此版本的C语言标准称为C89。在C89标准中,包含了C语言基础函数库,由C89指定的C语言基础函数库就称为ANSI C标准运行库(简称标准库)。其后在1995年C语言标准委员会对C89标准进行了一次修订,在此次修订中,ANSI C标准库得到了第一次扩充,头文件iso646.h、wchar.h和wctype.h加入了标准库的大家庭。在1999年,C99标准诞生,C语言标准库得到了进一步的扩充,头文件complex.h、fenv.h、inttypes.h、stdbool.h、stdint.h和tgmath.h进入标准库。C11标准是C语言标准的第三版,前一个标准版本是C99标准。C11标准中又新增了5个头文件stdalign.h、stdatomic.h、stdnoreturn.h、threads.h、uchar.h。至此,C标准函数库共29个头文件。除了之前的14个头文件,剩下的15个头文件(C89标准)为:assert.h、ctype.h、errno.h、float.h、limits.h、locale.h、math.h、setjmp.h、signal.h、stdarg.h、stddef.h、stdio.h、stdlib.h、string.h、time.h。C语言的标准库非常轻量,它仅仅包含了数学函数、字符/字符串处理、I/O等基本方面。关于每个头文件的介绍可以参考:http://www.cplusplus.com/reference/clibrary/
变长参数:是C语言的特殊参数形式,例如printf函数,其声明如下:
int printf(const char * format, ...);
如此的声明表明,printf函数除了第一个参数类型为const char*之外,其后可以追加任意数量、任意类型的参数。在函数的实现部分,可以使用stdarg.h里的多个宏来访问各个额外的参数:假设lastarg是变长参数函数的最后一个具名参数(例如printf里的format),那么在函数内部定义类型为va_list的变量:va_list ap; 该变量以后将会依次指向各个可变参数。ap必须用宏va_start初始化一次,其中lastarg必须是函数的最后一个具名的参数。va_start(ap, lastarg); 此后可以使用va_arg宏来获得下一个不定参数(假设已知其类型为type):type next=va_arg(ap, type); 在函数结束前,还必须用宏va_end来清理现场。关于这几个宏的用法可以参考:https://blog.csdn.net/fengbingchun/article/details/78483471
变长参数宏:在很多时候我们希望在定义宏的时候也能够像printf一样可以使用变长参数,即宏的参数可以是任意个,这个功能可以由编译器的变长参数宏实现。
// 在GCC编译器下,变长参数宏可以使用”##”宏字符串连接操作实现,比如:
#define printf(args …) fprintf(stdout, ##args)
// 而在MSVC下,我们可以使用__VA_ARGS__这个编译器内置宏,比如:
#define printf(…) fprintf(stdout, __VA_ARGS__) // 它的效果与前面的GCC下使用##效果一样
glibc与MSVC CRT:运行库是平台相关的,因为它与操作系统结合得非常紧密。C语言的运行库从某种程度上来讲是C语言的程序和不同操作系统平台之间的抽象层,它将不同的操作系统API抽象成相同的库函数。比如我们可以在不同的操作系统平台下使用fread来读取文件,而事实上fread在不同的操作系统平台下的实现是不同的,但作为运行库的使用者我们不需要关心这一点。Linux和Windows平台下的两个主要C语言运行库分别为glibc(GNU C Library)和MSVCRT(Microsoft Visual C Run-time)。值得注意的是,像线程操作这样的功能并不是标准的C语言运行库的一部分,但是glibc和MSVCRT都包含了线程操作的库函数。比如glibc有一个可选的pthread库中的pthread_create()函数可以用来创建线程;而MSVCRT中可以使用_beginthread()函数来创建线程。所以glibc和MSVCRT事实上是标准C语言运行库的超集,它们各自对C标准库进行了一些扩展。
glibc:即GNU C Library,是GNU旗下的C标准库。最初由自由软件基金会FSF(Free Software Foundation)发起开发,目的是为GNU操作系统开发一个C标准库。glibc的发布版本主要由两部分组成,一部分是头文件,比如stdio.h、stdlib.h等,它们往往位于/usr/include;另外一部分则是库的二进制文件部分。二进制部分主要的就是C语言标准库,它有静态和动态两个版本。动态的标准库为/lib/x86_64-linux-gnu/libc.so.6,而静态标准库为/usr/lib/x86_64-linux-gnu/libc.a。事实上glibc除了C标准库之外,还有几个辅助程序运行的运行库,这几个文件可以称得上是真正的”运行库”,它们就是/usr/lib/x86_64-linux-gnu/crti.o、/usr/lib/x86_64-linux-gnu/crt1.o、/usr/lib/x86_64-linux-gnu/crtn.o,虽然它们都很小,但这几个文件都是程序运行的最关键的文件。
glibc启动文件:crt1.o里面包含的就是程序的入口函数_start,由它负责调用__libc_start_main初始化libc并且调用main函数进入真正的程序主体。crti.o和crtn.o两个目标文件中包含的代码实际上是_init()函数和_finit()函数的开始和结尾部分,当这两个文件和其它目标文件按照顺序链接起来以后,刚好形成两个完整的函数_init()和_finit()。可以用objdump查看这两个文件的反汇编代码,结果如下图所示:于是在最终链接完成之后,输出的目标文件中的”.init”段只包含了一个函数_init(),这个函数的开始部分来自于crti.o的”.init”段,结束部分来自于crtn.o的”.init”段。为了保证最终输出文件中的”.init”和”.finit”的正确性,我们必须保证在链接时,crti.o必须在用户目标文件和系统库之前,而crtn.o必须在用户目标文件和系统库之后。链接器的输入文件顺序一般是:ld crt1.o crti.o [user_objects] [system_libraries] crtn.o,由于crt1.o(crt0.o)不包含”.init”段和”.finit”段,所以不会影响最终生成”.init”和”.finit”段时的顺序。
在默认情况下,ld链接器会将libc、crt1.o等这些CRT和启动文件与程序的模块链接起来,但是有些时候,我们可能不需要这些文件,或者希望使用自己的libc和crt1.o等启动文件,以替代系统默认的文件,这种情况在嵌入式系统或操作系统内核编译的时候很常见。GCC提供了两个参数”-nostartfile”和”-nostdlib”,分别用来取消默认的启动文件和C语言运行库。
其实C++全局对象的构造函数和析构函数并不是直接放在.init和.finit段里面的,而是把一个执行所有构造/析构的函数的调用放在里面,由这个函数进行真正的构造和析构。除了全局对象构造和析构之外,.init和.finit还有其它的作用。由于它们的特殊性(在main之前/之后执行),一些用户监控程序性能、调试等工具经常利用它们进行一些初始化和反初始化的工作。当然我们也可以使用”__atrribute__((section(“.init”)))”将函数放到.init段里面,但是要注意的是普通函数放在”.init”是会破坏它们的结构的,因为函数的返回指令使得__init()函数会提前返回,必须使用汇编指令,不能让编译器产生”ret”指令。
GCC平台相关目标文件:crtbeginT.o、libgcc.a、libgcc_eh.a、crtend.o这几个文件实际上不属于glibc,它们是GCC的一部分,它们都位于GCC的安装目录/usr/lib/gcc/x86_64-linux-gnu/4.9/下。crtbeginT.o及crtend.o这两个文件是真正用于实现C++全局构造和析构的目标文件。C++这样的语言的实现是跟编译器密切相关的,而glibc只是一个C语言运行库,它对C++的实现并不了解。而GCC是C++的真正实现者,它对C++的全局构造和析构了如指掌。于是它提供了两个目标文件crtbeginT.o和crtend.o来配合glibc实现C++的全局构造和析构。由于GCC支持诸多平台,能够正确处理不同平台之间的差异性也是GCC的任务之一。比如有些32位平台不支持64位的long long类型的运算,编译器不能够直接产生相应的CPU指令,而是需要一些辅助的例程来帮助实现计算。libgcc.a里面包含的就是这种类似的函数,这些函数主要包括整数运算、浮点数运算(不同的CPU对浮点数的运算方法很不相同)等,而libgcc_eh.a则包含了支持C++的异常处理(Exception Handing)的平台相关函数。另外GCC的安装目录下往往还有一个动态链接版本libgcc_s.so。
MSVC CRT:同一个版本的MSVC CRT根据不同的属性提供了多种子版本,以供不同需求的开发者使用。按照静态/动态链接,可以分为静态版和动态版;按照单线程/多线程,可以分为单线程版和多线程版;按照调试/发布,可分为调试版和发布版;按照是否支持C++分为纯C运行库版和支持C++版;按照是否支持托管代码分为支持本地代码/托管代码和纯托管代码版。这些属性很多时候是相互正交的,也就是说它们之间可以相互组合。比如可以有静态单线程纯C纯本地代码调试版;也可以有动态的多线程纯C纯本地代码发布版等。但有些组合是没有的,比如动态链接版本的CRT是没有单线程的,所有的动态链接CRT都是多线程安全的。这样的不同组合将会出现非常多的子版本,于是微软提供了一套运行库的命名方法。这个命名方法是这样的,静态版和动态版完全不同。静态版的CRT位于MSVC安装目录下的C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\lib,它们的命名规则为:p表示C Plusplus,即C++标准库;mt表示Multi-Thread,即表示支持多线程;d表示Debug,即表示调试版本。
libc [p] [mt] [d] .lib
动态版的CRT的每个版本一般有两个相对应的文件,一个用于链接的.lib文件,一个用于运行时用的.dll动态链接库。它们的命名方式与静态版的CRT非常类似,稍微有所不同的是,CRT的动态链接库DLL文件名中会包含版本号。比如Visual C++ 2013的多线程、动态链接版的DLL文件名为msvcr120.dll。下表列举了一些最常见的MSVC CRT版本(以Visual C++ 2013为例):
C++ CRT:MSVC还提供了相应的C++标准库。如果你的程序是使用C++编写的,那么就需要额外链接相应的C++标准库。这里的”额外”的意思是,如下表所列的C++标准库里面包含的仅仅是C++的内容,比如iostream、string、map等,不包含C的标准库。
3. 运行库与多线程
CRT的多线程困扰:
线程的访问权限:线程的访问能力非常自由,它可以访问进程内存里的所有数据,甚至包括其它线程的堆栈(如果它知道其它线程的堆栈地址,然后这是很少见的情况),但实际运用中线程也拥有自己的私有存储空间,包括:栈(尽管并非完全无法被其它线程访问,但一般情况下仍然可以认为是私有的数据);线程局部存储(Thread Local Storage, TLS),是某些操作系统为线程单独提供的私有空间,但通常只具有很有限的尺寸;寄存器(包括PC寄存器),是执行流的基本数据,因此为线程私有。从C程序员的角度来看,数据在线程之间是否私有如下表所示:
多线程运行库:对于C/C++标准库来说,线程相关的部分是不属于标准库的内容的,它跟网络、图形图像等一样,属于标准库之外的系统相关库。这里所说的”多线程相关”主要有两个方面,一方面是提供那些多线程操作的接口,比如创建线程、退出线程、设置线程优先级等函数接口;另外一方面是C运行库本身要能够在多线程的环境下正确运行。对于第一方面,主流的CRT都会有相应的功能。比如Windows下,MSVC CRT提供了诸如_beginthread()、_endthread()等函数用于线程的创建和退出;而Linux下,glibc也提供了一个可选的线程库pthread(POSIX Thread),它提供了诸如pthread_create()、pthread_exit()等函数用于线程的创建和退出。很明显,这些函数都不属于标准的运行库,它们都是平台相关的。对于第二个方面,C语言运行库必须支持多线程的环境,实际上,最初CRT在设计的时候是没有考虑多线程环境的,因为当时根本没有多线程这样的概念。
CRT改进:(1). 使用TLS;(2). 加锁:在多线程版本的运行库中,线程不安全的函数内部都会自动地进行加锁;(3). 改进函数调用方式:C语言的运行库为了支持多线程特性,一种改进的办法就是修改所有的线程不安全的函数的参数列表,改成某种线程安全的版本。但是很多时候改变标准库函数的做法是不可行的。标准库之所以称之为”标准”,就是它具有一定的权威性和稳定性,不能随意更改。
线程局部存储实现:TLS的用法很简单,如果要定义一个全局变量为TLS类型的,只需要在它定义前加上相应的关键字即可。对于GCC来说,这个关键字就是__thread。对于MSVC来说,相应的关键字为__declspec(thread)。一旦一个全局变量被定义成TLS类型的,那么每个线程都会拥有这个变量的一个副本,任何线程对该变量的修改都不会影响其它线程中该变量的副本。
Windows TLS的实现:对于Windows系统来说,正常情况下一个全局变量或静态变量会被放到”.data”或”.bss”段中,但当我们使用__declspec(thread)定义一个线程私有变量的时候,编译器会把这些变量放到PE文件的”.tls”段中。当系统启动一个新的线程时,它会从进程的堆中分配一块足够大小的空间,然后把”.tls”段中的内容复制到这块空间中,于是每个线程都有自己独立的一个”.tls”副本。所以对于用__declspec(thread)定义的同一个变量,它们在不同线程中的地址都是不一样的。对于一个TLS变量来说,它有可能是一个C++的全局对象,那么每个线程在启动时不仅仅是复制”.tls”的内容那么简单,还需要把这些TLS对象初始化,必须逐个地调用它们的全局构造函数,而且当线程退出时,还要逐个地将它们析构,正如普通的全局对象在进程启动和退出时都要构造、析构一样。
显示TLS:使用__thread或__declspec(thread)关键字定义全局变量为TLS变量的方法往往被称为隐式TLS,即程序员无须关心TLS变量的申请、分配赋值和释放,编译器、运行库还有操作系统已经将这一切悄悄处理妥当了。在程序员看来,TLS全局变量就是线程私有的全局变量。相对于隐式TLS,还有一种叫做显示TLS的方法,这种方法是程序员需要手工申请TLS变量,并且每次访问该变量时都要调用相应的函数得到变量的地址,并且在访问完成之后需要释放该变量。在Windows平台上,系统提供了TlsAlloc()、TlsGetValue()、TlsSetValue()和TlsFree()这4个API函数用于显示TLS变量的申请、取值、赋值和释放。Linux下相对应的库函数为pthread库中的pthread_key_create()、pthread_getspecific()、pthread_setspecific()和pthread_key_delete()。相对于隐式的TLS变量,显式的TLS变量的使用十分麻烦,而且有诸多限制。
在Windows下创建一线程的方法有两种,一种是调用Windows API CreateThread()来创建线程;另外一种就是调用MSVC CRT的函数_beginthread()或_beginthreadex()来创建线程。在使用静态链接CRT(/MT, /MTd)时,CreateThread()可能会导致内存泄漏。当使用CRT时(基本上所有的程序都使用CRT),尽量使用_beginthread()/_beginthreadex()/_endthread()/_endthreadex()这组函数来创建线程。
4. C++全局构造与析构
glibc全局构造与析构: 对于每个编译单元(.cpp),GCC编译器会遍历其中所有的全局对象,生成一个特殊的函数,这个特殊函数的作用就是对本编译单元里的所有全局对象进行初始化。GCC在目录代码中生成了一个名为_GLOBAL_I_Hw的函数,由这个函数负责本编译单元所有的全局/静态对象的构造和析构。由于全局对象的构建和析构都是由运行库完成的,于是在程序或共享库中有全局对象时,记得不能使用”-nonstartfiles”或”-nostdlib”选项,否则,构建与析构函数将不能正确执行。
MSVC CRT的全局构造和析构:MSVC CRT的全局构造实现在机制上与Glibc基本是一样的,只不过它们的名字略有不同。Glibc下通过__cxa_exit()向exit()函数注册全局析构函数,MSVC CRT也通过atexit()实现全局析构,它们除了函数命名不同之外几乎没有区别。
5. fread实现
缓冲(Buffer):缓冲最为常见于IO系统中,设想一下,当希望向屏幕输出数据的时候,由于程序逻辑的关系,可能要多次调用printf函数,并且每次写入的数据只有几个字符,如果每次写数据都要进行一次系统调用,让内核向屏幕写数据,就明显过于低效,因为系统调用的开销是很大的,它要进行上下文切换、内核参数检查、复制等,如果频繁进行系统调用,将会严重影响程序和系统的性能。一个显而易见的可行方案是将对控制台连续的多次写入放在一个数组里,等到数组被填满之后再一次性完成系统调用写入,实际上这就是缓冲最基本的想法。当读文件的时候,缓冲同样存在。我们可以在CRT中为文件建立一个缓冲,当要读取数据的时候,首先看看这个文件的缓冲里有没有数据,如果有数据就直接从缓冲中取。如果缓冲是空的,那么CRT就通过操作系统一次性读取文件一块较大的内容填充缓冲。这样,如果每次读取文件都是一些尺寸很小的数据,那么这些读取操作大多都直接从缓冲中获得,可以避免大量的实际文件访问。除了读文件有缓冲以外,写文件也存在着同样的情况,而且写文件比读文件要更加复杂。因为当我们通过fwrite向文件写入一段数据时,此时这些数据不一定被真正地写入到文件中,而是有可能还存在于文件的写缓冲里面,那么此时如果系统崩溃或进程意外退出时,有可能导致数据丢失,于是CRT还提供了一系列与缓冲相关的操作用于弥补缓冲所带来的问题。C语言标准库提供与缓冲相关的几个基本函数,如下表所示:所谓flush一个缓冲,是指对写缓冲而言,将缓冲内的数据全部写入实际的文件,并将缓冲清空,这样可以保证文件处于最新的状态。之所以需要flush,是因为写缓冲使得文件处于一种不同步的状态,逻辑上一些数据已经写入了文件,但实际上这些数据仍然在缓冲中,如果此时程序意外地退出(发生异常或断电等),那么缓冲里的数据将没有机会写入文件,flush可以在一定程度上避免这样的情况发生。C语言支持两种缓冲,即行缓冲(Line Buffer)和全缓冲(Full Buffer)。全缓冲是经典的缓冲形式,除了用户手动调用fflush外,仅当缓冲满的时候,缓冲才会被自动flush掉。而行缓冲则比较特殊,这种缓冲仅用于文本文件,在输入输出遇到一个换行符时,缓冲就会被自动flush,因此叫行缓冲。
文本换行:在Windows的文本文件中,回车(换行)的存储方式是0x0D(用CR表示),0x0A(用LF表示)这两个字节,以C语言字符串表示则是”\r\n”。而在其它的一些操作系统中,回车的表示却有区别。例如,Linux/Unix,回车用\n表示;Mac OS,回车用\r表示;Windows,回车用\r\n表示。而在C语言中,回车始终用\n来表示,因此在以文本模式读取文件的时候,不同的操作需要将各自的回车符表示转换为C语言的形式,也就是,Linux/Unix,不做改变;Mac OS,每遇到\r就将其改为\n;Windows,将\r\n改为\n。
GitHub:https://github.com/fengbingchun/Messy_Test
相关文章:

iOS下JS与OC互相调用(三)--MessageHandler
使用WKWebView的时候,如果想要实现JS调用OC方法,除了拦截URL之外,还有一种简单的方式。那就是利用WKWebView的新特性MessageHandler来实现JS调用原生方法。 MessageHandler 是什么? WKWebView 初始化时,有一个参数叫…
北大教授张大庆:无线感知,让你变老也优雅
受访者 | 张大庆记者 | 胡巍巍出品 | CSDN(ID:CSDNnews)在国内高校中,北大的校庆日很特殊——5月4日。这一天,也是青年节。北大,是五四运动的策源地。100年来,“爱国、进步、民主、科学”的五四…

总结 20 个开发细节
2019独角兽企业重金招聘Python工程师标准>>> 1:提交到SVN的代码必须有提交备注,以便于以后查看。 2:如考虑页面缓存,可以在路径后增加随机数:url "&TimeS" Math.random();。 3:…

程序员的自我修养--链接、装载与库笔记:系统调用与API
系统调用(System Call)是应用程序(运行库也是应用程序的一部分)与操作系统内核之间的接口,它决定了应用程序是如何与内核打交道的。无论程序是直接进行系统调用,还是通过运行库,最终还是会到达系统调用这个层面上。 1. 系统调用介绍 什么是…

iOS下JS与OC互相调用(四)--JavaScriptCore
前面讲完拦截URL的方式实现JS与OC互相调用,终于到JavaScriptCore了。它是从iOS7开始加入的,用 Objective-C 把 WebKit 的 JavaScript 引擎封装了一下,提供了简单快捷的方式与JavaScript交互。 关于JavaScriptCore的使用有两篇很好的文章&…
围巾都这么黑科技了,是我见识少了
有一个永恒的话题:北方冷一点还是南方冷一点?答案是:哪里都冷!冬天最痛苦的莫过于走出空调房——刺骨的风直直的从领口处灌进去那叫一个“透心凉,心飞扬”缠了好几圈的大围巾却根本没什么保暖效果每当这时候࿰…

【教程】【FLEX】#004 反射机制
总结: 目前用到反射的主要有两个方法 1. getDefinitionByName //根据类名,返回对象(反射实例化对象) 2. describeType //根据对象,返回XML格式的属性,方法等信息(反射得到…

iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge
WebViewJavascriptBridge是一个有点年代的JS与OC交互的库,使用该库的著名应用还挺多的,目前这个库有7000star。我去翻看了它的第一版本已经是4年前了,在版本V4.1.4以及之前,该库只有一个类和一个js 的txt文件,所以旧版…

OpenCV代码提取:Windows上通过DShow获取Camera视频
在OpenCV 3.1中获取视频的模块在videoio(video input and output module)中,调用VideoCapture类接口,除了videoio模块外还依赖core、highgui、imgproc、imgcodecs四个模块,而OpenCV 2.4.13.6仅需要core、highgui、imgproc三个模块。3.1中的vi…
迁移学习与图神经网络“合力”模型:用DoT-GNN克服组重识别难题
作者 | Ziling Huang、Zheng Wang、Wei Hu、Chia-Wen Lin、Shin’ichi Satoh译者 | 刘畅编辑 | Jane出品 | AI科技大本营(ID:rgznai100)【导读】目前,大多数行人重识别(ReID)方法主要是从收集的单个人图像数…

struts2 select 默认选中
jsp:<s:select list"#{1:男,2:女}" name"sex"/> action:private String sex;sex属性有get/set方法。在业务方法中设置sex "2";select会默认选中。

通过Windows DShow获取设备名、支持的编解码及视频size列表实现
之前在https://blog.csdn.net/fengbingchun/article/details/102641967中介绍过通过DShow获取Camera视频的实现,即调用VideoCapture类。在OpenCV的VideoCapture类中并没有提供获取Camera设备列表、支持的编解码类型列表及支持的video size列表接口,这里基…
15篇论文全面概览BERT压缩方法
作者 | Mitchell A. Gordon译者 | 孙薇出品 | AI科技大本营(ID:rgznai100)模型压缩可减少受训神经网络的冗余——由于几乎没有BERT或者BERT-Large模型可用于GPU及智能手机上,这一点就非常有用了。另外,内存与推理速度的提高也能节…

iOS下JS与OC互相调用(七)--Cordova 基础
Cordova 简介 在介绍Cordova之前,必须先提一下PhoneGap。PhoneGap 是Nitobi软件公司2008年推出的一个框架,旨在弥补web 和iOS 之间的不足,使得web 和 iPhone SDK 之间的交互更容易。后来又加入了Android SDK 和BlackBerry SDK,再然…

在linux上MySQL的三种安装方式
安装MySQL的方式常见的有三种:方式一:rpm安装(1) 操作系统发行商提供的(2) MySQL官方提供的(版本更新,修复了更多常见BUG)www.mysql.com/downloads关于MySQL中rpm包类型的介绍:MySQL-client 客户端…

通过libjpeg-turbo实现对jpeg图像的解码
之前在https://blog.csdn.net/fengbingchun/article/details/89715416中介绍过通过libjpeg-turbo接口实现将数据编码或压缩成jpeg数据并通过FILE的fwrite接口将其直接保存成*.jpg图像,当时用的是libjpeg的接口,其实还可以使用turbojpeg api的接口即tjCom…
AI+大数据顶级技术盛会开幕在即,6.6折特惠票限时抢购
2019年12月5-7日,由中国计算机学会主办,CCF 大数据专家委员会承办,CSDN、中科天玑数据科技股份有限公司协办的中国大数据技术大会(BDTC 2019)将于北京长城饭店隆重举行。届时,超过百位顶尖技术专家将齐聚于…

iOS下JS与OC互相调用(八)--Cordova详解+实战
1.新建工程,添加Cordova 关键类 我这里用Xcode 8 新建了一个工程,叫 JS_OC_Cordova,然后将Cordova关键类添加进工程。 有哪些关键类呢? 这里添加config.xml 、Private 和 Public 两个文件夹里的所有文件。工程目录结构如下: 然后…

iOS多线程编程之NSOperation和NSOperationQueue的使用
使用 NSOperation的方式有两种, 一种是用定义好的两个子类: NSInvocationOperation 和 NSBlockOperation。 另一种是继承NSOperation 如果你也熟悉Java,NSOperation就和java.lang.Runnable接口很相似。和Java的Runnable一样,NSOpe…

Swift - 使用SwiftHTTP通过HTTPS进行网络请求,及证书的使用
(本文代码已升级至Swift3)一,证书的生成,以及服务器配置参考我前面写的这篇文章:Tomcat服务器配置https双向认证(使用keytool生成证书)文章详细介绍了HTTPS,SSL/TLS。还有使用key to…

Linux下通过v4l2获取视频设备名、支持的编解码及视频size列表实现
早些时候给出了在Windows下通过dshow获取视频设备信息的实现,包括获取视频设备名、获取每种视频设备支持的编解码格式列表、每种编解码格式支持的video size列表,见:https://blog.csdn.net/fengbingchun/article/details/102806822 下面给出…
12种主流编程语言输出“ Hello World ”,把我给难住了!
作为一名程序员,在初步学习编程想必都绕不开一个最为基础的入门级示例“Hello World”,那么,你真的了解各个语言“Hello World”的正确写法吗?在我们刚开始打开编程世界的时候,往往写的第一个程序都是简单的文本输出&a…

军哥lnmp一键安装包nginx支持pathinfo配置
ssh里执行:cat > /usr/local/nginx/conf/pathinfo.conf << EOF set $real_script_name $fastcgi_script_name; if ($fastcgi_script_name ~ "(.?\.php)(/.*)") { set $real_script_name $1; set $path_info $2; } fastcgi_param SCRIPT_FILENAM…

Effective STL 50条有效使用STL的经验笔记
Scott Meyers大师Effective三部曲:Effective C、More Effective C、Effective STL,这三本书出版已很多年,后来又出版了Effective Modern C。 Effective C的笔记见:https://blog.csdn.net/fengbingchun/article/details/102761542…

HTTPS网络加密双向验证-使用AFNetworking封装
1.首先使用OC封装请求头 #import <Foundation/Foundation.h> #import "AFNetworking.h" interface HttpsHandler : NSObject (AFHTTPSessionManager *)setHttpsMange; end 2.实现方法 (AFHTTPSessionManager *)setHttpsMange; { NSString *certFilePath […
30分钟搞定数据竞赛刷分夺冠神器LightGBM!
作者 | 梁云1991来源 | Python与算法之美(ID:Python_Ai_Road)【导读】LightGBM可以看成是XGBoost的升级加强版本,2017年经微软推出后,便成为各种数据竞赛中刷分夺冠的神兵利器。一,LightGBM和XGBoost对比正如其名字中的Light所蕴含…

js模块化例子
最近在看一本书,里面提到js的模块化,觉得很有必要,所以记录下来 Game.js /*** This is the main class that handles the game life cycle. It initializes* other components like Board and BoardModel, listens to the DOM events and* tr…

swift3.0提示框新用法
var alert: UIAlertController! alert UIAlertController(title: "提示", message: "添加照片", preferredStyle: UIAlertControllerStyle.actionSheet) let cleanAction UIAlertAction(title: "取消", style: UIAlertActionStyle.cancel,han…

FFmpeg在Windows上通过dshow编解码方式设置为mjpeg并实时显示测试代码
Windows上默认的内置摄像头一般支持两种编解码格式:rawvideo和mjpeg。在调用FFmpeg接口时默认的采用rawvideo。这里通过DirectShow实现为mjpeg进行编解码。 通过命令行调用FFmpeg可执行文件: (1). 可获取Windows上连接的视频设备,命令如下&…
基于深度学习的低光照图像增强方法总结(2017-2019)| CSDN博文精选
扫码参与CSDN“原力计划”作者 | hyk_1996来源 | CSDN博客精选之前在做光照对于高层视觉任务的影响的相关工作,看了不少基于深度学习的低光照增强(low-light enhancement)的文章[3,4,5,7,8,9,10],于是决定简单梳理一下。光照估计&…