int __pthread_create_2_1(pthread_t *thread, const pthread_attr_t *attr,void * (*start_routine)(void *), void *arg)// thread 为pthread_t,实则是pthread指针。// typedef struct pthread *pthread_t; ->unsigned long int// attr 创建线程时的附加属性// start_routine 执行的线程函数// arg参数首地址指针{pthread_descr self = thread_self();// 向管理线程的请求struct pthread_request request;// 在初始化时 全局 int __pthread_manager_request = -1;if (__pthread_manager_request < 0) {// 首先Linuxthreads需要有__pthread_manager_thread,这个线程是个很特殊的线程// 初始化Linuxthreads系统线程if (__pthread_initialize_manager() < 0) return EAGAIN;}request.req_thread = self;// 请求类型 CREATE ,并将新线程(轻量进程)的参数都打包到request结构中。request.req_kind = REQ_CREATE;request.req_args.create.attr = attr;request.req_args.create.fn = start_routine;request.req_args.create.arg = arg;//sigprocmask用于改变进程的当前阻塞信号集sigprocmask(SIG_SETMASK, (const sigset_t *) NULL,&request.req_args.create.mask);// 向__pthread_manager_request写入request请求。 request请求将被_pthread_manager_thread读取并处理。__libc_write(__pthread_manager_request, (char *) &request, sizeof(request));// 挂起一下suspend(self);if (THREAD_GETMEM(self, p_retcode) == 0)*thread = (pthread_t) THREAD_GETMEM(self, p_retval);return THREAD_GETMEM(self, p_retcode);}#if defined HAVE_ELF && defined PIC && defined DO_VERSIONINGdefault_symbol_version (__pthread_create_2_1, pthread_create, GLIBC_2.1);int __pthread_create_2_0(pthread_t *thread, const pthread_attr_t *attr,void * (*start_routine)(void *), void *arg){/* The ATTR attribute is not really of type `pthread_attr_t *'. It hasthe old size and access to the new members might crash the program.We convert the struct now. */pthread_attr_t new_attr;if (attr != NULL){size_t ps = __getpagesize ();memcpy (&new_attr, attr,(size_t) &(((pthread_attr_t*)NULL)->__guardsize));new_attr.__guardsize = ps;new_attr.__stackaddr_set = 0;new_attr.__stackaddr = NULL;new_attr.__stacksize = STACK_SIZE - ps;attr = &new_attr;}return __pthread_create_2_1 (thread, attr, start_routine, arg);}symbol_version (__pthread_create_2_0, pthread_create, GLIBC_2.0); // 由此可知pthread_create入口函数为__pthread_create_2_1#elsestrong_alias (__pthread_create_2_1, pthread_create)#endif由上面的函数可知,pthread_create对线程的创建实际上是向pthread_manager_thread发送个request请求。下面我们就看pthread_manager_thread是怎样初始化的:首先要进行基本系统的初始化工作:即填充__pthread_initial_thread(算是模板吧?)其他属性:/* Descriptor of the initial thread */struct _pthread_descr_struct __pthread_initial_thread = {&__pthread_initial_thread, /* pthread_descr p_nextlive */ // 将进程中所有用户线程串在了一起&__pthread_initial_thread, /* pthread_descr p_prevlive */NULL, /* pthread_descr p_nextwaiting */NULL, /* pthread_descr p_nextlock */PTHREAD_THREADS_MAX, /* pthread_t p_tid */0, /* int p_pid */0, /* int p_priority */&__pthread_handles[0].h_lock, /* struct _pthread_fastlock * p_lock */0, /* int p_signal */NULL, /* sigjmp_buf * p_signal_buf */NULL, /* sigjmp_buf * p_cancel_buf */0, /* char p_terminated */0, /* char p_detached */0, /* char p_exited */NULL, /* void * p_retval */0, /* int p_retval */NULL, /* pthread_descr p_joining */NULL, /* struct _pthread_cleanup_buffer * p_cleanup */0, /* char p_cancelstate */0, /* char p_canceltype */0, /* char p_canceled */NULL, /* int *p_errnop */0, /* int p_errno */NULL, /* int *p_h_errnop */0, /* int p_h_errno */NULL, /* char * p_in_sighandler */0, /* char p_sigwaiting */PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */{NULL}, /* void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE] */{NULL}, /* void * p_libc_specific[_LIBC_TSD_KEY_N] */0, /* int p_userstack */NULL, /* void * p_guardaddr */0, /* size_t p_guardsize */&__pthread_initial_thread, /* pthread_descr p_self */0, /* Always index 0 */0, /* int p_report_events */{{{0, }}, 0, NULL}, /* td_eventbuf_t p_eventbuf */ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */0, /* char p_woken_by_cancel */NULL /* struct pthread_extricate_if *p_extricate */};static void pthread_initialize(void){struct sigaction sa;sigset_t mask;struct rlimit limit;int max_stack;//__pthread_initial_thread_bos: Limit between the stack of the initial thread (above) and the//stacks of other threads (below). Aligned on a STACK_SIZE boundary./* 如果初始化工作已完成,则退出 */if (__pthread_initial_thread_bos != NULL) return;#ifdef TEST_FOR_COMPARE_AND_SWAP/* Test if compare-and-swap is available */__pthread_has_cas = compare_and_swap_is_available();#endif/* For the initial stack, reserve at least STACK_SIZE bytes of stackbelow the current stack address, and align that on aSTACK_SIZE boundary. */__pthread_initial_thread_bos =(char *)(((long)CURRENT_STACK_FRAME. - 2 * STACK_SIZE) & ~(STACK_SIZE - 1));/* 对initial_thread.pid赋值:主进程pid */__pthread_initial_thread.p_pid = __getpid();/* If we have special thread_self processing, initialize that for themain thread now. */#ifdef INIT_THREAD_SELFINIT_THREAD_SELF(&__pthread_initial_thread, 0);#endif/* 共享主进程errno/h_errno.*/__pthread_initial_thread.p_errnop = &_errno;__pthread_initial_thread.p_h_errnop = &_h_errno;/* Play with the stack size limit to make sure that no stack ever growsbeyond STACK_SIZE minus two pages (one page for the thread descriptorimmediately beyond, and one page to act as a guard page). */getrlimit(RLIMIT_STACK, &limit); // 获取STACK limitmax_stack = STACK_SIZE - 2 * __getpagesize();if (limit.rlim_cur > max_stack) {limit.rlim_cur = max_stack;setrlimit(RLIMIT_STACK, &limit);}#ifdef __SIGRTMIN/* Initialize real-time signals. */init_rtsigs ();#endif/* Setup signal handlers for the initial thread.Since signal handlers are shared between threads, these settingswill be inherited by all other threads. */// 设置initial thread信号处理函数 其他线程都将继承#ifndef __i386__sa.sa_handler = pthread_handle_sigrestart;#elsesa.sa_handler = (__sighandler_t) pthread_handle_sigrestart;#endifsigemptyset(&sa.sa_mask);sa.sa_flags = 0;__sigaction(__pthread_sig_restart, &sa, NULL);#ifndef __i386__sa.sa_handler = pthread_handle_sigcancel;#elsesa.sa_handler = (__sighandler_t) pthread_handle_sigcancel;#endifsa.sa_flags = 0;__sigaction(__pthread_sig_cancel, &sa, NULL);if (__pthread_sig_debug > 0) {sa.sa_handler = pthread_handle_sigdebug;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;__sigaction(__pthread_sig_debug, &sa, NULL);}/* Initially, block __pthread_sig_restart. Will be unblocked on demand. */sigemptyset(&mask);sigaddset(&mask, __pthread_sig_restart);sigprocmask(SIG_BLOCK, &mask, NULL);/* Register an exit function to kill all other threads. *//* Do it early so that user-registered atexit functions are calledbefore pthread_exit_process. */__on_exit(pthread_exit_process, NULL);}// 下面是__pthread_manager_thread 的初始化/* Descriptor of the manager thread; none of this is used but the errorvariables, the p_pid and p_priority fields,and the address for identification. */struct _pthread_descr_struct __pthread_manager_thread = {NULL, /* pthread_descr p_nextlive */ // 这两个值为空!NULL, /* pthread_descr p_prevlive */NULL, /* pthread_descr p_nextwaiting */NULL, /* pthread_descr p_nextlock */0, /* int p_tid */0, /* int p_pid */0, /* int p_priority */&__pthread_handles[1].h_lock, /* struct _pthread_fastlock * p_lock */0, /* int p_signal */NULL, /* sigjmp_buf * p_signal_buf */NULL, /* sigjmp_buf * p_cancel_buf */0, /* char p_terminated */0, /* char p_detached */0, /* char p_exited */NULL, /* void * p_retval */0, /* int p_retval */NULL, /* pthread_descr p_joining */NULL, /* struct _pthread_cleanup_buffer * p_cleanup */0, /* char p_cancelstate */0, /* char p_canceltype */0, /* char p_canceled */&__pthread_manager_thread.p_errno, /* int *p_errnop */0, /* int p_errno */NULL, /* int *p_h_errnop */0, /* int p_h_errno */NULL, /* char * p_in_sighandler */0, /* char p_sigwaiting */PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */{NULL}, /* void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE] */{NULL}, /* void * p_libc_specific[_LIBC_TSD_KEY_N] */0, /* int p_userstack */NULL, /* void * p_guardaddr */0, /* size_t p_guardsize */&__pthread_manager_thread, /* pthread_descr p_self */1, /* Always index 1 */0, /* int p_report_events */{{{0, }}, 0, NULL}, /* td_eventbuf_t p_eventbuf */ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */0, /* char p_woken_by_cancel */NULL /* struct pthread_extricate_if *p_extricate */};int __pthread_initialize_manager(void){// 管理线程与其他线程之间的通信是通过管道完成的。/*在一个进程空间内,管理线程与其他线程之间通过一对"管理管道(manager_pipe[2])"来通讯,该管道在创建管理线程之前创建,在成功启动了管理线程之后,管理管道的读端和写端分别赋给两个全局变量__pthread_manager_reader和__pthread_manager_request,之后,每个用户线程都通过__pthread_manager_request向管理线程发请求,但管理线程本身并没有直接使用__pthread_manager_reader,管道的读端(manager_pipe[0])是作为__clone()的参数之一传给管理线程的,管理线程的工作主要就是监听管道读端,并对从中取出的请求作出反应。*/int manager_pipe[2];int pid;struct pthread_request request;/* 如果基本的初始化未进行,那么进行初始化*/if (__pthread_initial_thread_bos == NULL) pthread_initialize();/* 设置stack */__pthread_manager_thread_bos = malloc(THREAD_MANAGER_STACK_SIZE);if (__pthread_manager_thread_bos == NULL) return -1;// top值__pthread_manager_thread_tos =__pthread_manager_thread_bos + THREAD_MANAGER_STACK_SIZE;/* 设置通信管道 */if (pipe(manager_pipe) == -1) {free(__pthread_manager_thread_bos);return -1;}/* 在创建线程时 pid一定不会为0 */pid = 0;if (__pthread_initial_thread.p_report_events){/* It's a bit more complicated. We have to report the creation ofthe manager thread. */int idx = __td_eventword (TD_CREATE);uint32_t mask = __td_eventmask (TD_CREATE);if ((mask & (__pthread_threads_events.event_bits[idx]| __pthread_initial_thread.p_eventbuf.eventmask.event_bits[idx]))!= 0){// sys_fork:创建 pthread_manager线程 :共享VM FS FILES等pid = __clone(__pthread_manager_event,(void **) __pthread_manager_thread_tos,CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,(void *)(long)manager_pipe[0]); // 读管道if (pid != -1){/* Now fill in the information about the new thread inthe newly created thread's data structure. We cannot letthe new thread do this since we don't know whether it wasalready scheduled when we send the event. */// 对新线程添加属性__pthread_manager_thread.p_eventbuf.eventdata =&__pthread_manager_thread;__pthread_manager_thread.p_eventbuf.eventnum = TD_CREATE;__pthread_last_event = &__pthread_manager_thread;// _pthread_manager_thread的是2*PTHREAD_THREADS_MAX+1__pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1;__pthread_manager_thread.p_pid = pid; // clone进程pid/* Now call the function which signals the event. */__linuxthreads_create_event ();/* Now restart the thread. */__pthread_unlock(__pthread_manager_thread.p_lock);}}}// 如果__pthread_initial_thread.p_report_events标记了,自然不再创建新线程了if (pid == 0)pid = __clone(__pthread_manager, (void **) __pthread_manager_thread_tos,CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,(void *)(long)manager_pipe[0]);if (pid == -1) {free(__pthread_manager_thread_bos);__libc_close(manager_pipe[0]);__libc_close(manager_pipe[1]);return -1;}// __pthread_manager_request fd = manager_pipe[1]; 交给其他线程写请求__pthread_manager_request = manager_pipe[1]; /* 全局变量writing end */__pthread_manager_reader = manager_pipe[0]; /* 全局变量reading end */__pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1;__pthread_manager_thread.p_pid = pid;/* Make gdb aware of new thread manager */if (__pthread_threads_debug && __pthread_sig_debug > 0){raise(__pthread_sig_debug);/* We suspend ourself and gdb will wake us up when it isready to handle us. */__pthread_wait_for_restart_signal(thread_self());}/* Synchronize debugging of the thread manager */request.req_kind = REQ_DEBUG;__libc_write(__pthread_manager_request, (char *) &request, sizeof(request));return 0;}至此,__pthread_manager_thread创建成功。下面看看它是怎样管理的:__pthread_manager函数/* The server thread managing requests for thread creation and termination */int __pthread_manager(void *arg){int reqfd = (int) (long int) arg; // 管道的读口fdstruct pollfd ufd;sigset_t mask;int n;struct pthread_request request;/* If we have special thread_self processing, initialize it. */#ifdef INIT_THREAD_SELFINIT_THREAD_SELF(&__pthread_manager_thread, 1);#endif/* Set the error variable. */__pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno;__pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno;/* Block all signals except __pthread_sig_cancel and SIGTRAP */sigfillset(&mask);sigdelset(&mask, __pthread_sig_cancel); /* for thread termination */sigdelset(&mask, SIGTRAP); /* for debugging purposes */sigprocmask(SIG_SETMASK, &mask, NULL);/* 提高优先级 等于主线程的优先级 */__pthread_manager_adjust_prio(__pthread_main_thread->p_priority);/* Synchronize debugging of the thread manager */// 从管道中读取请求n = __libc_read(reqfd, (char *)&request, sizeof(request));ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG);ufd.fd = reqfd;ufd.events = POLLIN;/* Enter server loop */while(1) {n = __poll(&ufd, 1, 2000); // 2秒超时/* main thread的终止 将导致所有thread终止 */if (getppid() == 1) {pthread_kill_all_threads(SIGKILL, 0);_exit(0);}/* Check for dead children */if (terminated_children) {terminated_children = 0;pthread_reap_children();}/* 读取请求 */if (n == 1 && (ufd.revents & POLLIN)) {n = __libc_read(reqfd, (char *)&request, sizeof(request));ASSERT(n == sizeof(request));switch(request.req_kind) {case REQ_CREATE: //创建request.req_thread->p_retcode =pthread_handle_create((pthread_t *) &request.req_thread->p_retval,request.req_args.create.attr,request.req_args.create.fn,request.req_args.create.arg,&request.req_args.create.mask,request.req_thread->p_pid,request.req_thread->p_report_events,&request.req_thread->p_eventbuf.eventmask);restart(request.req_thread);break;case REQ_FREE:pthread_handle_free(request.req_args.free.thread_id);break;case REQ_PROCESS_EXIT:pthread_handle_exit(request.req_thread,request.req_args.exit.code);break;case REQ_MAIN_THREAD_EXIT:main_thread_exiting = 1;if (__pthread_main_thread->p_nextlive == __pthread_main_thread) {restart(__pthread_main_thread);return 0;}break;case REQ_POST:__new_sem_post(request.req_args.post);break;case REQ_DEBUG:/* Make gdb aware of new thread and gdb will restart thenew thread when it is ready to handle the new thread. */if (__pthread_threads_debug && __pthread_sig_debug > 0)raise(__pthread_sig_debug);break;}}}}下面我们看看新的线程是怎样创建的:static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,void * (*start_routine)(void *), void *arg,sigset_t * mask, int father_pid,int report_events,td_thr_events_t *event_maskp){size_t sseg;int pid;pthread_descr new_thread;char * new_thread_bottom;pthread_t new_thread_id;char *guardaddr = NULL;size_t guardsize = 0;int pagesize = __getpagesize();/* First check whether we have to change the policy and if yes, whetherwe can do this. Normally this should be done by examining thereturn value of the __sched_setscheduler call in pthread_start_threadbut this is hard to implement. FIXME */if (attr != NULL && attr->__schedpolicy != SCHED_OTHER && geteuid () != 0)return EPERM;/* Find a free segment for the thread, and allocate a stack if needed */for (sseg = 2; ; sseg++){if (sseg >= PTHREAD_THREADS_MAX)return EAGAIN;if (__pthread_handles[sseg].h_descr != NULL)continue;if (pthread_allocate_stack(attr, thread_segment(sseg), pagesize,&new_thread, &new_thread_bottom,&guardaddr, &guardsize) == 0)break;}__pthread_handles_num++;/* Allocate new thread identifier */pthread_threads_counter += PTHREAD_THREADS_MAX;new_thread_id = sseg + pthread_threads_counter;/* Initialize the thread descriptor. Elements which have to beinitialized to zero already have this value. */// 对新的线程进行属性赋值new_thread->p_tid = new_thread_id; //tid=n*PTHREAD_THREADS_MAX+n+1new_thread->p_lock = &(__pthread_handles[sseg].h_lock);new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE;new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED;new_thread->p_errnop = &new_thread->p_errno;new_thread->p_h_errnop = &new_thread->p_h_errno;new_thread->p_guardaddr = guardaddr;new_thread->p_guardsize = guardsize;new_thread->p_self = new_thread;new_thread->p_nr = sseg;/* Initialize the thread handle */__pthread_init_lock(&__pthread_handles[sseg].h_lock);__pthread_handles[sseg].h_descr = new_thread;__pthread_handles[sseg].h_bottom = new_thread_bottom;/* Determine scheduling parameters for the thread */new_thread->p_start_args.schedpolicy = -1;if (attr != NULL) {new_thread->p_detached = attr->__detachstate;new_thread->p_userstack = attr->__stackaddr_set;switch(attr->__inheritsched) {case PTHREAD_EXPLICIT_SCHED:new_thread->p_start_args.schedpolicy = attr->__schedpolicy;memcpy (&new_thread->p_start_args.schedparam, &attr->__schedparam,sizeof (struct sched_param));break;case PTHREAD_INHERIT_SCHED:new_thread->p_start_args.schedpolicy = __sched_getscheduler(father_pid);__sched_getparam(father_pid, &new_thread->p_start_args.schedparam);break;}new_thread->p_priority =new_thread->p_start_args.schedparam.sched_priority;}/* Finish setting up arguments to pthread_start_thread */// 新线程 函数 参数赋值new_thread->p_start_args.start_routine = start_routine;new_thread->p_start_args.arg = arg;new_thread->p_start_args.mask = *mask;/* Raise priority of thread manager if needed */__pthread_manager_adjust_prio(new_thread->p_priority);/* Do the cloning. We have to use two different functions dependingon whether we are debugging or not. */pid = 0; /* Note that the thread never can have PID zero. */if (report_events){/* See whether the TD_CREATE event bit is set in any of themasks. */int idx = __td_eventword (TD_CREATE);uint32_t mask = __td_eventmask (TD_CREATE);if ((mask & (__pthread_threads_events.event_bits[idx]| event_maskp->event_bits[idx])) != 0){/* Lock the mutex the child will use now so that it will stop. */__pthread_lock(new_thread->p_lock, NULL);/* We have to report this event. */pid = __clone(pthread_start_thread_event, (void **) new_thread,CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |__pthread_sig_cancel, new_thread);if (pid != -1){/* Now fill in the information about the new thread inthe newly created thread's data structure. We cannot letthe new thread do this since we don't know whether it wasalready scheduled when we send the event. */new_thread->p_eventbuf.eventdata = new_thread;new_thread->p_eventbuf.eventnum = TD_CREATE;__pthread_last_event = new_thread;/* We have to set the PID here since the callback functionin the debug library will need it and we cannot guaranteethe child got scheduled before the debugger. */new_thread->p_pid = pid;/* Now call the function which signals the event. */__linuxthreads_create_event ();/* Now restart the thread. */__pthread_unlock(new_thread->p_lock);}}}if (pid == 0)pid = __clone(pthread_start_thread, (void **) new_thread,CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |__pthread_sig_cancel, new_thread);/* Check if cloning succeeded */if (pid == -1) {/* Free the stack if we allocated it */if (attr == NULL || !attr->__stackaddr_set){if (new_thread->p_guardsize != 0)munmap(new_thread->p_guardaddr, new_thread->p_guardsize);munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),INITIAL_STACK_SIZE);}__pthread_handles[sseg].h_descr = NULL;__pthread_handles[sseg].h_bottom = NULL;__pthread_handles_num--;return errno;}/* 将线程添加到__pthread_main_thread 双向链表中 由管理线程管理*/new_thread->p_prevlive = __pthread_main_thread;new_thread->p_nextlive = __pthread_main_thread->p_nextlive;__pthread_main_thread->p_nextlive->p_prevlive = new_thread;__pthread_main_thread->p_nextlive = new_thread;/* Set pid field of the new thread, in case we get there before thechild starts. */new_thread->p_pid = pid; // 记录了clone 返回的pid/* We're all set */*thread = new_thread_id;return 0;}
总结:
1)Linuxthread 是用sys_clone系统调用完成的。本质上说,每个线程其实就是一个进程,只不过线程共享部分资源。
2)操作系统对线程的管理,实则就是对进程的管理。在/proc目录下我们可以找到这些进程
3)Linuxthread 是通过 __pthread_manager_thread 管理其他线程的。
4)线程有各自的线程id,由LinuxThreads分配和维护。其实也有各自的真实pid,但由结构隐藏了,对外的pid为主进程的pid
5)线程对操作系统来说就是进程,因此对信号的处理是以进程为单位分发的
6)线程总数受限于系统进程数
7)系统管理线程的瓶颈:所有线程由管理线程管理
8)线程之间的调度是由内核调度器来处理的
9)由于计算线程本地数据的方法是基于堆栈地址的位置。

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

