Linux的进程管理之进程与线程—2

1. 背景描述

1.1 多进程或者多线程?

我们先想想多进程多线程的特点:
多进程特点
每个进程独立运行,一个进程挂死,不影响其他进程的运行;
虚拟内存隔离,内存安全。
需要增加进程切换的开销。
多线程特点
内存无法进行隔离,一个线程挂死,整个程序挂死;
但是线程切换的开销相对进程低;
在此特点的基础上,回答为何需要多进程多线程,当然是我们需要多进程与多线程的优点,Linux是一个多进程的操作系统,同时也支持多线程。

1.2.freertos uCOS等系统与Linux系统之间的区别?

比如stm32系列可以运行freertos uCOS等内核,此系统也可以内存管理、任务调度等功能,但是最大的区别是不能运行多进程的程序,因为它没有mmu,不支持多进程的切换。

1.3.多进程与mmu?

考虑下这个问题,多进程是通过什么进行切换的,通过mmu控制器来进行切换,正是因为有了mmu,才能运行linux之类的多进程操作系统,才会有虚拟地址的概念。

2.Linux进程描述之task_struct

进程与线程在Linux下都是通过task_struct的结构体来进行描述的,仅有的不同的是资源分配的不同而已,结构体比较大,下面只截取关键的部分进行说明;

2.1 进程状态

volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
void *stack;
atomic_t usage;
unsigned int flags;	/* per process flags, defined below */
unsigned int ptrace;

进程的基本状态,内核中描述进程的状态:

#define TASK_RUNNING		    0
#define TASK_INTERRUPTIBLE	    1
#define TASK_UNINTERRUPTIBLE	2
#define __TASK_STOPPED		    4
#define __TASK_TRACED		    8
/* in tsk->exit_state */
#define EXIT_ZOMBIE		       16
#define EXIT_DEAD		       32
/* in tsk->state again */
#define TASK_DEAD		       64
#define TASK_WAKEKILL		  128
#define TASK_WAKING		      256
#define TASK_STATE_MAX		  512

/* Convenience macros for the sake of set_task_state */
#define TASK_KILLABLE		(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED		(TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED		    (TASK_WAKEKILL | __TASK_TRACED)

/* Convenience macros for the sake of wake_up */
#define TASK_NORMAL		(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
#define TASK_ALL		(TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED)

/* get_task_state() */
#define TASK_REPORT		(TASK_RUNNING | TASK_INTERRUPTIBLE | \
				 TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \
				 __TASK_TRACED)

flags反应进程的状态信息,用于内核识别当前进程的状态;
ptrace提供了一种父进程,它可以被用来控制子进程,常被用来进行断点调试;

2.2 进程调度字段

int prio, static_prio, normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;

包含进程优先级,调度类sched_class,以及调度实体sched_entity、 sched_rt_entity 等;

2.3 内存字段

	struct mm_struct *mm, *active_mm;
#ifdef CONFIG_COMPAT_BRK
	unsigned brk_randomized:1;
#endif
#if defined(SPLIT_RSS_COUNTING)
	struct task_rss_stat	rss_stat;
#endif

进程的内存空间描述符等;

2.4 进程关系的字段

	/* 
	 * pointers to (original) parent process, youngest child, younger sibling,
	 * older sibling, respectively.  (p->father can be replaced with 
	 * p->real_parent->pid)
	 */
	struct task_struct *real_parent; /* real parent process */
	struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
	/*
	 * children/sibling forms the list of my natural children
	 */
	struct list_head children;	/* list of my children */
	struct list_head sibling;	/* linkage in my parent's children list */
	struct task_struct *group_leader;	/* threadgroup leader */

	/*
	 * ptraced is the list of tasks this task is using ptrace on.
	 * This includes both natural children and PTRACE_ATTACH targets.
	 * p->ptrace_entry is p's link on the p->parent->ptraced list.
	 */
	struct list_head ptraced;
	struct list_head ptrace_entry;

包括父进程、子进程以及进程组的领头进程等;

2.5 资源字段

/* CPU-specific state of this task */
	struct thread_struct thread;
/* filesystem information */
	struct fs_struct *fs;
/* open file information */
	struct files_struct *files;
