分析过程:
互斥关系:
1、对缓冲区(箱子)的访问要互斥的进行
同步关系:
1、只有箱子有空位时工人1或者工人2才能把车架放入箱子中
2、只有箱子中有超过1个车架和2个车轮时,工人3才能组装1台车
分析中涉及到一个问题
如果箱子中因为线程竞争出现没有车架或者低于2个车轮的情况就会发生死锁,为了防止出现这种情况,需要把箱子分为2部分,一部分放车轮,一部分放车架,使其空间比例为2:1。对于缓冲区设置一个循环队列,对于循环队列%3=0的位置放车架,%3!=0的位置放车轮,这样就解决了死锁问题。
需要四个信号量bike_frame_sem, bike_wheel_sem, product_sem, box_sem分别对应工人1生产车架,工人2生产车轮,工人3组装车,以及互斥访问盒子
源码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define MAX_N 12 // 箱子中位置的最大值
#define TRUE 1
#define FALSE 0
sem_t bike_frame_sem, bike_wheel_sem, product_sem, box_sem; // 四个信号量
int bike_frame_count = 0, bike_wheel_count = 0, product_count = 0; // 全局变量
int full_queue[MAX_N]; // 队列变量,使用环形队列
void *bike_frame_worker(void *arg)
{
while (TRUE)
{
sem_wait(&bike_frame_sem);
// 队列中 %3 == 0的位置放车架
for(int i = 0; i < MAX_N; i += 3)
if(!full_queue[i]){
full_queue[i] = 1;
break;
}
bike_frame_count ++;
sem_wait(&box_sem);
printf("Successfully Manufacturing a frame!\n");
sem_post(&box_sem);
sleep(1); // 加工车架需要3秒钟
// 互斥访问
sem_wait(&box_sem);
if(bike_wheel_count >= 2 && bike_frame_count >= 1)
{
bike_wheel_count -= 2;
bike_frame_count -= 1;
sem_post(&product_sem);
}
sem_post(&box_sem);
}
}
void *bike_wheel_worker(void *arg)
{
while (TRUE)
{
sem_wait(&bike_wheel_sem);
// 队列中 %3 != 0的位置放车轮
for(int i = 0; i < MAX_N; i ++)
{
if(i % 3 == 0) continue;
if(!full_queue[i]){
full_queue[i] = 2;
break;
}
}
bike_wheel_count ++;
sem_wait(&box_sem);
printf("Successfully Manufacturing a wheel!\n");
sem_post(&box_sem);
sleep(1); // 加工车轮需要3秒钟
// 互斥访问
sem_wait(&box_sem);
if(bike_wheel_count >= 2 && bike_frame_count >= 1)
{
bike_wheel_count -= 2;
bike_frame_count -= 1;
sem_post(&product_sem);
}
sem_post(&box_sem);
}
}
void *bike_assembler(void *arg)
{
while (TRUE)
{
sem_wait(&product_sem);
// 从队列中取出 1个 车架
for(int i = 0; i < MAX_N; i ++)
if(full_queue[i] == 1)
{
full_queue[i] = 0;
break;
}
// 从队列中取出 1个 车轮
for(int i = 0; i < MAX_N; i ++)
if(full_queue[i] == 2)
{
full_queue[i] = 0;
break;
}
for(int i = 0; i < MAX_N; i ++)
if(full_queue[i] == 2)
{
full_queue[i] = 0;
break;
}
product_count++;
sem_wait(&box_sem);
printf("Successfully Assembled No.%d bike! frame count = %d, wheel count = %d, product count = %d\n", product_count, bike_frame_count, bike_wheel_count, product_count);
sem_post(&box_sem);
sem_post(&bike_frame_sem);
sem_post(&bike_wheel_sem);
sem_post(&bike_wheel_sem);
}
}
int main()
{
sem_init(&bike_frame_sem, 0, MAX_N / 3); // 创建并初始化信号量,表示车架数量
sem_init(&bike_wheel_sem, 0, MAX_N / 3 * 2); // 创建并初始化信号量,表示车轮数量
sem_init(&product_sem, 0, 0); // 创建并初始化信号量,表示已经生产的自行车数量
sem_init(&box_sem, 0, 1);// 创建并初始化信号量,表示互斥
pthread_t bike_frame_thread, bike_wheel_thread, bike_assembler_thread;
pthread_create(&bike_frame_thread, NULL, bike_frame_worker, NULL);
pthread_create(&bike_wheel_thread, NULL, bike_wheel_worker, NULL);
pthread_create(&bike_assembler_thread, NULL, bike_assembler, NULL);
pthread_join(bike_frame_thread, NULL);
pthread_join(bike_wheel_thread, NULL);
pthread_join(bike_assembler_thread, NULL);
sem_destroy(&bike_frame_sem);
sem_destroy(&bike_wheel_sem);
sem_destroy(&product_sem);
sem_destroy(&box_sem);
return 0;
}
运行过程:
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ vim main.c
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ gcc main.c -o main -pthread
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ ./main
运行结果
当生产一个车轮和车架所用时间一样,都为1s时,可以看出车轮明显不够用,车架过载
因此把车轮和车架生产时间改为1:2,就会出现车轮和车架趋于平稳,不会造成挤压。
2、编写一个用于检查关于命令“ls px ct"的实验报告的Shell脚本,该脚本检查命令的执行结果中是否存在包含“px”和“ct”的文件或目录,显示包含“px”的文件或目录数,包含“ct”的文件或目录数。如果两者的文件或目录数均大于0,则对该实验项目给予100分。如果其中之一的文件或目录数为0,则对该实验项目给予50分,如果两者的文件或目录数均为0,则对该实验项目给予0分。(25分)
源码:]
#!/bin/bash
# 保存输出
output=$(ls *px* *ct* 2>/dev/null)
# 判断是否为空
if [ -z "$output" ]; then
echo "No files or directories found with 'px' or 'ct' in their names."
exit 0
fi
# 查询包含px的数量
px_count=$(echo "$output" | grep -c 'px')
# 查询包含ct的数量
ct_count=$(echo "$output" | grep -c 'ct')
# 输出结果
echo "$px_count files/directories found with 'px' in their names."
echo "$ct_count files/directories found with 'ct' in their names."
运行方法:
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ vim findpc.sh
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ chmod +x findpc.sh
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ ./findpc.sh
运行结果:
当一个文件也没有时
结果为:No files or directories found with ‘px’ or ‘ct’ in their names.
当仅含有px时
结果为:
6 files/directories found with ‘px’ in their names.
0 files/directories found with ‘ct’ in their names.
当同时含有px和ct时
结果为:
6 files/directories found with ‘px’ in their names.
5 files/directories found with ‘ct’ in their names.
3、编写套接字通信程序,实现简单的文字发送、接收形式的会议功能。主持人运行服务器程序,参会者运行客户端程序。主持人发送的文字信息每个客户端用户都可以看到。任一客户端用户发送的文字信息包括主持人在内的其他参会者也都可以看到。服务器或者客户端显示当前开会人数,参会者标识,以及当前发送信息的程序或者用户的标识。(50分)
分析过程:
主持人运行服务器程序发给所有客户端,客户运行客户端程序发送给主持人和其它客户端,如果把主持人看作一个特殊的客户,这个问题可以转化为 客户把信息发给中间服务器,中间服务器在发给其它客户。
需要写三个程序,一个是中间服务器,接受主持人和客户的通信,一个是主持人程序,有主持人唯一标识通过中间服务器发给客户,一个客户端程序,信息通过中间服务器发个主持人和客户端。
程序需要用到多线程,服务器端开2个线程,主线程接收套接字,子线程处理客户端发来的信息,把他转发给其它客户端。客户端需要开3个线程,主线程连接服务器,一个线程接受数据,一个线程发送数据。
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define MAX_CLIENTS 10
#define BUFFER_SIZE 2048
void * handle_client(void *);
int client_count = 0;
struct Client{
int client_fd;
char username[20];
char id[20];
}client[MAX_CLIENTS];
int main(int argc, char *argv[]) {
int server_sockfd, client_sockfd;
struct sockaddr_in server_addr, client_addr;
pthread_t tid;
if (argc != 2) {
printf("Usage: %s <port>\n", argv[0]);
exit(1);
}
// initialize
int idx = 0;
for (idx = 0; idx < MAX_CLIENTS; idx++) {
client[idx].client_fd = -1;
}
// initialize server address
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(atoi(argv[1]));
// create server socket
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (server_sockfd < 0) {
perror("Failed to create server socket");
exit(EXIT_FAILURE);
}
// bind the socket to the server address
if (bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Failed to bind server socket");
exit(EXIT_FAILURE);
}
// listen on the socket for incoming connections
if (listen(server_sockfd, MAX_CLIENTS) < 0) {
perror("Failed to listen for incoming connections");
exit(EXIT_FAILURE);
}
printf("Waiting for incoming connections...\n");
while (1) {
socklen_t client_addr_size = sizeof(client_addr);
// accept an incoming connection
client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_addr_size);
if (client_sockfd < 0) {
perror("Failed to accept connection");
exit(EXIT_FAILURE);
}
int i = 0;
for (idx = 0; idx < MAX_CLIENTS; idx++) {
if(client[idx].client_fd == -1)
{
i = idx;
break;
}
}
client[i].client_fd = client_sockfd;
printf("Client %d connected\n", i + 1);
// get the username from the client
if (recv(client_sockfd, client[i].username, sizeof(client[i].username), 0) <= 0) {
perror("Failed to receive username from client");
close(client_sockfd);
return 0;
}
// get the id from the client
if (recv(client_sockfd, client[i].id, sizeof(client[i].id), 0) <= 0) {
perror("Failed to receive id from client");
close(client_sockfd);
return 0;
}
client_count ++;
// create a new thread to handle the client
if (pthread_create(&tid, NULL, handle_client, &client_sockfd) != 0) {
perror("Failed to create thread for client");
exit(EXIT_FAILURE);
}
// detach the thread so it can run in the background
if (pthread_detach(tid) != 0) {
perror("Failed to detach thread for client");
exit(EXIT_FAILURE);
}
}
// close the server socket
close(server_sockfd);
return 0;
}
void * handle_client(void *arg) {
int client_sockfd = *(int*)arg;
int idx;
char buffer[BUFFER_SIZE];
char tmp[BUFFER_SIZE];
char room_number[BUFFER_SIZE];
char username[20];
char id[20];
for (idx = 0; idx < MAX_CLIENTS; idx++) {
if (client[idx].client_fd == client_sockfd) {
memcpy(username, client[idx].username,sizeof(client[idx].username));
memcpy(id, client[idx].id,sizeof(client[idx].id));
break;
}
}
printf("No.%d Client %s joined the chat\n",idx + 1, username);
// send attebdees number
sprintf(room_number, "Now, There are %d attendees at the meeting.", client_count);
for (idx = 0; idx < MAX_CLIENTS; idx++) {
if(client[idx].client_fd != -1){
send(client[idx].client_fd, room_number, strlen(room_number), 0);
}
}
while (1) {
// receive message from the client
int msg_len = recv(client_sockfd, buffer, sizeof(buffer), 0);
if (msg_len <= 0) {
printf("Client %s left the chat\n", username);
break;
}
memcpy(tmp, id,sizeof(id));
strcat(tmp, username);
strcat(tmp, ": ");
strcat(tmp, buffer);
msg_len += strlen(id) + strlen(username) + 2;
memcpy(buffer, tmp,sizeof(tmp));
// forward message to all other clients
for (idx = 0; idx < MAX_CLIENTS; idx++) {
if (client[idx].client_fd != -1 && client[idx].client_fd != client_sockfd) {
send(client[idx].client_fd, buffer, msg_len, 0);
}
}
}
// delete this socket
for (idx = 0; idx < MAX_CLIENTS; idx++) {
if (client[idx].client_fd == client_sockfd) {
client[idx].client_fd = -1;
}
}
client_count --;
// exit update client_count
sprintf(room_number, "Now, There are %d attendees at the meeting.", client_count);
for (idx = 0; idx < MAX_CLIENTS; idx++) {
if(client[idx].client_fd != -1){
send(client[idx].client_fd, room_number, strlen(room_number), 0);
}
}
// close the client socket
close(client_sockfd);
return NULL;
}
主持人端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 2048
void * send_message(void *);
void * receive_message(void *);
int main(int argc, char *argv[]) {
int sockfd;
struct sockaddr_in server_addr;
char username[20];
if (argc < 3) {
printf("Usage: %s <IP address> <port>\n", argv[0]);
exit(EXIT_FAILURE);
}
// initialize server address
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2]));
// create socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Failed to create socket");
exit(EXIT_FAILURE);
}
// connect to server
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Failed to connect to server");
exit(EXIT_FAILURE);
}
// get the username from the user
printf("Enter your username: ");
fgets(username, sizeof(username), stdin);
strtok(username, "\n");
// send the username to the server
if (send(sockfd, username, strlen(username), 0) < 0) {
perror("Failed to send username to server");
exit(EXIT_FAILURE);
}
// send the id to the server
char id[20] = "<Host>";
if (send(sockfd, id, strlen(id), 0) < 0) {
perror("Failed to send id to server");
exit(EXIT_FAILURE);
}
// create threads for sending and receiving messages
pthread_t send_tid, recv_tid;
if (pthread_create(&send_tid, NULL, send_message, &sockfd) != 0) {
perror("Failed to create thread for sending messages");
exit(EXIT_FAILURE);
}
if (pthread_create(&recv_tid, NULL, receive_message, &sockfd) != 0) {
perror("Failed to create thread for receiving messages");
exit(EXIT_FAILURE);
}
// wait for threads to finish
pthread_join(send_tid, NULL);
pthread_join(recv_tid, NULL);
// close the socket
close(sockfd);
return 0;
}
void * send_message(void *arg) {
int sockfd = *(int*)arg;
char buffer[BUFFER_SIZE];
while (1) {
// read input from the user
if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
break;
}
buffer[strcspn(buffer, "\n")] = 0; // remove newline character
// send message to the server
if (send(sockfd, buffer, strlen(buffer), 0) < 0) {
perror("Failed to send message to server");
break;
}
}
// signal the receiving thread to exit
pthread_cancel(pthread_self());
return NULL;
}
void * receive_message(void *arg) {
int sockfd = *(int*)arg;
char buffer[BUFFER_SIZE];
while (1) {
// receive message from the server
int msg_len = recv(sockfd, buffer, sizeof(buffer), 0);
if (msg_len <= 0) {
printf("Disconnected from server\n");
break;
}
// print message to the user
buffer[msg_len] = '\0';
printf("%s\n", buffer);
}
// signal the sending thread to exit
pthread_cancel(pthread_self());
return NULL;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 2048
void * send_message(void *);
void * receive_message(void *);
int main(int argc, char *argv[]) {
int sockfd;
struct sockaddr_in server_addr;
char username[20];
if (argc < 3) {
printf("Usage: %s <IP address> <port>\n", argv[0]);
exit(EXIT_FAILURE);
}
// initialize server address
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2]));
// create socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("Failed to create socket");
exit(EXIT_FAILURE);
}
// connect to server
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Failed to connect to server");
exit(EXIT_FAILURE);
}
// get the username from the user
printf("Enter your username: ");
fgets(username, sizeof(username), stdin);
strtok(username, "\n");
// send the username to the server
if (send(sockfd, username, strlen(username), 0) < 0) {
perror("Failed to send username to server");
exit(EXIT_FAILURE);
}
// send the id to the server
char id[20] = "<Attendee>";
if (send(sockfd, id, strlen(id), 0) < 0) {
perror("Failed to send id to server");
exit(EXIT_FAILURE);
}
// create threads for sending and receiving messages
pthread_t send_tid, recv_tid;
if (pthread_create(&send_tid, NULL, send_message, &sockfd) != 0) {
perror("Failed to create thread for sending messages");
exit(EXIT_FAILURE);
}
if (pthread_create(&recv_tid, NULL, receive_message, &sockfd) != 0) {
perror("Failed to create thread for receiving messages");
exit(EXIT_FAILURE);
}
// wait for threads to finish
pthread_join(send_tid, NULL);
pthread_join(recv_tid, NULL);
// close the socket
close(sockfd);
return 0;
}
void * send_message(void *arg) {
int sockfd = *(int*)arg;
char buffer[BUFFER_SIZE];
while (1) {
// read input from the user
if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
break;
}
buffer[strcspn(buffer, "\n")] = 0; // remove newline character
// send message to the server
if (send(sockfd, buffer, strlen(buffer), 0) < 0) {
perror("Failed to send message to server");
break;
}
}
// signal the receiving thread to exit
pthread_cancel(pthread_self());
return NULL;
}
void * receive_message(void *arg) {
int sockfd = *(int*)arg;
char buffer[BUFFER_SIZE];
while (1) {
// receive message from the server
int msg_len = recv(sockfd, buffer, sizeof(buffer), 0);
if (msg_len <= 0) {
printf("Disconnected from server\n");
break;
}
// print message to the user
buffer[msg_len] = '\0';
printf("%s\n", buffer);
}
// signal the sending thread to exit
pthread_cancel(pthread_self());
return NULL;
}
运行方法:
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ vim server.c
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ gcc server.c -o server -lpthread
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ ./server 5050
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ vim client.c
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ gcc client.c -o client -lpthread
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ ./client 121.43.36.60 5005
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ vim host.c
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ gcc host.c -o host -lpthread
lgy@iZbp16wq7lq85vhufu8oqnZ:~/test$ ./host 121.43.36.60 5005
运行结果:
启动服务器并连接一个主持人和两个客户
一个主持人和客户连接
主持人讲话,其它参会者可以看到标识符和主持人名字
其它参会者讲话,主持人和其它参会者能看到标识符和参会者名字
新人加入,会更新参加会议总人数
有人离开会议会更新参会者总人数