linux 线程 pthread create 源码 剖析相关推荐

  1. Android多线程研究(1)——线程基础及源码剖析

    从今天起我们来看一下Android中的多线程的知识,Android入门容易,但是要完成一个完善的产品却不容易,让我们从线程开始一步步深入Android内部. 一.线程基础回顾 package com. ...

  2. Web bench源码剖析

    一.Web bench是什么? 首先提一个概念--压力测试. 在运维工作中,压力测试是一项很重要的工作.比如在一个网站上线之前,能承受多大访问量.在大访问量情况下性能怎样,这些数据指标好坏将会直接影响 ...

  3. Mongoose源码剖析:Introduction and Installation

    引言 要剖析Mongoose的源码,首先你得知道它的一些基本情况和特性.并去使用它.本文就是介绍Mongoose是个什么东西?及如何安装和使用?这里假设你知道什么web服务器软件.web服务器使用什么 ...

  4. 自己实现多线程的socket,socketserver源码剖析

    1,IO多路复用 三种多路复用的机制:select.poll.epoll 用的多的两个:select和epoll 简单的说就是: 1,select和poll所有平台都支持,epoll只有linux支持 ...

  5. 深入理解RCU | RCU源码剖析

    hi, 上次分析了RCU核心思想:深入理解RCU|核心原理,后面说会分享一篇RCU的源码剖析,其实我这边已经总结得差不多: 但自己思考了一下,发现大部分都是代码分析,这样很多人其实并不喜欢看源代码分析 ...

  6. Zookeeper--Watcher机制源码剖析一

    Watcher-- 数据变更通知 我们知道Zookeeper提供来分布式数据的订阅/发布功能,一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能让多个订阅者同时监听某个主题对象,当这个被监听对 ...

  7. tomcat(12)org.apache.catalina.core.StandardContext源码剖析

    [0]README 0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(12)StandardContext源码剖析" 的 ...

  8. tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析

    [0]README 0.0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(11)StandardWrapper源码剖析" ...

  9. Python envoy 模块源码剖析

    Kenneth Reitz 是公认的这个世界上 Python 代码写得最好的人之一.抱着学习的心态,我阅读了 Reitz 写的 envoy 模块的源码,将笔记记录如下. 介绍 和 requests 模 ...

