Learn And Life.

Focus on php and open source.


  • Home

  • Archives

  • Tags
Learn And Life.

go defer and panic

Posted on 2018-07-28

go编程异常的处理是个很热的话题,不像java,可以使用try…catch来捕获异常,而是直接产生panic,中断程序的执行,
这样带来的一个问题就是,如果我在同一个目录下面,有几个协同工作的job,但是其中一个发生了panic,最终导致所有的job都会产生中断执行,
这明显从可靠性来说是很不合理的设计,因此go从设计上,强烈建议不要使用panic,除非你知道你自己是在做什么,
但是panic是避免不了的,那如何来处理panic的行为呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
func test(){
status := true
defer go func(){
if status {
println("panic happend")
  }
}()
panic("panic")
status = false
}
func main(){
test()
}

从上面的代码可以看出,如果status等于true,说明是panic发生了,反之,说明没有发生panic,这样就可以实现一些收尾的工作,
注意panic是不能跟defer交换的,那样defer就不会执行.

Learn And Life.

C结构体的理解

Posted on 2017-09-07

先看看基础类型,给出下面的一段代码

1
2
3
4
5
void swap(int *a, int *b){
int temp = a;
a = b;
b = temp;
}

这段代码实现了两个整数的交换,再看看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<stdio.h>
#include<stdlib.h>
typedef struct _Num{
int a;
int b;
}Num;
int main(int argc, char **argv){
int addr = 0x40000000;
Num *num = (Num*)addr;
printf("%p %p\n", &(num->a), &(num->b));
printf("%p %p a:%d b:%d\n", &(num->a), &(num->b), num->a, num->b);
return 0;
}

运行的结果如下:

1
2
3
4
root@chenjingxiu:~/project# gcc -o testStruct testStruct.c
root@chenjingxiu:~/project# ./testStruct
0x40000000 0x40000004
段错误 (核心已转储)

可以看到虽然指定了结构体的地址,但是当要访问其成员变量a和b时发生了段错误,说明a和b并没有实际分配内存空间,至于为什么会出现a的地址0x40000000和b的地址0x40000004,实际上这里只是根据类型计算出偏移量,也就是说结构体就像数组一样,结构体名称本身就是一个指针,再看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode
{
int data;
struct LNode *next;
}LNode;
//必须返回LNode
LNode* InitLinkListOne(LNode *L)
{
L=(LNode *)malloc(sizeof(LNode));
L->data=0;
L->next=NULL;
return L;
}
//这里没有返回值
void InitLinkListTwo(LNode **L)
{
(*L)=(LNode *)malloc(sizeof(LNode));
(*L)->data=0;
(*L)->next=NULL;
}
int main()
{
LNode *L1=NULL;
InitLinkListTwo(&L1); //这里传的指针的引用
printf("node:%p data:%d next:%p\n",L1, L1->data, L1->next);
LNode *L2=NULL;
L2 = InitLinkListOne(L2); //这里传的指针
printf("node:%p data:%d next:%p\n",L2, L2->data, L2->next);
return 0;
}

InitLinkListOne虽然传进去的是一个指针,但是只是结构体本身,而InitLinkListTwo传进去的参数才是真正的结构体的指针,也就是InitLinkListOne传的是值,而InitLinkListTwo传的是引用,所以对于结构体的操作,需要注意指针的使用,到底是对引用操作还是对值的操作,这影响到你接口的设计。
结果如下:

1
2
node:0x815e008 data:0 next:(nil)
node:0x815e420 data:0 next:(nil)

Learn And Life.

nginx基础类型

Posted on 2017-07-21

nginx基础类型

nginx-data-type

Learn And Life.

PHP之线程资源

Posted on 2017-07-21

扩展全局资源和内核全局资源

在写php扩展的时候,我们需要使用一些线程全局的资源该如何操作呢?拿yaf来看看

1
2
3
4
5
6
7
8
9
10
11
#ifdef ZTS
#include "TSRM.h"
#endif
#ifdef ZTS
#define YAF_G(v) TSRMG(yaf_globals_id, zend_yaf_globals *, v)
#else
#define YAF_G(v) (yaf_globals.v)
#endif
extern ZEND_DECLARE_MODULE_GLOBALS(yaf);

这里定义了宏YAF_G和ZEND_DECLARE_MODULE_GLOBALS,我们先看看ZEND_DECLARE_MODULE_GLOBALS

1
2
3
4
5
6
7
8
9
#ifdef ZTS
#define ZEND_DECLARE_MODULE_GLOBALS(module_name) \
ts_rsrc_id module_name##_globals_id;
...
#else
#define ZEND_DECLARE_MODULE_GLOBALS(module_name) \
zend_##module_name##_globals module_name##_globals;
...
#endif

可以看到在线程安全的环境下,ZEND_DECLARE_MODULE_GLOBALS(module_name)被定义成为ts_rsrc_id类型的,再来看看TSRMG

1
#define TSRMG(id, type, element) (TSRMG_BULK(id, type)->element)

其中TSRMG_BULK

1
#define TSRMG_BULK(id, type) ((type) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(id)])

TSRM_UNSHUFFLE_RSRC_ID是对id进行运算,暂且不管,可以看到最终的调用者tsrm_get_ls_cache(),同样还是来看看这个函数和其内部调用的tsrm_tls_get()函数

