热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

假脱机打印程序与虚拟设备

一、 设计目的 理解虚拟设备的工作原理,理解守护程序的概念。图7-1表示假脱机打印程序的工作原理。    在网络环境下,连在网络服务器上的打印机要为多

一、 设计目的

 理解虚拟设备的工作原理,理解守护程序的概念。

7-1表示假脱机打印程序的工作原理。

 

 

 

 

在网络环境下,连在网络服务器上的打印机要为多个终端服务,每个终端上的用户都可以通过客户端程序向服务器发送打印请求,服务器端的打印请求接收程序接收来自客户端的打印请求,并将该请求存放到磁盘上的打印请求队列中,由服务器端的假脱机打印程序在CPU空闲时从打印请求队列中取出请求信息,并将文件输出到打印机中。这种工作方式不是将文件直接输出到打印机,而是先将待打印的文件缓存到磁盘上,然后立即返回用户程序,从而缩短了用户响应时间,为用户提供了虚拟的快速打印机。这里的磁盘缓存空间就是虚拟设备。服务器端的打印请求接收程序和打印程序都是守护程序,即从开机后就一直运行的程序。

 

二、 设计要求

  利用多线程技术编写假脱机打印程序,并设计测试数据以验证程序的正确性。

1、界面要求:

程序采用简单的控制台界面,运行后在屏幕上显示功能菜单,列出该程序具有的功能,供用户选择。

2、功能要求:

1)发送打印请求;

2)查看假脱机打印队列;

3)打印文件;

4)退出。

用户选择功能后应该转到相应的处理程序,并在需要时显示程序的执行结果。

若用户选择(1)则提示用户输入待打印的文件名称,程序接收输入后将打印请求传送到打印队列中,并回到主菜单;

若用户选择(2)则在屏幕上列出打印队列情况,提示按任意键回到主界面;

若用户选择(3)则打印队首的文件,显示所打印的文件名称,按任意键回到主界面;

若用户选择(4)则退出程序的执行。

 

三、 算法设计与分析



  1. 程序结构设计

 

















字段名称


作用


file_name


文件名称


file_size


文件大小(以KB为单位)


 

需要两个数据结构,一个FILE_INFO用来描述打印请求,包括文件名称和文件大小,如图7-2所示。另一个数据结构SPOOL用来描述打印请求队列,如图7-3所示。图7-4描述了程序运行时刻结构体SPOOL的内容。

 

























字段名称


作用


spool_count


记录打印队列中的文件个数


spool_in


记录下一个打印请求存放的位置


spool_out


记录下一个被打印文件的位置


spool_queue


打印请求队列(用数组实现)


 

 













































spool_count


3


 


spool_in


3


 


spool_out


0


 


spool_queue[0]


sever


4KB


spool_queue[1]


client


3KB


spool_queue[2]


myfile


7KB


spool_queue[3]


 


 


spool_queue[4]


 


 

7.4  程序结构

1、线程划分

为了模拟假脱机打印程序,需要三个线程,主线程用于显示主菜单,接收用户的功能选择,显示打印队列情况;打印请求接收/发送线程接收用户的打印请求,并将打印请求存放到打印请求队列;打印线程用来从打印队列中取文件并将其输出到屏幕。

2、线程互斥要求

三个线程都需要通过控制台终端与用户交互,因此对终端的使用要互斥,以免屏幕混乱(用互斥体h_screen_mutex实现);三个线程都要访问打印请求队列,因此对它要进行互斥操作(用互斥体h_spool_mutex实现);

3、同步要求

主线程与打印请求接收/发送线程要同步,当用户选择功能(1)时,主线程要通知打印请求接收/发送线程开始接收用户请求(用初始值为0的信号量h_print实现),然后主线程要等待打印请求接收/发送线程发来接收完毕的信号(用初始值为0的信号量h_sendthread_to_mainthread实现);

