各学科课程设计linux课件讲稿第4章进程_第1页
各学科课程设计linux课件讲稿第4章进程_第2页
各学科课程设计linux课件讲稿第4章进程_第3页
各学科课程设计linux课件讲稿第4章进程_第4页
各学科课程设计linux课件讲稿第4章进程_第5页
已阅读5页,还剩81页未读, 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

进程进程进程运行环境进程结构体与进程相关的系统调用与进程环境相关的系统调用与进程关系相关的系统调用与进程控制相关的系统调用进程运行环境进程的运行环境是由进程的程序代码和程序运行所需的数据结构以及硬件环境组成的,进程就是这个运行环境的总和。程序代码:代码段:代码段中存放的是CPU执行的指令。数据段:包括初始化数据段(data)和非初始化数据(bss)。初始化数据段包含程序中明确给定初值的全局变量和静态变量。该段在程序运行过程中大小不能发生改变。如:int

max=100;static

int

count=10;没有明确给定初值的全局变量和静态变量存放在非初始化数据段中。栈:所有的自动变量及函数调用时需要保存的信息

(返回地址,函数调用前各寄存器的值)都保存在栈上。堆:堆用于存储用户申请的内存空间,常在堆中进行动态内存分配。环境变量:提供进程运行所需环境信息。如:shell类型、IP地址、命令搜索路径等。系统数据:对进程进行管理、控制所需的信息,如:任务结构体、内核堆栈。数据结构:Task_struct结构体进程存在的标志,采用task_struct结构(任务结构体)描述,Linux为task_struct结构分配存储空间时,分配2个页面(8192).上部为系统栈,下部为task_structtask_struct包含进程的所有管理信息系统堆栈,约7KBTask_struct,约1KB数据结构:Task_struct结构体struct

task_struct

{volatile

long

state;/*

-1

unrunnable,

0

runnable,

>0

stopped*/void

*stack;atomic_t

usage;unsigned

int

flags;unsigned

int

ptrace;inton_rq;int

prio,

static_prio,

normal_prio;unsigned

int

rt_priority;struct

list_head

tasks;struct

mm_struct

*mm,

*active_mm;u32

vmacache_seqnum;struct

vm_area_struct

*vmacache[VMACACHE_SIZE];int

exit_state;int

exit_code,

exit_signal;int

pdeath_signal;

/* The

signal

sent

when

the

parent

dies

*/unsigned

sched_reset_on_fork:1;unsigned

sched_contributes_to_load:1;unsigned

sched_migrated:1;unsigned

sched_remote_wakeup:1;unsigned

:0;

/*

forcealignment

to

the

next

boundary*/unsigned

in_execve:1;

/*

bit

to

tellLSMs

we're

in

execve

*/unsigned

in_iowait:1;struct

restart_block

restart_block;pid_t

pid;pid_t

tgid;structtask_struct

rcu

*real_parent;

/*

real

parent

process*/

structtask_struct

rcu

*parent;

/*

recipient

of

SIGCHLD,

wait4()reports

*//*

list

of

my

children

*//*

linkage

in

my

parent's

children

list

*/struct

list_head

children;struct

list_head

sibling;struct

task_struct

*group_leader; /*

threadgroup

leader

*/struct

pid_link

pids[PIDTYPE_MAX];struct

list_head

thread_group;struct

list_head

thread_node;cputime_t

utime,

stime;cputime_t

gtime;struct

prev_cputime

prev_cputime;u64

start_time;u64

real_start_time;struct

task_cputime

cputime_expires;struct

list_head

cpu_timers[3];char

comm[TASK_COMM_LEN];struct

nameidata

*nameidata;int

link_count,

total_link_count;struct

fs_struct

*fs;struct

files_struct

*files;struct

nsproxy

*nsproxy;struct

signal_struct

*signal;struct

sighand_struct

*sighand;struct

callback_head

*task_works;struct

audit_context

*audit_context;struct

p

p;u32

parent_exec_id;u32

self_exec_id;void

*journal_info;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.*/u64

timer_slack_ns;u64

default_timer_slack_ns;struct

thread_struct

thread;};Task_struct主要功能进程状态:即下面所提到的五种状态的其中一种;调度信息:操作系统调度时需要的信息以便对进程实施公平调度;进程标识:需要特别指出的是Linux从其安全角度出发,它的进程标识多达八个,不过可分为用户标识和用户组标识两大类;进程间通讯:Linux支持的进程间通讯机制不仅包括传统Unix系统中的信号、管道和信号量机制,也支持Unix

