1 / 68

实例研究: UNIX 进程模型

实例研究: UNIX 进程模型. 1 进程模型的基本结构和工作过程 2 进程状态及转换 3 UNIX 的进程控制与管理 4 进程通信. 1 进程模型的基本结构和工作过程. UNIX 的进程由三部分组成: proc 结构 ( 常驻内存的 PCB) 数据段 ( 执行时用到的数据 ) 正文段 ( 程序代码 ) 合称进程映像, UNIX 中把进程定义为映像的执行。. PCB 由基本控制块 proc 结构和扩充控制块 user 结构组成。 proc 结构存放一个进程最基本、必需的信息,常驻内存;

mai
Download Presentation

实例研究: UNIX 进程模型

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 实例研究: UNIX进程模型 • 1 进程模型的基本结构和工作过程 • 2 进程状态及转换 • 3 UNIX的进程控制与管理 • 4 进程通信

  2. 1 进程模型的基本结构和工作过程 UNIX的进程由三部分组成: proc结构(常驻内存的PCB) 数据段(执行时用到的数据) 正文段(程序代码) 合称进程映像,UNIX中把进程定义为映像的执行。

  3. PCB由基本控制块proc结构和扩充控制块user结构组成。PCB由基本控制块proc结构和扩充控制块user结构组成。 • proc结构存放一个进程最基本、必需的信息,常驻内存; • user结构存放进程运行时才用到的数据和状态信息,当进程暂时不在处理机上运行时,就把它放在磁盘的对换区中。

  4. 系统中维持一张名叫proc的进程表,每个表目为一个proc结构,供一个进程使用。创建进程时,在proc表中找一个空表目,以建立起相应于该进程的proc结构。系统中维持一张名叫proc的进程表,每个表目为一个proc结构,供一个进程使用。创建进程时,在proc表中找一个空表目,以建立起相应于该进程的proc结构。 内存中设置一张text正文段表。每个表目都是一个text结构,记录一个共享正文段的属性(磁盘和主存中的位置、尺寸、共享的进程数等、正文段文件节点指针)。

  5. proc表 text表 p-addr p-textp x-daddr x-caddr 进程数据段首地址 共享正文段的起始块号 text表指针 常驻内存部分 进程系统数据区 user 核心栈 数据段 用户栈 共享正文段 非常驻内存部分 用户地址空间 进程映像的基本结构

  6. (1)进程基本控制块结构 struct proc{ char p_stat; /*进程状态*/ char p_flag; /*进程标志*/ char p_pri; /*进程优先级*/ char p_sig;/*软中断号*/ char p_uid; /*用户号*/ char p_time; /*驻留时间*/ char p_cpu;/*进程占据CPU的时间量*/ char p_nice; /*用于计算优先级*/

  7. int p_ttyp; /*控制终端tty结构的地址*/ int p_pid;/*进程号*/ int p_ppid; /*父进程号*/ int p_addr;/*数据段地址*/ int p_size; /*数据段大小*/ int p_wchan; /*等待的原因*/ int *p_textp; /*对应正文段的text项地址*/ }proc[NPROC];

  8. p_flag的助记符包括: SLOAD 01 在内存 SSYS 02 进程0# SLOCK 03 锁住,不能换出内存 SSWAP 04 正在换出 STRC 05 被跟踪

  9. NULL 0 proc为空 • SSLEEP 1 睡眠 • SWAIT 2 等待 • SRUN 3 运行或就绪 • SIDL 4 创建时的临时状态 • SZOMB 5 僵死状态 • SSTOP 6 被跟踪 • SXBRK 7 因数据段扩展未满足的换出状态 • SXSTK 8 因栈段扩展未满足的换出状态 • SXFRK 9 创建子进程时内存不够,父进程锁定 • 在内存的状态 • SXTXT 10 因正文段扩展未满足的换出状态 p_stat的助记符:UNIX系统Ⅴ定义了10种进程状态

  10. (2)进程扩展控制块结构 struct user{ int u_rsav[2]; /*保留现场保护区指针*/ char u_segflg; /*用户/核心空间标志*/ char u_error; /*返回出错代码*/ char u_uid; /*有效用户号*/ char u_gid; /*有效组号*/ int u_procp; /*proc结构地址*/ char u_base; /*内存地址*/ char *u_count; /*传送字节数*/ char *u_offset[2]; /*文件读写位移*/

  11. int *u_cdir; /*当前目录I节点地址*/ char *u_dirp; /*I节点当前指针*/ Struct { int u_ino; char u_name [DIRSIZ]; }u_dent; /*当前目录项*/ int u_ofile [NOFILE]; /*用户打开文件表*/ int u_arg[5]; /*存系统调用的自变量*/ int u_tsize; /*正文段大小*/

  12. int u_dsize; /*用户资料区大小*/ int u_ssize; /*用户栈大小*/ int u_utime; /*用户态执行时间*/ int u_stime; /*核心态执行时间*/ int u_cutime; /*子进程用户态执行时间*/ int u_cstime; /*子进程核心态执行时间*/ int u_ar0; /*当前中断保护区内r0地址*/ };

  13. (3)正文段结构 struct text{ int x_daddr; /*磁盘地址*/ int x_caddr; /*内存地址*/ int x_size; /*内存块数*/ int x_iptr; /*文件内存I节点地址*/ char x_count; /*共享进程数*/ char x_ccount; /*内存副本的共享进程数*/ }text[NTEXT];

  14. 运行 exit 僵死 核心态 返回 中断 自陷 用户态 sleep sleep 被抢占 switch 内存充足 wakeup fork 高优先睡 眠在内存 低优先睡 眠在内存 在内存就绪 创建 内存 wakeup 换入 换出 换出 换出 磁盘交换区 wakeup 高优先睡 眠且换出 就绪且换出 低优先睡 眠且换出 wakeup 2 进程状态及转换 UNIX的进程状态及转换图

  15. 3 UNIX的进程控制与管理 (1)创建进程 UNIX系统中,进程分为两大类:系统进程和用户进程。 • 系统进程执行操作系统程序,提供系统功能,例如资源分配、进程调度。 • 用户进程执行用户程序,提供用户功能。

  16. 0号是swap进程,系统自举时被创建; • 1号是init进程,由0号进程孵化而创建。其他进程都是1号进程调用fork()创建的子进程。 • fork系统调用生成一个新进程,新生成的进程称为子进程,生成新进程的进程为父进程。

  17. Fork举例 #include <stdio.h> main() { int pid=0; printf(“the parent is going to fork\n”); pid=fork(); if(pid!=0) printf(“I am the father of %d“,pid); else printf(“I am the child"); } 运行结果?

  18. fork() 内存有空? n 在proc 数组中找一个空表项 找到否? 在磁盘复制 y y 为子进程申请空间; 复制user,数据段; u_procp指向proc; u_utime=0; u_stime=0; 栈指针-->u_rsav[2]; p_pid=系统赋标识号码 p_stat=SRUN; p_ppid=当前进程pid; 复制父进程的p_textp,p_size p_uid,p_ttyp,p_nice... 子进程返回0; 父进程返回子进程pid; x_count++; f_count++ 子: 0 父: 子pid fork系统调用的工作流程

  19. 父进程创建子进程 proc[N] text[ ] p_textp 父 x_caddr p_textp 子 p_addr p_addr 核心区 用户区 用户栈 数据段 核心栈 user 用户栈 数据段 核心栈 user 正文段

  20. fork( )说明 fork( ):创建子进程,系统执行操作: •为子进程分配进程表。 •为子进程分配进程标识符。 •复制父进程的进程映像,但不复制共享内存区。 •增加父进程所打开文件的计数,表示新进程也使用这些文件。 •把子进程置为“就绪”状态。 •返回子进程标识符给父进程,把0值返回给子进程。

  21. 父进程还应执行以下操作之一,以完成进程指派:父进程还应执行以下操作之一,以完成进程指派: •继续待在父进程中。把进程控制切换到父进程的用户模式,在fork()点继续运行,而子进程进入“就绪”状态。 •把进程控制传递到子进程,子进程在fork()点继续运行,父进程进入“就绪”态。 •把进程控制传递到其他进程,父进程和子进程进入“就绪”状态。

  22. fork( )说明 1 子进程是父进程的复制品,完全一样 2 父子进程的指令执行点都在fork( )语句之后的那个语句 3 fork( )没有参数,但返回值pid不一样,子进程中pid为0;父进程中pid为非0正整数(即子进程的内部标识号) 4 父子进程可根据不同pid值执行不同程序,完成不同工作

  23. 子1 父 子1 子2 父 子2 main( ) { if(fork()==0) { 子1的代码段; if(fork()==0) {子2的代码段} else { 子1的代码段} } else {父代码段} } main( ) { if(fork()==0) { 子1的代码段} else {if(fork()==0) {子2的代码段} else {父代码段} } }

  24. 例1 #include <stdio.h> main() { int p,x; x=2; if((p=fork())>0) printf("this is parent:%d",++x); else printf("this is child:%d",++x); }

  25. B C D E F G H A • 例2 main() { fork(); fork(); fork(); } 写出程序生成的进程树

  26. B C D E F A 例3 main() { int p; if ((p=fork())>0) fork(); else { fork(); fork(); } } 写出程序生成的进程树

  27. 例4 main() { int pid; printf("Before fork\n"); while((pid=fork())==-1); if(pid){ printf("It is parent process:PID=%d\n", getpid()); printf(“Produce child‘sPID=%d\n”,pid);} else printf("It is child process:PID=%d/n",getpid()); printf("It is parent or child process:PID==%d/n",getpid()); }

  28. (2)进程映像的改换 更换进程执行代码,更换正文段,数据段 调用格式:exec (文件名,参数表,环境变量表) exec系列函数有execl、execv、execlp、execvp等

  29. 例5 #include <stdio.h> #include <sys/types.h> #include <unistd.h> main() { int pid; printf(“Now this is process 1.\n”); if ((pid=fork())==-1) { perror(“fork failed”); exit(1); } else if(pid==0) { printf(“This is child process.\n”); if (execl(“/bin/ps”,“ps”,“-ef”,0)<0) perror(“execl of ps failed”); printf(“this is subprocess.\n”); exit(0);} else if(pid>0) printf(“this is process1(parent process).\n”); exit(0);}

  30. (3) 父、子进程的同步 1) 进程的自我终止 当一个进程执行结束后,进程可以调用exit自我终止。终止时它放弃占用的所有资源,处于等待父进程善后处理状态。

  31. exit(int status) 寻找父进程 Y 关闭打开 文件表目录 u_ofile,u_cdir 找到了? N 将父进程改为0#进程 释放正文段 x_count--; ... wakeup(p_ppid); 将所有子进程的父 进程改为1#进程 释放user,所有栈, 数据段 switch p_stat=SZOMB exit的工作流程

  32. 调用格式: exit(status) 在用户态的进程程序中,调用exit(status)将使进程终止,参数status是终止进程向父进程传递的参数。在父进程中,利用系统调用wait(status)获取该参数。

  33. 2)父进程等待子进程终止 系统调用wait,对处于等待善后处理状态的子进程进行善后处理。在用户态进程中,父进程调用wait(status)等待它的一个子进程终止。 它的子进程还没有终止,则它进入阻塞态。

  34. wait(int * stat) 从盘上取出子进程user 有子进程结束? (father) ( sun) u_cutime+=u_utime; u_cstime+=u_stime; u_cutime+=u_cutime; u_cstime+=u_cstime Y N sleep(u_procp,SWAIT) user中的status-->stat 释放子进程的proc 返回子进程的pid

  35. 例 父、子进程的同步关系。 main() { int proc_id;   while((proc_id=fork())==-1); if (proc_id) { proc_id=wait(); printf("the child process has finished.\n"); printf("The child process PID=%d\n", proc_id); } else{ printf(“ In child process.\n"); exit(); }}

  36. 该程序的运行结果: In child process. The child process has finished. The child process PID=177

  37. 4.进程通信 • 管道 • 消息队列 • 共享内存

  38. 1 管道(pipe) • 管道(pipe)是连接读写进程的一个特殊文件,允许进程按先进先出方式传送数据,也能使进程同步执行操作。 • 发送进程以字符流形式把大量数据送入管道,接收进程从管道中接收数据,所以叫管道通信。

  39. 共享 文件 写进程 读进程 共享文件通信机制 • 管道的实质是一个共享文件,基本上可借助于文件系统的机制实现,包括(管道)文件的创建、打开、关闭和读写。

  40. UNIX管道相关系统调用 • 通过pipe系统调用创建无名管道,得到两个文件描述符,分别用于写和读。 • int pipe(int fildes[2]); 文件描述符fildes[0]为读端,fildes[1]为写端; • int read(int fd, char * buf, unsigned nbyte) 从fd所指示的文件中读出nbyte个字节的数据,送至由指针buf所指示的缓冲区中。 • int write(int fd, char * buf, unsigned nbyte) 把nbyte 个字节的数据,从buf所指向的缓冲区写到由fd所指向的文件中。

  41. 进程间双向通信,通常需要两个管道; • 只适用于父子进程之间或父进程安排的各个子进程之间; • 使用时先创建管道再创建进程。

  42. 父子进程之间的管道

  43. 管道使用 • 父-子进程/子-子进程 • 使用模式: pipe——fork——write(写进程) ——read(读进程) 注意:pipe-fork顺序 例 建立一个pipe,父进程生成两个子进程,子进程向pipe中写入字符串,父进程从pipe 中读出该字符串。

  44. #include <string.h> #include <unistd.h> #include <signal.h> #include <stdio.h> main() { int fd[2],pid,pid2,n; char buffer[256],dat[40]="hello world\n"; pipe(fd); pid=fork(); if(pid==0) { lockf(fd[1],1,0); sprintf(dat,"child 1 process is sending message1!\n"); write(fd[1],dat,strlen(dat)); lockf(fd[1],0,0); printf("child %d write%d byge: %s\n",getpid(),strlen(dat),dat); }

  45. else {pid2=fork(); if(pid2==0) {lockf(fd[1],1,0); sprintf(dat,"child 2 process is sending message1!\n"); write(fd[1],dat,strlen(dat)); lockf(fd[1],0,0); printf("child %d write%d byge: %s\n",getpid(),strlen(dat),dat); } else { wait(0); wait(0); printf("parent1\n"); n=read(fd[0],buffer,256); printf("parent %d read %d bytes:%s",getpid(),n,buffer); } } }

  46. 2 消息队列(message) 消息队列 头结构 消息缓冲区 消息头结构 消息队列头表 消息缓冲区池

  47. struct msqid_ds msg_perm P Q msg msg msg 类型正文 类型正文 正文 正文 正文 消息队列数据结构

  48. UNIX消息队列API • msgget 依据用户给出的整数值key,创建新消息队列或打开现有消息队列,返回一个消息队列ID; • msgsnd 发送消息; • msgrcv 接收消息,可以指定消息类型;没有消息时,返回-1; • msgctl 对消息队列进行控制,如删除消息队列;

  49. UNIX消息队列API • 四个函数使用头文件 #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> • int msgqid=msgget(key_t key,int flag) key:用户指定的消息队列的名字 flag是用户设置的标志和访问方式。 如 IPC_CREAT |0400 该队列是否已被创建。无则创建,是则打开

More Related