/* namespaces */
	struct nsproxy *nsproxy;
/* signal handlers */
	struct signal_struct *signal;
	struct sighand_struct *sighand;

包括文件描述符、打开的文件、信号等参数;

3.进程与线程的创建

task_struct描述了对资源的分配以及进程的状态信息等;
进程与线程的创建函数:fork vfork clone pthread_create kernel_thread,前四个是用户态的函数,kernel_thread是内核态创建线程所使用的;

3.1 fork vfork clone pthread_create函数区别于联系

以上函数实现皆在c库里面进行实现,即glibc中实现,查看glibc源码即可知,以上都是调用了clone函数,然后通过sys_clone的系统调用进行实现,然后通过do_fork函数进行进程或者线程的实际创建。

在这里插入图片描述

可以看见,用户态是通过系统调用进入内核,然后最终调用到do_fork函数进行进程或者线程的创建,也更能说明进程与线程的关系在Linux中其实是类似的;
为何没有通过fork直接系统调用,此问题可以参考http://cn.voidcc.com/question/p-rxufpyok-bhk.html

3.2 kernel_thread函数

以上四个接口都是用户态的函数:内核态也有一个函数接口kernel_thread,此函数用于内核态的线程创建,

//arch/arm/kernel/process.c
/*
 * Create a kernel thread.
 */
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
	struct pt_regs regs;

	memset(&regs, 0, sizeof(regs));

	regs.ARM_r4 = (unsigned long)arg;
	regs.ARM_r5 = (unsigned long)fn;
	regs.ARM_r6 = (unsigned long)kernel_thread_exit;
	regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;
	regs.ARM_pc = (unsigned long)kernel_thread_helper;
	regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;

	return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}

内核的线程创建,最终还是调用的函数do_fork函数,仅有的差异也只是标志的差异;

3.3 clone_flags

fork vfork clone pthread_create kernel_thread最大的差异也就是clone_flags,此标志控制着资源的分配以及是否继承父进程/线程等信息;
从下表也可以看出fork 、vfork、pthread_create调用了clone的系统调用也是容易理解的,因为clone可以方便用户传参,而其余几个glibc中已经规定好了参数;
在这里插入图片描述

内核的clone_flag的定义

//include/linux/sched.h
/*
 * cloning flags:
 */
#define CSIGNAL		0x000000ff	/* signal mask to be sent at exit */
#define CLONE_VM	0x00000100	/* set if VM shared between processes */
#define CLONE_FS	0x00000200	/* set if fs info shared between processes */
#define CLONE_FILES	0x00000400	/* set if open files shared between processes */
#define CLONE_SIGHAND	0x00000800	/* set if signal handlers and blocked signals shared */
#define CLONE_PTRACE	0x00002000	/* set if we want to let tracing continue on the child too */
#define CLONE_VFORK	0x00004000	/* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT	0x00008000	/* set if we want to have the same parent as the cloner */
#define CLONE_THREAD	0x00010000	/* Same thread group? */
#define CLONE_NEWNS	0x00020000	/* New namespace group? */
#define CLONE_SYSVSEM	0x00040000	/* share system V SEM_UNDO semantics */
#define CLONE_SETTLS	0x00080000	/* create a new TLS for the child */
#define CLONE_PARENT_SETTID	0x00100000	/* set the TID in the parent */
#define CLONE_CHILD_CLEARTID	0x00200000	/* clear the TID in the child */
#define CLONE_DETACHED		0x00400000	/* Unused, ignored */
#define CLONE_UNTRACED		0x00800000	/* set if the tracing process can't force CLONE_PTRACE on this clone */
#define CLONE_CHILD_SETTID	0x01000000	/* set the TID in the child */
/* 0x02000000 was previously the unused CLONE_STOPPED (Start in stopped state)
   and is now available for re-use. */
#define CLONE_NEWUTS		0x04000000	/* New utsname group? */
#define CLONE_NEWIPC		0x08000000	/* New ipcs */
#define CLONE_NEWUSER		0x10000000	/* New user namespace */
#define CLONE_NEWPID		0x20000000	/* New pid namespace */
#define CLONE_NEWNET		0x40000000	/* New network namespace */
#define CLONE_IO		0x80000000	/* Clone io context */
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值