System

V进程间通讯的共享内存、信号量和消息队列机制;进程间关联:从进程派生角度看,进程间有父子关系,兄弟关系;而从进程管理角度看,进程往往处于一个进程的双向链表中,进程间有前后关系。进程需要维护相应的指针来阐明这些关系。时间和定时器:进程需要维护其创建时间,从而决定分配给它的处理器时间片的消耗情况;另外,进程在发送信号等方面需要和定时器打交道,因此需要维护与进程相关的内部定时器;文件系统:进程在运行期间会打开某个文件,因此需要维护它所打开文件的相关信息;虚拟内存:进程一般都要用到虚拟内存,Linux内核需要相关信息来跟踪进程对内存的使用状况;处理器相关信息:各个寄存器的内容等。进程状态进程状态运行态Running进程正在或准备运行。进程被标示为运行态,可能会被放到可运行进程队列中。可以认为进程处于随时可以运行的(准备)就绪状态。可唤醒阻塞态Interruptible进程处于等待队列中,待资源有效时被激活,也可由其他进程通过发送信号或者由定时器中断唤醒后进入就绪队列。不可唤醒阻塞态Uninterruptible进程处于等待队列中,待资源有效时被激活,不可由其它信号或定时器中断唤醒。僵死状态Zombie进程已经结束运行且释放大部分资源,但尚未释放进程控制块。停滞状态Stopped进程停止运行,通常是由进程接收到一个信号所致。当某个进程处于调试状态时也可能被暂停运行。Linux进程的状态转换变量名说明state进程当前状态need_resched布尔值,表示该进程是否需要申请调度policy进程调度类型rt_priority进程实时优先级nice用户控制的进程优先级priority静态优先级,实时进程忽略该成员count当前时间片内该进程的剩余运行时间cpus_allowed以位向量形式表示可执行该进程的CPUcpus_runnable以位向量形式表示当前运行该进程的CPUprocessor本进程当前(或最近)所在的CPUthread用于保存进程执行环境(各寄存器的值及IO操作许可权映射表)调度信息调度信息每个进程的task_struct结构中有以下四项:policy、priority、counter、rt_priority、nice。进程的调度策略:policy:SCHED_FIFO:不同的进程根据静态优先级进行排队,然后在同一优先级的队列中,谁先准备好运行就先调度谁,并且正在运行的进程不会被终止直到以下情况发生:1.被有更高优先级的进程所强占CPU;2.自己因为资源请求而阻塞;3.自己主动放弃CPU(调用sched_yield);SCHED_RR:它给每个进程分配一个时间片,时间片到了,正在执行的进程就放弃执行。SCHED_FIFO和SCHED_RR是实时进程调度策略,只针对优先级相同并且都是就绪状态的的进程。SCHED_OTHER是普通进程,常规分时调度策略。静态优先级:priority(0~70)表示分配给进程的时间片个数;指明在被迫和其他进程竞争CPU之前,进程允许的最大时间片;只能由用户进行修改,不随时间而改变;;动态优先级(剩余时间片):counter是进程使用CPU运行时间的计数值。初始值为priority,随时钟滴答减1。counter=0时,进程的时间片已用完。用priority对counter重新赋值。小于0时,标记进程重新调度。实时进程优先级:rt_priority(0~99)确定实时进程的调度顺序。非实时进程的优先级取值范围100-139

,实时进程总优先于非实时进程。通过chrt命令设置进程优先级:chrt

10

bash

(设置bash进程优先级为10)chrt-p

$$

