sub_process.c
#include <stdio.h> // 标准输入输出库
#include <pthread.h> // POSIX线程库
#include <sys/ipc.h> // IPC基础定义(如消息队列/共享内存)
#include <sys/msg.h> // 消息队列操作相关
#include <sys/shm.h> // 共享内存操作相关
#include <unistd.h> // 标准符号常量和类型
#include <string.h> // 字符串操作相关
#define NUM_THREADS 4 // 定义线程数量为4
#define MAX_MSG_SIZE 1024 // 定义单条消息的最大长度
// 消息队列数据结构
struct msg_buffer
{
long msg_type; // 消息类型标识(必须非0)
char msg_text[MAX_MSG_SIZE]; // 实际消息内容缓冲区
};
// 共享内存数据结构
struct shm_data
{
int char_count[256]; // ASCII字符频率统计数组(支持扩展ASCII)
pthread_mutex_t lock; // 互斥锁保护共享数据
};
int shmid; // 全局共享内存ID,供子进程或线程使用
// 线程处理函数:负责从消息队列读取日志并统计字符
void* process_log(void* arg)
{
int msgid = *(int*)arg; // 从参数获取消息队列ID(潜在问题:传递局部变量地址)
struct msg_buffer msg; // 定义接收消息的临时缓冲区
// 阻塞式接收消息队列中类型为1的消息
ssize_t bytes_received = msgrcv(msgid, &msg, MAX_MSG_SIZE, 1, 0);
if (bytes_received == -1)
{
perror("msgrcv"); // 如果接收失败则打印错误信息
return NULL;
}
// 将共享内存附加到当前进程的地址空间
struct shm_data* shm = shmat(shmid, NULL, 0);
if (shm == (void*)-1)
{
perror("shmat"); // 如果附加失败则打印错误信息
return NULL;
}
// 遍历接收到的消息内容进行字符统计
for (int i = 0; i < bytes_received; i++)
{
unsigned char c = msg.msg_text[i]; // 取当前字符(无符号避免负数索引)
pthread_mutex_lock(&shm->lock); // 加锁保护共享数据
shm->char_count[c]++; // 对应字符计数+1
pthread_mutex_unlock(&shm->lock); // 解锁
}
shmdt(shm); // 分离共享内存(不影响其他进程/线程的挂接)
return NULL;
}
int main() {
// 生成IPC对象唯一键值(基于文件路径和项目ID)
key_t key = ftok("/tmp/logfile", 65);
if (key == -1)
{
perror("ftok"); // 如果生成失败则退出程序
return 1;
}
// 创建消息队列(权限0666,若不存在则创建)
int msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1)
{
perror("msgget"); // 如果获取失败则退出程序
return 1;
}
// 创建共享内存段(大小为shm_data结构体,权限0666,若不存在则创建)
shmid = shmget(key, sizeof(struct shm_data), 0666 | IPC_CREAT);
if (shmid == -1)
{
perror("shmget"); // 如果创建失败则退出程序
return 1;
}
// 将共享内存附加到当前进程地址空间并进行初始化
struct shm_data* shm = shmat(shmid, NULL, 0);
memset(shm, 0, sizeof(struct shm_data)); // 清空共享内存内容
pthread_mutex_init(&shm->lock, NULL); // 初始化互斥锁
// 创建线程池(NUM_THREADS个线程)
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++)
{
// 创建线程并传递消息队列ID指针(潜在问题:msgid是栈变量)
if (pthread_create(&threads[i], NULL, process_log, &msgid) != 0)
{
perror("pthread_create"); // 如果线程创建失败则退出程序
return 1;
}
}
// 等待所有线程执行完毕
for (int i = 0; i < NUM_THREADS; i++)
{
pthread_join(threads[i], NULL);
}
// 分离共享内存(主进程不再需要访问)
shmdt(shm);
return 0;
}
main_process.c
#include <stdio.h> // 标准输入输出库
#include <sys/ipc.h> // IPC基础定义(消息队列/共享内存等)
#include <sys/shm.h> // 共享内存操作相关函数
#include <pthread.h> // POSIX线程库
#include <unistd.h> // 标准符号常量和类型
// 共享内存结构体(与子进程保持一致)
struct shm_data
{
int char_count[256]; // ASCII字符频率统计数组(支持扩展ASCII)
pthread_mutex_t lock; // 互斥锁保护共享数据
};
int main()
{
// 生成唯一键值(基于文件路径和项目ID)
key_t key = ftok("/tmp/logfile", 65);
if (key == -1)
{
perror("ftok"); // 如果生成键值失败则退出程序
return 1;
}
// 创建或获取共享内存段(大小为shm_data结构体)
int shmid = shmget(key, sizeof(struct shm_data), 0666 | IPC_CREAT);
if (shmid == -1)
{
perror("shmget"); // 如果获取共享内存失败则退出程序
return 1;
}
// 将共享内存映射到当前进程地址空间
struct shm_data* shm = shmat(shmid, NULL, 0);
if (shm == (void*)-1)
{
perror("shmat"); // 如果映射失败则退出程序
return 1;
}
// 实时监控循环(永久运行直到程序被强制终止)
while (1)
{
sleep(1); // 每隔1秒更新一次统计信息
// 加锁以保护共享数据
pthread_mutex_lock(&shm->lock);
printf("===== 字符统计 =====");
// 遍历所有可能的ASCII字符
for (int i = 0; i < 256; i++)
{
if (shm->char_count[i] > 0) // 仅显示出现过的字符
{
if (i == ' ')
{
printf("空格: %d", shm->char_count[i]); // 特殊处理空格字符
}
else
{
printf("%c: %d", (char)i, shm->char_count[i]); // 显示可打印字符
}
}
}
// 解锁以允许其他线程访问
pthread_mutex_unlock(&shm->lock);
}
// 分离共享内存(实际上不会执行到这里)
shmdt(shm);
return 0;
}
monitor.c
#include <stdio.h> // 标准输入输出库
#include <sys/ipc.h> // IPC基础定义(消息队列/共享内存等)
#include <sys/shm.h> // 共享内存操作相关函数
#include <pthread.h> // POSIX线程库
#include <unistd.h> // 标准符号常量和类型
// 共享内存结构体(与子进程保持一致)
struct shm_data
{
int char_count[256]; // ASCII字符频率统计数组(支持扩展ASCII)
pthread_mutex_t lock; // 互斥锁保护共享数据
};
int main()
{
// 生成唯一键值(基于文件路径和项目ID)
key_t key = ftok("/tmp/logfile", 65);
if (key == -1)
{
perror("ftok"); // 如果生成键值失败则退出程序
return 1;
}
// 创建或获取共享内存段(大小为shm_data结构体)
int shmid = shmget(key, sizeof(struct shm_data), 0666 | IPC_CREAT);
if (shmid == -1)
{
perror("shmget"); // 如果获取共享内存失败则退出程序
return 1;
}
// 将共享内存映射到当前进程地址空间
struct shm_data* shm = shmat(shmid, NULL, 0);
if (shm == (void*)-1)
{
perror("shmat"); // 如果映射失败则退出程序
return 1;
}
// 实时监控循环(永久运行直到程序被强制终止)
while (1)
{
sleep(1); // 每隔1秒更新一次统计信息
// 加锁以保护共享数据
pthread_mutex_lock(&shm->lock);
printf("===== 字符统计 =====");
// 遍历所有可能的ASCII字符
for (int i = 0; i < 256; i++)
{
if (shm->char_count[i] > 0) // 仅显示出现过的字符
{
if (i == ' ')
{
printf("空格: %d", shm->char_count[i]); // 特殊处理空格字符
}
else
{
printf("%c: %d", (char)i, shm->char_count[i]); // 显示可打印字符
}
}
}
// 解锁以允许其他线程访问
pthread_mutex_unlock(&shm->lock);
}
// 分离共享内存(实际上不会执行到这里)
shmdt(shm);
return 0;
}
编译和运行
# 创建共享内存键值文件
touch /tmp/logfile
# 编译代码
gcc main_process.c -o main_process
gcc sub_process.c -o sub_process -lpthread
gcc monitor.c -o monitor -lpthread
# 生成测试日志
base64 /dev/urandom | head -n 1000 > test.log
# 按顺序启动进程
./sub_process &
./monitor &
./main_process
手动终止进程
-
查找进程ID
在终端中执行以下命令,找到相关进程的PID:
ps aux | grep -E 'main_process|sub_process|monitor'
-
终止进程
使用
kill
命令逐个终止进程:
牛客网刷题