Linux C下实现进程间通信

进程间通信概述

在Linux C下,进程间通信(Inter-Process Communication,IPC)是实现不同进程之间数据传递和同步的机制。进程间通信可以在同一台计算机上的不同进程之间进行,也可以在不同计算机上的进程之间进行。

进程间通信在操作系统中起着非常重要的作用,它可以使不同的进程能够互相协作并共享资源。Linux提供了多种可用的进程间通信机制,包括管道、消息队列、共享内存、信号量和套接字等。每种机制都有自己的特点和适用场景。

管道(Pipe)

管道是一种最基本的进程间通信机制,它是一个单向的字节流管道。在Linux C中,可以使用pipe系统调用来创建管道。

管道一般用于有亲缘关系的进程之间通信,其中一个进程作为管道的输入方,另一个进程作为管道的输出方。管道的数据传输是通过内核缓冲区来实现的。

创建管道

int pipe(int pipefd[2]);

pipefd是一个长度为2的整数数组,其中pipefd[0]用于读取数据,pipefd[1]用于写入数据。

使用管道进行通信

创建管道后,父进程和子进程可以通过pipefd[0]和pipefd[1]进行数据的读取和写入。

int pipefd[2];

char buffer[100];

if(pipe(pipefd) == -1){

perror("pipe");

exit(EXIT_FAILURE);

}

int pid = fork();

if(pid == -1){

perror("fork");

exit(EXIT_FAILURE);

}

if(pid == 0){ // 子进程

close(pipefd[0]); // 关闭读取端

// 在写入端写入数据

write(pipefd[1], "Hello from child!", 17);

close(pipefd[1]); // 关闭写入端

exit(EXIT_SUCCESS);

} else { // 父进程

close(pipefd[1]); // 关闭写入端

// 在读取端读取数据

read(pipefd[0], buffer, sizeof(buffer));

printf("Received: %s\n", buffer);

close(pipefd[0]); // 关闭读取端

exit(EXIT_SUCCESS);

}

上面的示例代码中,父进程创建了一个管道后,fork出了一个子进程。子进程在写入端写入了字符串"Hello from child!",而父进程在读取端读取了子进程写入的数据,并打印出来。

管道的使用需要注意的一点是,如果写入端没有关闭,读取端将会一直阻塞等待数据的到来,而如果读取端没有关闭,写入端将会一直阻塞等待读取端读取数据。

消息队列(Message Queue)

消息队列是一种消息的链表,每个链表节点都有一个消息类型和消息数据。Linux C中使用msgget、msgsnd和msgrcv等系统调用来创建和操作消息队列。

消息队列适用于需要按照消息的先后顺序进行通信的场景,可以实现多个进程之间的同步与异步通信。

创建消息队列

int msgget(key_t key, int msgflg);

key是一个表示消息队列ID的唯一键值,msgflg表示创建消息队列的标志。

发送和接收消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msqid是消息队列ID,msgp是指向消息的指针,msgsz是消息的长度,msgflg是发送消息的标志。

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

msqid是消息队列ID,msgp是指向消息的指针,msgsz是接收消息的长度,msgtyp是接收消息的类型,msgflg是接收消息的标志。

使用消息队列进行通信

创建消息队列后,进程可以使用msgsnd发送消息,其他进程可以使用msgrcv接收消息。消息队列中的每个消息都有一个消息类型,接收消息时可以指定类型来选择接收哪些消息。

key_t key = ftok("/tmp/msgqueue", 'A');

int msqid = msgget(key, 0666 | IPC_CREAT);

if(msqid == -1){

perror("msgget");

exit(EXIT_FAILURE);

}

struct message{

long msg_type;

char msg_text[100];

};

struct message msg;

msg.msg_type = 1;

strcpy(msg.msg_text, "Hello from parent!");

if(msgsnd(msqid, &msg, sizeof(msg.msg_text), 0) == -1){

perror("msgsnd");

exit(EXIT_FAILURE);

}

if(msgrcv(msqid, &msg, sizeof(msg.msg_text), 1, 0) == -1){

perror("msgrcv");

exit(EXIT_FAILURE);

}

printf("Received: %s\n", msg.msg_text);

上面的示例代码中,创建了一个消息队列,发送了一个消息类型为1的消息,并从消息队列中接收了类型为1的消息。

共享内存(Shared Memory)

共享内存是一种最高效的进程间通信方式,通过将一块内存区域映射到多个进程的虚拟地址空间中,实现不同进程之间的数据共享。

共享内存适用于需要频繁交换大量数据的场景,可以通过直接读写内存来完成数据交换,不需要通过内核进行复制。

创建共享内存

int shmget(key_t key, size_t size, int shmflg);

key是共享内存ID的唯一键值,size是共享内存的大小,shmflg表示共享内存的标志。

映射和解除映射

void *shmat(int shmid, const void *shmaddr, int shmflg);

int shmdt(const void *shmaddr);

shmat函数将共享内存映射到进程的虚拟地址空间中,shmaddr为NULL时系统自动选择一个合适的地址。shmdt函数解除共享内存的映射。

映射后的共享内存可以直接读写,通过指针来访问共享内存的数据。

使用共享内存进行通信

