博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
进程与进程描写叙述符(task_struct)
阅读量:5240 次
发布时间:2019-06-14

本文共 12168 字,大约阅读时间需要 40 分钟。

一、 进程

进程(Process)

计算机中的程序关于某数据集合上的一次执行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描写叙述,进程是程序的实体。

———————————————————————————————————————————————————————————————

  • 释义:一段程序的执行过程
  • 特征:动态、独立、异步、并发
  • 结构特征:程序、数据和
  • 相关概念:线程。管程

定义

狭义定义:进程是正在执行的程序的实例(an instance of a computer program that is being executed)。

广义定义:

进程是一个具有一定独立功能的程序关于某个数据集合的一次执行活动。
它是是操作系统动态执行的基本单元。在传统的操作系统中,进程既是主要的分配单元,也是主要的执行单元。

进程的概念主要有两点:

第一,进程是一个实体

'每一个进程都有它自己的地址空间,普通情况下,包含文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。'

第二。进程是一个“执行中的程序”

'程序是一个没有生命的实体,仅仅有处理器赋予程序生命时(操作系统执行之)。它才干成为一个活动的实体。我们称其为进程。

'

特征

动态性:进程的实质是程序在多道程序系统中的一次执行过程。进程是动态产生,动态消亡的。

并发性:不论什么进程都能够同其它进程一起并发执行

独立性:进程是一个能独立执行的基本单位,同一时候也是系统分配资源和调度的独立单位;

异步性:因为进程间的相互制约。使进程具有执行的间断性。即进程按各自独立的、不可预知的速度向前推进

结构特征:进程由程序、数据和进程控制块三部分组成。

多个不同的进程能够包含同样的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果。可是执行过程中。程序不能发生改变。

内容

一个计算机系统进程包含(或者说“拥有”)下列数据:

那个程序的可执行机器码的一个在存储器的映像。

分配到的存储器(通常包含虚拟内存的一个区域)。

存储器的内容包含可执行代码、特定于进程的数据(输入、输出)、调用堆栈、堆栈(用于保存执行时运数中途产生的数据)。 分配给该进程的资源的操作系统描写叙述符,诸如文件描写叙述符(Unix术语)或文件句柄(Windows)、数据源和数据终端。 安全特性,诸如进程拥有者和进程的权限集(能够容许的操作)。 处理器状态(内文),诸如寄存器内容、物理存储器寻址等。

当进程正在执行时。状态通常储存在寄存器,其它情况在存储器。

切换

进行进程切换就是从正在执行的进程中收回处理器。然后再使待执行进程来占用处理器。

这里所说的从某个进程收回处理器,实质上就是把进程存放在处理器的寄存器中的中间数据找个地方存起来。从而把处理器的寄存器腾出来让其它进程使用。那么被中止执行进程的中间数据存在何处好呢?当然这个地方应该是进程的私有堆栈。
让进程来占用处理器,实质上是把某个进程存放在私有堆栈中寄存器的数据(前一次本进程被中止时的中间数据)再恢复到处理器的寄存器中去,并把待执行进程的断点送入处理器的程序指针PC,于是待执行进程就開始被处理器执行了,也就是这个进程已经占有处理器的使用权了。
这就像多个同学要分时使用同一张课桌一样。所谓要收回正在使用课桌同学的课桌使用权,实质上就是让他把属于他的东西拿走。而赋予某个同学课桌使用权,仅仅只是就是让他把他的东西放到课桌上罢了。
在切换时,一个进程存储在处理器各寄存器中的中间数据叫做进程的上下文,所以进程的 切换实质上就是被中止执行进程与待执行进程上下文的切换。在进程未占用处理器时,进程 的上下文是存储在进程的私有堆栈中的。

状态

进程执行时的间断性。决定了进程可能具有多种状态。其实,执行中的进程可能具有下面三种基本状态。

1)就绪状态(Ready):