主线程要与打印线程同步,当用户选择功能(3)时,主线程要通知打印线程开始打印文件(用初始值为0的信号量h_semaphore_spool实现),然后主线程要等待打印线程发来打印完毕的信号(用初始值为0的信号量h_spoolthread_to_mainthread实现);

(注意:在实际的系统中,打印线程不会等待用户从控制台发命令才开始打印,而是只要有打印请求且CPU空闲就循环不停地打印文件,直至打印队列为空,这时打印线程睡眠。本设计这样做是为了避免打印队列迅速变空。)

请求接收/发送线程和打印线程要同步,当打印队列为空时打印线程阻塞,直到请求接收/发送线程将新的请求放入队列;当打印队列满时,请求接收/发送线程阻塞,直到打印线程打印完一个文件空出新位置才能将其唤醒。这两个线程的同步遵循生产者/消费者模型,同步信号量为h_spool_empty和h_spool_full,前者跟踪空位置,后者跟踪打印请求。

4、函数设计

为了简化程序设计,我们只考虑单机环境,而且将打印请求队列存放在内存中,而不是存放在磁盘中,另外用屏幕输出模拟实际的打印机输出。该程序包括三个线程:主线程模拟客户端程序,sendthread线程模拟打印请求接收程序,spool_thread线程模拟打印程序。

该程序使用以下全局变量:

spool_buffer是打印请求队列,互斥体h_spool_mutex用来实现对spool_buffer的互斥访问,互斥体h_screen_mutex用来实现对终端的互斥访问,h_send和h_spool_thread分别是打印请求接收/发送线程和打印线程的句柄,h_semaphore_spool和h_spoolthread_to_mainthread是主线程和打印线程之间的同步信号量,h_print和h_sendthread_to_mainthread是主线程和打印请求接收/发送线程之间的同步信号量,h_spool_full和h_spool_empty是打印线程和打印请求接收/发送线程之间的同步信号量。

该程序共有五个函数,它们的名称及作用如图7-5所示。
































函数名称


作用


sendthread


接收用户的打印请求并将其发送到打印请求队列中


spool_thread


从打印请求队列中取待打印文件并将其输出到屏幕上


print_space


显示若干个空格


list_spool_queue


列出打印队列


main


创建线程,初始化信号量,显示主菜单,根据用户选择执行相应功能


7-5  假脱机打印程序包括的函数及其作用


下面给出每个函数的算法描述。

1)list_spool_queue函数

{

申请打印队列互斥使用权P(h_spool_mutex);

申请屏幕互斥使用权P(h_screen_mutex);

清屏;

显示打印队列中当前请求个数;

显示表头;

对请求队列中的每一个请求

{在屏幕上输出待打印文件名称以及大小};

释放打印队列互斥使用权V(h_spool_mutex);

释放屏幕互斥使用权V(h_screen_mutex);

}

2)sendthread函数

{

  while(1){

    等待主线程发送唤醒信号直到用户选择“发送打印请求功能”P(h_print);

    申请屏幕互斥使用权P(h_screen_mutex);

清屏;

    提示并接收用户输入文件名称;

    释放屏幕互斥使用权V(h_screen_mutex);

    产生随机数作为文件大小;

    申请打印队列中的空闲位置P(h_spool_empty);

申请打印队列互斥使用权P(h_spool_mutex);

将请求放入打印队列当前位置;

调整位置指针至下一位置;

释放打印队列互斥使用权V(h_spool_mutex);

通知打印线程多了一个打印请求V(h_spool_full);

唤醒主线程继续画主菜单V(h_sendthread_to_mainthread);

}

}

3)spool_thread函数

{

  while(1){

    等待主线程发送唤醒信号直到用户选择“打印文件功能”P(h_semaphore_spool);

    等待直到sendthread线程发来打印请求P(h_spool_full);

    申请打印队列互斥使用权P(h_spool_mutex);

    申请屏幕互斥使用权P(h_screen_mutex);

    将打印队列中的文件数减1;

    在屏幕上输出正在打印的文件名称;

    回收该位置(置文件名称为空白,文件大小为0);

    下调打印指针;

释放屏幕互斥使用权V(h_screen_mutex);

释放打印队列互斥使用权V(h_spool_mutex);

通知sendthread线程多了一个空位置V(h_spool_empty);

唤醒主线程继续画主菜单V(h_spoolthread_to_mainthread);

  }

}