1
2
3
4
5
6
TSRM_API void *tsrm_get_ls_cache(void)
{/*{{{*/
return tsrm_tls_get();
}/*}}}*
# define tsrm_tls_get() pthread_getspecific(tls_key)

哦, 看到这里,你会发现pthread_getspecific(),一定存在pthread_setspecific(),我们再来看看pthread_setspecific()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# define tsrm_tls_set(what) pthread_setspecific(tls_key, (void*)(what))
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
{
.....
(*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry));
(*thread_resources_ptr)->storage = NULL;
if (id_count > 0) {
(*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
}
(*thread_resources_ptr)->count = id_count;
(*thread_resources_ptr)->thread_id = thread_id;
(*thread_resources_ptr)->next = NULL;
/* Set thread local storage to this new thread resources structure */
tsrm_tls_set(*thread_resources_ptr);
if (tsrm_new_thread_begin_handler) {
tsrm_new_thread_begin_handler(thread_id);
}
for (i=0; i<id_count; i++) {
if (resource_types_table[i].done) {
(*thread_resources_ptr)->storage[i] = NULL;
} else
{
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
if (resource_types_table[i].ctor) {
resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]);
}
}
}
if (tsrm_new_thread_end_handler) {
tsrm_new_thread_end_handler(thread_id);
}
tsrm_mutex_unlock(tsmm_mutex);
......
}

可以看到在分配一个新的资源的时候,会调用tsrm_tls_set(*thread_resources_ptr),而thread_resources_ptr是一个指向线程资源对象tsrm_tls_entry的数组指针,tsrm_tls_entry对象的结构体如下

1
2
3
4
5
6
struct _tsrm_tls_entry {
void **storage;
int count;
THREAD_T thread_id;
tsrm_tls_entry *next;
};

可以看到真正资源数据存储在storage指针数组里,那么storage存储的是什么数据呢?

1
resource_types_table[i].ctor((\*thread_resources_ptr)->storage[i]);

这里调用了每种资源的构造方法,看看yaf是如何来注册其全局资源对象的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
ZEND_BEGIN_MODULE_GLOBALS(yaf)
zend_string *ext;
zend_string *base_uri;
zend_string *directory;
zend_string *local_library;
zend_string *local_namespaces;
zend_string *view_directory;
zend_string *view_ext;
zend_string *default_module;
zend_string *default_controller;
zend_string *default_action;
zend_string *bootstrap;
char *global_library;
char *environ_name;
char *name_separator;
size_t name_separator_len;
zend_bool lowcase_path;
zend_bool use_spl_autoload;
zend_bool throw_exception;
zend_bool action_prefer;
zend_bool name_suffix;
zend_bool autoload_started;
zend_bool running;
zend_bool in_exception;
zend_bool catch_exception;
zend_bool suppressing_warning;
/\* {{{ This only effects internally \*/
zend_bool st_compatible;
/\* }}} \*/
long forward_limit;
HashTable *configs;
zval modules;
zval *default_route;
zval active_ini_file_section;
zval *ini_wanted_section;
uint parsing_flag;
zend_bool use_namespace;
ZEND_END_MODULE_GLOBALS(yaf)
#define ZEND_BEGIN_MODULE_GLOBALS(module_name) \
typedef struct _zend_##module_name##_globals {
#define ZEND_END_MODULE_GLOBALS(module_name) \
} zend_##module_name##_globals;

可以看到它申明了zen_yaf_globals结构体对象,而其实yaf是不支持多线程环境下运行的,zen_yaf_globals只是在扩展内部使用

1
ZEND_DECLARE_MODULE_GLOBALS(yaf);

但是cgi, php-fpm, apache2handler是支持多线程的

1
2
3
4
5
root@chenjingxiu:~/php-src# egrep -Ern "ts_allocate_id"|grep sapi
main/SAPI.c:84: ts_allocate_id(&sapi_globals_id, sizeof(sapi_globals_struct), (ts_allocate_ctor) sapi_globals_ctor, (ts_allocate_dtor) sapi_globals_dtor);
sapi/cgi/cgi_main.c:1801: ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
sapi/fpm/fpm/fpm_main.c:1500: ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
sapi/apache2handler/php_functions.c:543: ts_allocate_id(&php_apache2_info_id, sizeof(php_apache2_info_struct), (ts_allocate_ctor) NULL, NULL);

这些模块中都会定义自己的析构方法,比如cgi和fpm都是php_cgi_globals_ctor,全局资源类型都是php_cgi_globals_struct

线程全局资源原理

先看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<stdio.h>
#include<pthread.h>
#include<string.h>
/**
* 线程内模块数据共享
* 功能: 共享全局唯一标识
*/
pthread_key_t global_id_key;
typedef struct global_id
{
char *name;
int len;
} global_id;
/* 读共享数据 */
void *get_cache_id()
{
return pthread_getspecific(global_id_key);
}
/* 写共享数据 */
void *thread_global_run(void *args)
{
pthread_setspecific(global_id_key,args);
#ifdef DEBUG
global_id *id = (global_id*)pthread_getspecific(global_id_key);
printf("params | name:%s len:%d\n", id->name, id->len);
global_id *cache_id = (global_id *)get_cache_id();
printf("the result | name:%s len:%d\n", cache_id->name, cache_id->len);
#endif
return pthread_getspecific(global_id_key);
}
int main(int argv, char **argc)
{
pthread_t p;
global_id id = {"kivmi", 5};
pthread_key_create(&global_id_key, NULL);
pthread_create(&p, NULL, thread_global_run, &id);
pthread_join(p, NULL);
return 0;
}

