一、 實(shí)驗(yàn)?zāi)康?/p>
- 掌握無名管道與有名管道的進(jìn)程通信;
- 掌握消息隊(duì)列的讀寫操作;
- 掌握共享內(nèi)存的通信機(jī)制。
二、 實(shí)驗(yàn)任務(wù)與要求
- 管道讀寫程序的編寫與應(yīng)用;
- 消息隊(duì)列的發(fā)送和接收程序的編寫和應(yīng)用;
- 共享內(nèi)存的創(chuàng)建、連接和分離編程和應(yīng)用。
三、 實(shí)驗(yàn)工具和環(huán)境
PC機(jī)、Linux Ubuntu操作系統(tǒng)。
四、 實(shí)驗(yàn)內(nèi)容與結(jié)果
- 利用無名管道通信編寫程序?qū)崿F(xiàn)命令cat的功能。
7.2.1-1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
// printf("%d", argc);
if (argc < 2)
{
printf("argv lost\n");
exit(0);
}
int fd[2];
int err = pipe(fd);
if (err == -1)
{
printf("pipe err\n");
exit(0);
}
pid_t pid = fork();
if (pid == -1)
exit(0);
else if (pid == 0)
{
close(fd[1]);
char buf[256] = {0};
int size = read(fd[0], buf, 256);
if (size > 0)
printf("son --- %s\n", buf);
else
printf("son read err\n");
close(fd[0]);
exit(0);
}
else if (pid > 0)
{
close(fd[0]);
int fd2, size2;
char buf2[256];
fd2 = open(argv[1], O_RDONLY);
if (fd2)
{
size2 = read(fd2, buf2, 256);
write(fd[1], buf2, 256);
}
close(fd2);
sleep(5);
close(fd[1]);
wait(NULL);
exit(0);
}
}
這段代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的管道通信,父進(jìn)程通過讀取文件內(nèi)容,將數(shù)據(jù)寫入管道,子進(jìn)程從管道中讀取數(shù)據(jù)并打印。其中使用了fork創(chuàng)建子進(jìn)程,pipe創(chuàng)建管道,open函數(shù)打開文件,read和write函數(shù)進(jìn)行讀寫操作。程序在父進(jìn)程中使用wait函數(shù)等待子進(jìn)程退出。
- 設(shè)計(jì)兩個(gè)程序:有名管道的讀程序和寫程序,要求利用有名管道實(shí)現(xiàn)聊天程序,每次發(fā)言后自動(dòng)在后面增加當(dāng)前系統(tǒng)時(shí)間。增加結(jié)束字符,比如最后輸入“886”后結(jié)束進(jìn)程。
寫程序
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
int main(void)
{
int fd;
int len;
char buf[PIPE_BUF];
time_t tp;
printf("I am %d\n", getpid());
if ((fd = open("fifo1", O_RDWR)) < 0)
{
perror("open");
exit(EXIT_FAILURE);
}
while (1)
{
time(&tp);
printf("\n[%d]請(qǐng)輸入文字:", getpid());
char text[256];
fgets(text, (sizeof text / sizeof text[0]), stdin);
if (strcmp(text, "886\n") == 0)
{
printf("\n886!\n");
close(fd);
exit(EXIT_SUCCESS);
}
len = sprintf(buf, "-[%d]: %s%s", getpid(), text, ctime(&tp));
if ((write(fd, buf, len)) < 0)
{
perror("write");
close(fd);
exit(EXIT_FAILURE);
}
sleep(3);
}
close(fd);
exit(EXIT_SUCCESS);
}
讀程序
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
int main(void)
{
int fd;
int len;
char buf[PIPE_BUF];
mode_t mode = 0666;
system("rm fifo1 > null");
if ((mkfifo("fifo1", mode)) < 0)
{
perror("mkfifo");
exit(EXIT_FAILURE);
}
if ((fd = open("fifo1", O_RDONLY)) < 0)
{
perror("open");
exit(EXIT_FAILURE);
}
while ((len = read(fd, buf, PIPE_BUF)) > 0)
{
printf("%s", buf);
}
close(fd);
exit(EXIT_SUCCESS);
}
這兩段代碼是一個(gè)進(jìn)程間通信的例子,使用了命名管道(FIFO)來實(shí)現(xiàn)。第一個(gè)程序是寫程序,不斷從命令行讀取用戶輸入的文字,將其和當(dāng)前時(shí)間一起發(fā)送到命名管道中。第二個(gè)程序是讀程序,不斷從命名管道中讀取數(shù)據(jù)并輸出到控制臺(tái)。通過命名管道,實(shí)現(xiàn)了兩個(gè)進(jìn)程之間的通信。其中,mkfifo函數(shù)用于創(chuàng)建命名管道,open函數(shù)用于打開命名管道,read和write函數(shù)用于讀寫數(shù)據(jù),close函數(shù)用于關(guān)閉文件描述符。
- 設(shè)計(jì)一個(gè)程序,要求用函數(shù)msgget創(chuàng)建消息隊(duì)列,從鍵盤輸入的字符串添加到消息隊(duì)列,然后應(yīng)用函數(shù)msgrcv讀取隊(duì)列中的消息并在計(jì)算機(jī)屏幕上輸出。程序先調(diào)用msgget函數(shù)創(chuàng)建、打開消息隊(duì)列,接著調(diào)用msgsnd函數(shù),把輸入的字符串添加到消息隊(duì)列中,然后調(diào)用msgrcv函數(shù),讀取消息隊(duì)列中的消息并打印輸出,最后調(diào)用msgctl函數(shù),刪除系統(tǒng)內(nèi)核中的消息隊(duì)列。
// q3reader.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st
{
long int my_msg_type;
char some_text[BUFSIZ];
};
int main(void)
{
int running = 1;
int msgid;
struct my_msg_st some_data; // 定義消息變量
long int msg_to_receive = 0;
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if (msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
/*創(chuàng)建消息隊(duì)列*/
/*循環(huán)從消息隊(duì)列中接收消息*/
while (running)
{
/*讀取消息*/
if (msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)
{
fprintf(stderr, "msgrcv failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s", some_data.some_text);
/*接收到的消息為“end”時(shí)結(jié)束循環(huán)*/
if (strncmp(some_data.some_text, "end", 3) == 0)
{
running = 0;
}
}
/*從系統(tǒng)內(nèi)核中移走消息隊(duì)列*/
if (msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
// q3writer.c
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st
{
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main(void)
{
int running = 1;
struct my_msg_st some_data;
int msgid;
char buffer[BUFSIZ];
/*創(chuàng)建消息隊(duì)列*/
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if (msgid == -1)
{
fprintf(stderr, "msgget failed with error:%d\n", errno);
exit(EXIT_FAILURE);
}
while (running)
{ /*循環(huán)向消息隊(duì)列中添加消息*/
printf("Enter some text:");
fgets(buffer, BUFSIZ, stdin); // 從標(biāo)準(zhǔn)輸入文件讀取字符串賦給buffer
some_data.my_msg_type = 1;
strcpy(some_data.some_text, buffer); // buffer的內(nèi)容復(fù)制給消息
/*添加消息*/
if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1)
{
fprintf(stderr, "msgsed failed\n");
exit(EXIT_FAILURE);
}
/*用戶輸入的為“end”時(shí)結(jié)束循環(huán)*/
if (strncmp(buffer, "end", 3) == 0)
{
running = 0;
}
}
exit(EXIT_SUCCESS);
}
這兩段代碼分別實(shí)現(xiàn)了消息隊(duì)列的讀和寫操作。其中,q3reader.c創(chuàng)建了一個(gè)消息隊(duì)列,并通過循環(huán)從中接收消息,如果接收到的消息為“end”,則結(jié)束程序;而q3writer.c循環(huán)向消息隊(duì)列中添加消息,如果用戶輸入的消息為“end”,則結(jié)束程序。兩段代碼都使用了結(jié)構(gòu)體my_msg_st來定義消息,其包含了消息類型my_msg_type和消息內(nèi)容some_text。在創(chuàng)建/添加消息的時(shí)候,需要使用msgsnd/mssgrcv函數(shù),并將my_msg_st作為參數(shù)傳遞進(jìn)去。同時(shí),需要使用msgget函數(shù)獲取消息隊(duì)列的ID,并使用msgctl函數(shù)移走消息隊(duì)列。
- 設(shè)計(jì)兩個(gè)程序要求用消息隊(duì)列實(shí)現(xiàn)簡(jiǎn)單的聊天功能。
// q3reader.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st
{
long int my_msg_type;
char some_text[BUFSIZ];
};
int main(void)
{
int running = 1;
int msgid;
struct my_msg_st some_data; // 定義消息變量
long int msg_to_receive = 0;
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if (msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
/*創(chuàng)建消息隊(duì)列*/
/*循環(huán)從消息隊(duì)列中接收消息*/
while (running)
{
/*讀取消息*/
if (msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)
{
fprintf(stderr, "msgrcv failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s", some_data.some_text);
/*接收到的消息為“end”時(shí)結(jié)束循環(huán)*/
if (strncmp(some_data.some_text, "end", 3) == 0)
{
running = 0;
}
}
/*從系統(tǒng)內(nèi)核中移走消息隊(duì)列*/
if (msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
// q3writer.c
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st
{
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main(void)
{
int running = 1;
struct my_msg_st some_data;
int msgid;
char buffer[BUFSIZ];
/*創(chuàng)建消息隊(duì)列*/
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if (msgid == -1)
{
fprintf(stderr, "msgget failed with error:%d\n", errno);
exit(EXIT_FAILURE);
}
while (running)
{ /*循環(huán)向消息隊(duì)列中添加消息*/
printf("Enter some text:");
fgets(buffer, BUFSIZ, stdin); // 從標(biāo)準(zhǔn)輸入文件讀取字符串賦給buffer
some_data.my_msg_type = 1;
strcpy(some_data.some_text, buffer); // buffer的內(nèi)容復(fù)制給消息
/*添加消息*/
if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1)
{
fprintf(stderr, "msgsed failed\n");
exit(EXIT_FAILURE);
}
/*用戶輸入的為“end”時(shí)結(jié)束循環(huán)*/
if (strncmp(buffer, "end", 3) == 0)
{
running = 0;
}
}
exit(EXIT_SUCCESS);
}
- 在主程序中先調(diào)用shmget函數(shù)創(chuàng)建一個(gè)共享內(nèi)存,得到共享內(nèi)存的id,然后利用shmat函數(shù)將創(chuàng)建的共享內(nèi)存連接到一個(gè)進(jìn)程的地址空間,返回值為該內(nèi)存空間的地址指針,利用地址指針對(duì)共享內(nèi)存進(jìn)行訪問;最后利用shmdt函數(shù)分離進(jìn)程和共享內(nèi)存。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define BUFSZ 4096
int main(int argc, char *argv[])
{
int shm_id;
char *shm_buf;
key_t key;
system("touch shmfile");
// 生成一個(gè)共享內(nèi)存段的唯一鍵
key = ftok("shmfile", 65);
if (key == -1)
{
perror("ftok");
exit(1);
}
// 用鍵和共享內(nèi)存段的大小創(chuàng)建一個(gè)共享內(nèi)存段并獲取它的 ID
shm_id = shmget(key, BUFSZ, 0666 | IPC_CREAT);
if (shm_id < 0)
{
perror("shmget");
exit(1);
}
printf("successfully created segment: %d \n", shm_id);
// 將共享內(nèi)存段連接到進(jìn)程地址空間并獲取一個(gè)指向它的指針
if ((shm_buf = shmat(shm_id, NULL, 0)) == (char *)-1)
{
perror("shmat");
exit(1);
}
printf("segment attached at %p\n", shm_buf);
system("ipcs -m");
sleep(3); /*休眠*/
// 將共享內(nèi)存段從進(jìn)程地址空間分離
if ((shmdt(shm_buf)) < 0)
{
perror("shmdt");
exit(1);
}
printf("segment detached \n");
system("ipcs -m "); /*再次查看系統(tǒng)IPC狀態(tài)*/
// 刪除共享內(nèi)存段
if (shmctl(shm_id, IPC_RMID, NULL) == -1)
{
perror("shmctl");
exit(1);
}
printf("segment removed \n");
system("ipcs -m "); /*再次查看系統(tǒng)IPC狀態(tài)*/
exit(0);
}
這段代碼演示了創(chuàng)建、連接、分離、刪除共享內(nèi)存段的過程。首先使用ftok函數(shù)生成一個(gè)共享內(nèi)存段的唯一鍵,然后使用shmget函數(shù)創(chuàng)建一個(gè)共享內(nèi)存段并獲取它的ID。接著使用shmat函數(shù)將共享內(nèi)存段連接到進(jìn)程地址空間并獲取一個(gè)指向它的指針。然后可以使用shm_buf指針來讀寫共享內(nèi)存段。當(dāng)不需要使用共享內(nèi)存段時(shí),可以使用shmdt函數(shù)將它從進(jìn)程地址空間分離。最后使用shmctl函數(shù)刪除共享內(nèi)存段并釋放它的系統(tǒng)資源。在代碼中還使用了system函數(shù)調(diào)用ipcs命令來查看系統(tǒng)中的IPC狀態(tài)。
- 編寫生產(chǎn)者、消費(fèi)者程序。
(1) 消費(fèi)者程序中創(chuàng)建一個(gè)共享內(nèi)存段,并將其中的內(nèi)容顯示出來;
消費(fèi)者程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024 // 共享內(nèi)存大小
#define SHM_KEY 1234 // 共享內(nèi)存鍵值
int main()
{
int shmid;
char *shmaddr;
// 連接到已有的共享內(nèi)存段并獲取其地址
shmid = shmget(SHM_KEY, SHM_SIZE, 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)(-1)) {
perror("shmat");
exit(1);
}
printf("Content of shared memory:\n%s", shmaddr); // 顯示共享內(nèi)存中的內(nèi)容
// 斷開共享內(nèi)存連接
shmdt(shmaddr);
return 0;
}
(2) 生產(chǎn)者連接到一個(gè)已有的共享內(nèi)存段,并允許向其中寫入數(shù)據(jù)。
生產(chǎn)者程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024 // 共享內(nèi)存大小
#define SHM_KEY 1234 // 共享內(nèi)存鍵值
int main()
{
int shmid;
char *shmaddr;
char buffer[256];
// 創(chuàng)建共享內(nèi)存段
shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
// 連接到共享內(nèi)存段并獲取其地址
shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)(-1)) {
perror("shmat");
exit(1);
}
while (1) {
printf("Enter message: ");
fgets(buffer, sizeof(buffer), stdin);
strncpy(shmaddr, buffer, SHM_SIZE); // 寫入共享內(nèi)存
}
// 斷開共享內(nèi)存連接
shmdt(shmaddr);
// 刪除共享內(nèi)存段
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
生產(chǎn)者程序使用 shmget()
函數(shù)創(chuàng)建了一個(gè)大小為 SHM_SIZE
的共享內(nèi)存段,并使用 shmat()
函數(shù)將其連接到進(jìn)程的虛擬地址空間,從而獲取其地址指針 shmaddr
。然后通過 fgets()
函數(shù)從標(biāo)準(zhǔn)輸入讀取字符串,再使用 strncpy()
將其寫入共享內(nèi)存中。這個(gè)過程循環(huán)執(zhí)行,直到程序結(jié)束。最后使用 shmdt()
斷開共享內(nèi)存連接,使用 shmctl()
刪除共享內(nèi)存段。
消費(fèi)者程序先使用 shmget()
函數(shù)連接到已有的共享內(nèi)存段,并使用 shmat()
函數(shù)將其連接到進(jìn)程的虛擬地址空間,從而獲取其地址指針 shmaddr
。然后通過 printf()
打印共享內(nèi)存中的內(nèi)容。最后使用 shmdt()
斷開共享內(nèi)存連接。文章來源:http://www.zghlxwxcb.cn/news/detail-466175.html
五、 實(shí)驗(yàn)總結(jié)文章來源地址http://www.zghlxwxcb.cn/news/detail-466175.html
到了這里,關(guān)于《嵌入式系統(tǒng)開發(fā)實(shí)踐》實(shí)驗(yàn)三 進(jìn)程通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!