- 实现两个没有亲缘关系的进程之间通过共享内存实现一个小文件(小于10K)的数据拷贝。
(可申请文件大小的共享内存,一次性写入文件所有内容,读取共享内存的进程访问数据后,进行文件存储)
思路
要实现两个进程之间通过共享内存进行文件拷贝,可以按照以下步骤进行:
创建共享内存:进程A创建一个共享内存段,并将其映射到内存中,用于存储文件数据。
打开文件:进程A打开待拷贝的文件,并读取文件内容。
写入共享内存:进程A将文件内容写入到共享内存中。
打开共享内存:进程B打开之前创建的共享内存段,并将其映射到内存中。
读取共享内存:进程B从共享内存中读取数据。
存储文件:进程B将读取到的数据存储到文件中。
写入端
#include "head.h"
#define PATH "/home/st"
int main(int argc, char const *argv[])
{
if (argc < 2)
{
perror("输入参数不够");
return -1;
}
key_t key = ftok(PATH, 666);
// 创建并赋读写权限
int shmid = shmget(key, 1024 * 10, IPC_CREAT | SHM_R | SHM_W);
if (shmid == -1)
{
perror("failed");
return -1;
}
int fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC);
// 内存映射
void *p = shmat(shmid, NULL, 0);
// 从内存中读取数据写入文件
write(fd, p, 1024 * 10);
sleep(10);
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
}
这段代码的功能是将共享内存中的数据写入文件。
代码分析如下:
引入头文件 “head.h”,该头文件应该包含了需要使用的函数和常量的声明。
使用#define宏定义了一个路径常量PATH,表示共享内存的路径。
进入main函数,首先判断命令行参数的数量。如果参数数量小于2,输出错误信息并返回-1。
使用ftok函数生成一个唯一的key,该key由共享内存的路径和一个序号确定。
使用shmget函数创建共享内存,传入key、内存大小和标志。如果创建失败,输出错误信息并返回-1。
使用open函数打开一个文件,传入参数argv[1]作为文件名,使用可写、创建和截断模式。如果打开失败,输出错误信息并返回-1。
使用shmat函数将共享内存与进程连接起来,参数为共享内存的id、连接地址和标志。函数返回一个指向共享内存的指针。
使用write函数将共享内存的数据写入文件,参数为文件描述符、写入数据的指针和要写入的字节数。这里写入的字节数为1024 * 10,即共享内存的大小。
使用sleep函数休眠10秒,等待其他进程对共享内存的操作完成。
使用shmdt函数将共享内存与进程分离。
使用shmctl函数删除共享内存,参数为共享内存的id和命令。
读取端
#include "head.h"
#define PATH "/home/st"
int main(int argc, char const *argv[])
{
if (argc < 2)
{
perror("输入参数不够");
return -1;
}
key_t key = ftok(PATH, 666);
// 创建并赋读写权限
int shmid = shmget(key, 1024 * 10, IPC_CREAT | SHM_R | SHM_W);
if (shmid == -1)
{
perror("failed");
return -1;
}
int fd = open(argv[1], O_RDONLY);
// 内存映射
void *p = shmat(shmid, NULL, 0);
// 从内存中读取数据写入文件
read(fd, p, 1024 * 10);
sleep(10);
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
}
-
两个没有亲缘关系的进程之间通过共享内存传递文件内容(文件内容很大,可能无法申请对应大小的共享内存),需要多次写入共享内存,编程完成整个传递过程。
提示:对共享内存进行同步访问,读写操作分别多次循环,但提前应该先写入文件总大小,以便读进程在获取文件大小的字节后退出循环。
思路
首先,我们需要创建一个共享内存区,用于存储文件内容。由于文件内容很大,可能无法申请对应大小的共享内存,我们可以分块多次写入。
确定每个块的大小,例如每个块的大小为N字节。这样,文件总大小为S字节时,需要写入共享内存的块数为ceil(S/N)。
确定每个进程的读取和写入起始位置:
写入进程从文件的起始位置开始,每次写入一个块的数据。
读取进程从共享内存的起始位置开始,每次读取一个块的数据。
具体实现过程如下:
写入进程:
打开文件,获取文件总大小S。
创建共享内存区,大小为N字节 * 块数。
将S写入共享内存的开头位置,作为文件总大小的标记。
将文件内容按块写入共享内存,直到写入完成。每次写入时,先检查当前写入位置加上块的大小是否超过了共享内存的大小,如果超过了,则只写入剩余共享内存大小的数据;否则,写入一个块的数据。
关闭文件。
读取进程:
创建共享内存区,大小为N字节 * 块数。
读取共享内存开头位置的文件总大小S,作为文件总大小的标记。
循环读取共享内存中的数据,直到读取完成或者读取的字节数等于文件总大小S。每次循环读取一个块的数据。
关闭共享内存。
写入端
#include "mysem.h"
#define PATH "/home/st"
int main(int argc, char const *argv[])
{
if (argc < 2)
{
puts("参数不够");
return -1;
}
key_t key = ftok(PATH, 666);
int exit = mysem_exit(key, 2);
int semid = mysem_create(key, 2);
mysem_init(semid, 1, 0);
mysem_init(semid, 0, 1);
int shmid = shmget(key, 1024, IPC_CREAT | SHM_R | SHM_W);
int fa = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC);
void *p = shmat(shmid, NULL, 0);
int i = 0;
mysem_p(semid, 0);
memcpy(&i, p, sizeof(int));
mysem_v(semid, 1);
printf("需要接收%d次\n", i);
int n = 0;
while (i--)
{
mysem_p(semid, 0);
char buf[1024] = {0};
memcpy(buf, p, 1024);
write(fa, buf, strlen(buf));
printf("read第%d次\n", ++n);
memset(p, 0, 1024);
mysem_v(semid, 1);
}
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
mysem_destory(semid);
return 0;
}
这段代码的功能是从共享内存中读取数据并写入到指定文件中。
代码的主要逻辑包括:
检查命令行参数数量,如果参数数量不够,则输出错误提示并返回-1。
使用ftok函数将路径和标识符转换为一个key值。
通过mysem_exit函数获取指定key值的信号量标识符。
使用mysem_create函数创建一个新的信号量并获得其标识符。
使用mysem_init函数初始化信号量,将第一个信号量的值设为1,第二个信号量的值设为0。
使用shmget函数创建或获取共享内存,在这里指定的key值和大小为1024字节。
打开指定文件,如果文件不存在则创建,如果文件已存在则清空原有内容。
使用shmat函数将共享内存连接到当前进程的地址空间,并返回指向共享内存的指针。
使用mysem_p函数对第一个信号量进行P操作,以获取读取次数。
通过memcpy函数将共享内存中的整数值读取到变量i中。
使用mysem_v函数对第二个信号量进行V操作,以释放其他进程对共享内存的访问。
打印需要接收的次数i。
在循环中,通过mysem_p函数对第一个信号量进行P操作,以获取共享内存中的数据。
将共享内存中的数据复制到缓冲区buf中。
将缓冲区buf中的数据写入到指定文件中。
打印读取次数n。
使用memset函数将共享内存清空。
使用mysem_v函数对第二个信号量进行V操作,以释放其他进程对共享内存的访问。
使用shmdt函数将共享内存与当前进程分离。
使用shmctl函数删除共享内存。
使用mysem_destory函数销毁信号量。
读取端
#include "mysem.h"
#define PATH "/home/st"
int main(int argc, char const *argv[])
{
if (argc < 2)
{
puts("参数不够");
return -1;
}
key_t key = ftok(PATH, 666);
int exit = mysem_exit(key, 2);
int semid = mysem_create(key, 2);
mysem_init(semid, 1, 0);
mysem_init(semid, 0, 1);
int shmid = shmget(key, 1024, IPC_CREAT | SHM_R | SHM_W);
int fa = open(argv[1], O_RDONLY);
off_t size = lseek(fa, 0, SEEK_END);
lseek(fa, 0, SEEK_SET);
int i = size / 1024 + 1;
printf("循环次数为%d", i);
void *p = shmat(shmid, NULL, 1);
mysem_p(semid, 0);
memcpy(&i, p, sizeof(int));
mysem_v(semid, 0);
printf("需要接收%d次\n", i);
int n = 0;
while (i--)
{
mysem_p(semid, 1);
char buf[1024] = {0};
ssize_t bytes_size = read(fa, buf, 1024);
if (bytes_size > 0)
{
memcpy(p, buf, bytes_size);
}
printf("写第%d次\n", ++n);
mysem_v(semid, 0);
}
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
mysem_destory(semid);
return 0;
}
- 两个没有亲缘关系的进程之间通过消息队列实现简单的聊天。在进程在发送“bye”数据,或者接收到“bye”数据后,结束进程,完成聊天过程。
思路
实现两个没有亲缘关系的进程之间通过消息队列实现简单的聊天可以分为以下几个步骤:
创建消息队列:在操作系统中创建一个消息队列,用于进程间通信。
创建进程1和进程2:可以使用fork()函数创建两个子进程,一个作为发送消息的进程,一个作为接收消息的进程。
进程1发送消息:进程1通过消息队列将需要发送的消息发送给进程2,可以使用msgsnd()函数实现。
进程2接收消息:进程2通过消息队列接收来自进程1发送的消息,可以使用msgrcv()函数实现。
判断消息内容并结束进程:进程2在接收到消息后,判断消息的内容是否为"bye",如果是,则结束进程2;进程1也可以在发送消息前判断是否为"bye",如果是,则结束进程1。
关闭消息队列:在聊天结束后,关闭消息队列,可以使用msgctl()函数实现。
写入端
#include "head.h"
#define PATH_NAME "/home/st/msgfile"
#define MSG_TYPE 1
typedef struct
{
long type;
char data[100];
} msg_t;
int main(int argc, char const *argv[])
{
key_t key = ftok(PATH_NAME, 123);
int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1)
{
perror("msgget failed");
return -1;
}
msg_t send_msg, recv_msg;
bool sending = false; // 初始状态为接收消息
char input[100];
while (1)
{
if (sending)
{
// 发送消息模式
printf("输入要发送的消息(键入“rec”退出发送模式) ");
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = '\0'; // 移除换行符
strcpy(send_msg.data, input);
send_msg.type = MSG_TYPE;
if (msgsnd(msgid, &send_msg, sizeof(send_msg.data), 0) == -1)
{
perror("msgsnd failed");
msgctl(msgid, IPC_RMID, NULL); // 清理资源
return -1;
}
if (strcmp(input, "bye") == 0)
{
break; // 收到'bye'消息,退出聊天
}
if (strcmp(input, "rec") == 0)
{
sending = false; // 退出发送模式,进入接收模式
}
}
else
{
// 接收消息模式
if (msgrcv(msgid, &recv_msg, sizeof(recv_msg), MSG_TYPE, 0) == -1)
{
perror("msgrcv failed");
msgctl(msgid, IPC_RMID, NULL); // 清理资源
return -1;
}
printf("Received: %s\n", recv_msg.data);
if (strcmp(recv_msg.data, "bye") == 0)
{
break; // 收到'bye'消息,退出聊天
}
// 在这里可以添加逻辑来切换到发送模式,或者保持接收模式
// 为了简化,我们让用户通过发送'send'命令来切换到发送模式
printf("输入 'send' 切换到发送模式,或输入任何其他键继续接收: ");
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = '\0'; // 移除换行符
if (strcmp(input, "send") == 0)
{
sending = true; // 切换到发送模式
}
}
}
msgctl(msgid, IPC_RMID, NULL); // 聊天结束,删除消息队列
return 0;
}
这段代码是一个简单的聊天程序,使用Linux的消息队列来实现进程间通信。
主要步骤如下:
使用ftok函数生成一个键值,用于获取消息队列的ID。
调用msgget函数创建或获取消息队列,如果失败则报错并返回。
定义两个msg_t类型的变量,用于发送和接收消息。
使用一个循环来不断接收和发送消息。
如果处于发送模式,提示用户输入消息并发送给消息队列。
如果输入的消息是"bye",则退出聊天。
如果输入的消息是"rec",则切换为接收模式。
如果处于接收模式,调用msgrcv函数接收消息。
如果接收到的消息是"bye",则退出聊天。
提示用户输入"send"切换为发送模式,否则继续保持接收模式。
循环结束后,调用msgctl函数删除消息队列。
读取端
#include "head.h"
#define PATH_NAME "/home/st/msgfile"
#define MSG_TYPE 1
typedef struct
{
long type;
char data[100];
} msg_t;
int main(int argc, char const *argv[])
{
key_t key = ftok(PATH_NAME, 123);
int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1)
{
perror("msgget failed");
return -1;
}
msg_t send_msg, recv_msg;
bool sending = false; // 初始状态为接收消息
char input[100];
while (1)
{
if (sending)
{
// 发送消息模式
printf("输入要发送的消息(键入“rec”退出发送模式) ");
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = '\0'; // 移除换行符
strcpy(send_msg.data, input);
send_msg.type = MSG_TYPE;
if (msgsnd(msgid, &send_msg, sizeof(send_msg.data), 0) == -1)
{
perror("msgsnd failed");
msgctl(msgid, IPC_RMID, NULL); // 清理资源
return -1;
}
if (strcmp(input, "bye") == 0)
{
break; // 收到'bye'消息,退出聊天
}
if (strcmp(input, "rec") == 0)
{
sending = false; // 退出发送模式,进入接收模式
}
}
else
{
// 接收消息模式
if (msgrcv(msgid, &recv_msg, sizeof(recv_msg), MSG_TYPE, 0) == -1)
{
perror("msgrcv failed");
msgctl(msgid, IPC_RMID, NULL); // 清理资源
return -1;
}
printf("Received: %s\n", recv_msg.data);
if (strcmp(recv_msg.data, "bye") == 0)
{
break; // 收到'bye'消息,退出聊天
}
// 在这里可以添加逻辑来切换到发送模式,或者保持接收模式
// 为了简化,我们让用户通过发送'send'命令来切换到发送模式
printf("输入 'send' 切换到发送模式,或输入任何其他键继续接收: ");
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = '\0'; // 移除换行符
if (strcmp(input, "send") == 0)
{
sending = true; // 切换到发送模式
}
}
}
msgctl(msgid, IPC_RMID, NULL); // 聊天结束,删除消息队列
return 0;
}
标签:练习,--,编程,int,mysem,key,进程,input,共享内存
From: https://blog.csdn.net/weixin_69851948/article/details/145213287