首页 > 代码库 > 进程间关系
进程间关系
POSIX规定一个进程内部的多个thread要共享一个PID,
但是,在linux kernel中不论是进程还是线程,都是会分配一个task struct并且分配一个唯一的PID(这时候PID其实就是thread ID)。
这样,为了满足POSIX的线程规定,linux引入了线程组的概念,一个进程中的所有线程所共享的那个PID被称为线程组ID,也就是task struct中的tgid成员。
因此,在linux kernel中,线程组ID(tgid,thread group id)就是传统意义的进程ID。
对于getpid系统调用,linux内核返回了tgid。对于gettid系统调用,本意是要求返回线程ID,在linux内核中,返回了task struct的pid成员。
一言以蔽之,POSIX的进程ID就是linux中的线程组ID。POSIX的线程ID也就是linux中的pid。
我们可以通过如下命令获取进程的状态信息:
# cat /proc/1350/stat 1350 (system_server) S 451 451 0 0 -1 4194624 169945 0 1536 ...
其中第一个数字是pid,S后面的三个数分别是ppid、pgid、sid。
# cat /proc/1350/status
Name: system_server
State: S (sleeping)
Tgid: 1350
Pid: 1350
PPid: 451
TracerPid: 0
...
通过上面两个命令,能列出如下几个比较典型的进程之间的关系:
comm | pid | ppid | tgid | pgid | sid |
---|---|---|---|---|---|
init | 1 | 0 | 1 | 0 | 0 |
kthreadd | 2 | 0 | 2 | 0 | 0 |
ksoftirqd/0 | 3 | 2 | 3 | 0 | 0 |
zygote64 | 451 | 1 | 451 | 451 | 0 |
zygote | 452 | 1 | 452 | 452 | 0 |
system_server | 1350 | 451 | 1350 | 451 | 0 |
PackageManager | 1454 | 451 | 1350 | 451 | 0 |
com.miui.video | 7041 | 452 | 7041 | 451 | 0 |
0号进程:swapper进程、又名idle进程,内核启动时的第一个执行流。负责初始化内核各个模块,并创建init进程和ktheadd进程,最后进入idle循环,负责idle的管理和cpu热插拔之类的事务。
1号进程:init进程,用户空间的第一个进程,也是所有用户态进程的始祖进程,负责创建和管理各个native进程。
2号进程:kthreadd进程,内核线程的始祖进程,负责创建ksoftirqd/0等内核线程。
ksoftirqd/0:内核线程,只能在内核态执行。
zygote进程:init创建的,有64位和32位两种,所有的java进程都是由他们孵化而来,他们是所有java进程的父进程。
system_server进程:Android的核心进程,1350号线程是其主线程
PackageManager线程:system_server进程里的一个子线程。
com.miui.video:普通的一个32位java进程。
【pid】
表示一个调度单位task的id:
struct task_struct {
...
pid_t pid;
...
}
调度单位即执行流,每个执行流都对应一个task_struct。每个task_struct都有唯一的id就是pid。
【ppid】
一个task_struct可以创建另一个task_struct,两者有父子关系,ppid就是一个执行流的父执行流。
但这种父子关系并非是绝对的,比如:
static struct task_struct *copy_process(unsigned long clone_flags,...) { ... /* CLONE_PARENT re-uses the old parent */ if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) { p->real_parent = current->real_parent; } else { p->real_parent = current; } ... }
如果创建task_struct时设置了CLONE_THREAD或CLONE_PARENT,
被创建者的父执行流就是当前执行流的父执行流,否则被创建者的父执行流就是当前执行流。
比如PackageManager(1454)是system_server的主线程(1350)创建的,
但1454的ppid不是1350,而是451,也就是1350的父执行流zygote64的主线程。
【tgid】
一个或多个线程可以组成一个线程组,线程组内的各个线程会共享地址空间、信号处理函数、文件表舒服等资源。
线程组中主线程的pid就是这个线程组所属线程的tgid。
比如PackageManager(1454)和system_server(1350)同属一个线程组,tgid同为1350。
线程是通过pthread_create()函数创建的:
int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, void* (*start_routine)(void*), void* arg) { .. int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; int rc = clone(__pthread_start, child_stack, flags, thread, &(thread->tid), tls, &(thread->tid)); ... } asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, int __user *parent_tidptr, int tls_val, int __user *child_tidptr, struct pt_regs *regs) { if (!newsp) newsp = regs->ARM_sp; return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr); } long do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr) { ... p = copy_process(clone_flags, stack_start, regs, stack_size, child_tidptr, NULL, trace); ... } static struct task_struct *copy_process(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *child_tidptr, struct pid *pid, int trace) { ... p->tgid = p->pid; //tgid默认为自己的pid if (clone_flags & CLONE_THREAD) p->tgid = current->tgid; //tgid为当前线程的tgid ... }
【pgid】
上面说的线程组,其实就是我们常说的进程。
多个进程也能组成组叫进程组,它的领头进程的主线程pid就是pgid。
首先默认情况下,进程组id都是从父进程继承过来的,但是init在创建naitive 服务后会修改pgid。
void service_start(struct service *svc, const char *dynamic_args) { ... pid_t pid = fork(); if (pid == 0) { //子进程 ... setpgid(0, getpid()); ... } } SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid) { struct task_struct *p; struct task_struct *group_leader = current->group_leader; //native进程的grop_leader就是自己 struct pid *pgrp; if (!pid) //如果传入的pid为0,则 pid = task_pid_vnr(group_leader); pid = task_pid_vnr(group_leader); p = find_task_by_vpid(pid); pgrp = task_pid(p); if (pgid != pid) { ... pgrp = find_vpid(pgid); } if (task_pgrp(p) != pgrp) //原先的pgrp是0号进程,而新的pgrp是当前进程自身 change_pid(p, PIDTYPE_PGID, pgrp); //修改pgid为当前进程自身 ... }
setpgid(0, getpid())其实就是将自己的pgid设置成自己的pid,
因此所有init创建出来的native进程,他们的pgid就是自身的pid。
如果没有特殊设置,子进程会继承父进程的gpid。这样做有啥用呢?
原来init在收到子进程的退出信号SIGCHLD的后,会直接将子进程所属的进程组里的所有进程杀掉,代码如下:
static bool wait_for_one_process() { int status; pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG)); ... service* svc = service_find_by_pid(pid); ... if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) { NOTICE("Service ‘%s‘ (pid %d) killing any children in process group\n", svc->name, pid); kill(-pid, SIGKILL); //这里会杀掉进程组里的所有进程 } }
一般我们用kill系统调用的时,传入的pid是正数,而这里却传入负数。
kill系统调用传正数pid会杀线程组,传负数pid则会杀掉进程组。
相关代码如下:
SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) { struct siginfo info; info.si_signo = sig; info.si_errno = 0; info.si_code = SI_USER; info.si_pid = task_tgid_vnr(current); info.si_uid = current_uid(); return kill_something_info(sig, &info, pid); } static int kill_something_info(int sig, struct siginfo *info, pid_t pid) { int ret; if (pid > 0) { ret = kill_pid_info(sig, info, find_vpid(pid)); //杀线程组 return ret; } if (pid != -1) { ret = __kill_pgrp_info(sig, info, pid ? find_vpid(-pid) : task_pgrp(current)); //杀进程组 } else { ... } return ret; }
因此zygote创建出来的所有子进程,它的pgid都是zygote的pid,所以zygote挂掉,它的子进程都会被init杀掉。
64位下有两个zygote,zygote64和zygote32。
64位应用的父进程是zygote64,它的pgid也是zygote64的pid;
32位应用的父进程是zygote32,它的pgid却是zygote64的pid,这是怎么回事呢?
原来不管32位或64位的zygote,它在创建完子进程后,会调用setChildPgid()来改变子进程的pgid。
如下代码:
private void setChildPgid(int pid) { try { Os.setpgid(pid, Os.getpgid(peer.getPid())); } catch (ErrnoException ex) { ... } }
这里的peer是socket的对端,也就是system_server。而system_server的pgid就是zygote64的pid。
这样,所有zygote32创建出来的子进程,他们的pgid都是zygote64的pid了。
例如:
comm | pid | ppid | tgid | pgid | sid |
---|---|---|---|---|---|
com.miui.video | 7041 | 452 | 7041 | 451 | 0 |
com.miui.video()的父进程是zygote32(452),但它的pgid是zygote64(451)。
因此如下面的rc:
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary class main socket zygote_secondary stream 660 root system onrestart restart zygote
当zygote32退出重启时,同时也会重启zygote64,这样zygote32创建出来的进程才能退出。
不过奇怪的进程间关系在某种情况下会有问题,比如:http://www.cnblogs.com/YYPapa/p/6848806.html
【sid】
Android中绝大部分情况下都没使用设个sid,绝大部分的sid都是0,只有用sh程序会设置这个sid,这里就不展开了。
进程间关系