(

$$

是运行脚本的当前进程ID号)进程优先级修正数值:nice(-20~19)其表示进程可被执行的优先级的修正数值,(new)PRI

=(old)PRI+nice;nice值越小,优先级越高,分到的时间片越多。通过nice命令设置将要执行的进程的nice值:nice

-n

-5

/usr/local/mysql/bin/mysqld_safe

&通过renice命令设置正在运行的进程的nice值:renice-5-p

5200

(设置PID为5200的进程nice值设为-5)进程调度时机时机1.进程状态发生变化时。时机2.当前进程时间片用完时。时机3.进程从系统调用返回到用户态时。时机4.中断处理后,进程返回到用户态时。Linux下进程间的关系系统第一个进程init进程在Linux系统中,没有一个进程是和其他进程完全无关的。在每一个进程的task_struct中都有指向它的父进程和兄弟进程(拥有相同的父进程的进程)以及它的子进程的的指针。在Linux系统中,用户可以用pstree命令看到正在运行的进程的家庭关系。创建了子进程后,父进程有两种选择。一是不受阻的(non-blocking)方式,也称为“异步”方式,即父进程继续走自己的路,与子进程分道扬镳。这种情况下如果子进程先

于父进程结束,则内核会向父进程发一个信号告知子进程已经结束。二是受阻的(blocking)方式,也称为“同步”方式,即父进程停下来进入睡眠状态,等待子进程结束,然后父进程再继续运行。进程族谱图p_opptr

:

originalparent (process

1或者创建它的父进程)p_pptr: parent(父进程,有时候是调试时的调试监管进程)p_cptr: child(指向

自己最年轻的子进程)p_ysptr:指向比自己年轻的兄弟进程p_osptr:指向比自己老的兄弟进程Linux进程标识进程标识:系统识别进程的依据,进程访问设备和文件时的凭证。进程有多种标识,记录在task_struct中标识的值是一个整数Linux进程的主要标识:int

pid;

//进程标识符,32位unsigned

short

uid,

gid;unsigned

short

euid,

egid;unsigned

short

suid,

sgid;//进程的用户、组标识符//用户、组有效标识符//用户、组备份标识符unsigned

short

fsuid,

fsgid;//用户、组文件标识号进程相关重要数据结构(链表)Linux把系统中所有进程的任务结构体集中进行管理,并根据需要把它们组织成各种不同的链表(队列)。一个主要的链表结构是把所有进程的任务结构体相互链接成一个环行双向链表.使用指针:struct

task_struct

*next_task;struct

task_struct

*prev_task;链表首结点就是初始化进程init的任务结构体init_task,系统还在内核空间设置了一个指针数组task[],该数组的每个元素指向一个任务结构体struct

task_struct

*task[NR_TASKS]={&init_task};进程相关重要数据结构(链表)符号常量NR_TASKS决定了数组task的大小,它的默认值为512:#define

NR_TASKS

512为了记录系统中实际存在的进程数目,系统定义了一个全

局变量nr_tasks,其值随系统中存在的进程数目而变化:int

nr_tasks=1;创建进程时,nr_tasks加1,撤销进程时nr_tasks减1遍历所有进程结构体:#define

for_each_task(p)for(p=&init_task;(p=p->next_task)!=&init_task;)进程相关重要数据结构(链表)进程环境系统调用进程控制系统调用与进程相关的系统调用环境表和环境变量每个进程都会接收到一张环境表通过environ找到环境表extern

char

**environ;环境字符串:name=value环境表和环境变量访问环境变量的方法直接使用environ使用getenv和putenv等函数修改环境变量的三种方法:putenvsetenvunsetenv访问环境变量#include

<stdio.h>#include

<stdlib.h>#include

<unistd.h>extern

char**environ;int

main(int

argc,

char

**argv){char

**env

=

environ;while(*env){printf(“%s\n”,*env);env++;}//printf("HOME:

%s\n",

getenv("HOME"));return0;}getenv头文件#include<stdlib.h>函数原型char*getenv(const

char*name);函数说明getenv()用来取得参数name环境变量的内容。参数

