- 浏览: 716745 次
- 性别:
- 来自: 北京
最新评论
-
wxweven:
<div class="quote_title ...
SkipList 跳表 -
暮雪云然:
写的不错,很透彻
Java静态内部类 -
bzhao:
好,赞扬!
Linux信号详解 -
jacktao219:
赞一个~! ,现在正在看redis 所以接触到跳表
SkipList 跳表 -
is_leon:
vote--后还要判断是否为0吧,如果为0则废掉重新置位can ...
现在有一个整数数组,已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数
什么是进程
直观点说,保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,
这个内存体有自己的地址空间,有自己的堆,上级挂靠单位是操作系统。操作系统
会以进程为单位,分配系统资源,所以我们也说,进程是资源分配的最小单位。
什么是线程
线程存在与进程当中,是操作系统调度执行的最小单位。说通俗点,线程就是干活的。
进程和线程的区别与联系
如果说进程是一个资源管家,负责从主人那里要资源的话,那么线程就是干活的苦力。
一个管家必须完成一项工作,就需要最少一个苦力,也就是说,一个进程最少包含一
个线程,也可以包含多个线程。苦力要干活,就需要依托于管家,所以说一个线程,
必须属于某一个进程。进程有自己的地址空间,线程使用进程的地址空间,
也就是说,进程里的资源,线程都是有权访问的,比如说堆啊,栈啊,静态存储区什么的。
线程就是个无产阶级,但无产阶级干活,总得有自己的劳动工具吧,这个劳动工具就是栈,
线程有自己的栈,这个栈仍然是使用进程的地址空间,只是这块空间被线程标记为了栈。
每个线程都会有自己私有的栈,这个栈是不可以被其他线程所访问的。
下面两副图展示了Linux下典型的进程环境和线程环境:
图1 Linux进程环境示意图
图2 Linux线程环境示意图
一个牛叉的函数 -- 创建进程和线程的基础
Linux下进程和线程的创建都是通过clone实现的. clone函数功能强大,带了众多参数,
clone可以让你有选择性的继承父进程的资源,你可以选择想vfork一样和父进程
共享一个虚存空间,从而创造的是线程,你也可以不和父进程共享,你甚至可以选
择创造出来的进程和父进程不再是父子关系,而是 兄弟关系。先有必要说下这个函数的结构
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
这里fn是函数指针,我们知道进程的4要素,这个就是指向程序的指针, child_stack是为子
进程分配系统堆栈空间(在linux下系统堆栈空间是2页面,就是8K的内存,其中在这块内存中,
低地址上放入了值,这个值就是进程控制块task_struct的 值),flags就是标志用来描述你
需要从父进程继承那些资源, arg就是传给子进程的参数)。下面是flags可以取的值
标志 含义
CLONE_PARENT 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了兄弟而不是父子
CLONE_FS 子进程与父进程共享相同的文件系统,包括root、当前目录、umask
CLONE_FILES 子进程与父进程共享相同的文件描述符(file descriptor)表
CLONE_NEWNS 在新的namespace启动子进程,namespace描述了进程的文件hierarchy
CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表
CLONE_PTRACE 若父进程被trace,子进程也被trace
CLONE_VFORK 父进程被挂起,直至子进程释放虚拟内存资源
CLONE_VM 子进程与父进程运行于相同的内存空间
CLONE_PID 子进程在创建时PID与父进程一致
CLONE_THREAD Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群
下面的例子是创建一个线程(子进程共享了父进程虚存空间,没有自己独立的虚存空间不能称其为进程)。
父进程被挂起当子线程释放虚存资源后再继续执行。
#include <stdio.h> #include <sched.h> #include <signal.h> #define FIBER_STACK 8192 int a; void * stack; int do_something() { printf("This is son, the pid is:%d, the a is: %d\n", getpid(), ++a); free(stack); exit(1); } int main() { void * stack; a = 1; //为子进程申请系统堆栈 stack = malloc(FIBER_STACK); if(!stack) { printf("The stack failed\n"); exit(0); } printf("creating son thread!!!\n"); clone(&do_something, (char *)stack + FIBER_STACK, CLONE_VM|CLONE_VFORK, 0); printf("This is father, my pid is: %d, the a is: %d\n", getpid(), a); exit(1); }
关于fork, vfork,和clone的底层实现
asmlinkage int sys_vfork(struct pt_regs regs) { return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0); } asmlinkage int sys_fork(struct pt_regs regs) { return do_fork(SIGCHLD, regs.esp, ®s, 0); } asmlinkage int sys_clone(struct pt_regs regs) { unsigned long clone_flags; unsigned long newsp; clone_flags = regs.ebx; newsp = regs.ecx; if (!newsp) newsp = regs.esp; return do_fork(clone_flags, newsp, ®s, 0); }
这里可以看到它们都是对do_fork的调用,不过是参数不同而已.其中最重要的是 clone_flags
参数, clone_flags由2部分组成,最低字节为信号类型,用于规定子进程去世时向父进程发出的信号。
fork和vfork中这个信号就是SIGCHLD,而clone则可以由用户自己定义。高字节部分表示子进程
从父进程继承哪些资源,fork的clone_flags参数的第2部分全部是0表示对所有资源都要复制给
子进程。而vfork的参数是 (CLONE_VFORK | CLONE_VM), 表示子进程和父进程共享虚存内存
CLONE_VFORK让父进程挂起, 直到子进程退出,至于clone则是由用户自己来定义的.
pthread_create的实现
pthread_create是基于clone实现的, 创建出来的其实是进程, 但这些进程与父进程共享很多东西,
共享的东西都不用复制给子进程, 从而节省很多开销, 因此,这些子进程也叫轻量级进程(light-weight process)
简称LWP. 每个LWP都会与一个内核线程绑定, 这样它就可以作为独立的调度实体参与cpu竞争. 如下图所示:
LWP被pthread封装后, 以线程面目示人, 它有自己的id, 这里要区分 phtread_create
给LWP分配的id, 和操作系统给LWP分配的进程id. 这两者是不同的, 前者用于标志线程,可以暴露给
用户, 后者其实就是进程的id, 它没有暴露出来, 必须通过系统调用来得到.
下面的例子演示了如何获取LWP的进程id
现出LWP的进程id
cat lwp.c
#include <unistd.h> #include <stdio.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <linux/unistd.h> #define N 5 pid_t gettid() { //直接使用224代替__NR_gettid也可以 return syscall(__NR_gettid); } void *thread_func(void *arg) { printf("thread started, pid = %d\n", gettid()); while (1) { sleep(1); } } int main(void) { int i; pthread_t tid[N]; for (i = 0; i < N; i++) { pthread_create(&tid[i], NULL, thread_func, NULL); } sleep(1); for (i = 0; i < N; i++) { printf("tid = %lu\n", tid[i]); } while (1) { sleep(1); } return 0; }
work> gcc lwp.c -o lwp -lpthread
work> ./lwp
thread started, pid = 12248
thread started, pid = 12249
thread started, pid = 12250
thread started, pid = 12251
thread started, pid = 12252
tid = 3078187888
tid = 3069795184
tid = 3061402480
tid = 3053009776
tid = 3044617072
ps ax
12247 pts/1 Sl+ 0:00 ./lwp
12259 pts/3 R+ 0:00 ps ax
work> ps -Lf 12247
UID PID PPID LWP C NLWP STIME TTY STAT TIME CMD
kenby 12247 3758 12247 0 6 20:22 pts/1 Sl+ 0:00 ./lwp
kenby 12247 3758 12248 0 6 20:22 pts/1 Sl+ 0:00 ./lwp
kenby 12247 3758 12249 0 6 20:22 pts/1 Sl+ 0:00 ./lwp
kenby 12247 3758 12250 0 6 20:22 pts/1 Sl+ 0:00 ./lwp
kenby 12247 3758 12251 0 6 20:22 pts/1 Sl+ 0:00 ./lwp
kenby 12247 3758 12252 0 6 20:22 pts/1 Sl+ 0:00 ./lwp
此程序创建了5个线程, 但有6个LWP, 因为主线程也包含在里面, 主线程的线程id和进程id
相同, 然后这6个线程的进程id都相同, 因为他们属于同一个进程.
关于内核态和用户态
核心态可以执行特权指令,但用户态只能执行非特权指令.这是如何实现的呢?
Linux将内核程序和用户程序分开处理,分别运行在用户态和核心态。
以32位x86架构为例,虚拟空间共4G,高地址的1G为系统程序运行的核心栈,
低地址的3G空间为用户程序运行的用户栈。Linux进程的4GB地址空间,
3G-4G部分大家是共享的,是内核态的地址空间,这里存放在整个内核的代码和所有
的内核模块,以及内核所维护的数据。用户运行一个程序,该程序所创建的进程开始是
运行在用户态的,如果要执行文件操作,网络数据发送等操作,必须通过write,send
等系统调用,这些系统调用会调用内核中的代码来完成操作,这时,必须切换到Ring0,
然后进入3GB-4GB中的内核地址空间去执行这些代码完成操作,完成后,切换回Ring3,
回到用户态。这样,用户态的程序就不能 随意操作内核地址空间,具有一定的安全保护作用。
关于内核线程
ps命令列出来的线程, 如果被"[]"括起来了, 这就是内核线程
再论fork
fork后, 子进程复制哪些东西
一句话总结, 就是所有 writeable 的东西都会复制.包括:
堆,栈, 数据段, 未初始化数据段, 打开的文件描述符,
信号安装过的handler, 共享库, ipc(共享内存,消息队列,信号量)
注意未决信号不会继承过来, 新进程会重置它的未决信号链
子进程和父进程共享哪些东西
一句话总结, 就是所有 read-only 的东西都不用复制, 父子进程共享, 包括:
正文段和字符串常量
哪些东西, 子进程不会从父进程继承
进程id, 各种锁(内存锁,文件锁), 定时器, 未决信号
发表评论
-
Virtualbox下Windows和Linux实现文件互传
2012-07-17 21:05 33121 Windows安装好Linux虚拟机 2 在Lin ... -
Memcached源码分析之网络模型篇
2012-03-02 01:46 4088memcached 采用多线程的工作方式, 主线程接收连接, ... -
Memcached源码分析之内存管理篇
2012-02-26 15:04 11985使用命令 set(key, value) ... -
多线程与volatile变量
2012-02-25 17:07 5353volatile 修饰的变量表示改变量的值是易变的,编译 ... -
items
2011-11-12 19:30 71 上肢长 2 上臂长 ... -
fds
2011-11-12 19:23 10身高(静态) 眼高 ... -
xml
2011-11-12 18:58 7<item idx = "1" ... -
fff
2011-11-12 18:30 8上肢长 上臂长 两下颌角宽 两眼内宽 两耳屏点间 ... -
(转) memcached采用的网络模型
2011-10-12 01:58 12memcached采用的网络模型 ... -
Nginx 内存池
2011-10-12 01:46 7nginx的内存管理,主要是用来实现防止内存泄露,和内存碎片, ... -
Nginx负载均衡
2011-10-12 01:40 10nginx的upstream目前支持5种方式的分配 ... -
守护进程的实现
2011-09-30 01:43 18086个步骤 步骤1:创建子进程,杀死父进程,目的是为了步 ... -
非阻塞connect的实现
2011-09-30 01:12 14711步骤1: 设置非阻塞,启动连接 实现非阻塞 connect ... -
Memcached内存管理机制
2011-09-29 20:57 2290Slab 分配机制 Memcache ... -
关于大端法和小端法
2011-09-28 23:15 2298typedef union { int n; ... -
vim配置文件精简版
2011-09-19 09:37 1907"Get out of VI's compatibl ... -
(转) Linux 的僵尸(zombie)进程
2011-09-17 20:29 3114原文地址: http://cool ... -
Linux信号详解
2011-09-17 01:02 36218一 信号的种类 可靠信号与不可靠信号, 实时信号与非实时信号 ... -
消息队列
2011-09-15 22:16 12195一 应用场景 有很多业务, 客户端和内网都要进行数据传 ... -
vim + taglist + ctags + cscope 简单使用
2011-09-08 21:58 3517ctags用来跳转, taglist用来列出当前文件的变量, ...
相关推荐
linux 进程和线程编程 pipe --原始管道 命名管道 消息队列 信号量 内存共享 线程编程
linux下的进程、线程
Linux进程、线程和调度(1).pdf 进程调度学习资料共享。
linux unix 进程 线程linux unix 进程 线程linux unix 进程 线程linux unix 进程 线程linux unix 进程 线程linux unix 进程 线程
linux 进程 线程 fork 的深入思考 一道面试题的思考
2012.3.22 linux进程,线程,网络编程
基础知识:线程和进程,二.Linux 2.4内核中的轻量进程实现,三.LinuxThread的线程机制,1.线程描述数据结构及实现限制,2.嵌入式linux开发教程:管理线程,3.嵌入式linux开发教程:线程栈,4.嵌入式linux开发教程:...
linux进程的最大线程数 及最大进程数.zip
linux进程和线程编程PPT学习教案.pptx
代码 任务书 课程设计报告都有 直接可以用
Linux进程、线程和调度(3).pdf Linux进程调度学习资料共享
Linux进程、线程和调度(2).pdf Linux进程调度学习资料共享
Linux进程、线程和调度(4).pdf Linux进程调度学习资料共享
详细介绍了linux下的程序、进程、线程的区别与共同点,是学习linux不可多得的好帮手
linux系统下,C语言多线程多进程编程
这个程序创建一个子进程,执行an_ch2_1b。这个程序不断地输出如下行: Those output come from child,[系统时间] 观察程序运行的结果,并对你看到的现象进行解释。 2.在linux环境下编写一个控制台应用程序,程序中有...
Linux下多线程及多进程及同步与互斥编程详细介绍
linux进程与线程汇总.ppt
全双工邮箱通讯,两个程序分别同过共享内存(邮箱)相互传输数据, 分别都有一个读写线程,读写个自通讯数据区域,一个读线程对应另一个进程的写线程;
这个资料是我精心寻找的一些LINUX进程线程方面的资料,包括了LINUX进程线程编程,进程间通信等内容,是学习LINXU系统编程比较好的而且容易理解的一些编程资料