文档库

最新最全的文档下载
当前位置:文档库 > 2.4 Linux的进程管理

2.4 Linux的进程管理

2.4Linux的进程管理

Linux中的每一个进程用一个task_struct数据结构来表示,所有指向这些数据结构的指针构成进程的向量数组task,系统的进程向量数组大小是512,系统中最多能容纳512个进程(Linux2.4及以后内核无此限制)。当新的进程创建的时候,从系统内存中分配一个新的task_struct结构,并加到task数组中。当前进程指针current指向当前运行的进程。task_struct的结构描述在/include/linux/seched.h 文件中。

2.4.1Linux的进程控制块task_struct及进程状态

1.Linux的进程控制块task_struct

在Linux中进程称为任务,进程控制块是task_struct结构,它在include/linux/sched.h中定义并解释。

struct task_struct {

/* these are hardcoded - don't touch */

volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */

long counter;

long priority;

unsigned long signal;

unsigned long blocked; /* bitmap of masked signals */

unsigned long flags; /* per process flags, defined below */

int errno;

long debugreg[8]; /* Hardware debugging registers */

struct exec_domain *exec_domain;

/* various fields */

struct linux_binfmt *binfmt;

struct task_struct *next_task, *prev_task;

struct task_struct *next_run, *prev_run;

unsigned long saved_kernel_stack;

unsigned long kernel_stack_page;

int exit_code, exit_signal;

/* ??? */

unsigned long personality;

int dumpable:1;

int did_exec:1;

int pid;

int pgrp;

int tty_old_pgrp;

int session;

/* boolean value for session group leader */

int leader;

int groups[NGROUPS];

/*

* pointers to (original) parent process, youngest child, younger

sibling,

* older sibling, respectively. (p->father can be replaced with Linux Kernel Page 147

* p->p_pptr->pid)

*/

struct task_struct *p_opptr, *p_pptr, *p_cptr,

*p_ysptr, *p_osptr;

struct wait_queue *wait_chldexit;

unsigned short uid,euid,suid,fsuid;

unsigned short gid,egid,sgid,fsgid;

unsigned long timeout, policy, rt_priority;

unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr;

struct timer_list real_timer;

long utime, stime, cutime, cstime, start_time;

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

unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap; int swappable:1;

unsigned long swap_address;

unsigned long old_maj_flt; /* old value of maj_flt */

unsigned long dec_flt; /* page fault count of the last time */

unsigned long swap_cnt; /* number of pages to swap on next pass */ /* limits */

struct rlimit rlim[RLIM_NLIMITS];

unsigned short used_math;

char comm[16];

/* file system info */

int link_count;

struct tty_struct *tty; /* NULL if no tty */

/* ipc stuff */

struct sem_undo *semundo;

struct sem_queue *semsleeping;

/* ldt for this task - used by Wine. If NULL, default_ldt is used */ struct desc_struct *ldt;

/* tss for this task */

struct thread_struct tss;

/* filesystem information */

struct fs_struct *fs;

/* open file information */

struct files_struct *files;

/* memory management info */

struct mm_struct *mm;

/* signal handlers */

struct signal_struct *sig;

#ifdef __SMP__

int processor;

Linux Kernel Page 148

int last_processor;

int lock_depth; /* Lock depth.

We can context switch in and out

of holding a syscall kernel lock... */

#endif

};

2.Linux中的进程状态及其转换

Linux中进程有6种状态,分别是:

1)可运行状态(TASK_RUNNING),正在运行或准备运行的进程处于这种状态。

2)可中断阻塞状态(TASK_INTERRUPTIBLE),处于这种阻塞状态中的进程,只要阻塞的原因解除,就可以被唤醒到就绪队列,也可以由其他进程通过信号或定时中断唤醒。

3)不可中断阻塞状态(TASK_UNINTERRUPTIBLE),处于这种阻塞状态的进程,只能在资源请求得到满足时唤醒到就绪队列,不能通过信号或定时中断唤醒。

4)僵死状态(TASK_ZOMBIE),处于这种状态的进程已经结束运