key_t key = ftok("/tmp/sharedmemory", 'A');

int shmid = shmget(key, sizeof(int), 0666 | IPC_CREAT);

if(shmid == -1){

perror("shmget");

exit(EXIT_FAILURE);

}

int *shared_data = (int *)shmat(shmid, NULL, 0);

*shared_data = 123;

shmdt(shared_data);

上面的示例代码中,创建了一块共享内存,将其映射到进程的虚拟地址空间后,可以通过shared_data指针来访问共享内存中的数据。这里将共享内存的值设置为123后,使用shmdt解除了共享内存的映射。

信号量(Semaphore)

信号量是一种用于实现进程同步和互斥的机制,它可以通过记录某个资源的使用情况来控制进程的行为。

信号量适用于需要临界区保护的场景,可以防止多个进程同时访问共享资源。

创建信号量

int semget(key_t key, int nsems, int semflg);

key是信号量ID的唯一键值,nsems是信号量的数量,semflg表示信号量的标志。

操作信号量

int semop(int semid, struct sembuf *sops, unsigned int nsops);

semid是信号量ID,sops是一个指向sembuf结构体数组的指针,nsops是sops数组的长度。

sembuf结构体定义如下:

struct sembuf {

unsigned short sem_num; // 信号量的编号

short sem_op; // 操作类型(增加、减少等)

short sem_flg; // 操作标志(IPC_NOWAIT等)

};

使用信号量进行同步与互斥

key_t key = ftok("/tmp/semaphore", 'A');

int semid = semget(key, 1, 0666 | IPC_CREAT);

if(semid == -1){

perror("semget");

exit(EXIT_FAILURE);

}

struct sembuf sop;

sop.sem_num = 0;

sop.sem_op = -1; // P操作,减1

sop.sem_flg = 0;

if(semop(semid, &sop, 1) == -1){

perror("semop");

exit(EXIT_FAILURE);

}

// 临界区代码,对共享资源进行操作

sop.sem_op = 1; // V操作,加1

if(semop(semid, &sop, 1) == -1){

perror("semop");

exit(EXIT_FAILURE);

}

上面的示例代码中,创建了一个信号量,使用semop函数对信号量进行操作。其中,sop.sem_num为0表示操作第一个信号量,sop.sem_op为-1表示进行P操作(减1),sop.sem_flg为0表示没有特殊标志。

套接字(Socket)

套接字是一种用于实现进程间网络通信的机制,它可以通过网络传输数据来进行进程间通信。

套接字适用于在网络中的不同计算机上的进程之间通信,可以实现跨越网络的数据传递和同步。

创建套接字

int socket(int domain, int type, int protocol);

domain表示网络通信的协议族,type表示套接字的类型,protocol表示具体的协议。

绑定地址和端口

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd是套接字的文件描述符,addr是一个指向存放地址信息的结构体的指针,addrlen是地址的长度。

监听和接受连接

int listen(int sockfd, int backlog);

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

listen用于将套接字设置为监听状态,backlog表示最大的连接请求队列长度。

accept用于接受客户端的连接请求,返回一个新的套接字文件描述符用于后续通信。

发送和接收数据

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

send用于发送数据,buf为要发送的数据指针,len为数据的长度,flags为发送标志。

recv用于接收数据,buf为接收缓冲区,len为接收缓冲区的大小,flags为接收标志。

使用套接字进行网络通信

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

if(sockfd == -1){

perror("socket");

exit(EXIT_FAILURE);

}

struct sockaddr_in serv_addr;

serv_addr.sin_family = AF_INET;

serv_addr.sin_addr.s_addr = INADDR_ANY;

serv_addr.sin_port = htons(8080);

if(bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1){

perror("bind");

exit(EXIT_FAILURE);

}

if(listen(sockfd, 5) == -1){

perror("listen");

exit(EXIT_FAILURE);

}

int newsockfd = accept(sockfd, NULL, NULL);

if(newsockfd == -1){

perror("accept");

exit(EXIT_FAILURE);

}

char buffer[100] = "Hello from server!";

if(send(newsockfd, buffer, sizeof(buffer), 0) == -1){

perror("send");

exit(EXIT_FAILURE);

}

if(recv(newsockfd, buffer, sizeof(buffer), 0) == -1){

perror("recv");

exit(EXIT_FAILURE);

}

printf("Received: %s\n", buffer);

close(newsockfd);

close(sockfd);

上面的示例代码中,创建了一个套接字并将其绑定到8080端口上。通过listen和accept函数实现了服务器的监听和接受连接。发送和接收数据的过程使用了send和recv函数。

总结

Linux C下的进程间通信提供了多种机制,包括管道、消息队列、共享内存、信号量和套接字等。每种机制在不同的场景下有不同的特点和适用性。

管道适用于有亲缘关系的进程之间的数据传递。消息队列适用于需要按照消息的先后顺序进行通信的场景。共享内存适用于需要频繁交换大量数据的场景。信号量适用于需要临界区保护的场景。套接字适用于在网络中的不同计算机上的进程之间进行通信。

根据具体的应用场景和需求,可以选择适合的进程间通信机制来实现进程间的数据传递和同步。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

操作系统标签