进程已获得除处理器外的所需资源。等待分配处理器资源;仅仅要分配了处理器进程就可执行。就绪进程能够按多个优先级来划分队列。比如,当一个进程因为时间片用完而进入就绪状态时。排入低优先级队列;当进程由I/O操作完毕而进入就绪状态时,排入高优先级队列。

2)执行状态(Running):

进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。

在没有其它进程能够执行时(如全部进程都在堵塞状态),一般会自己主动执行系统的空暇进程。

3)堵塞状态(Blocked):

因为进程等待某种条件(如I/O操作或进程同步)。在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程。也无法执行。
差别

程序

程序是指令和数据的有序集合。其本身没有不论什么执行的含义。是一个静态的概念。而进程是程序在处理机上的一次执行过程。它是一个动态的概念。

程序能够作为一种软件资料长期存在,而进程是有一定生命期的。程序是永久的,进程是临时的。
进程更能真实地描写叙述并发,而程序不能;
进程是由进程控制块、程序段、数据段三部分组成;
进程具有创建其它进程的功能。而程序没有。
同一程序同一时候执行于若干个数据集合上。它将属于若干个不同的进程。也就是说同一程序能够相应多个进程。
在传统的操作系统中。程序并不能独立执行,作为资源分配和独立执行的基本单元都是进程。

线程

进程和线程关系

通常在一个进程中能够包含若干个线程。它们能够利用进程所拥有的资源,在引入线程的操作系统中。通常都是把进程作为分配资源的基本单位,而把线程作为独立执行和独立调度的基本单位。因为线程比进程更小。基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。

当下推出的通用操作系统都引入了线程。以便进一步提高系统的并发性,并把它视为现代操作系统的一个重要指标。

控制

进程控制是进程管理中最主要的功能。它用于创建一个新进程,终止一个已完毕的进程,或者去终止一个因出现某事件而使其无法执行下去的进程,还可负责进程执行中的状态转换。

创建进程

1.引起创建进程的事件

在多道程序环境中,仅仅有(作为)进程(时)才干在系统中执行。

因此,为使程序能执行。就必须为它创建进程。导致一个进程去创建还有一个进程的典型事件,能够有下面四类:

1) 用户登录

在分时系统中,用户在终端键入登录命令后,假设是合法用户。系统将为该终端建立一个进程,并把它插入到就绪队列中。

2)作业调度

在批处理系统中。当作业调度程序依照一定的算法调度到某作业时,便将该作业装入到内存,为它分配必要的资源,并马上为它创建进程,再插入到就绪队列中。

3) 提供服务

当执行中的用户程序提出某种请求后,系统将专门创建一个进程来提供用户所须要的服务。比如,用户程序要求进行文件打印。操作系统将为它创建一个打印进程,这样,不仅能够使打印进程与该用户进程并发执行,并且还便于计算出为完毕打印任务所花费的时间。

4) 应用请求

在上述三种情况中,都是由系统内核为它创建一个新进程。而这一类事件则是基于应用进程的需求,由它创建一个新的进程。以便使新进程以并发的执行方式完毕特定任务。

2.进程的创建过程
这里写图片描写叙述
一旦操作系统发现了要求创建新进程的事件后,便调用进程创建原语create()按下述步骤创建一个新进程。

1) 申请空白PCB。为新进程申请获得唯一的数字标识符。并从PCB集合中索取一个空白PCB。

2) 为新进程分配资源。为新进程的程序和数据以及用户栈分配必要的内存空间。显然,此时操作系统必须知道新进程所须要的内存大小。

3) 初始化进程控制块。PCB的初始化包含:

①初始化标识信息。将系统分配的标识符和父进程标识符,填入新的PCB中。
②初始化处理机状态信息。使程序计数器指向程序的入口地址。使栈指针指向栈顶。

③初始化处理机控制信息,将进程的状态设置为就绪状态或精巧就绪状态,对于优先级,一般是将它设置为最低优先级,除非用户以显式的方式提出高优先级要求。