最新文章

  1. Ubuntu 12.10 安装 jdk-7u10-linux-x64.tar.gz
  2. 修改所有列_宝塔面板安装完的一些列操作
  3. iOS开发UI篇—Quartz2D使用(绘制基本图形)
  4. cocos2d-x中几种存储数据的方式
  5. 1.2 操作系统的发展和分类(手工、单道/多道批处理、分时、实时、网络、分布式、嵌入式、个人计算机)
  6. 【linux】截断日志文件
  7. [Qt教程] 第30篇 XML(四)使用流读写XML
  8. 信息学奥赛一本通 1200:分解因数 | OpenJudge 2.2 1751:分解因数
  9. 利用Kubernetes名称空间来管理内存和CPU资源(一)
  10. html里,实现图片与文字对齐的最简洁方法
  11. 【图像融合】基于matlab遗传算法图像融合【含Matlab源码 1624期】
  12. mac 硬盘读写速度测试 软件,MAC硬盘速度测试技巧
  13. K均值算法(K_means)
  14. 机器学习-SVD(隐语义模型)协同过滤
  15. 从BlueSky.h和BlueSky.cpp到BlueSky.out的那些事儿
  16. Unity3D Texture2D转换成Sprite格式
  17. clamav --reload 加载病毒库源码分析
  18. python文件ab,Python文件
  19. 一文看懂计算机视觉!7个专业术语别说你不知道!
  20. 数据结构—— 一元多项式的运算(相加,相减,相乘)【C语言实现】

热门文章

  1. 聊聊性能测试、操作系统优化对性能测试的影响,以及如何优化操作系统
  2. Silverlight+WCF 新手实例 象棋 WCF通讯跨域(十五)
  3. Android笔记-网络篇:网络状态
  4. corosync+pacemaker+crmsh的高可用web集群的实现
  5. 破解 Windows 2003终端服务许可证
  6. 云计算&大数据 “下一幕”智能变革之力
  7. 用python+tornado+mongodb写的一个博客系统
  8. html中Marquee属性详解
  9. python3 报错 ‘builtin_function_or_method‘ object has no attribute 解决方法
  10. webmin远程命令执行漏洞(cve-2019-15107)深入分析