行,离开CPU,并归还所占用的资源,只是进程控制块PCB结构还没有归还释放。

5)暂停状态(TASK_STOPPED),处于这种状态的进程被暂停执行而阻塞,通过其他进程的信号才能唤醒。

6)交换状态(TASK_SWAPPING),处于这种状态时,进程的页面可以从内存换出。

2.4.2 Linux的进程控制

进程控制主要包括进程的创建、执行、撤消、等待和结束等。这些系统调用是进程编程中最常用和基本的函数。

1.进程的创建fork

1)fork系统调用

在Linux中,一个已有的进程只有通过fork()系统调用才能创建一个新的进程,其描述为:

int fork()

正确返回:0:从子进程返回

>0:从父进程返回,返回值为子进程的进程标识号错误返回:-1

一个成功的fork调用将使内核创建一个新的进程,该进程是调用进程的一个精确的副本。也即新的进程运行了创建者进程的一个副本。

printf(“One\n”); ←PC

pid=fork();

printf(“Two\n”);

A进程之前

---------------------------------------------------------------------------------

之后printf(“One\n”); printf(“One\n”);

pid=fork(); pid=fork();

printf(“Two\n”); ←PC printf(“Two\n”); ←PC A进程B进程

fork之后部分内的两处PC箭头,表示在fork 调用之后父子进程均执行的下一条语句。也即A和B都处于程序代码的同一点上。

下面是一个简单的父进程创建子进程的例子:

# include

# include

# include

main()

{

pid_t pid;

pid=fork();

if (pid<0)

{

printf(“fork error\n”);

exit(0);

}

else if (pid==0)

{

printf(“This is child process!\n”);

}else {

printf(“This is parent process!\n”);

}

exit(0)

}

在fork()中,调用do-fork()函数来完成进程的创建(见kernel/fork.c),子进程被创建后,进入就绪队列和父进程分别独立地等待调度。子进程继承父进程的程序代码,子进程被调度执行时,和父进程一样从fork返回。从fork返回后,都执行pid=fork()语句,得到返回的值,pid的值有三种可能性:

A.小于0,表示fork出错;

B.等于0,表示当前进程是子进程;

C.大于0,表示当前进程是父进程。

从进程调度的角度来看,子进程被父进程创建后处于就绪态,此时,父进程和子进程作为二个独立的进程,共享同一个代码段,分别参加调度、执行,直到进程结束,但是谁先得到调度,与系统的调度策略和系统当前的资源状态有关,是不确定的。因此,谁先从fork函数中返回继续执行后面的语句也是不确定的。

2)vfork调用

vfork函数用于创建子进程,其调用方法和返回值与fork函数相同,但处理子进程与父进程的关系上有所不同,子进程创建后,先于父进程运行。

3)实例

调用fork创建一个子进程,改变在父进程创建子进程之前定义的变量的值,观察该变量在子进程和父进程中的值。

# include

# include

# include

main()

{

pid_t pid;

int data=5;

if ((pid=fork())<0)

{

printf(“fork error\n”);

exit(0);

}

else if (pid==0)

{

data--;

printf(“child’s data is:%d\n”,data);

exit(0);

}

else

{

printf(“parent’s data is:%d\n”,data);

}

exit(0);

}

2.进程的执行exec

在应用中,常需要在一个程序中启动另一个应用程序,在Linux中用函数exec()实现这样的功能。

# include

int execv (const char *file,char **argv,char **envp)

正确返回:0

错误返回:-1

当一个进程调用exec函数执行另一个程序后,这个进程被新程序取代,包括代码段、数据段、堆栈段等,同时继承原进程的进程标识号。execv()函数调用do-execve()函数,实现一个新程序的启动,代码详见/src/linux/fs/exec.c。

例:在程序中直接调用execv启动shell命令ps查看系统当前的进程信息。

# include

# include

# include

main()

{

int ret;

char *path=”/bin/ps”;

char *argv[5]={“ps”,”-a”,”-x”,NULL};

if (execv(path,argv))

{

syslog(LOG_INFO,”Error executing a program”);

}

return(0);

}