linux用户空间和内核exit的语义--linux没有线程

简介:

如果你在程序中调用了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和不同的特性组合就可以表示程不同的外延,比如,它和gid,uid等组合就是进程,它和线程组或者TGID组合就是线程。

理解了一行原则,exit的内核行为就十分简单了,既然内核原始的只有task_struct这一个执行绪概念,那么它就是应该有它的创建和销毁的内核api,并且在哲学意义上这两个内核api是对称的,我们很多人都了解linux的线程创建,其实也是用的fork,cone在内核不也是用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(i++>15)

{

direct_exit();//直接退出,和线程没有关系,子线程handler继续运行,不受影响

//exit(); //调用库里面的exit,当然要遵循posix的线程语义,所有线程退出

}

}

return 0;

}

作为最后,为了使得本文的副标题不是空设,稍微谈一下linux的进程uid等特征,前面的文章说过,linux靠uid,gid实现了多用户,这个多用户是进程意义上的,如果按照本文前面说的概念和外延的观点来看的话,多用户只是为了实现多用户而必须的一个执行绪的参数而已,其实每个执行绪即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的打开将失败,而a,b将成功,这里可以说明在不同的线程里面可以有不同的uid和gid,其实这里的执行绪已经不再是线程了,这个例子再次说明,在linux内核中没有线程的明确定义,再抽象一点其实也没有进程,而仅仅有执行绪而已,这个执行绪到底是什么,就看用户提供什么策略使他成为什么外延了。


 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1273411


相关文章
|
4天前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
32 15
|
29天前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
30天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
1月前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
30天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
1月前
|
负载均衡 算法 Linux
深入探索Linux内核调度机制:公平与效率的平衡####
本文旨在剖析Linux操作系统内核中的进程调度机制,特别是其如何通过CFS(完全公平调度器)算法实现多任务环境下资源分配的公平性与系统响应速度之间的微妙平衡。不同于传统摘要的概览性质,本文摘要将直接聚焦于CFS的核心原理、设计目标及面临的挑战,为读者揭开Linux高效调度的秘密。 ####
37 3
|
1月前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
3月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
7月前
|
API
linux---线程互斥锁总结及代码实现
linux---线程互斥锁总结及代码实现
|
6月前
|
安全 算法 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
58 0