name为环境变量的名称,如果该变量存在则会返回指向该内容的指针。环境变量的格式为name=value。返回值执行成功则返回指向该内容的指针,找不到符合的环境变量名称则返回NULL。getenv实例#include<stdlib.h>intmian(){char

*p;if((p=getenv(“USER”)))printf(“USER=%s\n”,p);return

0;}执行结果:USER=rootputenv实例头文件#include<stdlib.h>定义函数int

putenv(const

char

*

string);函数说明putenv函数将形式为name=value的字符串,放入环境表中;若name已经存在,则先删除其原来的定义。返回值执行成功则返回0,有错误发生则返回-1。putenv#include<stdlib.h>int

main(){char

*p;if((p

=

getenv(“USER”)))printf(“USER

=%s\n”,p);putenv(“USER=test”);printf(“USER=%s\n”,getenv(“USER”));}执行结果:USER=root

USER=test设置环境变量setenvsetenv函数原型:int

setenv(const

char*

name,const

char*

value,int

rewrite);setenv将环境变量name的值设置为value。若name已经存在rewrite!=0,该环境变量原已有内容会被改为参数value所指的变量内容。rewrite==0,且该环境变量已有内容,则参数value会被忽略。返回值执行成功则返回0,有错误发生时返回-1。删除环境变量unsetenv函数用于删除某个环境变量函数原型int

unsetenv(const

char*

name);设置环境变量#include<stdlib.h>int

main(){char*p;if((p=getenv(“USER”)))printf(“USER

=%s\n”,p);setenv(“USER”,“test”,1);printf(“USER=%s\n”,getenv(“USER”));unsetenv(“USER”);printf(“USER=%s\n”,getenv(“USER”));return

0}执行:USER

=rootUSER

=

testUSER

=

(null)实验一编写代码,实现以下功能:打印当前所有环境变量的值;添加新的环境变量NEWENV=first;修改环境变量NEWENV的值为second;打印环境变量NEWENV的值。进程环境系统调用进程控制系统调用与进程相关的系统调用进程标识符创建进程系统调用:fork函数Vfork函数Exec函数系列结束进程系统调用:exit函数_exit函数wait等函数更改用户ID和组IDsystem函数进程标识符每个进程都有一个非负整型表示的唯一进程ID进程ID总是唯一的当进程终止后,其ID值可以重用在unix中ID为0的进程:调度进程,称为swapperID为1的进程:init进程,自举过程结束时由内核调用ID为2的进程:页守护进程,负责支持虚拟存储系统的分页操作进程标识符获取进程常见标识符调用进程的进程ID:pid_t

getpid();调用进程的父进程ID:pid_tgetppid();调用进程的实际用户ID:uid_tgetuid();调用进程的有效用户ID:uid_t

geteuid();调用进程的实际组ID:gid_tgetgid();调用进程的有效组ID:gid_tgetegid();查看进程情况(4.8)$ps

-ef

|

lessfork函数一个进程可以调用fork函数创建一个新进程新进程被称为子进程函数原型pid_t

fork(void);返回值fork函数调用一次,但是返回两次在子进程中返回0,在父进程中返回子进程ID,出错返回-1通过返回值,可以确定是在父进程还是子进程中fork函数子进程和父进程继续执行fork调用之后的指令子进程是父进程的副本子进程获得父进程数据空间、堆和栈的副本父子进程并不共享这些存储空间父子进程共享正文段(只读的)为了提高效率,fork后不并立即复制父进程空间,采用了

COW(Copy-On-Write)当父子进程任意之一,要修改数据段、堆、栈时,进行复制操作,但仅复制修改区域fork()过程调用alloc_task_struct(

)函数为新进程分配8KB的task_struct空间和系统堆栈空间。让当前指针指向父进程的task_struct

,并把父进程task_struct的内容拷贝到子进程的task_struct中。检查新创建这个子进程后,当前用户所拥有的进程数目是否超出给他分配的资源的限制。接下来,子进程的状态被设置为