从中可以看到pthread_setspecific会将引用类型的参数临时存起来,在thread_global_run函数里可以做一些处理,当然DEBUG模块可以处理相当复杂的业务,这里只做了简单的处理,比如其它模块间资源的共享处理,看下运行结果

1
2
3
4
root@chenjingxiu:~/project# gcc specific.c -o specific -lpthread -DDEBUG
root@chenjingxiu:~/project# ./specific
params | name:kivmi len:5
the result | name:kivmi len:5

当然,这里只是关于线程资源的最简单的方式,php中线程全局资源的管理由于涉及到各种资源类型的管理,比这复杂的多!

Learn And Life.

PHP Graph

Posted on 2017-07-12

PHP内存模型

1
2
jemalloc
tcmalloc

PHP Memory Management

memory management
php内存管理分为3层
memory arch
当然你也可以通过setenv(“USE_ZEND_ALLOC”) = 0直接使用系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void alloc_globals_ctor(zend_alloc_globals *alloc_globals)
{
#if ZEND_MM_CUSTOM
char *tmp = getenv("USE_ZEND_ALLOC");
if (tmp && !zend_atoi(tmp, 0)) {
alloc_globals->mm_heap = malloc(sizeof(zend_mm_heap));
memset(alloc_globals->mm_heap, 0, sizeof(zend_mm_heap));
alloc_globals->mm_heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_STD;
alloc_globals->mm_heap->custom_heap.std._malloc = __zend_malloc;
alloc_globals->mm_heap->custom_heap.std._free = free;
alloc_globals->mm_heap->custom_heap.std._realloc = __zend_realloc;
return;
}
#endif
#ifdef MAP_HUGETLB
tmp = getenv("USE_ZEND_ALLOC_HUGE_PAGES");
if (tmp && zend_atoi(tmp, 0)) {
zend_mm_use_huge_pages = 1;
}
#endif
ZEND_TSRMLS_CACHE_UPDATE();
alloc_globals->mm_heap = zend_mm_init();
}

从alloc_globals_ctor可以看到,当关闭USE_ZEND_ALLOC之后,php会通过malloc来管理head的内存分配,而不是mmap的方式。同时,使用malloc分配内存的方式也称为持久的内存分配方式,php代码中也涉及相关的内容

持久内存分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define pemalloc(size, persistent) ((persistent)?__zend_malloc(size):emalloc(size))
#define safe_pemalloc(nmemb, size, offset, persistent) ((persistent)?_safe_malloc(nmemb, size, offset):safe_emalloc(nmemb, size, offset))
#define pefree(ptr, persistent) ((persistent)?free(ptr):efree(ptr))
#define pefree_size(ptr, size, persistent) ((persistent)?free(ptr):efree_size(ptr, size))
#define pecalloc(nmemb, size, persistent) ((persistent)?__zend_calloc((nmemb), (size)):ecalloc((nmemb), (size)))
#define perealloc(ptr, size, persistent) ((persistent)?__zend_realloc((ptr), (size)):erealloc((ptr), (size)))
#define perealloc2(ptr, size, copy_size, persistent) ((persistent)?__zend_realloc((ptr), (size)):erealloc2((ptr), (size), (copy_size)))
#define safe_perealloc(ptr, nmemb, size, offset, persistent) ((persistent)?_safe_realloc((ptr), (nmemb), (size), (offset)):safe_erealloc((ptr), (nmemb), (size), (offset)))
#define perealloc_recoverable(ptr, size, persistent) ((persistent)?realloc((ptr), (size)):erealloc_recoverable((ptr), (size)))
#define perealloc2_recoverable(ptr, size, persistent) ((persistent)?realloc((ptr), (size)):erealloc2_recoverable((ptr), (size), (copy_size)))
#define pestrdup(s, persistent) ((persistent)?strdup(s):estrdup(s))
#define pestrndup(s, length, persistent) ((persistent)?zend_strndup((s),(length)):estrndup((s),(length)))
#define pemalloc_rel(size, persistent) ((persistent)?__zend_malloc(size):emalloc_rel(size))
#define pefree_rel(ptr, persistent) ((persistent)?free(ptr):efree_rel(ptr))
#define pecalloc_rel(nmemb, size, persistent) ((persistent)?__zend_calloc((nmemb), (size)):ecalloc_rel((nmemb), (size)))
#define perealloc_rel(ptr, size, persistent) ((persistent)?__zend_realloc((ptr), (size)):erealloc_rel((ptr), (size)))
#define perealloc2_rel(ptr, size, copy_size, persistent) ((persistent)?__zend_realloc((ptr), (size)):erealloc2_rel((ptr), (size), (copy_size)))
#define perealloc_recoverable_rel(ptr, size, persistent) ((persistent)?realloc((ptr), (size)):erealloc_recoverable_rel((ptr), (size)))
#define perealloc2_recoverable_rel(ptr, size, copy_size, persistent) ((persistent)?realloc((ptr), (size)):erealloc2_recoverable_rel((ptr), (size), (copy_size)))
#define pestrdup_rel(s, persistent) ((persistent)?strdup(s):estrdup_rel(s))

我们可以拿几个来看看