4) 将新进程插入就绪队列,假设进程就绪队列能够接纳新进程。便将新进程插入到就绪队列中。

进程终止

1.引起进程终止的事件

1)正常结束

在不论什么计算机系统中,都应该有一个表示进程已经执行完毕的指示。

比如,在批处理系统中,通常在程序的最后安排一条Hold指令或终止的系统调用。当程序执行到Hold指令时。将产生一个中断,去通知OS本进程已经完毕。

2)异常结束

在进程执行期间,因为出现某些错误和故障而迫使进程终止。这类异常事件非常多,常见的有:越界错误,保护错。非法指令。特权指令错,执行超时,等待超时。算术运算错,I/O故障。

3)外界干预

外界干预并不是指在本进程执行中出现了异常事件。而是指进程应外界的请求而终止执行。这些干预有:操作员或操作系统干预。父进程请求,父进程终止。

  1. 进程的终止过程
    假设系统发生了上述要求终止进程的某事件后,OS便调用进程终止原语,按下述过程去终止指定的进程。

1)依据被终止进程的标识符。从PCB集合中检索出该进程的PCB。从中读出该进程状态。

2)若被终止进程正处于执行状态,应马上终止该进程的执行,并置调度标志为真。用于指示该进程被终止后应又一次进行调度。
3)若该进程还有子孙进程,还应将其全部子孙进程予以终止,以防他们成为不可控的进程。

4)将被终止的进程所拥有的全部资源。或者归还给其父进程。或者归还给系统。
5)将被终止进程(它的PCB)从所在队列(或链表)中移出,等待其它程序来搜集信息。

堵塞唤醒

1.引起进程堵塞和唤醒的事件

1)请求系统服务

当正在执行的进程请求操作系统提供服务时。因为某种原因。操作系统并不马上满足该进程的要求时。该进程仅仅能转变为堵塞状态来等待。一旦要求得到满足后,进程被唤醒。

2)启动某种操作

当进程启动某种操作后,假设该进程必须在该操作完毕之后才干继续执行。则必须先使该进程堵塞,以等待该操作完毕,该操作完毕后,将该进程唤醒。

3)新数据尚未到达

对于相互合作的进程,假设当中一个进程须要先获得还有一(合作)进程提供的数据才干执行以对数据进行处理,则是要其所需数据尚未到达,该进程仅仅有(等待)堵塞。等到数据到达后。该进程被唤醒。

4)无新工作可做

系统往往设置一些具有某特定功能的系统进程,每当这样的进程完毕任务后。便把自己堵塞起来以等待新任务到来,新任务到达后。该进程被唤醒。

2.进程堵塞过程

正在执行的进程,当发现上述某事件后,因为无法继续执行,于是进程便通过调用堵塞原语block()把自己堵塞。可见。进程的堵塞是进程自身的一种主动行为。

进入block过程后,因为此时该进程还处于执行状态,所以应先马上停止执行,把进程控制块中的现行状态由执行改为堵塞,并将PCB插入堵塞队列。假设系统中设置了因不同事件而堵塞的多个堵塞队列,则应将本进程插入到具有同样事件的堵塞(等待)队列。最后。转调度程序进行又一次调度,将处理机分配给还有一就绪进程,并进行切换,亦即,保留被堵塞进程的处理机状态(在PCB中)。再按新进程的PCB中的处理机状态设置CPU环境。

  1. 进程唤醒过程
    当被堵塞的进程所期待的事件出现时,如I/O完毕或者其所期待的数据已经到达,则由有关进程(比方,用完并释放了该I/O设备的进程)调用唤醒原语wakeup(),将等待该事件的进程唤醒。

    唤醒原语执行的过程是:首先把被堵塞的进程从等待该事件的堵塞队列中移出,将其PCB中的现行状态由堵塞改为就绪。然后再将该PCB插入到就绪队列中。

调度算法

进程的调度算法包含:

