文档库 最新最全的文档下载
当前位置:文档库 › linux进程的深入理解与分析

linux进程的深入理解与分析

linux进程的深入理解与分析
linux进程的深入理解与分析

linux进程的深入理解与分析

进程是程序的一次执行过程。用剧本和演出来类比,程序相当于剧本,而进程则相当于剧本的一次演出,舞台、灯光则相当于进程的运行环境。

进程的堆栈

每个进程都有自己的堆栈,内核在创建一个新的进程时,在创建进程控制块task_struct的同时,也为进程创建堆栈。一个进程有2个堆栈:用户堆栈和系统堆栈;用户堆栈的空间指向用户地址空间,内核堆栈的空间指向内核地址空间。当进程在用户态运行时,CPU堆栈指针寄存器指向用户堆栈地址,使用用户堆栈;当进程运行在内核态时,CPU堆栈指针寄存器指向的是内核栈空间地址,使用的是内核栈。

进程用户栈和内核栈之间的切换

当进程由于中断或系统调用从用户态转换到内核态时,进程所使用的栈也要从用户栈切换到内核栈。系统调用实质就是通过指令产生中断,称为软中断。进程因为中断(软中断或硬件产生中断),使得CPU切换到特权工作模式,此时进程陷入内核态,进程进入内核态后,首先把用户态的堆栈地址保存在内核堆栈中,然后设置堆栈指针寄存器的地址为内核栈地址,这样就完成了用户栈向内核栈的切换。

当进程从内核态切换到用户态时,最后把保存在内核栈中的用户栈地址恢复到CPU栈指针寄存器即可,这样就完成了内核栈向用户栈的切换。

这里要理解一下内核堆栈。前面我们讲到,进程从用户态进入内核态时,需要在内核栈中保存用户栈的地址。那么进入内核态时,从哪里获得内核栈的栈指针呢?

要解决这个问题,先要理解从用户态刚切换到内核态以后,进程的内核栈总是空的。这点很好理解,当进程在用户空间运行时,使用的是用户栈;当进程在内核态运行时,内核栈中保存进程在内核态运行的相关信息,但是当进程完成了内核态的运行,重新回到用户态时,此时内核栈中保存的信息全部恢复,也就是说,进程在内核态中的代码执行完成回到用户态时,内核栈是空的。

理解了从用户态刚切换到内核态以后,进程的内核栈总是空的,那刚才这个问题就很好理解了,因为内核栈是空的,那当进程从用户态切换到内核态后,把内核栈的栈顶地址设置给CPU 的栈指针寄存器就可以了。

Arm Linux内核栈定义如下(可能现在的版本有所改变,但不妨碍我们对内核栈的理解),在include/linux/sched.h中定义了如下一个联合结构:

union thread_union {

struct thread_info thread_info;

unsigned long stack[THREAD_SIZE/sizeof(long)];

};

从这个结构可以看出,内核栈占8KB的内存区。实际上,进程的task_struct结构所占的内存是由内核动态分配的,更确切地说,内核根本不给task_struct分配内存,而仅仅给内核栈分配8K 的内存,并把其中的一部分给task_struct使用。

这样内核栈的起始地址就是union task_union变量的地址+8K 字节的长度。例如:我们动态分配一个union task_union类型的变量如下:

unsigned char *gtaskkernelstack;

gtaskkernelstack = kmalloc(sizeof(union task_union));

那么该进程每次进入内核态时,内核栈的起始地址均为:(unsigned char *)gtaskkernelstack + 8096

进程上下文

进程切换现场称为进程上下文(context),包含了一个进程所具有的全部信息,一般包括:进程控制块(Process Control Block,PCB)、有关程序段和相应的数据集。

进程控制块PCB(任务控制块)

进程控制块是进程在内存中的静态存在方式,Linux内核中用struct task_struct表示一个进程(相当于进程的人事档案)。进程的静态描述必须保证一个进程在获得CPU并重新进入运行态时,能够精确的接着上次运行的位置继续进行,相关的程序段,数据以及CPU现场信息必须保存。处理器现场信息主要包括处理器内部寄存器和堆栈等基本数据。

进程控制块一般可以分为进程描述信息、进程控制信息,进程相关的资源信息和CPU现场保护机构。

进程的切换

当一个进程的时间片到时,进程需要让出CPU给其他进程运行,内核需要进行进程切换。Linux 的进程切换是通过调用函数进程切换函数schedule来实现的。进程切换主要分为2个步骤:

1. 调用switch_mm()函数进行进程页表的切换;

2. 调用 switch_to() 函数进行 CPU寄存器切换;

__switch_to定义在arch/arm/kernel目录下的entry-armv.S 文件中,源码如下:

ENTRY(__switch_to)

UNWIND(.fnstart )

UNWIND(.cantunwind )

add ip, r1, #TI_CPU_SAVE