持久化内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ZEND_API void * __zend_malloc(size_t len)
{
void *tmp = malloc(len);
if (EXPECTED(tmp || !len)) {
return tmp;
}
zend_out_of_memory();
}
ZEND_API void* ZEND_FASTCALL _safe_malloc(size_t nmemb, size_t size, size_t offset)
{
return pemalloc(zend_safe_address_guarded(nmemb, size, offset), 1);
}
ZEND_API void * __zend_calloc(size_t nmemb, size_t len)
{
void *tmp = _safe_malloc(nmemb, len, 0);
memset(tmp, 0, nmemb * len);
return tmp;
}

可以看到, 无论是_safe_malloc,还是zend_calloc,持久化方式最终调用的都是zend_malloc,而zend_malloc内部调用的malloc来进行内存的分配,其中zend_calloc和_safe_malloc的区别仅仅在于是否初始化内存空间为0

非持久化内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#define emalloc(size) _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define emalloc_large(size) _emalloc_large((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define emalloc_huge(size) _emalloc_huge((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define safe_emalloc(nmemb, size, offset) _safe_emalloc((nmemb), (size), (offset) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define efree(ptr) _efree((ptr) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define efree_large(ptr) _efree_large((ptr) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define efree_huge(ptr) _efree_huge((ptr) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define ecalloc(nmemb, size) _ecalloc((nmemb), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define erealloc(ptr, size) _erealloc((ptr), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define erealloc2(ptr, size, copy_size) _erealloc2((ptr), (size), (copy_size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define safe_erealloc(ptr, nmemb, size, offset) _safe_erealloc((ptr), (nmemb), (size), (offset) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define erealloc_recoverable(ptr, size) _erealloc((ptr), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define erealloc2_recoverable(ptr, size, copy_size) _erealloc2((ptr), (size), (copy_size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define estrdup(s) _estrdup((s) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define estrndup(s, length) _estrndup((s), (length) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
#define zend_mem_block_size(ptr) _zend_mem_block_size((ptr) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC)
/* Relay wrapper macros */
#define emalloc_rel(size) _emalloc((size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define safe_emalloc_rel(nmemb, size, offset) _safe_emalloc((nmemb), (size), (offset) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define efree_rel(ptr) _efree((ptr) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define ecalloc_rel(nmemb, size) _ecalloc((nmemb), (size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define erealloc_rel(ptr, size) _erealloc((ptr), (size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define erealloc2_rel(ptr, size, copy_size) _erealloc2((ptr), (size), (copy_size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define erealloc_recoverable_rel(ptr, size) _erealloc((ptr), (size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define erealloc2_recoverable_rel(ptr, size, copy_size) _erealloc2((ptr), (size), (copy_size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define safe_erealloc_rel(ptr, nmemb, size, offset) _safe_erealloc((ptr), (nmemb), (size), (offset) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define estrdup_rel(s) _estrdup((s) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define estrndup_rel(s, length) _estrndup((s), (length) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
#define zend_mem_block_size_rel(ptr) _zend_mem_block_size((ptr) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
ZEND_API void* ZEND_FASTCALL _emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
#if ZEND_MM_CUSTOM
if (UNEXPECTED(AG(mm_heap)->use_custom_heap)) {
if (ZEND_DEBUG && AG(mm_heap)->use_custom_heap == ZEND_MM_CUSTOM_HEAP_DEBUG) {
return AG(mm_heap)->custom_heap.debug._malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
} else {
return AG(mm_heap)->custom_heap.std._malloc(size);
}
}
#endif
return zend_mm_alloc_heap(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}

这里可以看到,非持久化方式,使用zend_mm_alloc_heap,来分配heap的空间,其中根据size的不同有3中分配方式