实时系统中:FIFO(First Input First Output。先进先出算法)。SJF(Shortest Job First。最短作业优先算法),SRTF(Shortest Remaining Time First。最短剩余时间优先算法)。

交互式系统中:RR(Round Robin。时间片轮转算法)。HPF(Highest Priority First,最高优先级算法)。多级队列。最短进程优先。保证调度。彩票调度,公平分享调度。

阶段

进程是由进程控制块、程序段、数据段三部分组成。一个进程能够包含若干线程(Thread),线程能够帮助应用程序同一时候做几件事(比方一个线程向磁盘写入文件,还有一个则接收用户的按键操作并及时做出反应。互相不干扰),在程序被执行后,系统首先要做的就是为该程序进程建立一个默认线程,然后程序能够依据须要自行加入或删除相关的线程。是可并发执行的程序。在一个数据集合上的执行过程。是系统进行资源分配和调度的一个独立单位,也是称活动、路径或任务,它有双方面性质:活动性、并发性。进程能够划分为执行、堵塞、就绪三种状态。并随一定条件而相互转化:就绪–执行,执行–堵塞,堵塞–就绪。

进程为应用程序的执行实例,是应用程序的一次动态执行。

看似高深,我们能够简单地理解为:它是操作系统当前执行的执行程序。

在系统当前执行的执行程序里包含:系统管理计算机个体和完毕各种操作所必需的程序;用户开启、执行的额外程序,当然也包含用户不知道。而自己主动执行的非法程序(它们就有可能是病毒程序)。


二、进程描写叙述符

1.概念

在linux中,每一个进程都有一个进程描写叙述符。这个”进程描写叙述符”是一个结构体名字叫做task_struct,在task_struct里面保存了很多关于进程控制的信息。

task_struct是Linux内核的一种数据结构,它会被装载到RAM里并包含进程的信息。每一个进程都把它的信息放在task_struct这个数据结构里面。而

2.task_struct内容

标示符:描写叙述本进程的唯一标示符。用来差别其它进程。

状态:任务状态,退出代码,退出信号等。

优先级:相对于其它进程的优先级。

程序计数器:程序中即将被执行的下一条指令的地址。

内存指针:包含程序代码和进程相关数据的指针。还有和其它进程共享的内存块的指针。

上下文数据:进程执行时处理器的寄存器中的数据。

I/O状态信息:包含显示的I/O请求,分配给进程的I/O设备和正在被进程使用的文件列表。

记账信息:可能包含处理器时间总和,使用的时钟总数,时间限制,记账号等。

3.task_struct的分类

调度数据成员

(1) volatile long states; //进程状态

(2) unsigned long flags; //进程标记符
(3) long priority; //用来保存动态优先级
(4) unsigned long rt_priority; //用来保存实时优先级,取值范围为0~99
(5) long counter;
(6) unsigned long policy;

信号处理

(1) unsigned long signal;

(2) unsigned long blocked;
(3) struct signal_struct *sig;

进程队列指针

(1) struct task_struct *next_task。*prev_task;

(2) struct task_struct *next_run,*prev_run;
(3) struct task_struct *p_opptr,*p_pptr;和struct task_struct *p_cptr。*p_ysptr,*p_osptr;

进程标识

(1) unsigned short uid。gid;

(2) int groups[NGROUPS];
(3) unsigned short euid。egid;
(4) unsigned short fsuid,fsgid;
(5) unsigned short suid,sgid;
(6) int pid。pgrp,session;
(7) int leader;

时间数据成员

(1) unsigned long timeout;

(2) unsigned long it_real_value,it_real_iner;
(3) struct timer_list real_timer;
(4) unsigned long it_virt_value。it_virt_incr;
(5) unsigned long it_prof_value,it_prof_incr;
(6) long utime,stime,cutime,cstime。start_time;

信号量数据成员

(1) struct sem_undo *semundo;

(2) struct sem_queue *semsleeping;

进程上下文环境

(1) struct desc_struct *ldt;

