上节我们总体概括了进程控制块中的内容,下来我们会慢慢的剖析里边的各个域。
这节我们介绍进程控制块中关于进程状态的定义。
linux内核中用一个长整型来标识一个进程的状态,<linux/sched.h>中是这样定义的(3.9版本内核):
struct task_struct { // 1> 状态信息——描述进程的动态变化,如就绪态、等待态、僵死态等。 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ // 2> 亲属关系——描述进程的亲属关系,如祖父进程、父进程、养父进程、子进程、兄进程、孙进程等。 // 3> 各种标识符——如进程标识符、用户标识符等等用来标识一个进程的数字。 // 4> 进程间通信信息——描述多个进程在同一任务上协作工作,如管道、消息队列、共享内存、套接字等。 // 5> 时间和定时器信息——描述进程在生存周期内使用CPU时间的统计、计费等信息。 // 6> 调度信息——描述进程的优先级、调度策略等信息,如静态优先级、动态优先级、时间片轮转、高优先级以及多级反馈队列等的调度策略。 // 7> 文件系统信息——对进程使用文件情况进行记录,如文件描述符、系统打开文件表、用户打开文件表等。 // 8> 虚拟内存信息——描述每个进程拥有的地址空间,也就是进程编译连接后形成的空间,这里肯定用到前边提到的分页机制。 // 9> 处理器环境信息——描述进程的执行环境(处理器的各种寄存器及堆栈等),这是体现进程动态变化的最主要的场景。 };
同时某一进程的状态的值也在该文件中进行编码:
/* * Task state bitmask. NOTE! These bits are also * encoded in fs/proc/array.c: get_task_state(). * * We have two separate sets of flags: task->state * is about runnability, while task->exit_state are * about the task exiting. Confusing, but this way * modifying one set can't modify the other one by * mistake. */ #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define __TASK_STOPPED 4 #define __TASK_TRACED 8 /* in tsk->exit_state */ #define EXIT_ZOMBIE 16 #define EXIT_DEAD 32 /* in tsk->state again */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 #define TASK_WAKING 256 #define TASK_PARKED 512 #define TASK_STATE_MAX 1024
就是说每一个进程在某一时刻一定取上述状态中的一个。下图是linux内核为状态转换指定的策略(该图来自陈莉君老师课件):
如图所示,通过fork()创建的进程处于运行状态,其PCB进入运行队列。如果调度程序schedule()运行,则从运行队列中选择一进程投入运行并占用CPU,在进程执行的过程中,因为输入、输出等原因调用interruptible_sleep_on()或者sleep_on(),则进程进入浅度睡眠或者深度睡眠。由于进程进入睡眠状态放弃CPU,因此调度程序重新选择一个程序占用CPU,依次类推。
我们前边说到进程一般有三个状态——运行态、就绪态、和等待态,任何操作系统的实现都肯定实现了这三个状态并且可能还加入自己的一些状态。linux设计者考虑到在任一时刻在CPU上运行的程序最多只有一个(多核结构依然如此,每个核在任意时刻仍只有一个进程在运行),而准备运行的进程可能有多个,于是linux把就绪态和运行态合并为一个状态,叫TASK_RUNNING(运行态),系统把这些运行态的进程放在一个队列中,调度程序从这个队列中选一个进程投入运行。主要的几个状态描述如下:
1> 运行态(TASK_RUNNING)——进程是可执行的;它或者正在执行,或者在运行队列中等待执行。这是进程在用户空间中唯一可能的状态;这种状态也可以应用到内核空间中正在执行的进程。
2> 可中断(TASK_INTERRUPTIBLE)——进程正在睡眠(也就是说被阻塞),等待某些条件的达成,一旦条件成立,内核就把进程状态改为运行态。处于此状态的进程也会因为接收到信号而被提前唤醒并随时准备投入运行。
3> 不可中断(TASK_UNINTERRUPTIBLE)——与前一个状态相似,不过在接收到信号时不会被唤醒。这个状态通常在进程必须在等待时不受干扰或等待事件很快就会发生时出现,较可中断状态来说用的较少。
4> 跟踪(__TASK_TRACED)——被其他进程跟踪的进程,想想我们的调试器吧,它能很好的说明这个状态是怎么回事。
5> 停止(__TASK_STOPPED)——进程停止执行;进程没有投入运行也不能投入运行,当进程接收到如下信号时进入暂停状态。
1)、SIGSTOP——停止进程执行
2)、SIGTSTP——从终端发来信号停止进程
3)、SIGTTIN——来自键盘的中断
4)、SIGTTOU——后台进程请求输出
6> 僵死状态(EXIT_ZOMBIE)——进程执行结束但尚未消亡的一种状态。此时进程已经结束且释放大部分资源,但尚未释放其PCB。
7> 死亡(EXIT_DEAD)——最终状态,进程将被彻底删除,但需要父进程来回收。
内核经常需要调整某个进程的状态。这时最好使用set_task_state(task, state)函数:
set_task_state(task, state);
该函数将制定进程设置为指定的状态。必要的时候,它会设置内存屏障里强制其他处理器作重新排序(一般只有在SMP(对称多处理器系统)中有此必要)。否则,它等价于task->state = state。
本例子使用到的相关技术有:springMVC+Mysql+jquery+jdbcTemplate+jstl+json+bootstrap等
(P:网上很少找到完整的springMVC+mysql实现分页查询,所以就自己详细的做了一个完整的例子)
(环境:jdk1.6 +tomcat7.0+mysql+eclipse....)
(包/文件:json.lib+jquery.js+spring3.0相关jar+jstl.jar+bootstrap相关css\js.....)
(传输数据格式为:json)
(
SpringMVC 的核心原理:
1,用户发送请求给服务器:url:user.do
2,服务器收到请求。发现DispatchServlet 可以处理。于是调用DispatchServlet。
3,DispatchServlet 内部,通过HandleMapping 检查这个url 有没有对应的Controller。如果有,则调用Controller.
4,Controller 开始执行。
5,Controller 执行完后,如果返回字符串,则ViewResolver 将字符串转化成相对应的视图对象;如果返回ModelAndView ,该对象本身就包含了视图对象信息。
6,DispatchServlet 将执行视图对象中的数据,输出给服务器。
7,服务器将数据输出给客户端。
)
一,先秀效果图(不是很美观,只为实现效果):
二:整体结构
1,src(由于工程名较敏感,所以******)
2, webContent
三:主要配置文件
1.web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>Spring3MVC</display-name> <filter> <filter-name>encodingFilter</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
2.spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- 对web包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能 --> <context:component-scan base-package="com.fingerknow.project" /> <!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="cacheSeconds" value="0" /> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean> </list> </property> </bean> <!--对模型视图名称的解析,即在模型视图名称添加前后缀 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp" /> <!--文件上传配置 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="utf-8"> <property name="maxUploadSize"> <value>104857600</value> </property> <property name="maxInMemorySize"> <value>4096</value> </property> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"> </property> <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"> </property> <property name="username" value="root"></property> <property name="password" value=""></property> </bean> </beans>
3.***.properties
projectURL=http://localhost:8080/fk/ downloadURL=http://localhost:8080/fk/upload/download/ uploadURL=http://localhost:8080/fk/temp/
四:java类(business为本例实例)
1,AbstractDao
public class AbstractDao extends JdbcDaoSupport { @Resource(name = "dataSource") public void setSuperDataSource(DataSource dataSource) { super.setDataSource(dataSource); } }
2,BusinessDao(只展示分页查询方法)
@Repository("businessDao") public class BusinessDao extends AbstractDao{ /** * 分页查询 * @param currentPage 当前页 * @param numPerPage
我们在介绍list.h文件时说道,linux是把所有的进程组织成双向链表来管理的,这个双向链表中的每个节点就是一个叫task_struct的数据结构,这个数据结构被叫做进程控制块(PCB),也叫进程描述符。该结构定义在<linux/sched.h>文件中。进程控制块中包含一个具体进程的所有信息,它大概包含以下信息:
struct task_struct { /* * 1> 状态信息——描述进程的动态变化,如就绪态、等待态、僵死态等。 * 2> 亲属关系——描述进程的亲属关系,如祖父进程、父进程、养父进程、子进程、兄进程、孙进程等。 * 3> 各种标识符——如进程标识符、用户标识符等等用来标识一个进程的数字。 * 4> 进程间通信信息——描述多个进程在同一任务上协作工作,如管道、消息队列、共享内存、套接字等。 * 5> 时间和定时器信息——描述进程在生存周期内使用CPU时间的统计、计费等信息。 * 6> 调度信息——描述进程的优先级、调度策略等信息,如静态优先级、动态优先级、时间片轮转、高优先级以及多级反馈队列等的调度策略。 * 7> 文件系统信息——对进程使用文件情况进行记录,如文件描述符、系统打开文件表、用户打开文件表等。 * 8> 虚拟内存信息——描述每个进程拥有的地址空间,也就是进程编译连接后形成的空间,这里肯定用到前边提到的分页机制。 * 9> 处理器环境信息——描述进程的执行环境(处理器的各种寄存器及堆栈等),这是体现进程动态变化的最主要的场景。 */ };
task_sruct相对较大,在32位机器上,它大约有1.7KB。但如果考虑到该结构体包含了内核管理一个进程所需的所有信息,那么它的大小也算相当小了。在进程的真个生命周期中,系统(也就是内核)总是通过PCB内进程进行控制的,也就是说,系统是根据进程的PCB来感知进程存在的。例如:当内核要调度某进程执行时,要从该进程的PCB查出其运行状态和优先级;在某进程被选中投入运行时,要从其PCB中取出其处理机环境信息,恢复其运行现场;进程在执行过程中,当需要和与之合作的进程实现同步、通信或访问文件时,也要访问PCB;当进程因某种原因暂停执行时,得将其断点的处理机环境保存在PCB中。所以说,PCB唯一标识一个进程的存在。
当系统创建一个新的进程时,就为它建立一个PCB,进程结束时又收回其PCB,进程就随之消亡。PCB是内核中被频繁读写的数据结构,故应常驻内存。
进程也叫任务。