TASK_UNINTERRUPTIBLE以保证它不会马上投入运行。调用get_pid()为新进程获取一个有效的PID。fork()过程更新不能从父进程继承的task_struct的其他所有域,例如,进程间亲属关系的域。把新的task_struct插入进程链表,以确保进程之间的亲属关系。

把新的task_struct插入pidhash哈希表。

把子进程task_struct中的状态域设置成TASK_RUNNING,并调用wake_up_process( )把子进程插入到运行队列链表。

让父进程和子进程平分剩余的时间片。

返回子进程的PID,这个PID最终由用户态下的父进程读取mm_struct父进程task_structPIDcounterMM(虚拟内存)p_pptr核心栈调度信息标识信息资源信息数据段用户栈代码段PIDcounterMM(虚拟内存)p_pptr核心栈调度信息标识信息资源信息子进程task_structmm_struct代码段数据段用户栈fork执行过程动画演示Difference

between

parent

and

child

after

forkProperties

inheritedfrom

parent:Real

user/group

ID,effective

user/group

IDSupplementary

group

IDProcessgroup

IDSession

ID;Control

terminal.Set-user/group-IDcurrent

work

directoryFile

modemaskSignalmask;environment;Resource

limitsDifference

between

parent

andchild:return

value

from

forkProcess

IDParent

process

ID;The

child’s

value

for

tms_utime,tms_stime

,tms_cutime,tms_ustime

are

set

to

0;File

locks

do

not

be

inherited

bychildPendingalarm

are

clearedforchildfork函数常见用法一个父进程希望复制自己,使父子进程同时执行不同的代码段网络服务程序中,父进程等待客户端的服务请求,当请求达到时,父进程调用fork,使子进程处理

该次请求,而父进程继续等待下一个服务请求到

达一个进程要执行一个不同的程序子进程从fork返回后,立即调用exec执行另外一个程序fork#include<stdio.h>#include<unistd.h>#include<stdlib.h>int