(2) struct thread_struct tss;
(3) unsigned long saved_kernel_stack;
(4) unsigned long kernel_stack_page;

文件系统数据成员

(1) struct fs_struct *fs;

(2) struct files_struct *files;
(3) int link_count;

内存数据成员

(1) struct mm_struct *mm;

页面管理

(1) int swappable:1;

(2) unsigned long swap_address;
(3) unsigned long min_flt。maj_flt;
(4) unsigned long nswap;
(5) unsigned long cmin_flt,cmaj_flt,cnswap;
(6) unsigned long old_maj_flt,dec_flt;
(7) unsigned long swap_cnt;

支持对称多处理器方式(SMP)时的数据成员

(1) int processor;

(2) int last_processor;
(3) int lock_depth;

其它数据成员

(1) unsigned short used_math;

(2) char comm[16];
(3) struct rlimit rlim[RLIM_NLIMITS];
(4) int errno;
(5) long debugreg[8];
(6) struct exec_domain *exec_domain;
(7) unsigned long personality;
(8) struct linux_binfmt *binfmt;
(9) int exit_code,exit_signal;
(10) int dumpable:1;
(11) int did_exec:1;
(12) int tty_old_pgrp;
(13) struct tty_struct *tty;
(14) struct wait_queue *wait_chldexit;

进程队列的全局变量

(1) current;

(2) struct task_struct init_task;
(3) struct task_struct *task[NR_TASKS];
(4) unsigned long volatile jiffies;
(5) int need_resched;
(6) unsigned long intr_count;

task_struct的定义

