一.3个生产者与3个消费者,3个生产者各自向缓冲区投放一个产品后,才能进入下一轮产品投放,3个消费者各自从缓冲区取出一个产品后,才能进入下一轮产品接收。使用信号量和PV操作编写同步互斥算法实现上述功能(使用C语言编程)。(25分)
源代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 3 // 缓冲区大小
sem_t mutex; // 互斥信号量
sem_t full; // 表示缓冲区中有多少个产品
sem_t empty; // 表示缓冲区中有多少个空位
int buffer[BUFFER_SIZE]; // 缓冲区
int in = 0, out = 0; // 缓冲区的生产和消费指针
// 生产者线程
void *producer(void *arg) {
int i;
for (i = 0; i < 10; i++) { // 每个生产者生产10个产品
sem_wait(&empty); // 等待有空位
sem_wait(&mutex); // 获取互斥锁
buffer[in] = i; // 放入产品
printf("Producer %d produced item %d\n", *(int *)arg, i);
in = (in + 1) % BUFFER_SIZE; // 更新生产指针
sem_post(&mutex); // 释放互斥锁
sem_post(&full); // 表示缓冲区中有一个产品
}
pthread_exit(NULL);
}
// 消费者线程
void *consumer(void *arg) {
int i, item;
for (i = 0; i < 10; i++) { // 每个消费者消费10个产品
sem_wait(&full); // 等待有产品
sem_wait(&mutex); // 获取互斥锁
item = buffer[out]; // 取出产品
printf("Consumer %d consumed item %d\n", *(int *)arg, item);
out = (out + 1) % BUFFER_SIZE; // 更新消费指针
sem_post(&mutex); // 释放互斥锁
sem_post(&empty); // 表示缓冲区中有一个空位
}
pthread_exit(NULL);
}
int main() {
pthread_t producers[3], consumers[3];
int i;
// 初始化信号量
sem_init(&mutex, 0, 1);
sem_init(&full, 0, 0);
sem_init(&empty, 0, BUFFER_SIZE);
// 创建生产者线程
for (i = 0; i < 3; i++) {
pthread_create(&producers[i], NULL, producer, &i);
}
// 创建消费者线程
for (i = 0; i < 3; i++) {
pthread_create(&consumers[i], NULL, consumer, &i);
}
// 等待线程结束
for (i = 0; i < 3; i++) {
pthread_join(producers[i], NULL);
pthread_join(consumers[i], NULL);
}
// 销毁信号量
sem_destroy(&mutex);
sem_destroy(&full);
sem_destroy(&empty);
return 0;
}
运行方法:源文件名为1.c,在控制台输入
gcc 1.c -o 1
得到可执行文件1
在控制台输入./1 运行文件
运行结果:
二.Linux内核源程序文件夹中有大量的数据结构定义及其引用,编写一个Shell脚本,将数据结构struct block_device的定义所在的文件名及其路径和定义在文件中的行号输出,将该数据结构的引用所在的文件名及其路径和引用在文件中的行号输出。(25分)
源代码:
#!/bin/bash
# 设置内核源程序文件夹路径
KERNEL_SRC_DIR="/usr/src"
# 搜索 struct block_device 的定义
echo "struct block_device definition:"
grep -rnE "struct\s+block_device\s*\{" "$KERNEL_SRC_DIR" | while read -r line; do
file=$(echo "$line" | cut -d':' -f1)
line_number=$(echo "$line" | cut -d':' -f2)
echo "File: $file"
echo "Line: $line_number"
done
# 搜索 struct block_device 的引用
echo "struct block_device references:"
grep -rnE "\bstruct\s+block_device\b" "$KERNEL_SRC_DIR" | while read -r line; do
file=$(echo "$line" | cut -d':' -f1)
line_number=$(echo "$line" | cut -d':' -f2)
echo "File: $file"
echo "Line: $line_number"
done
运行方法:源文件为2.sh,在控制台输入 ./2.sh
运行结果:
# 设置内核源程序文件夹路径
KERNEL_SRC_DIR="/usr/src"
内核源文件一般在/usr/src
三.利用套接字通信机制编写客户机-服务器应用系统,使用C语言实现如下功能:
(1)客户机有两种执行Linux命令的模式,一种是把命令发送到服务器执行,执行后将结果返回到客户端。另一种是命令在本地机上执行。这两种命令执行方式通过不同的命令提示符表示。
源代码:
server1.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
int main() {
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_size;
char buffer[BUFFER_SIZE];
char command[BUFFER_SIZE];
FILE *fp;
int status;
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
perror("Failed to create socket");
return 1;
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
// 绑定套接字到服务器地址
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Failed to bind socket");
close(server_socket);
return 1;
}
// 监听连接请求
if (listen(server_socket, 5) < 0) {
perror("Failed to listen on socket");
close(server_socket);
return 1;
}
printf("Server is listening on port %d...\n", SERVER_PORT);
while (1) {
// 接受客户端连接
client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_size);
if (client_socket < 0) {
perror("Failed to accept client connection");
continue;
}
printf("Client connected from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
// 接收客户端命令
memset(buffer, 0, BUFFER_SIZE);
recv(client_socket, buffer, BUFFER_SIZE - 1, 0);
// 检查命令是否需要在服务器上执行
if (strncmp(buffer, "remote:", 7) == 0) {
// 在服务器上执行命令
memset(command, 0, BUFFER_SIZE);
strncpy(command, buffer + 7, BUFFER_SIZE - 7);
fp = popen(command, "r");
if (fp == NULL) {
perror("Failed to execute command");
send(client_socket, "Failed to execute command", 24, 0);
} else {
memset(buffer, 0, BUFFER_SIZE);
fread(buffer, 1, BUFFER_SIZE - 1, fp);
send(client_socket, buffer, strlen(buffer), 0);
pclose(fp);
}
} else {
// 在客户端本地执行命令
fp = popen(buffer, "r");
if (fp == NULL) {
perror("Failed to execute command");
send(client_socket, "Failed to execute command", 24, 0);
} else {
memset(buffer, 0, BUFFER_SIZE);
fread(buffer, 1, BUFFER_SIZE - 1, fp);
send(client_socket, buffer, strlen(buffer), 0);
pclose(fp);
}
}
// 关闭客户端连接
close(client_socket);
}
// 关闭服务器套接字
close(server_socket);
return 0;
}
client1.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
int main() {
int client_socket;
struct sockaddr_in server_addr;
char buffer[BUFFER_SIZE];
char command[BUFFER_SIZE];
// 创建客户端套接字
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0) {
perror("Failed to create socket");
return 1;
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
// 连接到服务器
if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Failed to connect to server");
close(client_socket);
return 1;
}
while (1) {
// 提示用户输入命令
if (strncmp(command, "remote:", 7) == 0) {
printf("remote> ");
} else {
printf("local> ");
}
fgets(command, BUFFER_SIZE, stdin);
command[strcspn(command, "\n")] = '\0'; // 去掉换行符
// 如果输入"exit",则退出循环
if (strcmp(command, "exit") == 0) {
break;
}
// 发送命令到服务器
send(client_socket, command, strlen(command), 0);
// 接收服务器的响应
memset(buffer, 0, BUFFER_SIZE);
recv(client_socket, buffer, BUFFER_SIZE - 1, 0);
printf("%s\n", buffer);
}
// 关闭套接字
close(client_socket);
return 0;
}
运行方法:
gcc server1.c -o server1
gcc client1.c -o client1
./server1
./client1
默认状态是命令在本地执行,如果要在服务器执行命令,需要加前缀remote:
运行结果:
(2)客户机可以与服务器通信,互发信息。客户机也可以与已经连上服务器的另一个客户机通信,互发信息。客户机可以看到与服务器相连的所有客户机的IP地址、端口号和用户名。(50分)
源代码:
server2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 12345
#define MAX_CLIENTS 100
#define BUFFER_SIZE 1024
typedef struct {
struct sockaddr_in address;
int sockfd;
char username[32];
} client_t;
client_t *clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
void add_client(client_t *cl) {
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (!clients[i]) {
clients[i] = cl;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
}
void remove_client(int sockfd) {
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i]) {
if (clients[i]->sockfd == sockfd) {
clients[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
void send_message(char *message, int sockfd) {
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i]) {
if (clients[i]->sockfd != sockfd) {
if (send(clients[i]->sockfd, message, strlen(message), 0) < 0) {
perror("ERROR: send");
break;
}
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
void send_private_message(char *message, char *target_username, int sockfd) {
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i]) {
if (strcmp(clients[i]->username, target_username) == 0) {
if (send(clients[i]->sockfd, message, strlen(message), 0) < 0) {
perror("ERROR: send");
}
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
void send_client_list(int sockfd) {
char buffer[BUFFER_SIZE];
char client_info[BUFFER_SIZE];
pthread_mutex_lock(&clients_mutex);
strcpy(buffer, "Connected clients:\n");
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i]) {
snprintf(client_info, BUFFER_SIZE, "IP: %s Port: %d Username: %s\n",
inet_ntoa(clients[i]->address.sin_addr),
ntohs(clients[i]->address.sin_port),
clients[i]->username);
strcat(buffer, client_info);
}
}
pthread_mutex_unlock(&clients_mutex);
send(sockfd, buffer, strlen(buffer), 0);
}
void handle_broadcast(char *message, int sockfd) {
char buffer[BUFFER_SIZE];
sprintf(buffer, "[Broadcast] %s\n", message);
send_message(buffer, sockfd);
}
void *handle_client(void *arg) {
char buffer[BUFFER_SIZE];
char username[32];
int leave_flag = 0;
client_t *cli = (client_t *)arg;
// 接收用户名
if (recv(cli->sockfd, username, 32, 0) <= 0 || strlen(username) < 2 || strlen(username) >= 32 - 1) {
printf("Didn't enter the username.\n");
leave_flag = 1;
} else {
strcpy(cli->username, username);
sprintf(buffer, "%s has joined\n", cli->username);
printf("%s", buffer);
send_message(buffer, cli->sockfd);
}
bzero(buffer, BUFFER_SIZE);
while (1) {
if (leave_flag) {
break;
}
int receive = recv(cli->sockfd, buffer, BUFFER_SIZE, 0);
if (receive > 0) {
if (strlen(buffer) > 0) {
if (strncmp(buffer, "/list", 5) == 0) {
send_client_list(cli->sockfd);
} else if (strncmp(buffer, "/msg", 4) == 0) {
// 处理私聊消息
char target_username[32];
char *message = buffer + 5;
sscanf(buffer, "/msg %s", target_username);
message = strstr(buffer, target_username) + strlen(target_username) + 1;
send_private_message(message, target_username, cli->sockfd);
} else if (strncmp(buffer, "/broadcast", 10) == 0) {
// 处理全体消息广播
char *message = buffer + 11;
handle_broadcast(message, cli->sockfd);
} else {
send_message(buffer, cli->sockfd);
printf("%s", buffer);
}
}
} else if (receive == 0 || strcmp(buffer, "exit") == 0) {
sprintf(buffer, "%s has left\n", cli->username);
printf("%s", buffer);
send_message(buffer, cli->sockfd);
leave_flag = 1;
} else {
perror("ERROR: recv");
leave_flag = 1;
}
bzero(buffer, BUFFER_SIZE);
}
close(cli->sockfd);
remove_client(cli->sockfd);
free(cli);
pthread_detach(pthread_self());
return NULL;
}
int main() {
int sockfd, new_sock;
struct sockaddr_in server_addr, cli_addr;
socklen_t cli_len = sizeof(cli_addr);
pthread_t tid;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("ERROR: socket");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("ERROR: bind");
close(sockfd);
exit(EXIT_FAILURE);
}
if (listen(sockfd, 10) == -1) {
perror("ERROR: listen");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("=== WELCOME TO THE CHATROOM ===\n");
while (1) {
new_sock = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);
if (new_sock == -1) {
perror("ERROR: accept");
continue;
}
if ((MAX_CLIENTS - 1) == 0) {
printf("Max clients reached. Rejected: ");
printf("%s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
close(new_sock);
continue;
}
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->address = cli_addr;
cli->sockfd = new_sock;
strcpy(cli->username, "Anonymous");
add_client(cli);
pthread_create(&tid, NULL, &handle_client, (void *)cli);
}
return EXIT_SUCCESS;
}
client2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 12345
#define BUFFER_SIZE 1024
void *receive_message(void *sockfd) {
int socket = *(int *)sockfd;
char buffer[BUFFER_SIZE];
while (1) {
int receive = recv(socket, buffer, BUFFER_SIZE, 0);
if (receive > 0) {
printf("%s", buffer);
fflush(stdout);
} else if (receive == 0) {
break;
} else {
perror("ERROR: recv");
break;
}
memset(buffer, 0, BUFFER_SIZE);
}
return NULL;
}
int main() {
int sockfd;
struct sockaddr_in server_addr;
char username[32];
char message[BUFFER_SIZE];
pthread_t recv_thread;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("ERROR: socket");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("ERROR: connect");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("Enter your username: ");
fgets(username, 32, stdin);
username[strcspn(username, "\n")] = '\0';
if (send(sockfd, username, 32, 0) == -1) {
perror("ERROR: send");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("=== Welcome to the chatroom ===\n");
if (pthread_create(&recv_thread, NULL, receive_message, (void *)&sockfd) != 0) {
perror("ERROR: pthread");
close(sockfd);
exit(EXIT_FAILURE);
}
while (1) {
fgets(message, BUFFER_SIZE, stdin);
message[strcspn(message, "\n")] = '\0';
if (send(sockfd, message, strlen(message), 0) == -1) {
perror("ERROR: send");
break;
}
if (strcmp(message, "exit") == 0) {
break;
}
memset(message, 0, BUFFER_SIZE);
}
close(sockfd);
pthread_cancel(recv_thread);
pthread_join(recv_thread, NULL);
return EXIT_SUCCESS;
}
运行方法:
gcc server2.c -o server2
gcc client2.c -o client2
./server2
./client2
如果要显示当前连线的客户端,使用/list
如果要与某个客户端私聊,使用/msg 客户端 私聊内容
如果要使用公告,使用/broadcast 公告内容
运行结果:
显示与服务器相连的所有客户机的IP地址,端口号和用户名
客户机与连接上服务器的另一个客户机通信,互发消息
公告
要求:雷同者将扣分,以上仅供参考,大概知道实现效果是什么样的。