ldr r3, [r2, #TI_TP_VALUE]

ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack

THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack

THUMB( str sp, [ip], #4 )

THUMB( str lr, [ip], #4 )

#ifdef CONFIG_CPU_USE_DOMAINS

ldr r6, [r2, #TI_CPU_DOMAIN]

#endif

set_tls r3, r4, r5

#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) ldr r7, [r2, #TI_TASK]

ldr r8, =__stack_chk_guard

ldr r7, [r7, #TSK_STACK_CANARY]

#endif

#ifdef CONFIG_CPU_USE_DOMAINS

mcr p15, 0, r6, c3, c0, 0 @ Set domain register

#endif

mov r5, r0

add r4, r2, #TI_CPU_SAVE

ldr r0, =thread_notify_head

mov r1, #THREAD_NOTIFY_SWITCH

bl atomic_notifier_call_chain

#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) str r7, [r8]

#endif

THUMB( mov ip, r4 )

mov r0, r5

ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously THUMB( ldmia ip!, {r4 - sl, fp} ) @ Load all regs saved previously THUMB( ldr sp, [ip], #4 )

THUMB( ldr pc, [ip] )

UNWIND(.fnend )

ENDPROC(__switch_to)

switch_to的处理流程如下:

1. 保存本进程的CPU寄存器(PC、R0 ~ R13)到本进程的栈中;

2. 保存SP(本进程的栈基地址)到task->thread.save 中;

3. 从新进程的task->thread.save恢复SP为新进程的栈基地址;

4. 从新进程的栈中恢复新进程的CPU相关寄存器值,

5. 新进程开始运行,完成任务切换。

这里可能会想到,在进行任务切换的时候,到底是在运行进程1还是运行进程2呢?进程切换的时候,已经进行页表切换,那页表切换之后,切换进程使用的是进程1还是进程2的页表呢?要回答这个问题,首先我们要明白由谁来完成进程切换?

通过对操作系统的理解,毫无疑问,进程切换是由内核来完成的,也就是说,在进行进程切换时,CPU运行在内核模式,使用的是内核空间的内核代码,它既不属于进程1,也不属于进程2,当进程的时间片到时,内核提供服务来完成进程的切换。既不使用进程1的页表,也不使用进程2的页表,使用的内核映射页表。这样我们就很好理解上面的问题了。

什么是进程的“内核栈”

在每一个进程的生命周期中,必然会通过到系统调用陷入内核。在执行系统调用陷入内核之后,这些内核代码所使用的栈并不是原先用户空间中的栈,而是一个内核空间的栈,这个称作进程的“内核栈”。

比如,有一个简单的字符驱动实现了open方法。在这个驱动挂载后,应用程序对那个驱动所对应的设备节点执行open操作,这个应用程序的open其实就通过glib库调用了Linux的open 系统调用,执行系统调用陷入内核后,处理器转换为了特权模式(具体的转换机制因构架而异,对于ARM来说普通模式和用户模式的的栈针(SP)是不同的寄存器),此时使用的栈指针就是内核栈指针,他指向内核为每个进程分配的内核栈空间。

内核栈的作用

我个人的理解是:在陷入内核后,系统调用中也是存在函数调用和自动变量,这些都需要栈支持。用户空间的栈显然不安全,需要内核栈的支持。此外,内核栈同时用于保存一些系统调用前的应用层信息(如用户空间栈指针、系统调用参数)。

内核栈与进程结构体的关联

每个进程在创建的时候都会得到一个内核栈空间,内核栈和进程的对应关系是通过2个结构体中的指针成员来完成的:

(1)struct task_struct

在学习Linux进程管理肯定要学的结构体,在内核中代表了一个进程,其中记录的进程的所有状态信息,定义在sched.h (include/linux)。

其中有一个成员:void *stack;就是指向下面的内核栈结构体的“栈底”。

在系统运行的时候,宏current获得的就是当前进程的struct task_struct结构体。

struct task_struct {

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;

#ifdef CONFIG_SMP

struct llist_node wake_entry;

int on_cpu;

#endif

int on_rq;

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;

#ifdef CONFIG_CGROUP_SCHED

struct task_group *sched_task_group;

#endif

#ifdef CONFIG_PREEMPT_NOTIFIERS

/* list of struct preempt_notifier: */

struct hlist_head preempt_notifiers;

#endif

/*

* fpu_counter contains the number of consecutive context switches * that the FPU is used. If this is over a threshold, the lazy fpu * saving becomes unlazy to save the trap. This is an unsigned char * so that after 256 times the counter wraps and the behavior turns * lazy again; this to deal with bursty apps that only use FPU for * a short time

*/

unsigned char fpu_counter;

#ifdef CONFIG_BLK_DEV_IO_TRACE

unsigned int btrace_seq;

#endif

unsigned int policy;

cpumask_t cpus_allowed;

#ifdef CONFIG_PREEMPT_RCU

int rcu_read_lock_nesting;

char rcu_read_unlock_special;

struct list_head rcu_node_entry;

#endif /* #ifdef CONFIG_PREEMPT_RCU */

#ifdef CONFIG_TREE_PREEMPT_RCU

struct rcu_node *rcu_blocked_node;

#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */

#ifdef CONFIG_RCU_BOOST

struct rt_mutex *rcu_boost_mutex;

#endif /* #ifdef CONFIG_RCU_BOOST */

#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) struct sched_info sched_info;

#endif

struct list_head tasks;

#ifdef CONFIG_SMP

struct plist_node pushable_tasks;

#endif

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;

/* task state */

int exit_state;

int exit_code, exit_signal;

int pdeath_signal; /* The signal sent when the parent dies */

unsigned int jobctl; /* JOBCTL_*, siglock protected */

/* ??? */

unsigned int personality;

unsigned did_exec:1;

unsigned in_execve:1; /* Tell the LSMs that the process is doing an

* execve */

unsigned in_iowait:1;

/* Revert to default priority/policy when forking */

unsigned sched_reset_on_fork:1;

unsigned sched_contributes_to_load:1;

#ifdef CONFIG_GENERIC_HARDIRQS

/* IRQ handler threads */

unsigned irq_thread:1;

#endif

pid_t pid;

pid_t tgid;

#ifdef CONFIG_CC_STACKPROTECTOR

/* Canary value for the -fstack-protector gcc feature */

unsigned long stack_canary;

#endif

/*

* 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 __rcu *real_parent; /* real parent process */

struct task_struct __rcu *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;

/* PID/PID hash table linkage. */

struct pid_link pids[PIDTYPE_MAX];

struct list_head thread_group;

struct completion *vfork_done; /* for vfork() */

int __user *set_child_tid; /* CLONE_CHILD_SETTID */

int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */

cputime_t utime, stime, utimescaled, stimescaled;

cputime_t gtime;

#ifndef CONFIG_VIRT_CPU_ACCOUNTING

cputime_t prev_utime, prev_stime;

#endif

unsigned long nvcsw, nivcsw; /* context switch counts */

struct timespec start_time; /* monotonic time */

struct timespec real_start_time; /* boot based time */

/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */

unsigned long min_flt, maj_flt;

struct task_cputime cputime_expires;

struct list_head cpu_timers[3];

/* process credentials */

const struct cred __rcu *real_cred; /* objective and real subjective task

* credentials (COW) */

const struct cred __rcu *cred; /* effective (overridable) subjective task

* credentials (COW) */

struct cred *replacement_session_keyring; /* for KEYCTL_SESSION_TO_PARENT */

char comm[TASK_COMM_LEN]; /* executable name excluding path

- access with [gs]et_task_comm (which lock

it with task_lock())

- initialized normally by setup_new_exec */

/* file system info */

int link_count, total_link_count;

#ifdef CONFIG_SYSVIPC

/* ipc stuff */

struct sysv_sem sysvsem;

#endif

#ifdef CONFIG_DETECT_HUNG_TASK

/* hung task detection */

unsigned long last_switch_count;

#endif

/* 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;

sigset_t blocked, real_blocked;

sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */ struct sigpending pending;

unsigned long sas_ss_sp;

size_t sas_ss_size;

int (*notifier)(void *priv);

void *notifier_data;

sigset_t *notifier_mask;

struct audit_context *audit_context;

#ifdef CONFIG_AUDITSYSCALL

uid_t loginuid;

unsigned int sessionid;

#endif

seccomp_t seccomp;

/* Thread group tracking */

u32 parent_exec_id;

u32 self_exec_id;

/* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, * mempolicy */

spinlock_t alloc_lock;

/* Protection of the PI data structures: */

raw_spinlock_t pi_lock;

#ifdef CONFIG_RT_MUTEXES

/* PI waiters blocked on a rt_mutex held by this task */

struct plist_head pi_waiters;

/* Deadlock detection and priority inheritance handling */

struct rt_mutex_waiter *pi_blocked_on;

#endif

#ifdef CONFIG_DEBUG_MUTEXES

/* mutex deadlock detection */

struct mutex_waiter *blocked_on;

#endif

#ifdef CONFIG_TRACE_IRQFLAGS

unsigned int irq_events;

unsigned long hardirq_enable_ip;

unsigned long hardirq_disable_ip;

unsigned int hardirq_enable_event;

unsigned int hardirq_disable_event;

int hardirqs_enabled;

int hardirq_context;

unsigned long softirq_disable_ip;

unsigned long softirq_enable_ip;

unsigned int softirq_disable_event;

unsigned int softirq_enable_event;

int softirqs_enabled;

int softirq_context;

#endif

#ifdef CONFIG_LOCKDEP

# define MAX_LOCK_DEPTH 48UL

u64 curr_chain_key;

int lockdep_depth;

unsigned int lockdep_recursion;

struct held_lock held_locks[MAX_LOCK_DEPTH];

gfp_t lockdep_reclaim_gfp;

#endif

/* journalling filesystem info */

void *journal_info;

/* stacked block device info */

struct bio_list *bio_list;

#ifdef CONFIG_BLOCK

/* stack plugging */

struct blk_plug *plug;

#endif

/* VM state */

struct reclaim_state *reclaim_state;

struct backing_dev_info *backing_dev_info;

struct io_context *io_context;

unsigned long ptrace_message;

siginfo_t *last_siginfo; /* For ptrace use. */

struct task_io_accounting ioac;

#if defined(CONFIG_TASK_XACCT)

u64 acct_rss_mem1; /* accumulated rss usage */

u64 acct_vm_mem1; /* accumulated virtual memory usage */

cputime_t acct_timexpd; /* stime + utime since last update */ #endif

#ifdef CONFIG_CPUSETS

nodemask_t mems_allowed; /* Protected by alloc_lock */

seqcount_t mems_allowed_seq; /* Seqence no to catch updates */ int cpuset_mem_spread_rotor;

int cpuset_slab_spread_rotor;

#endif

#ifdef CONFIG_CGROUPS

/* Control Group info protected by css_set_lock */

struct css_set __rcu *cgroups;

/* cg_list protected by css_set_lock and tsk->alloc_lock */

struct list_head cg_list;

#endif

#ifdef CONFIG_FUTEX

struct robust_list_head __user *robust_list;

#ifdef CONFIG_COMPAT

struct compat_robust_list_head __user *compat_robust_list;

#endif

struct list_head pi_state_list;

struct futex_pi_state *pi_state_cache;

#endif

#ifdef CONFIG_PERF_EVENTS

struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];

struct mutex perf_event_mutex;

struct list_head perf_event_list;

#endif

#ifdef CONFIG_NUMA

struct mempolicy *mempolicy; /* Protected by alloc_lock */

short il_next;

short pref_node_fork;

#endif

struct rcu_head rcu;

/*

* cache last used pipe for splice

*/

struct pipe_inode_info *splice_pipe;

#ifdef CONFIG_TASK_DELAY_ACCT

struct task_delay_info *delays;

#endif

#ifdef CONFIG_FAULT_INJECTION

int make_it_fail;

#endif

/*

* when (nr_dirtied >= nr_dirtied_pause), it's time to call

* balance_dirty_pages() for some dirty throttling pause

*/

int nr_dirtied;

int nr_dirtied_pause;

unsigned long dirty_paused_when; /* start of a write-and-pause period */

#ifdef CONFIG_LATENCYTOP

int latency_record_count;

struct latency_record latency_record[LT_SAVECOUNT];

#endif

/*

* time slack values; these are used to round up poll() and

* select() etc timeout values. These are in nanoseconds.

*/

unsigned long timer_slack_ns;

unsigned long default_timer_slack_ns;

struct list_head *scm_work_list;

#ifdef CONFIG_FUNCTION_GRAPH_TRACER

/* Index of current stored address in ret_stack */

int curr_ret_stack;

/* Stack of return addresses for return function tracing */

struct ftrace_ret_stack *ret_stack;

/* time stamp for last schedule */

unsigned long long ftrace_timestamp;

/*

* Number of functions that haven't been traced

* because of depth overrun.

*/

atomic_t trace_overrun;

/* Pause for the tracing */

atomic_t tracing_graph_pause;

#endif

#ifdef CONFIG_TRACING

/* state flags for use by tracers */

unsigned long trace;

/* bitmask and counter of trace recursion */

unsigned long trace_recursion;

#endif /* CONFIG_TRACING */

#ifdef CONFIG_CGROUP_MEM_RES_CTLR /* memcg uses this to do batch job */ struct memcg_batch_info {

int do_batch; /* incremented when batch uncharge started */

struct mem_cgroup *memcg; /* target memcg of uncharge */

unsigned long nr_pages; /* uncharged usage */

unsigned long memsw_nr_pages; /* uncharged mem+swap usage */ } memcg_batch;

#endif

#ifdef CONFIG_HAVE_HW_BREAKPOINT

atomic_t ptrace_bp_refcnt;

#endif

};

(2)内核栈结构体union thread_union

union thread_union {

struct thread_info thread_info ;

unsigned long stack [ THREAD_SIZE / sizeof ( long ) ] ;

} ;

其中struct thread_info是记录部分进程信息的结构体,其中包括了进程上下文信息:

/ *

* low level task data that entry .S needs immediate access to .

* __switch_to ( ) assumes cpu_context follows immediately after cpu_domain .

* /

struct thread_info {

unsigned long flags ; / * low level flags * /

int preempt_count ; / * 0 = > preemptable , < 0 = > bug * / mm_segment_t addr_limit ; / * address limit * /

struct task_struct *task; /* main task structure */

struct exec_domain * exec_domain ; / * execution domain * /

__u32 cpu ; / * cpu * /

__u32 cpu_domain ; / * cpu domain * /

struct cpu_context_save cpu_context ; / * cpu context * /

__u32 syscall ; / * syscall number * /

__u8 used_cp [ 16 ] ; / * thread used copro * /

unsigned long tp_value ;

struct crunch_state crunchstate ;

union fp_state fpstate __attribute__ ( ( aligned ( 8 ) ) ) ;

union vfp_state vfpstate ;

#ifdef CONFIG_ARM_THUMBEE

unsigned long thumbee_state ; / * ThumbEE Handler Base register * / #endif

struct restart_block restart_block ;

} ;

关键是其中的task成员,指向的是所创建的进程的struct task_struct结构体

而其中的stack成员就是内核栈。从这里可以看出内核栈空间和 thread_info是共用一块

空间的。如果内核栈溢出, thread_info就会被摧毁,系统崩溃了~~~

内核栈---struct thread_info----struct task_struct三者的关系如下图:

内核栈的产生

在进程被创建的时候,fork族的系统调用中会分别为内核栈和struct task_struct分配空间,调用过程是: kernel/fork.c

fork族的系统调用->do_fork->copy_process->dup_task_struct 在dup_task_struct函数中:

static struct task_struct * dup_task_struct ( struct task_struct * orig ) {

struct task_struct * tsk ;

struct thread_info * ti ;

unsigned long * stackend ;

int err ;

prepare_to_copy ( orig ) ;

tsk = alloc_task_struct();

if ( ! tsk )

return NULL ;

ti = alloc_thread_info(tsk);

if ( ! ti ) {

free_task_struct ( tsk ) ;

return NULL ;

}

err = arch_dup_task_struct ( tsk , orig ) ;

if ( err )

goto out ;

tsk->stack = ti;

err = prop_local_init_single ( & tsk - > dirties ) ;

if ( err )

goto out ;

setup_thread_stack(tsk, orig);

. . . . . .

其中 alloc_task_struct使用内核的slab分配器去为所要创建的进程分配struct

task_struct的空间

而 alloc_thread_info使用内核的伙伴系统去为所要创建的进程分配内核栈(union

thread_union )空间

注意:

后面的tsk->stack = ti;语句,这就是关联了struct task_struct和内核栈

而在setup_thread_stack(tsk, orig);中,关联了内核栈和struct task_struct:

static inline void setup_thread_stack ( struct task_struct * p , struct task_struct * org )

{

* task_thread_info ( p ) = * task_thread_info ( org ) ;

task_thread_info ( p ) - > task = p ;

}

内核栈的大小

由于是每一个进程都分配一个内核栈空间,所以不可能分配很大。这个大小是构架相关的,一般以页为单位。其实也就是上面我们看到的THREAD_SIZE,这个值一般为4K或者8K。对于ARM 构架,这个定义在thread_info.h (arch/arm/include/asm),

#define THREAD_SIZE_ORDER 1

#define THREAD_SIZE 8192

#define THREAD_START_SP ( THREAD_SIZE - 8 )

所以ARM的内核栈是8KB

在(内核)驱动编程时需要注意的问题:

由于栈空间的限制,在编写的驱动(特别是被系统调用使用的底层函数)中要注意避免对栈空间消耗较大的代码,比如递归算法、局部自动变量定义的大小等等

初始内核栈的设置

在arch/arm/kernel/head.S中,系统初始化阶段(ENTRY(stext))

会调用ldr r13, =__mmap_switched @ address to jump to after

在arch/arm/kernel/head-common.S中

__mmap_switched:

adr r3, __mmap_switched_data

ldmia r3!, {r4, r5, r6, r7}

cmp r4, r5 @ Copy data segment if needed

1: cmpne r5, r6

ldrne fp, [r4], #4

strne fp, [r5], #4

bne 1b

mov fp, #0 @ Clear BSS (and zero fp)

1: cmp r6, r7

strcc fp, [r6],#4

bcc 1b

ARM( ldmia r3, {r4, r5, r6, r7, sp})

THUMB( ldmia r3, {r4, r5, r6, r7} )

THUMB( ldr sp, [r3, #16] )

str r9, [r4] @ Save processor ID

str r1, [r5] @ Save machine type

str r2, [r6] @ Save atags pointer

bic r4, r0, #CR_A @ Clear 'A' bit

stmia r7, {r0, r4} @ Save control register values

b start_kernel

ENDPROC(__mmap_switched)

.type __mmap_switched_data, %object

__mmap_switched_data:

.long __data_loc @ r4

.long _sdata @ r5

.long __bss_start @ r6

.long _end @ r7

.long processor_id @ r4

.long __machine_arch_type @ r5

.long __atags_pointer @ r6

.long cr_alignment @ r7

.long init_thread_union + THREAD_START_SP @ sp

.size __mmap_switched_data, . - __mmap_switched_data

基于嵌入式Linux多线程聊天系统的设计与实现

基于嵌入式Linux多线程聊天系统的设计与实现 学生姓名王宣达 学号 S2******* 所在系(院)电子信息工程系 专业名称电路与系统年级 2009级 2011年8月3日

中文摘要

外文摘要

目录 1.引言 (1) 2.Linux多线程聊天系统的设计思想 (3) 2.1 聊天系统中服务器的设计思想 (3) 2.2 聊天系统中客户端的设计思想 (3) 3. Linux多线程聊天系统的实现过程 (5) 3.1 多线程聊天系统中服务器端的实现过程 (5) 3.2 多线程聊天系统中客户端的实现过程 (7) 4.Linux多线程系统设计中出现的问题和解决的方法 (12) 4.1 多线程中资源的释放问题 (12) 4.2 (12) 参考文献 (12)

1.引言 在80年代中期,线程技术就应用到了操作系统中,那时在一个进程中只允许有一个线程,这样多线程就意味着多进程,虽然实现了多任务,但是资源消耗还是非常可观的。而到现在,多线程技术已经被许多操作系统所支持,有Windows/NT,还有Linux。 多线程和进程相比有两点优势: 1.它是一种消耗资源非常少的多任务操作方式。在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种消耗非常大的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,这样创建一个线程所占用的空间远远小于创建一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。当然,随着系统的不同,这个差距也不不同。 2.线程间比进程间的通信机制更为便利。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,这时就要用到互斥锁机制来保证线程间的同步。 所以在本文的多线程聊天程序的设计中,采用多线程的方式设计系统更为适宜。其中,系统中用到的操作主要是:线程操作,设置互斥锁。其中,线程操作包括:线程创建,退出,。设置互斥锁包括:创建互斥锁,加锁和解锁。 但是,要实现网络聊天,系统中还要用到linux下的网络编程。 Linux下的网络编程通过socket接口实现。socket 是一种特殊的I/O,可以实现网络上的通信机制。Socket也是一种文件描述符。它具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。常用的Socket

linux进程控制 实验报告

长安大学 操作系统实验报告 实验课程:操作系统 实验名称:linux进程控制 学院:信息学院 专业:软件工程 学号:2406090106 姓名:刘建 日期:2012-5-09

一、实验目的 熟悉进程的创建过程,了解系统调用函数fork() 和execl()。 二、实验内容 1、阅读实例代码fork1,并编辑、编译、运行,记录程序的运行结果,尝试给出合理的解释,查阅有关资料,掌握系统调用fork( )的用法,返回值的意义。 2、阅读实例代码fork2,并编辑、编译、运行,记录程序的运行结果,尝试给出合理的解释,查阅有关资料,掌握在程序中运行一个操作系统命令和运行一个程序的方法。 3、修改fork2,使之能把运行的命令和程序作为参数传给fork2。 三、设计思想 1、程序框架

pid = -1 pid = 0pid> 0 2、用到的文件系统调用函数 fork() 和execl() 四、调试过程 1、测试数据设计 (1)fork1 命名程序1: 编写程序1:

编译程序1: 运行程序1: (2)fork2

编写程序2: 运行程序2:

(3)修改fork2 编写修改程序2: 修改后的运行结果: 2、测试结果分析 (1)对于程序1:因为系统调用fork()函数是一次调用两次返回值,而且先生成子进程还是父进程是不确定的,所以第一次执行生成子进程的时候返回的pid = 0,判断pid!=-1,所以输出了I’m the child. I’m the parent. 第二次,执行父进程的时候,返回的是子进程的进程号pid> 0,即pid的值仍然不为-1,所以又输出了一次I’m the child. I’m the parent。 (2)对于程序2:第一次调用fork()函数时,由于执行的是子进程还是父进程是随机的,所以第一次对父进程返回的是子进程的进程号(大于0),即pid> 0,所以输出I’m the parent. Program end.当第二次执行子进程时返回值是0,即pid = 0,所以输出I’m the child. 并调用了execl()函数,查看了指定路径中的文件。

基于linux的socket多线程通信

1、网络中进程之间如何通信? 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: ?消息传递(管道、FIFO、消息队列) ?同步(互斥量、条件变量、读写锁、文件和写记录 锁、信号量) ?共享内存(匿名的和具名的) ?远程过程调用(Solaris门和Sun RPC) 但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的―ip地址‖可以唯一标识网络中的主机,而传输层的―协议+端口‖可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。 使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说―一切皆socket‖。 2、什么是Socket? 上面我们已经知道网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是―一切皆文件‖,都可以用―打开open –> 读写write/read –> 关闭close‖模式来操作。我的理解就是Socket就是该模式的一个实现,socket 即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭),这些函数我们在后面进行介绍。 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的,撰写者为Stephen Carr、Steve Crocker和Vint Cerf。根据美国计算机历史博物馆的记载,Croker写道:―命名空间的元素都可称为套接字接口。一个套接字接口构成一个连接的一端,而一个连接可完全由一对套接字接口规定。‖计算机历史博物馆补充道:―这比BSD的套接字接口定义早了大约12年。‖ 3、socket的基本操作 既然socket是―open—write/read—close‖模式的一种实现,那么socket就提供了这些操作对应的函数接口。下面以TCP为例,介绍几个基本的socket接口函数。 3.1、socket()函数 int socket(int domain, int type, int protocol); socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。 正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数创建不同的socket描述符,socket 函数的三个参数分别为:

linux实验项目 进程控制操作

重庆电力高等专科学校 实 验 报 告 书 实验名称:Linux文件命令学院:信息工程学院 指导老师:黄泽伟 班级:软件1311 学号:201303100243 姓名:周贵波

实验项目进程控制操作 一、实验目的 1.了解进程的概念。 2.熟悉Linux的前台与后台进程控制操作。 3.掌握利用进程监控工具来维护系统的正常运行。 二、实验环境 安装了Red Hat Linux9.0和windows虚拟机系统的计算机一台。 三、实验内容 1.Linux的前台与后台进程控制操作。 2.系统监视器(system monitor)的使用和系统维护。 四、实验步骤 1.进程的前台工作方式 1)yes ok ←┘ 终端窗口显示___ok___,此时键盘能否输入其它命令____不能___,为什么? 2)按键:Ctrl+Z,暂停一个前台进程的运行,并转为挂起进程。 3)jobs ←┘记录下看到的结果_____yes ok_________________。 4)fg %1 ←┘作用:把挂起进程转为前台进程执行; 5)再按键:Ctrl+Z 作用与2)相同; 6)jobs ←┘记录下看到的结果________yes ok______________。 7)bg %1 ←┘作用:重新运行挂起进程,但以后台方式运行; 8)此时能否再按键:Ctrl+Z暂停一个后台进程的运行?____不能__,

