1、设自行车生产线上有一只箱子,其中有N个位置(N≥3),每个位置可存放一个车架或一个车轮,又设有三名工人,其活动分别为:
用信号量PV操作编程实现三个工人的合作,不能出现死锁。要求N的大小可自定义,显示自行车生产组装的过程。(25分)
解:
分析:根据题目意义分析,该题所述作业工人1、工人2与工人3构成生产者与消费者关系,存在着生产者-消费者问题的同步关系,涉及的知识点包括:信号量、PV原语、进程同步、死锁问题等。
解决思路:先不考虑死锁问题,工人1、2分别与3是生产者和消费者关系。箱子内的空位是工人1、2的资源,车架和车轮是工人3的资源。定义三个信号灯如下:
semaphore empty=N;//箱子内的空位
semaphore weel=0;//车轮
semaphore frame=0;//车架
再来看死锁问题:如果说工人1或者工人2某一方生产速度很快,N个空位全部为车轮,或者≥N-1个空位是车架的话(此时最多只能放入一个车轮),工人3永远得不到组装一个完整的车的资源,就会发生死锁。所以车架最多不能达到N-2个,也就是车架的信号量初值要设置为N-2,车轮要设置为N-1。
伪代码如下:
semaphore empty = N;//空位置数量
semaphore maxframe = N-2;//箱中最大车架数量
semaphore maxwheel = N-1;//箱中最大车轮数量
semaphore frame = 0;//车架数量
semaphore wheel = 0;//车轮数量
do{ //工人1活动
P(maxframe);
P(empty);
加工一个车架;
车架放入箱中;
V(frame);
}while(1)
do{ //工人2活动
P(maxwheel);
P(empty);
加工一个车轮;
车轮放入箱中;
V(wheel);
}while(1)
do{ //工人3活动
P(frame);
P(wheel);
P(wheel);
箱中取一个车架;
箱中取一个车架;
组装一台车;
V(maxframe);
V(maxwheel);
V(maxwheel);
V(empty);
V(empty);
V(empty);
}while(1)
源代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>
sem_t empty;//箱子内的空位
sem_t wheel;//车轮
sem_t frame;//车架
sem_t s1;//车架最大值
sem_t s2;//车轮最大值
pthread_t worker1_thread, worker2_thread, worker3_thread;//声明三个工人的线程
void * worker1Thread(void *i)
{
while(1)
{
sleep(2);//工人1生产一个车架
printf("worker1 products a frame!\n");
sem_wait(&empty);
sem_wait(&s1);
sleep(1);//工人1将车架放入箱子
printf("worker1 places a frame in the box!\n");
sem_post(&frame);
}
}
void * worker2Thread(void *i)
{
while(1)
{
sleep(1);//工人2生产一个车轮
printf("worker2 products a wheel!\n");
sem_wait(&empty);
sem_wait(&s2);
sleep(1);//工人2将车轮放入箱子
printf("worker2 places a wheel in the box!\n");
sem_post(&wheel);
}
}
void * worker3Thread(void *i)
{
while(1)
{
sem_wait(&frame);
sleep(1);//工人3从箱子中拿一个车架
printf("worker3 takes out a frame from the box!\n");
sem_post(&empty);
sem_post(&s1);
sem_wait(&wheel);
sem_wait(&wheel);
sleep(2);//工人3从箱子中拿两个车轮
printf("worker3 takes out two wheels from the box!\n");
sem_post(&empty);
sem_post(&empty);
sem_post(&s2);
sem_post(&s2);
sleep(3);//工人3生产一辆自行车
printf("worker3 products a bicycle!\n");
}
}
int main(){
int N;
printf("Please enter the number of the box positions:");
scanf("%d",&N);
printf("\n");
//初始化信号量
sem_init(&empty, 1, N);
sem_init(&wheel, 1, 0);
sem_init(&frame, 1, 0);
sem_init(&s1, 1, N-2);
sem_init(&s2, 1, N-1);
int ret;
char taskNum;
printf("input 'q' to exit!\n");
ret=pthread_create(&worker1_thread,NULL,worker1Thread,NULL);
if(ret!=0)
{
printf("Create thread1 error\n");
exit(1);
}
ret=pthread_create(&worker2_thread,NULL,worker2Thread,NULL);
if(ret!=0)
{
printf("Create thread2 error\n");
exit(1);
}
ret=pthread_create(&worker3_thread,NULL,worker3Thread,NULL);
if(ret!=0)
{
printf("Create thread3 error\n");
exit(1);
}
while (scanf("%c", &taskNum) != EOF) {
if (taskNum == 'q') {
break;
}
}
sem_destroy(&empty);
sem_destroy(&frame);
sem_destroy(&wheel);
sem_destroy(&s1);
sem_destroy(&s2);
return 0;
}
编译链接:
gcc ceshi1.c -o ceshi1
运行:
./ceshi1
运行结果示意图:
2、编写一个用于检查关于命令“ls px ct"的实验报告的Shell脚本,该脚本检查命令的执行结果中是否存在包含“px”和“ct”的文件或目录,显示包含“px”的文件或目录数,包含“ct”的文件或目录数。如果两者的文件或目录数均大于0,则对该实验项目给予100分。如果其中之一的文件或目录数为0,则对该实验项目给予50分,如果两者的文件或目录数均为0,则对该实验项目给予0分。(25分)
解:
源代码:
#!/bin/bash
#统计文件个数
file_count1=0
file_count2=0
#查看所有目录
file_list=`ls *px* *ct*`
#输出所有符合要求的文件名以及目录名
for file_name in $file_list
do
echo $file_name
done
#计数名称包括*px*的文件或目录数
file_list=`ls *px*`
for file_name in $file_list
do
let file_count1++
done
#计数名称包括*ct*的文件或目录数
file_list=`ls *ct*`
for file_name in $file_list
do
let file_count2++
done
#分别输出两种的文件或目录数
echo -e "\n-------------------------------------------------"
echo -e "\n\n*px*Total: $file_count1"
echo -e "*ct*Total: $file_count2"
echo -e "\nFind Finished!\n"
运行:
. ./ceshi2.sh
运行结果:
3、编写套接字通信程序,实现简单的文字发送、接收形式的会议功能。主持人运行服务器程序,参会者运行客户端程序。主持人发送的文字信息每个客户端用户都可以看到。任一客户端用户发送的文字信息包括主持人在内的其他参会者也都可以看到。服务器或者客户端显示当前开会人数,参会者标识,以及当前发送信息的程序或者用户的标识。(50分)
解:
源程序:
```c
服务器程序ceshi3Server.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define LinkNum 5 //连接数
int client_sockfd[LinkNum]; /*分别记录服务器端的套接字与连接的多个客户端的套接字*/
int server_sockfd = -1; //命名套接字
int curLink=0; //当前连接数
sem_t mutex; //表示连接数的资源信号量
char stopmsg[1024]; //服务器端发送消息缓冲区
void quit()//客户服务通信结束处理函数
{
int i;
char mm[] = "会议结束!请输入'!q'退出会议!";
char msg[1024];
char s4[] = "主持人发言:";
int f1=0,f2=0,f3=0;
while(1)
{
memset(stopmsg, 0, 1024);
for(f1=0,f2=0;f1<sizeof(s4)-1&f2<sizeof(msg);f1++,f2++)
msg[f2]=s4[f1];
memset(stopmsg, 0, 1024);
scanf("%s",stopmsg);
for(f3=0;f3<sizeof(stopmsg)-1&f2<sizeof(msg);f3++,f2++)
msg[f2]=stopmsg[f3];
if(strcmp(stopmsg,"quit")==0)
{//如果服务器端发送消息为"quit",则提示服务器将关闭
printf("结束会议!\n");
for(i=0;i<LinkNum;i++)
if(client_sockfd[i]!=-1){
write(client_sockfd[i], msg, sizeof(msg));
write(client_sockfd[i], mm, sizeof(mm));}
/*依次向继续保持连接的客户端发出“会议结束”的通知消息*/
close(server_sockfd); //关闭服务器监听套接字
sem_destroy(&mutex); //销毁连接数资源信号量mutex
exit(0);
}
else{
for(i=0;i<LinkNum;i++)
if(client_sockfd[i]!=-1)
write(client_sockfd[i], msg, sizeof(msg));
}
}
}
void rcv_snd(int n)//服务器与客户端的收发通信函数,n为连接数组序号
{
int i=0;
int retval;
char recv_buf[1024]; //接收消息缓冲区
char send_buf[1024]; //发送消息缓冲区
int client_len = 0;
int rcv_num; //从客户端接收到的消息长度
pthread_t tid; //线程id
tid = pthread_self(); //获取当前线程id
char c = n +'0'+1;
char s[] = "您的用户标识是:";
s[sizeof (s) - 1] = c;
write(client_sockfd[n], s, sizeof(s));
do//服务器与客户端循环发送接收消息
{
memset(recv_buf, 0, 1024);//接收消息缓冲区清零
rcv_num = read(client_sockfd[n], recv_buf,sizeof(recv_buf));
printf("%d发言:(%d) :%s\n",n+1,rcv_num, recv_buf );
if(rcv_num==0) break;
if(strncmp(recv_buf,"!q",2)==0) break; //若接收到"!q",则结束循环,通信结束
else{
char s3[] = "n发言:";
s3[0]=c;
memset(send_buf, 0, 1024);
int f1=0,f2=0,f3=0;
for(;f1<sizeof(s3)-1&f2<sizeof(send_buf);f1++,f2++)
send_buf[f2]=s3[f1];
for(;f3<sizeof(recv_buf)&f2<sizeof(send_buf);f3++,f2++)
send_buf[f2]=recv_buf[f3];
for(int i=0;i<LinkNum;i++)
if(client_sockfd[i]!=-1)
write(client_sockfd[i], send_buf, sizeof(send_buf));
}
}while(strncmp(recv_buf,"!q",2)!=0);
printf("%d号发言人退出会议!\n",n+1);
close(client_sockfd[n]); //关闭连接套接字
client_sockfd[n]=-1; //被关闭连接套接字数组项置为空闲
curLink--; //当前连接数减1
printf("当前开会人数为:%d(<=%d)\n",curLink,LinkNum); //输出当前连接数和最大连接数
char s1 = curLink + '0';
char s2[] = "有1人退出会议,当前开会人数为:";
s2 [sizeof (s2) - 1] = s1;
for(int i=0;i<LinkNum;i++)
if(client_sockfd[i]!=-1)
write(client_sockfd[i], s2, sizeof(s2));
sem_post(&mutex); //释放可用连接数资源信号量mutex
pthread_exit(&retval); //当前服务器线程结束
}
int main(void)
{
char recv_buf[1024]; //接收消息缓冲区
char send_buf[1024]; //发送消息缓冲区
int client_len = 0;
struct sockaddr_in server_addr; //服务器端协议地址
struct sockaddr_in client_addr; //客户端协议地址
int i=0; //连接套接字数组循环变量
pthread_t thread;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET; //指定网络套接字
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //接受所有IP地址的连接
server_addr.sin_port = htons(9736); //绑定到9736端口
bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));//协议套接字命名为server_sockfd
printf("会议开始...\n");
listen(server_sockfd, LinkNum); /*创建连接数最大为LinkNum的套接字队列,监听命名套接字,listen不会阻塞,它向内核报告套接字和最大连接数*/
signal(SIGCHLD, SIG_IGN); //忽略子进程停止或退出信号
printf("输入quit,会议结束.\n");//输入quit,服务结束
printf("您的用户标识是主持人!\n");
pthread_create(&thread,NULL,(void*)(&quit),NULL);//创建线程,执行函数quit
for(i=0;i<LinkNum;i++) client_sockfd[i]=-1;//初始化连接队列
sem_init(&mutex,0,LinkNum); //信号量mutex初始化为连接数
while(1)
{
for(i=0;i<LinkNum;i++) //搜寻空闲连接
if(client_sockfd[i]==-1) break;
if(i==LinkNum)
{//如果达到最大连接数,则客户等待
printf("会议人数已达上限%d,请等待其它用户退出...\n",LinkNum);
sem_wait(&mutex); //阻塞等待空闲连接
continue; //被唤醒后继续监测是否有空闲连接
}
client_len = sizeof(client_addr);
client_sockfd[i] = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len);
printf("%d号用户进入会议\n",i+1);
curLink++; //当前连接数增1
sem_wait(&mutex); //可用连接数信号量mutex减1
printf("当前会议人数为:%d(<=%d)\n",curLink,LinkNum);
char c1 = i +'0' +1;
char c2 = curLink +'0' +1;
char c3[] = "n号用户进入会议,当前会议人数为:";
c3 [sizeof (c3) - 1] = c2;
c3[0] = c1;
for(int i=0;i<LinkNum;i++)
if(client_sockfd[i]!=-1)
write(client_sockfd[i], c3, sizeof(c3));
pthread_create(malloc(sizeof(pthread_t)),NULL,(void*)(&rcv_snd),(void*)i);
}
}
客户端程序ceshi3Client.c:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
struct mypara
{
int para1;//参数1
int para2;//参数2
};
void get(void* arg)//客户
{
char rcv_buf[1024]; //接收消息缓冲区
int rcv_num;
struct mypara *parat;
parat = (struct mypara*)arg;
int sockfd = (*parat).para2;
int cpid = (*parat).para1;
do{
memset(rcv_buf, 0, 1024); //接收缓冲区清零
rcv_num = read(sockfd, rcv_buf, sizeof(rcv_buf));
if(rcv_num==0)continue;
printf("%s\n",rcv_buf); //输出客户机从服务器接收的消息;
if(strncmp(rcv_buf,"quit",4)==0) break; //如果收到"quit",则结束循环,通信结束
}while(strncmp(rcv_buf,"!q",2)!=0); //如果收到"!q",则结束循环,通信结束
printf("会议结束!\n");
close(sockfd);
}
int main(void)
{
int sockfd; //客户端套接字描述符
int len = 0;
struct sockaddr_in address; //套接字协议地址
char rcv_buf[1024]; //接收消息缓冲区
int result;
int rcv_num; //接收消息长度
pid_t cpid; //客户进程标识符
pthread_t thread1;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("进入会议失败!\n");
return 1;
}
address.sin_family = AF_INET; //使用网络套接字
address.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器地址
address.sin_port = htons(9736); //服务器所监听的端口
if(inet_aton("127.0.0.1",&address.sin_addr)<0)
{
printf("inet_aton error.\n");
return -1;
}
len = sizeof(address);
cpid=getpid(); //获取客户进程标识符
result = connect(sockfd, (struct sockaddr*)&address, len);
if(result == -1)
{
perror("进入会议失败!\n");
exit(1);
}
struct mypara para;
para.para1=cpid;
para.para2=sockfd;
printf("输入!q,退出会议.\n");//输入quit,服务结束
pthread_create(&thread1,NULL,(void*)(&get),&(para));
char send_buf[1024]; //发送消息缓冲区
while(1)
{//客户机与服务器循环发送接收消息
memset(send_buf, 0, 1024); //发送缓冲区清零
scanf("%s",send_buf); //键盘输入欲发送给服务器的消息字符串
write(sockfd, send_buf, sizeof(send_buf)); //将消息发送到套接字
if(strncmp(send_buf,"!q",2)==0) break;//若发送"!q",则结束循环,通信结束
}
printf("您退出会议!\n");
close(sockfd); //关闭客户机套接字
}
编译链接命令:
gcc ceshi3Server.c -o ceshi3Server -lpthread
gcc ceshi3Client.c -o ceshi3Client
运行命令:
./test3Server
./test3Client
运行结果: