建设网站需要购买哪些,上海线上引流推广,漯河优惠网站建设价格,最新seo新手教程如果你在程序中调用了exit#xff0c;那么很显然你的程序会退出#xff0c;可是至于为何会退出那就是库的事情了#xff0c;我为什么说只是库的事情而不关linux内核的事情呢#xff1f;那是因为linux内核根本不管用户空间的行为策略。库的策略是什么#xff1f;很简单的退… 如果你在程序中调用了exit那么很显然你的程序会退出可是至于为何会退出那就是库的事情了我为什么说只是库的事情而不关linux内核的事情呢那是因为linux内核根本不管用户空间的行为策略。库的策略是什么很简单的退出当前进程吗如果是多线程的程序呢多线程的程序它的行为又是什么呢在我们探究库的行为以及探究库为何会有这样的行为之前首先谈谈内核对exit的实现sys_exit在sys_exit中我丝毫找不到关于退出线程的代码按照常规的思考如果sys_exit负责将本进程的所有线程都退出的话那么合理的办法就是在本进程的signal结构体上append上一个SIGKILL信号然后唤醒所有的线程的task_struct,因为一个进程的所有线程共享信号处理结构因此每个被唤醒的线程的task_struct都会在信号处理时自己退出这样看起来很不错也很合理如果是windows想到了这个主意那么它肯定会采用的可是这是linux。 linux中没有线程的概念我这么说不是在说linux很落后吗现代操作系统中都会有线程的实现。其实不然在linux中线程并不是一个操作系统的内秉性概念在linux中只有进程然后可以在进程的基础上轻松实现线程这个意义上线程仅仅是一个策略一个由很多的实体概念组合而成的概念。其实在linux上完全不能用传统的操作系统的标准来说明比如线程进程然后又分了什么X程这样岂不是越来越乱每引入一个概念操作系统的体系就要大动一番在linux中就有一个执行绪的概念该执行绪就是task_struct你把它理解成进程也好理解成线程也罢其实它可以表达成任何可以执行的东西在task_struct的基础上我们可以垒砌很多外延的东西比如进程线程等等在linux中只有task_struct一个概念进程线程只是它的外延而已task_struct和不同的特性组合就可以表示程不同的外延比如它和giduid等组合就是进程它和线程组或者TGID组合就是线程。 理解了一行原则exit的内核行为就十分简单了既然内核原始的只有task_struct这一个执行绪概念那么它就是应该有它的创建和销毁的内核api并且在哲学意义上这两个内核api是对称的我们很多人都了解linux的线程创建其实也是用的forkcone在内核不也是用fork实现的吗创建一个现代操作系统的线程在linux中就是创建一个执行绪也就是创建一个task_struct那么do_fork就是干这个的相反的对于退出并没有多少人去关注既然fork创建了一个task_struct那么exit就是销毁一个task_struct别的并不做什么fork没有线程的概念它只负责按照用户提供的参数创建一个task_struct这里线程这个外延是通过参数体现的既然参数可以赋予一个task_struct以线程的含义那么fork中也就根据此含义对task_struct的字段进行了设置以表示这是一个线程在linux内核中并没有线程的概念而仅有线程的外延既然创建行为fork如此那么销毁行为exit也是如此如此一来就可以理解exit中根本就不可能有什么向本进程的所有线程发送退出信号一说它只管销毁这个调用exit的执行绪的task_struct(其实是递减这个task_stuct的引用计数)而不管什么线程的概念那么谁会去管线程的概念呢当然是谁定义谁管了比如Posix或者用户的其它库内核将线程这个task_struct的外延导出给用户那么用户就可以用这个外延的一系列特性以及行为准则来操作这个线程外延故而用户库可以用内核提供的最小化的正交组合接口配上线程这个外延来组合成一个可以退出所有线程的接口其实就是对于每一个线程调用其exit。 内核实现毕竟是内核实现linux还是遵循posix的因此它提供了一个系统调用sys_exit_group之所以如此是因为这样的话用户库就不必再费劲心机切入每个线程并且在每个线程调用exit了当然这也不是linux内核所希望的。sys_exit_group中会调用zap_other_threads(current)来退出每个线程不管怎样exit_group的提供仅仅是为了遵循posix而exit才是linux的设计中原汁原味的执行绪操作系统调用其实在用户库里面完全可以用向所有的线程发送SIGKILL信号来实现exit。 举个例子来说明一切 #include.h #include #include.h #include void direct_exit() //这个函数直接用系统调用实现了exit即到了内核直接调用sys_exit { int a 1,b 0; asm(movl %0,%%eax/n/t / movl %1,%%ebx/n/t / int $0x80/n/t / ::r (a),r (b)); } int handler( void *p) { while(1) { sleep(1); printf(Sub thread is running/n); } } int main(int argc, char* argv[]) { int i 0; clone(handler, i-1024, CLONE_SIGHAND|CLONE_VM|CLONE_THREAD, NULL); while(1) { sleep(1); printf(Main thread is running/n); if(i15) { direct_exit();//直接退出和线程没有关系子线程handler继续运行不受影响 //exit(); //调用库里面的exit当然要遵循posix的线程语义所有线程退出 } } return 0; } 作为最后为了使得本文的副标题不是空设稍微谈一下linux的进程uid等特征前面的文章说过linux靠uidgid实现了多用户这个多用户是进程意义上的如果按照本文前面说的概念和外延的观点来看的话多用户只是为了实现多用户而必须的一个执行绪的参数而已其实每个执行绪即task_struct都有一个uid和gid等信息而并不一定仅仅指进程如下的例子可以证明 int handler( void *p) { open(/root/b,O_CREATE); perror(open b); setuid(500); seteuid(500); open(/root/c,O_CREATE); perror(open c); } int main(int argc, char* argv[]) { int i 0; clone(handler, i-1024, CLONE_SIGHAND|CLONE_VM|CLONE_THREAD, NULL); if(getchar()w) { open(/root/a,O_CREATE); perror(open a); } return 0; } 在以上的例子中以root用户运行这个代码c的打开将失败而ab将成功这里可以说明在不同的线程里面可以有不同的uid和gid其实这里的执行绪已经不再是线程了这个例子再次说明在linux内核中没有线程的明确定义再抽象一点其实也没有进程而仅仅有执行绪而已这个执行绪到底是什么就看用户提供什么策略使他成为什么外延了。 本文转自 dog250 51CTO博客原文链接:http://blog.51cto.com/dog250/1273411