键盘能否输入其它命令__不能_____,为什么? 9)用鼠标点击,关闭终端窗口。 2.进程的后台工作方式 除上面把前台进程转到后台进程的过程外,一般在shell提示符下,输入的命令后加上&,即以后台方式运行命令。再次进入终端方式。 1)xclock & ←┘后台进程1,记录其时钟的时间___16:40____ xcalc & ←┘后台进程2 find / -name ?*.jpg‘–print > templist & ←┘后台进程3 2)jobs ←┘有几个后台进程:__2______,记录显示结果: xclock xcalc & ps ←┘记录下看到的结果: 4036 pts/0 00:00:00 bash 4061 pts/0 00:00:00 xcalc 4063 pts/0 00:00:00 ps 3)fg %1 ←┘将时钟进程转到前台运行。 4)按键:Ctrl+Z, 将时钟进程挂起,记录时钟的时间___16:50______。 jobs ←┘观察显示结果____xcalc &________________。 回顾上述操作,经过二~三分钟后,看图形时钟有走动吗?____否___。 5)bg %1 ←┘ 将挂起的时钟进程转到后运行,现在再观察时钟是否会有变化,为什么? 有后台继续运行 jobs ←┘ kill %2 ←┘杀死计算器进程,看计算器是否消失__否______。 6)ps ←┘ 观察屏幕显示,记录时钟(xclock)进程的进程号pid=__________。 7)kill ←┘ pid为xclock的进程号,作用__newline____________。 试比较与上述步骤5)中的kill命令的区别。 8)ls –l ←┘是否有templist文件? ____否____。 9)rm templist ←┘删除临时文件。

Linux 进程管理实验

Linux 进程管理实验 一、实验内容: 1. 利用bochs观测linux0.11下的PCB进程控制结构。 2. 利用bochs观测linux0.11下的fork.c源代码文件,简单分析其中的重要函数。 3. 在fork.c适当位置添加代码,以验证fork函数的工作原理。 二、Linux进程管理机制分析 Linux有两类进程:一类是普通用户进程,一类是系统进程,它既可以在用户空间运行,又可以通过系统调用进入内核空间,并在内核空间运行;另一类叫做内核进程,这种进程只能在内核空间运行。在以i386为平台的Linux系统中,进程由进程控制块,系统堆栈,用户堆栈,程序代码及数据段组成。Linux系统中的每一个用户进程有两个堆栈:一个叫做用户堆栈,它是进程运行在用户空间时使用的堆栈;另一个叫做系统堆栈,它是用户进程运行在系统空间时使用的堆栈。 1.Linux进程的状态: Linux进程用进程控制块的state域记录了进程的当前状态,一个Linux 进程在它的生存期中,可以有下面6种状态。 1.就绪状态(TASK_RUNNING):在此状态下,进程已挂入就绪队列,进入准备运行状态。 2.运行状态(TASK_RUNNING):当进程正在运行时,它的state域中的值不改变。但是Linux会用一个专门指针(current)指向当前运行的

任务。 3.可中断等待状态(TASK_INTERRUPTIBLE):进程由于未获得它所申请的资源而处在等待状态。不管是资源有效或者中断唤醒信号都能使等待的进程脱离等待而进入就绪状态。即”浅睡眠状态”。 4.不可中断等待状态(TASK_UNINTERRUPTIBLE):这个等待状态与上面等待状态的区别在于只有当它申请的资源有效时才能被唤醒,而其它信号不能。即“深睡眠状态”。 5.停止状态(TASK_STOPPED):当进程收到一个SIGSTOP信号后就由运行状态进入停止状态,当收到一个SINCONT信号时,又会恢复运行状态。挂起状态。 6.终止状态(TASK_ZOMBIE):进程因某种原因终止运行,但进程控制块尚未注销。即“僵死状态”。 状态图如下所示: 2.Linux进程控制块:

Linux操作系统进程管理的分析与应用

Linux操作系统进程管理的分析与应用(1)发布时间:2006.05.19 07:12来源:LinuxSir作者:北南南北目录 1、程序和进程; 1.1 进程分类; 1.2 进程的属性; 1.3 父进程和子进程; 2、进程管理; 2.1 ps 监视进程工具; 2.1.1 ps参数说明; 2.1.2 ps 应用举例; 2.2 pgrep 3、终止进程的工具 kill 、killall、pkill、xkill; 3.1 kill 3.2 killall 3.3 pkill 3.4 xkill 4、top 监视系统任务的工具; 4.1 top 命令用法及参数; 4.2 top 应用举例; 5、进程的优先级: nice和renice; 6、关于本文; 7、后记; 8、参考文档; 9、相关文档; 1、程序和进程; 程序是为了完成某种任务而设计的软件,比如OpenOffice是程序。什么是进程呢?进程就是运行中的程序。 一个运行着的程序,可能有多个进程。比如 https://www.wendangku.net/doc/7b3070906.html, 所用的WWW服务器是apache服务器,当管理员启动服务后,可能会有好多人来访问,也就是说许多用户来同时请求httpd服务,

apache服务器将会创建有多个httpd进程来对其进行服务。 1.1 进程分类; 进程一般分为交互进程、批处理进程和守护进程三类。 值得一提的是守护进程总是活跃的,一般是后台运行,守护进程一般是由系统在开机时通过脚本自动激活启动或超级管理用户root来启动。比如在Fedora或Redhat中,我们可以定义httpd 服务器的启动脚本的运行级别,此文件位于/etc/init.d目录下,文件名是httpd, /etc/init.d/httpd 就是httpd服务器的守护程序,当把它的运行级别设置为3和5时,当系统启动时,它会跟着启动。 [root@localhost ~]# chkconfig --level 35 httpd on 由于守护进程是一直运行着的,所以它所处的状态是等待请求处理任务。比如,我们是不是访问 https://www.wendangku.net/doc/7b3070906.html, ,https://www.wendangku.net/doc/7b3070906.html, 的httpd服务器都在运行,等待着用户来访问,也就是等待着任务处理。 Linux操作系统进程管理的分析与应用(2)发布时间:2006.05.19 07:12来源:LinuxSir作者:北南南北 1.2 进程的属性; 进程ID(PID):是唯一的数值,用来区分进程; 父进程和父进程的ID(PPID); 启动进程的用户ID(UID)和所归属的组(GID); 进程状态:状态分为运行R、休眠S、僵尸Z; 进程执行的优先级; 进程所连接的终端名; 进程资源占用:比如占用资源大小(内存、CPU占用量); 1.3 父进程和子进程; 他们的关系是管理和被管理的关系,当父进程终止时,子进程也随之而终止。但子进程终止,父进程并不一定终止。比如httpd服务器运行时,我们可以杀掉其子进程,父进程并不会因为子进程的终止而终止。 在进程管理中,当我们发现占用资源过多,或无法控制的进程时,应该杀死它,以保护系统的稳定安全运行;

实验二-Linux进程、线程及编程