truct task_struct {volatile long state;  //说明了该进程能否够执行,还是可中断等信息unsigned long flags;  //Flage 是进程号,在调用fork()时给出int sigpending;    //进程上是否有待处理的信号mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同                        //0-0xBFFFFFFF for user-thead                        //0-0xFFFFFFFF for kernel-thread//调度标志,表示该进程是否须要又一次调度,若非0,则当从内核态返回到用户态,会发生调度volatile long need_resched;int lock_depth;  //锁深度long nice;       //进程的基本时间片//进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHERunsigned long policy;struct mm_struct *mm; //进程内存管理信息int processor;//若进程不在不论什么CPU上执行, cpus_runnable 的值是0,否则是1 这个值在执行队列被锁时更新unsigned long cpus_runnable, cpus_allowed;struct list_head run_list; //指向执行队列的指针unsigned long sleep_time;  //进程的睡眠时间//用于将系统中全部的进程连成一个双向循环链表, 其根是init_taskstruct task_struct *next_task, *prev_task;struct mm_struct *active_mm;struct list_head local_pages;       //指向本地页面      unsigned int allocation_order, nr_local_pages;struct linux_binfmt *binfmt;  //进程所执行的可执行文件的格式int exit_code, exit_signal;int pdeath_signal;     //父进程终止时向子进程发送的信号unsigned long personality;//Linux能够执行由其它UNIX操作系统生成的符合iBCS2标准的程序int did_exec:1; pid_t pid;    //进程标识符,用来代表一个进程pid_t pgrp;   //进程组标识,表示进程所属的进程组pid_t tty_old_pgrp;  //进程控制终端所在的组标识pid_t session;  //进程的会话标识pid_t tgid;int leader;     //表示进程是否为会话主管struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;struct list_head thread_group;   //线程链表struct task_struct *pidhash_next; //用于将进程链入HASH表struct task_struct **pidhash_pprev;wait_queue_head_t wait_chldexit;  //供wait4()使用struct completion *vfork_done;  //供vfork() 使用unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值//it_real_value,it_real_incr用于REAL定时器。单位为jiffies, 系统依据it_real_value//设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同一时候依据//it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器。单位为jiffies。//当进程执行时,无论在何种状态下。每一个tick都使it_prof_value值减一。当减到0时,向进程发送//信号SIGPROF,并依据it_prof_incr重置时间.//it_virt_value,it_virt_value用于Virtual定时器。单位为jiffies。

当进程执行时。无论在何种

//状态下,每一个tick都使it_virt_value值减一当减到0时。向进程发送信号SIGVTALRM,依据 //it_virt_incr重置初值。 unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_value; struct timer_list real_timer; //指向实时定时器的指针 struct tms times; //记录进程消耗的时间 unsigned long start_time; //进程创建的时间 //记录进程在每一个CPU上所消耗的用户态时间和核心态时间 long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS]; //内存缺页和交换信息: //min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换 //设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。 //cmin_flt, cmaj_flt, cnswap记录本进程为祖先的全部子孙进程的累计次缺页数,主缺页数和换出页面数。 //在父进程回收终止的子进程时。父进程会将子进程的这些信息累计到自己结构的这些域中 unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap; int swappable:1; //表示进程的虚拟地址空间是否同意换出 //进程认证信息 //uid,gid为执行该进程的用户的用户标识符和组标识符,一般是进程创建者的uid,gid //euid,egid为有效uid,gid //fsuid。fsgid为文件系统uid,gid。这两个ID号通常与有效uid,gid相等。在检查对于文件 //系统的訪问权限时使用他们。 //suid,sgid为备份uid,gid uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; int ngroups; //记录进程在多少个用户组中 gid_t groups[NGROUPS]; //记录进程所在的组 //进程的权能。各自是有效位集合。继承位集合,同意位集合 kernel_cap_t cap_effective, cap_inheritable, cap_permitted; int keep_capabilities:1; struct user_struct *user; struct rlimit rlim[RLIM_NLIMITS]; //与进程相关的资源限制信息 unsigned short used_math; //是否使用FPU char comm[16]; //进程正在执行的可执行文件名称 //文件系统信息 int link_count, total_link_count; //NULL if no tty 进程所在的控制终端,假设不须要控制终端。则该指针为空 struct tty_struct *tty; unsigned int locks; //进程间通信信息 struct sem_undo *semundo; //进程在信号灯上的全部undo操作 struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时。他在该队列中记录等待的操作 //进程的CPU状态,切换时。要保存到停止进程的task_struct中 struct thread_struct thread; //文件系统信息 struct fs_struct *fs; //打开文件信息 struct files_struct *files; //信号处理函数 spinlock_t sigmask_lock; struct signal_struct *sig; //信号处理函数 sigset_t blocked; //进程当前要堵塞的信号,每一个信号相应一位 struct sigpending pending; //进程上是否有待处理的信号 unsigned long sas_ss_sp; size_t sas_ss_size; int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; u32 parent_exec_id; u32 self_exec_id; spinlock_t alloc_lock; void *journal_info; };

博客參照于:

进程百度百科:

转载于:https://www.cnblogs.com/zsychanpin/p/7375330.html

你可能感兴趣的文章
个人寒假作业项目《印象笔记》第一天
查看>>
java 常用命令
查看>>
ZOJ 1666 G-Square Coins
查看>>
CodeForces Round #545 Div.2
查看>>
卷积中的参数
查看>>
Linux中Zabbix4.0的搭建
查看>>
《LoadRunner没有告诉你的》之六——获取有效的性能需求
查看>>
51nod1076 (边双连通)
查看>>
Item 9: Avoid Conversion Operators in Your APIs(Effective C#)
查看>>
js去除空格
查看>>
学习Spring Boot:(二十八)Spring Security 权限认证
查看>>
IT学习神器——慕课网App获App Store、Android应用市场重磅推荐
查看>>
Linux网络状态工具ss命令使用详解
查看>>
深入浅出JavaScript(2)—ECMAScript
查看>>
编程珠玑第十一章----排序
查看>>
Face The Right Way POJ - 3276 (开关问题)
查看>>
STEP2——《数据分析:企业的贤内助》重点摘要笔记(六)——数据描述
查看>>
变量的命名规范
查看>>
手机端自动跳转
查看>>
react中进入某个详情页URL路劲参数Id获取问题
查看>>