4)main函数

{

创建两个互斥体;

创建六个同步信号量;

创建两个线程;

while(1){

申请屏幕互斥使用权P(h_screen_mutex);

显示主菜单;

接收用户功能选择;

清屏;

释放屏幕互斥使用权V(h_screen_mutex);

根据功能选择进行分支{

选择了功能1:

唤醒发送线程允许其发送请求到打印队列V(h_print);

等待发送线程发送完打印请求P(h_sendthread_to_mainthread);

跳出循环;

选择了功能2:

显示打印队列;

跳出循环;

选择了功能3:

唤醒打印线程允许其打印文件V(h_semaphore_spool);

等待打印线程打印完文件P(h_spoolthread_to_mainthread);

跳出循环;

选择了功能4:

返回;

}

}

}

 

 

然后就直接放代码好了

1 #include
2 #include
3 #include
4 #include
5 #include <string.h>
6 #define SIZE rand()%1000//取0-999中的一个随机数
7 typedef struct{
8 char file_name[100];//文件名称
9 int file_size; //文件大小
10 int v; //排队序号
11 }FILE_INFO; //文件结构体
12
13 typedef struct{
14 int spool_count; //队列中文件的个数
15 int spool_in; //下一个打印请求存放的位置
16 int spool_out; //下一个被打印文件的位置
17 FILE_INFO spool_queue[5];//打印请求队列
18 }SPOOL; //打印请求队列
19
20 SPOOL spool_buffer; //打印请求队列
21 HANDLE h_spool_mutex; //线程互斥 (三个线程都要访问打印请求队列,实现对spool_buffer的互斥访问)
22 HANDLE h_screen_mutex; //屏幕互斥 (对终端的互斥访问;避免屏幕混乱)
23
24 HANDLE h_send; //申明打印请求接收/发送线程
25 HANDLE h_spool_thread; //申明打印线程
26
27 HANDLE h_semaphore_spool; //主线程通知打印线程开始打印文件(主线程同步信号量)
28 HANDLE h_spoolthread_to_mainthread;//等待打印线程发来打印结束的信号量(打印线程同步信号量)
29
30 HANDLE h_sendthread_to_mainthread; //等待打印请求接收和发送线程结束的信号量
31 HANDLE h_print; //主线程要通知打印请求接收和发送线程开始接收用户请求的信号量
32
33 HANDLE h_spool_full; //打印请求个数
34 HANDLE h_spool_empty; //空位置
35
36 //接收用户的打印请求并将其发送到打印请求队列中
37 DWORD WINAPI sendthread(LPVOID lpParameter)
38 {
39 FILE_INFO file_info;
40 while(1)
41 {
42 WaitForSingleObject(h_print,INFINITE); //等待主线程发送唤醒信号直到用户选择“发送打印请求功能”
43 WaitForSingleObject(h_screen_mutex,INFINITE); // 申请屏幕互斥使用权
44 printf("输入文件名:"); //提示并接收用户输入文件名称
45 scanf("%s",file_info.file_name);
46 ReleaseMutex(h_screen_mutex); // 释放屏幕互斥使用权
47 srand( (unsigned)time( NULL ) ); //产生随机数作为文件大小
48 file_info.file_size=SIZE;
49 printf("文件大小为:%d\n",file_info.file_size);
50 WaitForSingleObject(h_spool_empty,INFINITE); //申请打印队列中的空闲位置
51 WaitForSingleObject(h_spool_mutex,INFINITE); //申请打印队列互斥使用权
52 spool_buffer.spool_count++;
53 file_info.v=spool_buffer.spool_count;
54 spool_buffer.spool_queue[spool_buffer.spool_in]=file_info; //将请求放入打印队列当前位置
55 spool_buffer.spool_in=(spool_buffer.spool_in+1)%5; //调整位置指针至下一位置
56 ReleaseMutex(h_spool_mutex); //释放打印队列互斥使用权
57 ReleaseSemaphore(h_spool_full,1,NULL); //通知打印线程多了一个打印请求
58 ReleaseSemaphore(h_sendthread_to_mainthread,1,NULL); //唤醒主线程继续画主菜单
59 }
60 }
61
62
63 //输出空格
64 void print_space(int num){
65 int i;
66 for(i=0;i){
67 printf(" ");
68 }
69 }
70
71 //列出打印队列
72 void list_spool_queue()
73 {
74 char buffer[10];//存一下转换后的数据,字符型形式比较灵活?
75 WaitForSingleObject(h_spool_mutex,INFINITE); //结束阻塞状态,申请打印队列互斥使用权
76 WaitForSingleObject(h_screen_mutex,INFINITE); //申请屏幕互斥使用权
77 // system("cls"); 终端弹出所以清屏用不了
78 //显示表头
79 printf(" 假脱机队列中的文件数:%d\n\n",spool_buffer.spool_count);
80 printf(" 打印序列 \n");
81 printf("|--------|-------------------------------------|--------------|--------------|\n");
82 printf("| 序号 | 文件名 | 文件大小(KB) | 排队序号 |\n");
83 printf("|--------|-------------------------------------|--------------|--------------|\n");
84 for(int i=0;i<5;i++)
85 {
86 printf("| %d",i);
87 itoa(i, buffer, 10);//整型数字变量转换成字符数组变量
88 print_space(7-strlen(buffer));
89 printf("| %s",spool_buffer.spool_queue[i].file_name);
90 print_space(36-strlen(spool_buffer.spool_queue[i].file_name));
91 printf("| %d",spool_buffer.spool_queue[i].file_size);
92 itoa(spool_buffer.spool_queue[i].file_size,buffer,10);
93 print_space(12-strlen(buffer));
94 printf("| %d",spool_buffer.spool_queue[i].v);
95 itoa(spool_buffer.spool_queue[i].v,buffer,10);
96 print_space(12-strlen(buffer));
97 printf(" |\n");
98 }
99 printf("|--------|-------------------------------------|--------------|--------------|\n");
100
101 ReleaseMutex(h_spool_mutex); //释放打印队列互斥使用权V(h_spool_mutex);
102 ReleaseMutex(h_screen_mutex); //释放屏幕互斥使用权V(h_screen_mutex);
103 }
104
105
106 //从打印请求队列中取待打印文件并将其输出到屏幕上
107 DWORD WINAPI spool_thread(LPVOID lpParameter)
108 {
109 while(1)
110 {
111 WaitForSingleObject(h_semaphore_spool,INFINITE); //等待主线程发送唤醒信号直到用户选择“打印文件功能”
112 WaitForSingleObject(h_spool_full,INFINITE); //等待直到sendthread线程发来打印请求
113 WaitForSingleObject(h_spool_mutex,INFINITE); //申请打印队列互斥使用权
114 WaitForSingleObject(h_screen_mutex,INFINITE); //申请屏幕互斥使用权
115
116 spool_buffer.spool_count--; //将打印队列中的文件数减1
117 printf("打印一个文件:\n文件名:%s 文件大小:%d\n",spool_buffer.spool_queue[spool_buffer.spool_out].file_name,spool_buffer.spool_queue[spool_buffer.spool_out].file_size);
118 //在屏幕上输出正在打印的文件名称
119 strcpy(spool_buffer.spool_queue[spool_buffer.spool_out].file_name,"");
120 spool_buffer.spool_queue[spool_buffer.spool_out].file_size=0; // 回收该位置(置文件名称为空白,文件大小为0)
121 spool_buffer.spool_queue[spool_buffer.spool_out].v=0; //下调打印指针
122 for(int i=0;i<5;i++)
123 if(spool_buffer.spool_queue[i].v>0)
124 spool_buffer.spool_queue[i].v--;
125 spool_buffer.spool_out=(spool_buffer.spool_out+1)%5; //寻找下一个要打印文件的位置
126 ReleaseMutex(h_screen_mutex); //释放屏幕互斥使用权
127 ReleaseMutex(h_spool_mutex); //释放打印队列互斥使用权
128 ReleaseSemaphore(h_spool_empty,1,NULL); //通知sendthread线程多了一个空位置
129 ReleaseSemaphore(h_spoolthread_to_mainthread,1,NULL); //唤醒主线程继续画主菜单
130 }
131 return 0;
132 }
133
134 //主函数
135 int main(){
136 char select;
137 h_send=CreateThread(NULL,0,sendthread,NULL,0,NULL); //创建线程
138
139 h_spool_thread=CreateThread(NULL,0,spool_thread,NULL,0,NULL);
140 h_spool_mutex=CreateMutex(NULL,FALSE,NULL); //创建两个互斥体
141
142 h_screen_mutex=CreateMutex(NULL,FALSE,NULL);
143 h_spool_full=CreateSemaphore(NULL,0,5,NULL); //打印线程和打印请求接收/发送线程之间的同步信号量
144
145 h_spool_empty=CreateSemaphore(NULL,5,5,NULL);
146 h_print=CreateSemaphore(NULL,0,1,NULL); //主线程和打印请求接收/发送线程之间的同步信号量
147
148 h_sendthread_to_mainthread=CreateSemaphore(NULL,0,1,NULL);
149 h_semaphore_spool=CreateSemaphore(NULL,0,1,NULL); //主线程和打印线程之间的同步信号量
150
151 h_spoolthread_to_mainthread=CreateSemaphore(NULL,0,1,NULL);
152 WaitForSingleObject(h_screen_mutex,INFINITE); //申请屏幕互斥使用权
153
154 while(1){
155 printf("|-----------------------------------|\n");
156 printf("| (1):发送打印请求 |\n");
157 printf("| (2):查看假脱机打印队列 |\n");
158 printf("| (3):打印文件 |\n");
159 printf("| (4):退出 |\n");
160 printf("|-----------------------------------|\n");
161 printf("| 输入选择功能:");
162 do{
163 scanf("%c",&select);
164 }while(select!='1'&&select!='2'&&select!='3'&&select!='4');
165 //system("cls");
166 ReleaseMutex(h_screen_mutex);
167 switch(select){
168 case '1':
169 if(spool_buffer.spool_count<5){
170 ReleaseSemaphore(h_print,1,NULL); //唤醒发送线程允许其发送请求到打印队列V(h_print);
171 WaitForSingleObject(h_sendthread_to_mainthread,INFINITE); //等待发送线程发送完打印请求P(h_sendthread_to_mainthread);
172 }
173 else
174 printf("当前打印队列已满!\n");
175 break;
176
177 case '2':
178 if(spool_buffer.spool_count==0)
179 printf("当前打印队列为空!\n");
180 else
181 list_spool_queue(); //全部显示
182 break;
183
184 case '3':
185 if(spool_buffer.spool_count>0){
186 ReleaseSemaphore(h_semaphore_spool,1,NULL); //唤醒打印线程允许其打印文件V(h_semaphore_spool);
187 WaitForSingleObject(h_spoolthread_to_mainthread,INFINITE); //等待打印线程打印完文件P(h_spoolthread_to_mainthread);
188 }
189 else
190 printf("当前打印队列为空!\n");
191 break;
192
193 case '4':
194 return 0;
195 }
196 WaitForSingleObject(h_screen_mutex,INFINITE);
197 printf("\n按任意键回到菜单\n");
198 getch();
199 //system("cls");
200 ReleaseMutex(h_screen_mutex);
201 }
202 return 0;
203 }