实验二Linux进程、线程及编程实验 一、实验目的 1、通过编写一个完整的守护进程,掌握守护进程编写和调试的方法 2、进一步熟悉如何编写多进程程序 二、实验环境 硬件:PC机一台,JXARM9-2410教学实验平台。 软件:Windows98/XP/2000系统,虚拟机环境下的Linux系统。 三、预备知识 1、fork() fork()函数用于从已存在的进程中创建一个新进程。新进程称为子进程,而原进程称为父进程。使用fork()函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程上下文、代码段、进程堆栈、存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制和控制终端等,而子进程所独有的只有它的进程号、资源使用和计时器等 2、exit()和_exit()的区别 _exit()函数的作用最为简单:直接使进程停止运行,清除其使用的存空间,并销毁其在核中的各种数据结构; exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。 exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的容写回文件,就是图中的"清理I/O缓冲"一项。 3、wait()和waitpid() wait()函数是用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接到了一个指定的信号为止。如果该父进程没有子进程或者他的子进程已经结束,则wait()就会立即返回。 四、实验容 在该实验中,读者首先创建一个子进程1(守护进程),然后在该子进程中新建一个子进程2,该子进程2暂停10s,然后自动退出,并由子进程1收集子线程退出的消息。在这里,子进程1和子进程2的消息都在系统日志文件(例如“/var/log/messages”,日志文件的全路径名因版本的不同可能会有所不同)中输出。在向日志文件写入消息之后,守护进程(子进程1)循环暂停,其间隔时间为10s。 五、实验步骤

Linux程序设计上机指导书3:Linux进程控制

上机三:Linux进程控制 1.目的 (1)掌握系统调用fork(),exex(),exit()等实现进程创建; (2)掌握进程的终止方式(return、exit、_exit、abort); (3)掌握僵尸进程的产生和避免,以及wait,waitpid的使用; (4)了解守护进程的创建。 2.内容 主要上机分析代码文件。 systemtest.c 6-3.c 6-4.c 6-8.c 6-9.c 其他略。 3.步骤 1)Linux进程的创建 创建进程可以采用几种方式。可以执行一个程序(这会导致新进程的创建),也可以在程序内调用一个fork 或exec来创建新进程。fork 调用会导致创建一个子进程,而exec 调用则会用新程序代替当前进程上下文。exec系列函数并不创建新进程,调用exec前后的进程ID是相同的。

exec函数的主要工作是清除父进程的可执行代码映像,用新程序的代码覆盖调用exec 的进程代码。如果exec执行成功,进程将从新程序的main函数入口开始执行。调用exec 后,除进程ID保持不变外,还有下列进程属性也保持不变。 (1)进程的父进程ID。 (2)实际用户ID和实际用户组ID。 (3)进程组ID、会话ID和控制终端。 (4)定时器的剩余时间。 (5)当前工作目录及根目录。 (6)文件创建掩码UMASK。 (7)进程的信号掩码。 与exec系统调用不同,system将外部可执行程序加载执行完毕后继续返回调用进程。 【例6.3】设计一个程序,用fork函数创建一个子进程,在子进程中,要求显示子进程号与父进程号,然后显示当前目录下的文件信息,在父进程中同样显示子进程号与父进程号。

linux进程管理篇

目录:(内容较多,加个目录) |-进程管理 进程常用命令 |- w查看当前系统信息 |- ps进程查看命令 |- kill终止进程 |- 一个存放内存中的特殊目录/proc |- 进程的优先级 |- 进程的挂起与恢复 |- 通过top命令查看进程 计划任务 |- 计划任务的重要性 |- 一次性计划at和batch |- 周期性计划crontab 进程管理的概念 进程和程序区别 1.程序是静态概念,本身作为一种软件资源长期保存;而进程是程序的执行过程,它是动态概念,有一定的生命期,是动态产生和消亡的。 2.程序和进程无一一对应关系。一个程序可以由多个时程公用;另一一方面,一个进程在活动中有可顺序地执行若干个程序 父子进程的关系 1.子进程是由一个进程所产生的进程,产生这个子进程的进程称为父进程 2.在linux系统中,使用系统调用fork创建进程。fork复制的内容包括父进程的数据和堆栈段以及父进程的进程环境。 3.父进程终止子进程自然终止。 前台进程和后台进程 前台进程 在shell提示处理打入命令后,创建一个子进程,运行命令,Shell等待命令退出,然后返回到对用户给出提示符。这条命令与Shell异步运行,即在前台运行,用户在它完成之前不能执行别一个命令

很简单,我们在执行这个查找命令时,无法进行其它操作,这个查找就属于前台进程 后台进程 在Shell提示处打入命令,若后随一个&,Shell创建子进程运行此命令,但不等待命令退出,而直接返回到对用户给出提示。这条命令与Shell同步运行,即在后台运行。“后台进程必须是非交互式的” 再来看这个命令就变成了后台进程,我们用同样的条件进行查找,把查找记过放到hzh/test/init.find这个文件中。不影响我们前台其它的操作。 进程的状态

Linux下查看进程和线程

在Linux中查看线程数的三种方法 1、top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程。否则,它一行显示一个进程。 2、ps xH 手册中说:H Show threads as if they were processes 这样可以查看所有存在的线程。 3、ps -mp 手册中说:m Show threads after processes 这样可以查看一个进程起的线程数。 查看进程 1. top 命令 top命令查看系统的资源状况 load average表示在过去的一段时间内有多少个进程企图独占CPU zombie 进程:不是异常情况。一个进程从创建到结束在最后那一段时间遍是僵尸。留在内存中等待父进程取的东西便是僵尸。任何程序都有僵尸状态,它占用一点内存资源,仅仅是表象而已不必害怕。如果程序有问题有机会遇见,解决大批量僵尸简单有效的办法是重起。kill是无任何效果的stop模式:与sleep进程应区别,sleep会主动放弃cpu,而stop 是被动放弃cpu ,例单步跟踪,stop(暂停)的进程是无法自己回到运行状态的。 cpu states: nice:让出百分比irq:中断处理占用 idle:空间占用百分比iowait:输入输出等待(如果它很大说明外存有瓶颈,需要升级硬盘(SCSI)) Mem:内存情况 设计思想:把资源省下来不用便是浪费,如添加内存后free值会不变,buff值会增大。判断物理内存够不够,看交换分区的使用状态。 交互命令: [Space]立即刷新显示 [h]显示帮助屏幕

linux进程线程管理实验报告

linux进程线程管理实验报告

————————————————————————————————作者:————————————————————————————————日期:

西安郵電學院 操作系统LINUX实验报告 题目1:进程______ 题目2:线程管理__ 题目3:互斥_____系部名称:计算机学院 专业名称:软件工程 班级:0802 学号:04085048 学生姓名:郭爽乐 时间:2010-10-31

实验一: 进程管理 一.实验目的 通过观察、分析实验现象,深入理解进程及进程在调度执行和内存空间等方面的特点, 掌握在POSIX 规范中fork和kill系统调用的功能和使用。 二.实验要求 2.1 实验环境要求 1. 硬件 (1) 主机:Pentium III 以上; (2) 内存:128MB 以上; (3) 显示器:VGA 或更高; (4) 硬盘空间:至少100MB 以上剩余空间。 2. 软件 Linux 操作系统,内核2.4.26 以上,预装有X-Window 、vi、gcc、gdb 和任意web 浏览器。 2.2 实验前的准备工作 学习man 命令的用法,通过它查看fork 和kill 系统调用的在线帮助,并阅读参 考资料,学会fork 与kill 的用法。 复习C 语言的相关内容。 三、实验内容 3.1 补充POSIX 下进程控制的残缺版实验程序 3.2回答下列问题: 1. 你最初认为运行结果会怎么样? 2. 实际的结果什么样?有什么特点?试对产生该现象的原因进行分析。 3. proc_number 这个全局变量在各个子进程里的值相同吗?为什么? 4. kill 命令在程序中使用了几次?每次的作用是什么?执行后的现象是什么? 5. 使用kill 命令可以在进程的外部杀死进程。进程怎样能主动退出?这两种退出方式哪种更好一些? 四、实验结果 4.1 补充完全的源程序 #include #include #include