1
2
3
4
5
6
7
8
9
10
11
12
13
...........
if (size <= ZEND_MM_MAX_SMALL_SIZE) {
ptr = zend_mm_alloc_small(heap, size, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
............
} else if (size <= ZEND_MM_MAX_LARGE_SIZE) {
ptr = zend_mm_alloc_large(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
............
return ptr;
} else {
............
return zend_mm_alloc_huge(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}
..........

也就是
(0, ZEND_MM_MAX_SMALL_SIZE]
(ZEND_MM_MAX_SMALL_SIZE, ZEND_MM_MAX_LARGE_SIZE]
(ZEND_MM_MAX_LARGE_SIZE, ZEND_MM_PAGE_SIZE]
这里还需要注意的是storage层,并不是指zend_mm_storage对象,需要区分开,从源码看zend_mm_storage只会跟PHPDBG有关,PHPDBG是什么呢?

PHPDBG

PHPDBG是一个PHP的SAPI模块,可以在不用修改代码和不影响性能的情况下控制PHP的运行环境。PHPDBG的目标是成为一个轻量级、强大、易用的PHP调试平台。可以在PHP5.4和之上版本中使用。在php5.6和之上版本将内部集成。

主要功能:

  • 单步调试
  • 灵活的下断点方式(类方法、函数、文件:行、内存地址、opcode)
  • 可直接调用php的eval
  • 可以查看当前执行的代码
  • 用户空间API(userland/user space)
  • 方便集成
  • 支持指定php配置文件
  • JIT全局变量
  • readline支持(可选),终端操作更方便
  • 远程debug,使用java GUI
  • 操作简便(具体看help)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@chenjingxiu:~# phptest/bin/phpdbg
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
prompt> exec a.php
[Set execution context: /home/chenjingxiu/a.php]
[Successful compilation of /home/chenjingxiu/a.php]
prompt> b fun
[Breakpoint #0 added at fun]
prompt> r
[Script ended normally]
[Mon Jul 17 19:23:36 2017] Script: '-'
/home/chenjingxiu/php-src/sapi/phpdbg/phpdbg.c(1799) : Freeing 0xb7204018 (1 bytes), script=-
=== Total 1 memory leaks detected ===
prompt>

很像gdb是不?转回正题

php内存管理

Learn And Life.

Php垃圾回收

Posted on 2017-07-07

zend_gc_globals结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
typedef struct _zend_gc_globals {
zend_bool gc_enabled;
zend_bool gc_active;
zend_bool gc_full;
gc_root_buffer *buf; /* preallocated arrays of buffers */
gc_root_buffer roots; /* list of possible roots of cycles */
gc_root_buffer *unused; /* list of unused buffers */
gc_root_buffer *first_unused; /* pointer to first unused buffer */
gc_root_buffer *last_unused; /* pointer to last unused buffer */
gc_root_buffer to_free; /* list to free */
gc_root_buffer *next_to_free;
uint32_t gc_runs;
uint32_t collected;
#if GC_BENCH
uint32_t root_buf_length;
uint32_t root_buf_peak;
uint32_t zval_possible_root;
uint32_t zval_buffered;
uint32_t zval_remove_from_buffer;
uint32_t zval_marked_grey;
#endif
gc_additional_buffer *additional_buffer;
} zend_gc_globals;

说明:
gc_enabled 是否开启了GC
gc_active 是否GC正在运行
gc_full GC缓冲区是否已经满le
buf 预分配总的缓冲区大小
roots 可能需要回收对象列表
unused 未使用的缓存区
first_unused 未使用的缓冲区的第一个元素
last_unused 未使用的缓冲区的最后一个元素
to_free 垃圾列表
next_to_free 指一个垃圾列表元素
gc_runs GC运行次数
collected 是否垃圾收集完成
additional_buffer 其它缓冲区

GC初始化gc_globals

gc_globals_ctor -> ts_allocate_id -> gc_globals_ctor_ex

```

ZEND_API void gc_globals_ctor(void)
{

#ifdef ZTS
ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor);

#else
gc_globals_ctor_ex(&gc_globals);

#endif
}

参数说明:
rsrc_id 全局资源id (int型)
size 资源大小
ctor 构造器
dtor 析构器

TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
{
int i;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, “Obtaining a new resource id, %d bytes”, size));
/
加互斥锁 /
tsrm_mutex_lock(tsmm_mutex);
/
生成全局资源id / rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, “Obtained resource id %d”, rsrc_id));
/
注册新的资源到全局资源表 /
if (resource_types_table_size < id_count) { /
如果资源表的大小太小,则调整资源表的大小 /
/
扩展资源表的内存空间 /
resource_types_table = (tsrm_resource_type
) realloc(resource_types_table, sizeof(tsrm_resource_type)id_count);
/
如果内存分配失败,解锁并返回 /
if (!resource_types_table) {
tsrm_mutex_unlock(tsmm_mutex);
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, “Unable to allocate storage for resource”));
rsrc_id = 0;
return 0;
}
/ 如果分配成功,则修改资源表的大小 /
resource_types_table_size = id_count;
}
/ 注册全局资源对象 /
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(rsrc_id)].size = size;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(
rsrc_id)].ctor = ctor;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(rsrc_id)].dtor = dtor;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(
rsrc_id)].done = 0;
/ 遍历线程表,同步全局资源到各个线程对象 /
for (i=0; icount < id_count) {
int j;
/ 扩展线程资源数据内存空间 /
p->storage = (void ) realloc(p->storage, sizeof(void )id_count);
/
新增资源分配内存空间 /
for (j=p->count; jstorage[j] = (void
) malloc(resource_types_table[j].size);
if (resource_types_table[j].ctor) {
/ 调用构造器,初始化gc_globals对象 /
resource_types_table[j].ctor(p->storage[j]);
}
}
/ 修改线程资源数量 /
p->count = id_count;
}
/ 同步下一个线程对象 /
p = p->next;
}
}
/ 资源初始化完毕,解锁 /
tsrm_mutex_unlock(tsmm_mutex);
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, “Successfully allocated new resource id %d”, rsrc_id));
/
全局资源id /
return
rsrc_id;
}

/ 初始化gc_globals对象 /
static void gc_globals_ctor_ex(zend_gc_globals *gc_globals)
{
gc_globals->gc_enabled = 0;
gc_globals->gc_active = 0;

gc_globals->buf = NULL;

gc_globals->roots.next = &gc_globals->roots;
gc_globals->roots.prev = &gc_globals->roots;
gc_globals->unused = NULL;
gc_globals->next_to_free = NULL;

gc_globals->to_free.next = &gc_globals->to_free;
gc_globals->to_free.prev = &gc_globals->to_free;

gc_globals->gc_runs = 0;
gc_globals->collected = 0;

gc_globals->additional_buffer = NULL;

#if GC_BENCH
gc_globals->root_buf_length = 0;
gc_globals->root_buf_peak = 0;
gc_globals->zval_possible_root = 0;
gc_globals->zval_buffered = 0;
gc_globals->zval_remove_from_buffer = 0;
gc_globals->zval_marked_grey = 0;

#endif
}

Learn And Life.

Mysql in , not in, != 索引和插入

Posted on 2017-07-05

今天来看下mysql中的in,not in索引的使用,首先创建一张表

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE `User` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(100) NOT NULL DEFAULT '',
`age` int(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_username` (`username`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
insert into User values(1,"a",10);
insert into User values(2,"b",20);
insert into User values(3,"c",30);
insert into User values(4,"d",40);
insert into User values(5,"e",50);

使用explain查看索引的使用情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
mysql> explain select * from User where username in ("a","b") order by username;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | User | NULL | range | idx_username | idx_username | 302 | NULL | 2 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from User where username in("a");
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | User | NULL | ref | idx_username | idx_username | 302 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from User where username not in ("a","b") order by username;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | User | NULL | ALL | idx_username | NULL | NULL | NULL | 5 | 100.00 | Using where; Using filesort |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.02 sec)
mysql> explain select * from User where username !="a";
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | User | NULL | ALL | idx_username | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

可以看到在MYISAM存储引擎下,in无论怎样都使用了索引,而not int没有使用索引,!=没有使用索引,现在将MYISAM换成INNODB

1
alter table User engine=innodb

然后执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
mysql> explain select * from User where username in("a");
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | User | NULL | ref | idx_username | idx_username | 302 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.06 sec)
mysql> explain select * from User where username in ("a","b");
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | User | NULL | ALL | idx_username | NULL | NULL | NULL | 5 | 40.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
mysql> explain select * from User where username not in ("a","b");
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | User | NULL | ALL | idx_username | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from User where username !="a";
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | User | NULL | ALL | idx_username | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

结构发现INNODB存储引擎,只有当in中只有一个元素时,才会使用索引,其它情况都不使用索引,而not in不会使用索引,!=也不会使用索引,也就是INNODB中in中只有一个元素的时候跟等号操作符一样, order by 不会使用索引!

插入

1
2
insert into tb (...) values(...),(...)...;
insert into tb (...) values (...);insert into tb (...) values (...);...

当要插入10w条那个快?当然是第一个!

Learn And Life.

PHP中的线程

Posted on 2017-07-04

线程安全ZTS

这个宏是需要在编译的时候指定了,才会生成的。其中configure文件中有下面的一行

1
$as_echo "#define ZTS 1" >>confdefs.h

说明只有当TSRM被启用的时候,就会定义这个名为ZTS的宏

线程安全资源管理器(Thread Safe Resource Manager)TSRM

在写PHP扩展的时候经常使用的宏,先看下相关的宏的定义

1
2
3
4
5
6
7
8
9
10
11
#ifdef ZTS
#define TSRMLS_D void ***tsrm_ls
#define TSRMLS_DC , TSRMLS_D
#define TSRMLS_C tsrm_ls
#define TSRMLS_CC , TSRMLS_C
#else
#define TSRMLS_D void
#define TSRMLS_DC
#define TSRMLS_C
#define TSRMLS_CC
#endif

发现这些宏都跟tsrm_ls是相关的,找到sapi/fpm/fpm/fpm_main.c中1583行和1612行

1
2
void ***tsrm_ls;
tsrm_ls = ts_resource(0);

TSRM/TSRM.h文件112行

1
#define ts_resource(id) ts_resource_ex(id, NULL)

最终定位到ts_resource_ex这个函数,现在看下这个函数是如何实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/* 线程指针表头指针 */
static tsrm_tls_entry **tsrm_tls_table = NULL
/* 线程数量 */
static int tsrm_tls_table_size;
/* 初始化线程表 */
tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *));
typedef struct _tsrm_tls_entry tsrm_tls_entry;
/* 线程结构体 */
struct _tsrm_tls_entry {
void **storage; // 资源指针、就是指向自己的公共资源内存区
int count; // 资源数、就是 PHP内核 + 扩展模块 共注册了多少公共资源
THREAD_T thread_id; // 线程id
tsrm_tls_entry *next; // 指向下一个线程指针,当前每一个线程指针都存在一个线程指针表里(类似于hash表),这个next可以理解成是hash冲突链式解决法
};
/* 资源类型 */
typedef struct {
size_t size; // 资源大小
ts_allocate_ctor ctor; // 构造函数指针、在给每一个线程创建该资源的时候会调用一下当前ctor指针
ts_allocate_dtor dtor; // 析构函数指针、释放该资源的时候会调用一下当前dtor指针
int done; // 资源是否已经销毁 0:正常 1:已销毁
} tsrm_resource_type;
static tsrm_resource_type \*resource_types_table=NULL; // 公共资源类型表头指针
static int resource_types_table_size; // 当前公共资源类型数量
/* 全局资源 注册资源时给每一个资源生成一个唯一id,获取时根据该唯一id进行获取 */
/* 每个线程都会把当前注册的所有公共资源全部copy一份过来,也就是一个malloc()一个大数组,这个资源id就是该数组的索引 */
/* 然后需要公共资源时,通过资源id来读取即可 */
typedef int ts_rsrc_id;
static ts_rsrc_id id_count;
/*#################### 线程管理 #####################*/
/*# 1. tsrm_startup() #*/
/*# 2. ts_allocate_id() #*/
/*# 3. ts_reaource(id) #*/
/*# 4. allocate_new_resource() #*/
/*# 5. tsrm_shutdown() #*/
/*####################################################*/
//1. 内核初始化 tsrm_startup() Startup TSRM (call once for the entire process)
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename)
{
#if defined(GNUPTH)
pth_init();
#elif defined(PTHREADS)
pthread_key_create( &tls_key, 0 );
#elif defined(TSRM_ST)
st_init();
st_key_create(&tls_key, 0);
#elif defined(TSRM_WIN32)
tls_key = TlsAlloc();
#elif defined(BETHREADS)
tls_key = tls_allocate();
#endif
/* ensure singleton */
in_main_thread = 1;
tsrm_error_file = stderr;
tsrm_error_set(debug_level, debug_filename);
tsrm_tls_table_size = expected_threads;
tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *));
if (!tsrm_tls_table) {
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate TLS table"));
return 0;
}
id_count=0;
resource_types_table_size = expected_resources;
resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type));
if (!resource_types_table) {
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate resource types table"));
free(tsrm_tls_table);
tsrm_tls_table = NULL;
return 0;
}
tsmm_mutex = tsrm_mutex_alloc();
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected threads, %d expected resources", expected_threads, expected_resources));
return 1;
}
//2. 注册公共资源 ts_allocate_id() allocates a new thread-safe-resource id
TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
{
int i;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size));
tsrm_mutex_lock(tsmm_mutex);
/* obtain a resource id */
*rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
/* store the new resource type in the resource sizes table */
if (resource_types_table_size < id_count) {
resource_types_table = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
if (!resource_types_table) {
tsrm_mutex_unlock(tsmm_mutex);
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
*rsrc_id = 0;
return 0;
}
resource_types_table_size = id_count;
}
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
/* enlarge the arrays for the already active threads */
for (i=0; i<tsrm_tls_table_size; i++) {
tsrm_tls_entry *p = tsrm_tls_table[i];
while (p) {
if (p->count < id_count) {
int j;
p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
for (j=p->count; j<id_count; j++) {
p->storage[j] = (void *) malloc(resource_types_table[j].size);
if (resource_types_table[j].ctor) {
resource_types_table[j].ctor(p->storage[j]);
}
}
p->count = id_count;
}
p = p->next;
}
}
tsrm_mutex_unlock(tsmm_mutex);
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
return *rsrc_id;
}
// 3. 读取公共资源 ts_reaource(id) fetches the requested resource for the current thread
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
{
THREAD_T thread_id;
int hash_value;
tsrm_tls_entry *thread_resources;
if (!th_id) {
/* Fast path for looking up the resources for the current
* thread. Its used by just about every call to
* ts_resource_ex(). This avoids the need for a mutex lock
* and our hashtable lookup.
*/
thread_resources = tsrm_tls_get();
if (thread_resources) {
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d", id, (long) thread_resources->thread_id));
/* Read a specific resource from the thread's resources.
* This is called outside of a mutex, so have to be aware about external
* changes to the structure as we read it.
*/
TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
}
thread_id = tsrm_thread_id();
} else {
thread_id = *th_id;
}
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id));
tsrm_mutex_lock(tsmm_mutex);
hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
thread_resources = tsrm_tls_table[hash_value];
if (!thread_resources) {
allocate_new_resource(&tsrm_tls_table[hash_value], thread_id);
return ts_resource_ex(id, &thread_id);
} else {
do {
if (thread_resources->thread_id == thread_id) {
break;
}
if (thread_resources->next) {
thread_resources = thread_resources->next;
} else {
allocate_new_resource(&thread_resources->next, thread_id);
return ts_resource_ex(id, &thread_id);
/*
* thread_resources = thread_resources->next;
* break;
*/
}
} while (thread_resources);
}
tsrm_mutex_unlock(tsmm_mutex);
/* Read a specific resource from the thread's resources.
* This is called outside of a mutex, so have to be aware about external
* changes to the structure as we read it.
*/
TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
}
// 初始化当前线程 allocate_new_resource()
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
{
int i;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id));
(*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry));
(*thread_resources_ptr)->storage = NULL;
if (id_count > 0) {
(*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
}
(*thread_resources_ptr)->count = id_count;
(*thread_resources_ptr)->thread_id = thread_id;
(*thread_resources_ptr)->next = NULL;
/* Set thread local storage to this new thread resources structure */
tsrm_tls_set(*thread_resources_ptr);
if (tsrm_new_thread_begin_handler) {
tsrm_new_thread_begin_handler(thread_id);
}
for (i=0; i<id_count; i++) {
if (resource_types_table[i].done) {
(*thread_resources_ptr)->storage[i] = NULL;
} else
{
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
if (resource_types_table[i].ctor) {
resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]);
}
}
}
if (tsrm_new_thread_end_handler) {
tsrm_new_thread_end_handler(thread_id);
}
tsrm_mutex_unlock(tsmm_mutex);
}
// 注销线程资源tsrm_shutdown(void) Shutdown TSRM (call once for the entire process)
TSRM_API void tsrm_shutdown(void)
{
int i;
if (!in_main_thread) {
/* ensure singleton */
return;
}
if (tsrm_tls_table) {
for (i=0; i<tsrm_tls_table_size; i++) {
tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;
while (p) {
int j;
next_p = p->next;
for (j=0; j<p->count; j++) {
if (p->storage[j]) {
if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) {
resource_types_table[j].dtor(p->storage[j]);
}
free(p->storage[j]);
}
}
free(p->storage);
free(p);
p = next_p;
}
}
free(tsrm_tls_table);
tsrm_tls_table = NULL;
}
if (resource_types_table) {
free(resource_types_table);
resource_types_table=NULL;
}
tsrm_mutex_free(tsmm_mutex);
tsmm_mutex = NULL;
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM"));
if (tsrm_error_file!=stderr) {
fclose(tsrm_error_file);
}
#if defined(GNUPTH)
pth_kill();
#elif defined(PTHREADS)
pthread_setspecific(tls_key, 0);
pthread_key_delete(tls_key);
#elif defined(TSRM_WIN32)
TlsFree(tls_key);
#endif
if (tsrm_shutdown_handler) {
tsrm_shutdown_handler();
}
tsrm_new_thread_begin_handler = NULL;
tsrm_new_thread_end_handler = NULL;
tsrm_shutdown_handler = NULL;
}

