如果你在程序中调用了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

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

  1. Linux 用户空间和内核空间指的是什么?

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间.两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  2. linux 内核将两个设备相关联,linux用户空间和内核空间交换数据

    转载地址:http://www.poluoluo.com/server/201107/138420.html 在研究dahdi驱动的时候,见到了一些get_user,put_user的函数,不知道其来 ...

  3. Linux 用户空间和内核空间

    最近在微信群里看到有人提这个问题,然后查了下资料,觉得这篇文章是写得最能让人看懂的,分享给大家. 欢迎大家评论说出自己的见解,让更多的人更容易理解这部分知识. 之前的相关文章 Linux内存,先看这篇 ...

  4. Linux用户空间与内核空间(理解高端内存)

    目录 Linux内核地址映射模型 Linux内核地址空间划分 Linux内核高端内存的由来 Linux内核高端内存的理解 Linux内核高端内存的划分 常见问题 小结 1.虚拟内核空间到物理空间的映射 ...

  5. 嵌入式之linux用户空间与内核空间,进程上下文与中断上下文

    文章目录 前言 用户空间与内核空间 内核态与用户态 进程上下文和中断上下文 上下文 原子 进程上下文 中断上下文 进程上下文VS中断上下文 原子上下文 前言 之前在学习嵌入式linux系统的时候,一直 ...

  6. Linux用户空间和内核空间是什么?

    学习 Linux 时,经常可以看到两个词:User space(用户空间)和 Kernel space(内核空间). 简单说,Kernel space 是 Linux 内核的运行空间,User spa ...

  7. Linux用户空间与内核空间

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  8. Linux用户空间与内核空间内存映射

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  9. linux 用户空间 和 内核空间 延时函数

    用户空间: 1.unsigned int sleep(unsigned int seconds);   sleep()会使当前程序休眠seconds秒.如果sleep()没睡饱,它将会返回还需要补眠的 ...

最新文章

  1. C++ Primer plus 第12章类和动态内存分配复习题参考答案
  2. URAL1815 Farm in San Andreas(费马点,圆圆相交)
  3. Android 组件系列-----Activity保存状态
  4. Cesium:搭建运行环境
  5. matlab中的高阶导数,如何用matlab求函数的导数与高阶导数 需要技巧
  6. SpringCloud整合Hmily实现TCC分布式事务案例详解
  7. hpm1005能扫描不能打印_惠普M1005怎么只能打印复印不能扫描了?
  8. 浙江大学计算机学院科研团队,科研团队
  9. 京东页面html前台代码,京东页面代码
  10. verilog语言使用注意事项
  11. 产品经理如何自学入门?
  12. 递归_青蛙跳台阶(进阶版)
  13. Java 操作系统实验 时间片轮转法
  14. 最想和你做的100件事,我们一件一件的去完成
  15. html5 数值文本框,HTML5新控件 - 数值输入框
  16. VS2015 安装失败问题,如 安装包损坏或丢失(附安装包)
  17. 基于lamp框架搭建wordpress论坛
  18. 电赛设计报告模板及历年资源
  19. 【Steam VR 2.X】unity Skeleton Poser 骨骼姿势 编辑好手势后 运行时 抓取姿势会偏
  20. qt 判断字符串中是否含有中文字符_Qt 中文字符串问题

热门文章

  1. Java StringBuilder codePointCount()方法与示例
  2. Python---利用蒙特.卡罗方法计算圆周率近似值
  3. Java——集合(Map集合的两种迭代)
  4. FreeRTOS信号量---二值信号量
  5. 基于epoll+threadpool的webServer分析与实现
  6. task_struct 结构如何查看及分析
  7. 实现Linux select IO复用C/S服务器代码
  8. socket编程 -- epoll模型服务端/客户端通信的实现
  9. Linux--生产者与消费者
  10. linux网络编程(二)高并发服务器