2011180021-Linux操作系统-课程设计报告-基于Linux的进程调度模拟程序

河南中医学院 《linux操作系统》课程设计报告 题目:基于Linux的进程调度模拟程序 所在院系:信息技术学院 专业年级:2011级计算机科学与技术完成学生:2011180021 郭姗 指导教师:阮晓龙 完成日期:201X 年06 月22 日 目录 1. 课程设计题目概述3 2. 研究内容与目的4 3. 研究方法5 4. 研究报告6 5. 测试报告/实验报告7 6. 课题研究结论8 7. 总结9

1、课程设计题目概述 随着Linux系统的逐渐推广,它被越来越多的计算机用户所了解和应用. Linux是一个多任务的操作系统,也就是说,在同一个时间内,可以有多个进程同时执行。如果读者对计算机硬件体系有一定了解的话,会知道我们大家常用的单CPU计算机实际上在一个时间片断内只能执行一条指令,那么Linux是如何实现多进程同时执行的呢?原来Linux使用了一种称为"进程调度(process scheduling)"的手段,首先,为每个进程指派一定的运行时间,这个时间通常很短,短到以毫秒为单位,然后依照某种规则,从众多进程中挑选一个投入运行,其他的进程暂时等待,当正在运行的那个进程时间耗尽,或执行完毕退出,或因某种原因暂停,Linux就会重新进行调度,挑选下一个进程投入运行。因为每个进程占用的时间片都很短,在我们使用者的角度来看,就好像多个进程同时运行一样了。本文就是对进程调度进行研究、实验的。 本文首先对Linux系统进行了简要的介绍, 然后介绍了进程管理的相关理论知识。其次,又介绍最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)、先来先服务算法的相关知识,并对进程调度进行最高优先数优先的调度算法和先来先服务算法模拟实验,并对比分析两种算法的优缺点,从而加深对进程概念和进程调度过程/算法的理解 设计目的:在多道程序和多任务系统中,系统内同时处于就绪状态的进程可能有若干个。也就是说能运行的进程数大于处理机个数。为了使系统中的进程能有条不紊地工作,必须选用某种调度策略,选择某一进程占用处理机。使得系统中的进程能够有条不紊的运行,同时提高处理机的利用率以及系统的性能。所以设计模拟进程调度算法(最高优先数优先的调度算法、先来先服务算法),以巩固和加深处理进程的概念,并且分析这两种算法的优缺点。关键词:linux 进程调度调度算法

linux进程管理(一)

进程介绍 程序和进程 程序是为了完成某种任务而设计的软件,比如OpenOffice是程序。什么是进程呢?进程就是运行中的程序。 一个运行着的程序,可能有多个进程。比如自学it网所用的WWW服务器是apache服务器,当管理员启动服务后,可能会有好多人来访问,也就是说许多用户来同时请求httpd服务,apache服务器将会创建有多个httpd进程来对其进行服务。 进程分类 进程一般分为交互进程、批处理进程和守护进程三类。 值得一提的是守护进程总是活跃的,一般是后台运行,守护进程一般是由系统在开机时通过脚本自动激活启动或超级管理用户root来启动。比如在 Redhat中,我们可以定义httpd 服务器的启动脚本的运行级别,此文件位于/etc/init.d目录下,文件名是httpd,/etc/init.d/httpd 就是httpd服务器的守护程序,当把它的运行级别设置为3和5时,当系统启动时,它会跟着启动。 [root@localhost ~]# chkconfig --level 35 httpd on 进程的属性: 进程ID(PID):是唯一的数值,用来区分进程; 父进程和父进程的ID(PPID); 启动进程的用户ID(UID)和所归属的组(GID); 进程状态:状态分为运行R、休眠S、僵尸Z; 进程执行的优先级; 进程所连接的终端名; 进程资源占用:比如占用资源大小(内存、CPU占用量); 父进程和子进程: 他们的关系是管理和被管理的关系,当父进程终止时,子进程也随之而终止。但子进程终止,父进程并不一定终止。比如httpd服务器运行时,我们可以杀掉其子进程,父进程并不会因为子进程的终止而终止。 在进程管理中,当我们发现占用资源过多,或无法控制的进程时,应该杀死它,以保护系统的稳定安全运行; 进程管理

【IT专家】Linux中进程的几种状态

本文由我司收集整编,推荐下载,如有疑问,请与我司联系 Linux中进程的几种状态 2015/03/29 4411 1R (task_running) :可执行状态 ?只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态,这些进程的task_struct结构(进程控制块)被放入对应CPU的可执行队列中(一个进程最多只能出现在一个CPU的可执行队列中)。进程调度器的任务就是从各个CPU的可执行队列中分别选择一个进程在该CPU上运行。 ?很多操作系统教科书将正在CPU上执行的进程定义为RUNNING状态、而将可执行但是尚未被调度执行的进程定义为READY状态,这两种状态在linux下统一为TASK_RUNNING状态。 ? 2S (task_interruptible):可中断的睡眠状态 ?处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。 ?通过ps命令我们会看到,一般情况下,进程列表中的绝大多数进程都处于task_interruptible状态(除非机器的负载很高)。毕竟CPU就这么一两个,进程动辄几十上百个,如果不是绝大多数进程都在睡眠,CPU又怎么响应得过来。 ? 3D (task_uninterrupible)不可中断的睡眠状态 ?与task_interruptible状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号。绝大多数情况下,进程处在睡眠状态时,总是应该能够响应异步信号的。但是uninterruptible sleep状态的进程不接受外来的任何信号,因此无法用kill杀掉这些处于D状态的进程,无论是”kill”, “kill -9″还是”kill -15″,这种情况下,一个可选的方法就是reboot。

Linux进程管理-实验报告

(1) 加深对进程概念的理解,明确进程和程序的区别。 (2) 进一步认识并发执行的实质。 (3) 学习通过进程执行新的U 标程序的方法。 (4) 了解Linux 系统中进程信号处理的基本原理。 Red Hat Linux (1)进程的创建 编写一段程序,使用系统调用fork ()创建两个子进程,当此进程运 行时,在系统中有一个父进程和两个子进程活动,让每一个进程在 屏幕上显示一个字符,父进程显示字符“a” ;子进程分别显示字符 “b”和字符“c” ,试观察记录屏幕上的显示结果,并分析原因。 程序代码: # include int main() { int pl ,p2 ; while((p 1 =fork())==-1); if(pl==0) putchar(b); else { while((p2=fork())==-1); if(p2==0) putchar( c 1); else putchar( a r ); } return 0; ) 运行结果:bca 分析:第一个while 里调用fork()函数一次,返回两次。子进程P1 得到的返回值是0,父进程得到的返回值是新子进程的进程ID (正 整数);接下来父进程和子进程P1两个分支运行,判断P1二二0,子 进程P1符合条件,输出%";接下来else 里面的while 里再调用 fork()函数一次,子进程P2得到的返回值是0,父进程得到的返回值 是新子进程的进程ID (正整数);接下来判断P2=:0,子进程P2符 合条件,输出,接下来父进程输出“a” ,程序结束。 (2)进程的控制 ① 修改已编写的程序,将每个进程输出一个字符改为每个进程输出 一 《Linux 实验目的: 实验环境: 实验内容: 操作系统设计实践》实验一:进程管理

操作系统实验报告(包括线程,进程,文件系统管理,linux+shell简单命令)

操作系统实验报告 班级:030613 学号:03061331 姓名:裴帅帅