Learn And Life.

Thread local storage

Posted on 2017-07-03

数据类型

首先看下数据类型的分类,根据是单线程还是多线程,可以分为以下几类

1
2
3
全局数据
局部数据
线程特定数据(TSD)

全局数据和局部数据比较好理解,但是线程特定数据是什么?独立于线程的数据,也就是说只有某个线程可以访问,其它线程不可以访问,实现某个线程内,函数调用之间的共享,也就是线程全局数据

POSIX系统

KEY数据结构数组(至少128个TSD)

KEY数组
标志0
析构函数指针0 —– > 线程0 pthread_key_t
标志1
析构函数指针1 —– > 线程1 pthread_key_t
标志2
析构函数指针2 —– > 线程2 pthread_key_t
………….
标志127
析构函数指针127 —– > 线程127 pthread_key_t
1
2
标志: 是否已经被使用 (每次调用pthread_key_create创建一个新的TSD时,系统都会搜索key结构数组,找出其中未使用的元素,并通过pthread_key_t返回)
析构函数指针: 指向一个析构函数,用户线程结束时后期处理(析构函数参数是一个指向TSD的指针)

每一个TSD由标志字段和析构函数指针组成

进程数据结构

Pthread结构
指针null
指针null
指针null
………….
指针null

其中KEY数组中已分配的key的标志字段,都会跟Pthread结构相关联,而Pthread中存放指向实际数据的指针,也就是说进程维护KEY结构数组,而线程维护Pthread结构数组

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <pthread.h>
/**
*param 1:一个键值指针
*param 2: 一个destructor函数,如果这个参数不为空,那么每当线程结束时,系统将调用这个函数来释放绑定在这个键上的内存快
**/
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
/* 注销一个TSMG_STATIC(id, type, element)
(TSRMG_BULK_STATIC(id, type)→element)
int pthread_key_delete(pthread_key_t key);
/* 读取变量的地址 */
void *pthread_getspecific(pthread_key_t key);
/* 存储变量的地址, 将普通变量转换成TSD */
int pthread_setspecific(pthread_key_t key, const void *value);