main(int

argc,

char*

argv[]){intnum

=1;intpid;pid

=fork();if(pid

<0

){printf("fork

error!\n");return

0;}else

if(pid

==

0){printf("this

is

child,pid:%d,num:%d\n",getpid(),++num);exit(0);}else{sleep(2);printf("this

is

parent,pid:%d,

num:%d\n",getpid(),

num);}return

0;}实验二编写代码实现以下功能:打印字符串“hello

world!”在打印字符串“helloworld!”前调用三次fork,分析打印结果。vfork函数vfork与fork的函数原型相同,但两者的语义不同vfork用于创建新进程,而该新进程的目的是exec一个新程序(执行一个可执行的文件)vfork函数并不将父进程的地址空间完全复制到子进程中。子进程在调用exec或exit之前,在父进程的地址空间中运行vfork函数保证子进程先执行,在它调用exec或者exit之后,父进程才可能被调度执行vfork#include<stdio.h>#include<unistd.h>#include<stdlib.h>int

main(int

argc,

char*

argv[]){intnum

=1;intpid;pid

=vfork();if(pid

<0

){printf(“vfork

error!\n");return

0;}else

if(pid

==

0){printf("this

is

child,pid:%d,num:%d\n",getpid(),++num);_exit(0);//return

0?}else{printf("this

is

parent,pid:%d,

num:%d\n",getpid(),

num);}return

0;}Execexec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,颇有些神似"三十六计"中的"金蝉脱壳"。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执

行。进程执行#include

<unistd.h>int

execl(const

char

*path,

const

char

*arg,

...,

NULL);int

execlp(const

char

*file,

const

char

*arg,

...,

NULL);int

execle(const

char

*path,

const

char

*arg,

...,

NULL

,char

*const

envp[]);int

execv(const

char

*path,

char

*const

argv[]);int

execvp(const

char

*file,

char

*const

argv[]);int

execve(const

char

*path,

char

*const

argv[],

char

*const

envp[]);注意:exec函数一共有六个,其中execve为内核级系统调用,其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数。Exec系列函数说明第5位l:表示参数传递为逐个列举方式execl、execle、execlpv:表示参数传递为构造指针数组方式execv、execve、execvp第6位e:可传递新进程环境变量execle、execvep:表示系统会自动从环境变量“$PATH”所指出的路径中查找可执行程序。execlp、execvpexecve#include<unistd.h>函数定义:int

execve(const

char

*filename,

char

*const

argv[

],char

*const

envp[

]);返回值:

函数执行成功时没有返回值,执行失败时的返回值为-1.函数说明:

execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用数组指针来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。execve#include<unistd.h>main(){char

*argv[

]={"ls",

"-

al","/etc/passwd",

NULL};char

*envp[

]={"PATH=/bin",

NULL}execve("/bin/ls",

argv,

envp);}execl实例#include

<unistd.h>#include

<stdio.h>#include

<stdlib.h>int

main(){if(fork()==0){if(execl("/bin/ls","ls","-l",NULL)<0)perror("execlp

error!");elseprintf("can

you

see

this?");}return

0;}execlp实例#include<stdio.h>#include

<stdlib.h>#include

<unistd.h>intmain(int

argc,char*argv[]){execlp(“ls”,“ls”,”-l”,”/home”,

NULL);return

0;}execle实例#include

<unistd.h>#include

<stdio.h>#include

<stdlib.h>int

main(){char*envp[]={"PATH=/tmp","USER=zhanSan",NULL};if(fork()==0){/*调用execle函数,注意这里也要指出env的完整路径*/if(execle("/bin/env","env",NULL,envp)<0)perror("execle

error!");}exit(

0);}execv实例#include<unistd.h>int

main()3

{4char

*argv[]

=

{"ls",“-l","/usr/local",(char

*)NULL};5execv("/bin/ls",argv);•Printf(“hello\n”);6return

0;7

}execvp实例#include<stdio.h>#include

<stdlib.h>#include

<unistd.h>main(){char

*argv[]

={"ls","-l","/etc",(char

*)0};execvp("ls",argv);return

0;}fork与execve区别fork:在新的子进程运行相同的程序。新进程是父进程的复制品。execve:在当前进程的上下文加载并运行一个新的程序。覆盖当前进程的地址空间。但没有创建新进程。新的程序仍然有相同的PID,并且继承了调用execve函数时已打开的所有文件描述。进程终止正常终止:在main函数内执行return语句;调用exit函数;调用_exit或_Exit函数;

4.最后一个线程从其启动例程返回进程的最后一个线程调用pthread_exit函数。异常终止:调用abort,产生SIGABRT信号;进程接收到某些信号时。线程收到取消请求。exit函数若父进程在子进程之前终止了,则子进程的父进程将变为

init进程,其PID为1;保证每个进程都有父进程当子进程先终止,父进程如何知道子进程的终止状态(exit(5))内核为每个终止子进程保存了终止状态等信息父进程调用wait等函数,可获取该信息当父进程调用wait等函数后,内核将释放终止进程所使用的所有内存,关闭其打开的所有文件对于已经终止、但是其父进程尚未对其调用wait等函数的进程,被称为僵尸进程return与exitreturn语言级别的,它表示调用堆栈的返回;return是返回函数值并退出函数,通常0为正常退出,非0为异常退出。在主函数main中,return会结束当前进程。如果不是在main函数中,那就是退回上一层调用。exit系统调用级别的,它表示退出应用程序,也就是一个进程的结束,它将删除进程使用的内存空间。对于单独的进程,exit的返回值是返回给操作系统的。如果是多进程,则是返回给父进程的。这个返回值标识了应用程序的一些运行信息。通常情况:exit(0)表示程序正常退出,exit(1)和exit(-1)表示程序异常退出,exit(2)表示系统找不到指定的文件。#include<unistd.h>#include<stdio.h>#include<string.h>#include<stdlib.h>voidtest(){pid_tpid=vfork();if(

pid==0

){printf("1.this

is

child:pid=%d,

ppid=%d\n",

getpid(),

getppid());return

;//exit(1);}else

if(

pid>0

)printf("2.Thisis

farther:pid=%d,ppid=%d\n",

getpid(),

getppid());elseperror("vfork

error:");}voidfun(){int

buf[100];memset(buf,

0,

100);printf("3.pid=%d,ppid=%d\n",getpid(),getppid());}int

main(){test();fun();return0;}#include

<unistd.h>#include

<stdio.h>#include

<string.h>#include

<stdlib.h>void

test(){pid_t

pid=vfork();if(

pid==0

){

printf("1.this

is

child:pid=%d,

ppid=%d\n",

getpid(),getppid());//return

;exit(1);}else

if(

pid>0

)

printf("2.This

is

farther:pid=%d,

ppid=%d\n",

getpid(),getppid());elseperror("vfork

error:");}voidfun(){int

buf[100];memset(buf,

0,

100);printf("3.pid=%d,ppid=%d\n",getpid(),getppid());}int

main(){test();fun();return0;}进程终止三个终止函数:exit、_Exit、_exit函数原型:void

exit(int

status);void

_Exit(int

status);void

_exit(int

status);exit函数执行一个标准I/O库的清理关闭操作(为所有打开流调用fclose函数)后,进入内核。_Exit、_exit函数立即进入内核,直接使进程停止运行,清除其使用

的内存空间,并清除其在内核中的

各种数据结构exit()实例#include

<stdio.h>#include

<stdlib.h>int

main(){printf("using

exit....\n");printf("can

you

see

this?");exit(0);}_exit()实例#include

<stdio.h>#include

<unistd.h>#include

<unistd.h>int

main(){printf("using

_exit....\n");printf("can

you

see

this?");_exit(0);}atexit函数(登记函数)当进程终止时,程序可能需要进行一些自身的清理工作,如资源释放等等。atexit函数提供了进行这样工作的机会。它允许用户注册若干终止处理函数,当进程终止时,这些终止处理函数将会被自动调用。函数原型:int

atexit(void

(*func)(void));参数:func:函数指针,返回值为void,无参返回值:成功返回0,出错返回非0值注意:先注册的函数,后被运行。调用这些注册函数的顺序与它们登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。调用_exit函数并不会触发终止处理函数。#include<stdio.h>#include<stdlib.h>voidfunc1(void){printf("in

func1\n");}voidfunc2(void){printf("in

func2\n");}voidfunc3(void){printf("in

func3\n");}int

main(){atexit(func3);atexit(func2);atexit(func1);printf("In

main\n");return

0;//_exit(0);}进程销毁函数原型:pid_t wait(int

*status)参数:status可以为NULL,表明父进程不需要子进程的终止状态。若status不是空指针,则进程终止状态就存放在它指向的存储单元中,由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,就设计了一套专门的宏(macro)来完成这项工作。返回值:若成功返回终止进程ID,出错返回-1父进程调用wait可能出现的三种情况:①父进程的所有子进程都在运行,wait()阻塞。②父进程没有任何子进程,则立即出错返回。③有一个子进程已经终止,则取得该子进程的终止状态立即返回。检测退出状态的宏:WIFEXITED(status)判断子进程是否为正常退出(不是被其它进程杀死),如果是,返回一个非零值。WEXITSTATUS(status)当WIFEXITED返回非零值时,可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回

5。如果进程不是正常退出的,即WIFEXITED返回0,这个值就毫无意义。wait执行流程wait实例#include

<stdio.h>#include

<stdlib.h>#include

<unistd.h>#include

<sys/wait.h>int

main(){pid_t

pid;int

status,i;if(fork()==0){printf("This

is

the

child

process

.pid

=%d\n",getpid());exit(5);}else{sleep(1);printf("This

is

the

parent

process

,wait

for

child...\n");pid=wait(&status);if(WIFEXITED(status))i=WEXITSTATUS(status);printf("child’s

pid

=%d

.exit

status=%d\n",pid,i);}return

0;}waitpid函数原型#include

<sys/types.h>#include

<sys/wait.h>pid_t

waitpid(pid_t

pid,

int

*status,

int

options);参数pid>0时,只等待进程ID为pid的子进程结束,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。pid

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论