实验一:进程的建立 一、实验内容 创建进程及子进程,在父子进程间实现进程通信,创建进程并显示标识等进 程控制块的属性信息,显示父子进程的通信信息和相应的应答信息。 使用匿名管道实现父子进程之间的通信。 二、源程序 1、创建匿名管道 SECURITY_ATTRIBUTES sa; sa.bInheritHandle=true; sa.lpSecurityDescriptor=NULL; sa.nLength=sizeof(SECURITY_ATTRIBUTES); if(!CreatePipe(&m_hRead,&m_hWrite,&sa,0)) { MessageBox("创建匿名管道失败"); return false; } 2、创建子进程 STARTUPINFO si; ZeroMemory(&si,sizeof(STARTUPINFO)); si.cb=sizeof(STARTUPINFO); si.dwFlags=STARTF_USESTDHANDLES; si.hStdInput=m_hRead; si.hStdOutput=m_hWrite; si.hStdError=GetStdHandle(STD_ERROR_HANDLE); if(!CreateProcess(NULL,"子 进.exe",NULL,NULL,true,0,NULL,NULL,&si,&pi)) { MessageBox("创建子进程失败"); CloseHandle(m_hRead); CloseHandle(m_hWrite); m_hRead=NULL; m_hWrite=NULL; return; } 3、销毁子进程 if(m_hRead) CloseHandle(m_hRead);

Linux课程设计进程控制

课程设计报告 课程名称Linux操作系统课程设计 指导教师张玲 起止日期2014-03-01 至2014-06-13 实验项目实验二进程控制 学院信息与通信工程学院 专业电子信息工程 学生姓名 班级/学号 成绩 指导老师签字

1. 课程设计概述 本次课设意在利用进程控制相关的系统调用编程进行进程控制实验,观察进程从创建、运行到终止的控制过程,加深对进程概念的理解。 1.1 课程设计的目的 本实验的目的是掌握进程的概念,理解进程的创建、执行、等待、终止的过程。熟悉有关进程控制的命令和系统调用,理解Shell 的工作原理。 1.2 课程设计的内容 1) 进程控制命令 ● 使用进程控制命令ps ,kill 监视和控制进程的活动 2) 编程部分 ● 用fork ,wait ,exec 系统调用编程实现进程控制; ● 编写一个简单的shell 。 1.3 设计原理 进程是一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源。 构建的文件构架如图: 图1 实验目录树的结构 2012010948 exp2 exp1 exp3 vis vis2

2.实验步骤 2.1操作 2.1.1进程控制命令(在此终端窗口的操作截图见图1) 1.执行ps命令,查看bash进程的PID: PID就是各进程的身份标识,程序一运行系统就会自动分配给进程一个独一无二的PID。进程中止后PID被系统回收。 2.在后台运行一个yes进程: yes > /dev/null & 3.用ps -f命令观察yes进程的pid和ppid,用ps u命令观察yes进程的状态。 yes进程的父进程是哪个进程?通过查看yes程序的PPID可知其父进程为bash 4.根据命令名搜索特定的进程: ps-ef|grep yes: 5.用kill命令杀掉yes进程: kill 【PID】: 图1 进程控制命令

实验一——Linux环境下的进程管理之欧阳光明创编

软件学院 欧阳光明(2021.03.07) 上机实验报告 课程名称:操作系统 实验项目:实验一:Linux环境下进程管理 实验室:耘慧402 姓名:学号: 专业班级:实验时间:

一、实验目的及要求 1.加深对进程概念的理解,明确进程和程序的区别; 2.进一步认识并发执行的实质; 3.分析进程争用资源的现象,学习解决进程互斥的方法; 4.了解Linux系统中进程通信的基本原理; 二、实验性质 1.进程的创建:编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”;子进程分别显示字符“b”和字符“c”。试观察记录屏幕上的显示结果,并分析原因。 2.进程的控制:修改已编写的程序,将每个进程输出一个字符改为每个进程输出一句话,再观察程序执行时屏幕上出现的现象,并分析原因。如果在程序中使用系统调用lockf ()来给每一个进程加锁,可以实现进程之间的互斥,观察并分析出现的现象。 3.用fork( )创建一个进程,再调用exec( )用新的程序替换该子进程的内容;利用wait( )来控制进程执行顺序。 三、实验学时 实验性质:验证性 实验学时: 4学时 实验要求:必做 四、实验环境 1.实验环境: Linux系统开发环境 2.知识准备: (1) Linux系统开发环境搭建; (2) Linux环境下GCC编译器的使用; (3)语言中函数定义与调用、指针和类型的定义与使用、结构的定义、动态

内存的申请等预备知识。 五、实验内容及步骤 ①实验内容: (1)进程的创建 编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”;子进程分别显示字符“b”和字符“c”。试观察记录屏幕上的显示结果,并分析原因。 (2)进程的控制 修改已编写的程序,将每个进程输出一个字符改为每个进程输出一句话,再观察程序执行时屏幕上出现的现象,并分析原因。 如果在程序中使用系统调用lockf()来给每一个进程加锁,可以实现进程之间的互斥,观察并分析出现的现象。 (3)用fork( )创建一个进程,再调用exec( )用新的程序替换该子进程的内容;利用wait( )来控制进程执行顺序。 ②实验步骤: 1.进程的创建 1.1 进程 UNIX中,进程既是一个独立拥有资源的基本单位,又是一个独立调度的基本单位。一个进程实体由若干个区(段)组成,包括程序区、数据区、栈区、共享存储区等。每个区又分为若干页,每个进程配置有唯一的进程控制块PCB,用于控制和管理进程。 PCB的数据结构如下: (1)进程表项(Process Table Entry)。包括一些最常用的核心数据: 进程标识符PID、用户标识符UID、进程状态、事件描述符、进程和U区在内存或外存的地址、软中断信号、计时域、进程的大小、偏置值nice、指向就绪队列中下一个PCB的指针P_Link、指向U区进程正文、数据及栈在内存区域的指针。 (2)U区(U Area)。用于存放进程表项的一些扩充信息。 每一个进程都有一个私用的U区,其中含有:进程表项指针、真正用户标识符

最新整理Linux操作系统的进程管理详解

L i n u x操作系统的进程管理详解 L i n u x操作系统中进程的管理是很重要的一部分,下面由学习啦小编为大家整理了L i n u x操作系统的进程管理详解的相关知识,希望对大家有帮助! L i n u x操作系统的进程管理详解 对于L i n u x的进程的管理,是通过进程管理工具实现的,比如p s、k i l l、p g r e p等工具; L i n u x操作系统的进程管理/1、 p s监视进程工具; p s为我们提供了进程的一次性的查看,它所提供的查看结果并不动态连续的;如果想对进程时间监控,应该用t o p工具; 1.p s的参数说明; p s提供了很多的选项参数,常用的有以下几个; l长格式输出; u按用户名和启动时间的顺序来显示进程; j用任务格式来显示进程; f用树形格式来显示进程; a显示所有用户的所有进程(包括其它用户); x显示无控制终端的进程; r显示运行中的进程;

w w避免详细参数被截断; 我们常用的选项是组合是a u x或l a x,还有参数f 的应用; p s a u x或l a x输出的解释; U S E R进程的属主; P I D进程的I D; P P I D父进程; %C P U进程占用的C P U百分比; %M E M占用内存的百分比; N I进程的N I C E值,数值大,表示较少占用C P U时间; V S Z进程虚拟大小; R S S驻留中页的数量; W C H A N T T Y终端I D S T A T进程状态 D U n i n t e r r u p t i b l e s l e e p(u s u a l l y I O) R正在运行可中在队列中可过行的; S处于休眠状态; T停止或被追踪;

相关文档
相关文档 最新文档