PHP中的多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* Thread local storage */
static pthread_key_t tls_key;
# define tsrm_tls_set(what) pthread_setspecific(tls_key, (void*)(what))
# define tsrm_tls_get() pthread_getspecific(tls_key)
#elif defined(TSRM_ST)
static int tls_key;
# define tsrm_tls_set(what) st_thread_setspecific(tls_key, (void*)(what))
# define tsrm_tls_get() st_thread_getspecific(tls_key)
#elif defined(TSRM_WIN32)
static DWORD tls_key;
# define tsrm_tls_set(what) TlsSetValue(tls_key, (void*)(what))
# define tsrm_tls_get() TlsGetValue(tls_key)
#elif defined(BETHREADS)
static int32 tls_key;
# define tsrm_tls_set(what) tls_set(tls_key, (void*)(what))
# define tsrm_tls_get() (tsrm_tls_entry*)tls_get(tls_key)
#else
# define tsrm_tls_set(what)
# define tsrm_tls_get() NULL

由此可见,tsrm_tls(Thread local storage)对象,实现的只是将普通变量,转换成TSD的处理!

PHP中的相关的宏和函数

1
2
3
4
5
6
7
8
9
#define ZEND_TSRMLS_CACHE TSRMLS_CACHE
#define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache()
#define TSRMG_BULK_STATIC(id, type) ((type) (*((void ***) TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(id)])
#define TSRMG_STATIC(id, type, element) (TSRMG_BULK_STATIC(id, type)->element)
void *tsrm_get_ls_cache(void)
{
return tsrm_tls_get();
}
Learn And Life.

朴素算法

Posted on 2017-06-07

算法时间和空间复杂度

Table

排序法 时间复杂度 最坏时间复杂度 稳定度 空间复杂度 说明
冒泡 O(N2) O(N2) 稳定 O(1) n较小
选择 O(N2) O(N2) 不稳定 O(1) n较小
插入 O(N2) O(N2) 不稳定 O(1) n较小
希尔 O(NlgN) O(Ns)(1 < s <2) 不稳定 O(1) s是所选分组
快速 O(NlgN) O(N2) 不稳定 O(NlgN) n较大
堆排序 O(NlgN) O(NlgN) 不稳定 O(1) n较大
归并排序 O(NlgN) O(NlgN) 稳定 O(1) n较大

递归

分治法

最大值问题

尺子上画刻度问题

动态规划

斐波纳契数

背包问题

树

树的遍历

平衡树

B树

图

图的遍历

排序

快速排序

基数排序

搜索

二分搜索

基数搜索


符号表

12…4
Kivmi

Kivmi

代码也是有灵魂的

32 posts
4 tags
© 2018 Kivmi
Powered by Hexo
Theme - NexT.Muse