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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/393169.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode1328. 破坏回文串

给你一个回文字符串 palindrome ,请你将其中 一个 字符用任意小写英文字母替换,使得结果字符串的字典序最小,且 不是 回文串。 请你返回结果字符串。如果无法做到,则返回一个空串。 示例 1: 输入:palindro…

php补充 扩展,PHP安装扩展补充说明

上一篇文章中用到了,php的sodium扩展,那么如何安装PHP扩展呢?基于我之前踩过的一些坑,大致整理了几种安装php扩展的方法。已安装sodium为例1、先做点准备工作,安装sodium依赖rpm -ivh http://mirrors.whsir.com/centos…

Java调用存储过程出现Bug,sql语法错误

因为SQL Server运行没有正常,检查了传入参数的值,发现问题,然后传入默认参数,解决了问题.转载于:https://www.cnblogs.com/JimmySeraph/p/11043490.html

leetcode1438. 绝对差不超过限制的最长连续子数组

给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。 如果不存在满足条件的子数组,则返回 0 。 示例 1: 输入&#…

gitlab 2.7版本升级到2.8

第一步 关闭服务 /etc/init.d/gitlab stop第二部 更新代码cd /home/gitlab/gitlab# Get latest codesudo -u gitlab git pull origin stable# Install libssudo -u gitlab bundle install --without development test# update dbsudo -u gitlab bundle exec rake db:migrate RA…

arkit技术介绍_面向移动AR的触觉技术:如何以“触摸”感增强ARKit应用

arkit技术介绍by Neil Mathew通过尼尔马修(Neil Mathew) 面向移动AR的触觉技术:如何以“触摸”感增强ARKit应用 (Haptics for mobile AR: how to enhance ARKit apps with a sense of “touch”) I’m really excited about the future of haptics for AR and VR. …

Unity3D的坑系列:动态加载dll

Unity3D的坑系列:动态加载dll 我现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Android平台是可以动态加载dll的&#xff0…

微信小程序 php配置,微信小程序的配置

我们使用app.json文件来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。以下是一个包含了所有配置选项的简单配置app.json{"pages": ["pages/index/index","pages/logs/index"],"wi…

leetcode332. 重新安排行程(dfs)

给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。 …

PWA - service worker - Workbox(未完)

Get Started(开始) 只有get请求才能cache缓存吗?Create and Register a Service Worker File(创建和注册 Service Worker) Before we can use Workbox, we need to create a service worker file and register it to o…

draft.js_如何使用快捷方式在Draft.js中创建有序列表和无序列表

draft.jsby Andrey Semin通过安德烈塞米(Andrey Semin) 如何使用快捷方式在Draft.js中创建有序列表和无序列表 (How to create ordered and unordered lists in Draft.js with a shortcut) We at Propeller have encountered many differences between Draft.js and popular t…

当javaScript从入门到提高前需要注意的细节:变量部分

到了HTML5的时代,对javaScript的要求不是降低了,而是更提高了。javaScript语言的入门非常简单,如果你有java、C#等C风格的结构化语言的基础,那javaScript你最多半天就可以写点什么了。但是javaScript是一种动态语言,这…

PAT乙级 1003. 我要通过!

题目: “答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于PAT的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。 得到“答案正确”的条件是: 1. 字符串中必须仅有…

电台复活节_如何通过在控制台中隐藏复活节彩蛋使您的应用程序用户惊讶

电台复活节by Ethan Ryan由伊桑瑞安(Ethan Ryan) 如何通过在控制台中隐藏复活节彩蛋使您的应用程序用户惊讶 (How to surprise your app’s users by hiding Easter eggs in the console) I love console.logging(“stuff”).我喜欢console.logging(“ stuff”)。 I do it th…

leetcode1267. 统计参与通信的服务器(dfs)

这里有一幅服务器分布图,服务器的位置标识在 m * n 的整数矩阵网格 grid 中,1 表示单元格上有服务器,0 表示没有。 如果两台服务器位于同一行或者同一列,我们就认为它们之间可以进行通信。 请你统计并返回能够与至少一台其他服务…

System类入门学习

第三阶段 JAVA常见对象的学习 System类 System类包含一些有用的字段和方法,他不能被实例化 //用于垃圾回收 public static void gc()//终止正在运行的java虚拟机。参数用作状态码,根据惯例,非0表示异常终止 public static void exit(int stat…

gulpfile php,Laravel利用gulp如何构建前端资源详解

什么是gulp?gulp是新一代的前端项目构建工具,你可以使用gulp及其插件对你的项目代码(less,sass)进行编译,还可以压缩你的js和css代码,甚至压缩你的图片,gulp仅有少量的API,所以非常容易学习。 gulp 使用 st…

ios jenkins_如何使用Jenkins和Fastlane制作iOS点播构建系统

ios jenkinsby Agam Mahajan通过Agam Mahajan 如何使用Jenkins和Fastlane制作iOS点播构建系统 (How to make an iOS on-demand build system with Jenkins and Fastlane) This article is about creating iOS builds through Jenkins BOT, remotely, without the need of a de…

菜鸟也学hadoop(1)_搭建单节点的hadoop

其实跟官方的教程一样 只是 我想写下来 避免自己搞忘记了,,,,好记性不如烂笔头 首先确认自己是否安装了 java, ssh 以及 rsync 没有装的直接就 apt-get install 了嘛,,,java的不一定…

SP703 SERVICE - Mobile Service[DP]

题意翻译 Description   一个公司有三个移动服务员。如果某个地方有一个请求,某个员工必须赶到那个地方去(那个地方没有其他员工),某一时刻只有一个员工能移动。只有被请求后,他才能移动,不允许在同样的位…