由于实在是太菜了,所以就写了一大堆注释。

也不一定完全对,做个参考吧



推荐阅读
  • 本文详细解析 Skynet 的启动流程,包括配置文件的读取、环境变量的设置、主要线程的启动(如 timer、socket、monitor 和 worker 线程),以及消息队列的实现机制。 ... [详细]
  • 本文详细介绍了如何使用 Python 编程语言中的 Scapy 库执行 DNS 欺骗攻击,包括必要的软件安装、攻击流程及代码示例。 ... [详细]
  • System Center Operations Manager 2007(简称SCOM 2007)作为MOM 2005的升级版,不仅整合了监控与管理功能,还显著简化了操作流程,提供了更加全面和精准的服务管理。 ... [详细]
  • 利用Cookie实现用户登录状态的持久化
    本文探讨了如何使用Cookie技术在Web应用中实现用户登录状态的持久化,包括Cookie的基本概念、优势及主要操作方法,并通过一个简单的Java Web项目示例展示了具体实现过程。 ... [详细]
  • 本文探讨了在SharePoint环境中使用BDC(Business Data Catalog)时遇到的问题及其解决策略,包括XML文件导入SSP后的不可见性问题以及与远程SQL Server 2005连接的难题。 ... [详细]
  • 本文详细介绍了跨站脚本攻击(XSS)的基本概念、工作原理,并通过实际案例演示如何构建XSS漏洞的测试环境,以及探讨了XSS攻击的不同形式和防御策略。 ... [详细]
  • 探索OpenWrt中的LuCI框架
    本文深入探讨了OpenWrt系统中轻量级HTTP服务器uhttpd的工作原理及其配置,重点介绍了LuCI界面的实现机制。 ... [详细]
  • Hadoop集群搭建:实现SSH无密码登录
    本文介绍了如何在CentOS 7 64位操作系统环境下配置Hadoop集群中的SSH无密码登录,包括环境准备、用户创建、密钥生成及配置等步骤。 ... [详细]
  • Git版本控制基础解析
    本文探讨了Git作为版本控制工具的基本概念及其重要性,不仅限于代码管理,还包括文件的历史记录与版本切换功能。通过对比Git与SVN,进一步阐述了分布式版本控制系统的独特优势。 ... [详细]
  • 本文探讨了在不同场景下如何高效且安全地存储Token,包括使用定时器刷新、数据库存储等方法,并针对个人开发者与第三方服务平台的不同需求提供了具体建议。 ... [详细]
  • LoadRunner中的IP欺骗配置与实践
    为了确保服务器能够有效地区分不同的用户请求,避免多人使用同一IP地址造成的访问限制,可以通过配置IP欺骗来解决这一问题。本文将详细介绍IP欺骗的工作原理及其在LoadRunner中的具体配置步骤。 ... [详细]
  • 本文详细介绍了PHP中的几种超全局变量,包括$GLOBAL、$_SERVER、$_POST、$_GET等,并探讨了AJAX的工作原理及其优缺点。通过具体示例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 本文探讨了一个Web工程项目的需求,即允许用户随时添加定时任务,并通过Quartz框架实现这些任务的自动化调度。文章将介绍如何设计任务表以存储任务信息和执行周期,以及如何通过一个定期扫描机制自动识别并加载新任务到调度系统中。 ... [详细]
  • 本文详细介绍了在 CentOS 7 系统上安装中文宋体字体的方法,包括操作系统的环境配置、字体管理工具的安装、字体文件的传输与缓存重建等步骤。 ... [详细]
  • 本文详细介绍了在PHP中如何获取和处理HTTP头部信息,包括通过cURL获取请求头信息、使用header函数发送响应头以及获取客户端HTTP头部的方法。同时,还探讨了PHP中$_SERVER变量的使用,以获取客户端和服务器的相关信息。 ... [详细]
author-avatar
14795823364-
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有