概述
进程间通信
1.管道
1.方法
#include <unistd.h>
int pipe(int pipefd[2]);
管道是半双工的,只能在具有公共祖先的进程间使用。
pipefd[2]:传入大小为2的int数组,返回两个文件描述符,pipefd[0]为读端,pipefd[1]为写端。
2.实例
1.创建一个管道,用于父子进程间数据传输。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <wait.h>
#include <string.h>
#define BUF_SIZE 20
int main(int argc, char const *argv[])
{
int pipefd[2];
pid_t pid;
int err;
char buf[BUF_SIZE];
err = pipe(pipefd);
if(err < 0){
perror("pipe()");
exit(1);
}
pid = fork();
if(pid < 0){
perror("fork");
exit(1);
}
if(pid > 0){//parent
close(pipefd[0]);//父进程关闭读端
write(pipefd[1], "Hellon", 7);//往管道写端写入数据
waitpid(pid, NULL, 0);//0,表示父进程等待子进程终止
}else{
close(pipefd[1]);//子进程关闭写端
read(pipefd[0], buf, BUF_SIZE);//从管道读端读出数据
puts(buf);
}
exit(0);
}
2.使用管道实现父子进程间同步,如顺序输出abababab。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <wait.h>
#include <string.h>
static int fd_c[2], fd_p[2];
static int tell_wait()
{
if (pipe(fd_c) < 0 || pipe(fd_p) < 0)
return -1;
return 0;
}
static void wait_child()
{
char c;
//读子进程管道,如果管道中没有数据,父进程将会阻塞在这儿,直到子进程往管道中写入数据
if (read(fd_c[0], &c, 1) != 1)
{
perror("read fd_c");
exit(1);
}
if (c != 'c')
{
fprintf(stderr, "wait child: incorrect data.n");
}
}
static void tell_parent()
{
//往子进程管道中写入数据,父进程将会解除阻塞
if (write(fd_c[1], "c", 1) != 1)
{
perror("write fd_c");
exit(1);
}
}
static void wait_parent()
{
char c;
if (read(fd_p[0], &c, 1) != 1)
{
perror("read fd_p");
exit(1);
}
if (c != 'p')
{
fprintf(stderr, "wait parent: incorrect data.n");
}
}
static void tell_child()
{
if (write(fd_p[1], "p", 1) != 1)
{
perror("write fd_p");
exit(1);
}
}
int main(int argc, char const *argv[])
{
int err;
pid_t pid;
err = tell_wait();
int i = 0;
if (err < 0)
{
fprintf(stderr, "tell_wait()n");
exit(err);
}
pid = fork();
if (pid < 0)
{
perror("fork()");
exit(1);
}
if (pid > 0)
{
while (i < 10)
{
putchar('a');
fflush(stdout);//由于标准输出时行缓冲模式,所以每输出一个字节,需要flush一下
sleep(1);
tell_child();
wait_child();
i++;
}
wait(NULL);//等待子进程执行完,回收
puts("");//打印换行符
}
else
{
while (i < 10)
{
wait_parent();
putchar('b');
fflush(stdout);
sleep(1);
tell_parent();
i++;
}
}
return 0;
}
运行结果:
shbj@ubuntu:~/develop/workspace/c/ipc$ ./pipe2
abababababababababab
2.FIFO
通FIFO文件,不相关的进程也能交换数据。
1.方法
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
pathname : 指定FIFO文件的创建路径。
mode : FIFO文件权限。
2.实例
1.通过FIFO,将实现文件的复制,并将复制文件中的小写字母转换为大写字母。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <wait.h>
#define PATH "/tmp/fifo1"
#define BUF_SIZE 1024
static int dest_fd1, sou_fd, fifo_fd;
static int i;
static char buf[BUF_SIZE];
static void write_fun(const char *path)
{
char c;
fifo_fd = open(PATH, O_WRONLY);//以只写形式打开管道
int rang;
if (fifo_fd < 0)
{
perror("open fifo");
exit(1);
}
sou_fd = open(path, O_RDONLY);
if (sou_fd < 0)
{
perror("open source");
exit(1);
}
rang = 'z' - 'Z';
while ((i = read(sou_fd, &c, 1)) > 0)//从原文件中一个字节一个字节的读出数据
{
if (c < 'z' && c > 'a')//判断是否为小写字母
c -= rang;//转换为大写
write(fifo_fd, &c, i);//写入管道
}
close(fifo_fd);
close(sou_fd);
}
static void read_fun(const char *path)
{
fifo_fd = open(PATH, O_RDONLY);//以只读形式打开管道
if (fifo_fd < 0)
{
perror("open fifo");
exit(1);
}
dest_fd1 = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0660);
if (dest_fd1 < 0)
{
perror("open dest");
exit(1);
}
while ((i = read(fifo_fd, buf, BUF_SIZE)) > 0)//从管道中读出数据
{
write(dest_fd1, buf, i);//写入目标文件
}
close(fifo_fd);
close(dest_fd1);
}
int main(int argc, char const *argv[])
{
pid_t pid;
int err, i;
if (access(PATH, F_OK) < 0)
{
err = mkfifo(PATH, 0660);
if (err < 0)
{
perror("mkfifo()");
exit(1);
}
}
for (i = 0; i < 2; i++)//创建两个子进程
{
if ((pid = fork()) < 0)
{
perror("fork()");
exit(1);
}
if (pid == 0)
{
if (i == 0){//一号子进程写管道
sleep(2);
write_fun(argv[1]);
exit(0);
}
else if (i == 1){//二号子进程读管道
sleep(2);
read_fun(argv[2]);
exit(0);
}
}
}
//由于父进程没有调用wait()等待回收子进程,所以当父进程退出后,子进程会变成孤儿进程,子进程会被init进程托管。
return 0;
}
3.消息队列
1.方法
#include <sys/types.h>
#include <sys/ipc.h>
//根据一个指定的文件路径,得到一个key,主要是利用文件i_node的唯一性,使key也唯一。
key_t ftok(const char *pathname, int proj_id);
path:文件路径。
proj_id:相当于一个hash的混淆值,一般传入一个字符。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//根据ftok返回的key,得到id。
int msgget(key_t key, int msgflg);
key:ftok返回的key;如果是有亲缘关系进程间可以使用IPC_PRIVATE。
msgflg:接收端,使用和文件一样的权限位(如0660),如果消息队列以前不存在需要或上IPC_CREATE(0660 |IPC_CREATE);发送端,一般设为0就好。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msgp:传输数据的指针。
msgsz:数据的大小,需要减去mtype的大小,sizeof(buf) - sizeof(long);
msgflg:
0:接收端,msgrcv将会阻塞,直到队列中有msgtyp对应的消息;
发送端,当消息队列满时,msgsnd将会阻塞,直到有消息被取走。
IPC_NOWAIT:接收端,没有对应消息,msgrcv不等待,立即返回;
发送端,当消息队列满时,msgsnd不等待,立即返回。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
cmd:IPC_STAT、IPC_SET、IPC_RMID。
buf:如果不关心,可以传NULL。
2.实例
1.两个不相关进程间数据传输。
//头文件
#ifndef MSG1_H__
#define MSG1_H__
#define PATH "/etc/services"
#define PROJ_ID 'a'
struct stu_st
{
char name[20];
int math;
int chinese;
};
struct msg_stu
{
long mtype;
struct stu_st st;
};
#endif
//接收端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#include "msg1.h"
int main(int argc, char const *argv[])
{
key_t key;
int id;
int err;
struct msg_stu *stp;
key = ftok(PATH, PROJ_ID);
if (key < 0)
{
perror("ftok()");
exit(1);
}
id = msgget(key, 0660 | IPC_CREAT);
if (id < 0)
{
perror("msgget()");
exit(1);
}
stp = malloc(sizeof(*stp));
while((err = msgrcv(id, stp, sizeof(*stp) - sizeof(long), 1, 0)) < 0){
if(err != EINTR){
perror("msgrcv()");
msgctl(id, IPC_RMID, NULL);
exit(1);
}
}
msgctl(id, IPC_RMID, NULL);
printf("name: %s, chinese : %d, math : %dn",
stp->st.name, stp->st.chinese, stp->st.math);
return 0;
}
//发送端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#include "msg1.h"
int main(int argc, char const *argv[])
{
key_t key;
int id;
int err;
struct msg_stu *stp;
key = ftok(PATH, PROJ_ID);
if (key < 0)
{
perror("ftok()");
exit(1);
}
id = msgget(key, 0);
if (id < 0)
{
perror("msgget()");
exit(1);
}
stp = malloc(sizeof(*stp));
stp->mtype = 1;
strcpy(stp->st.name, "zhangsan");
stp->st.math = 90;
stp->st.chinese = 80;
//如果被信号打断在重新发送
while ((err = msgsnd(id, stp, sizeof(*stp) - sizeof(long), 0)) < 0)
{
if (err != EINTR)
{
perror("msgrcv()");
exit(1);
}
}
return 0;
}
4.信号量
1.方法
1.获取信号量列表。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
nsems:信号量列表中信号量的个数。
semflg:相当于文件的权限位;亲缘关系的进程(如父子进程)key可为IPC_PRIVATE,semflg可以不或上IPC_CREATE,其余需要,如0660|IPC_CREATE。
2.控制信号量列表。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
semid:信号量id。
semnum:哪一个信号量,信号量下标。
cmd:命令,IPC_STAT、IPC_SET、IPC_RMID、GETALL、GETVAL、SETALL、SETVAL。
返回值:根据命令的不同返回值不同。
3.操作信号量列表。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
sops:有如下几个成员
sem_num,信号量编号;
sem_op,操作,正数+,负数-;
sem_flg,选项;
nsops:sops的大小。
2.实例
1.多进程间的顺序累加。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <fcntl.h>
#include <wait.h>
#include <errno.h>
#define BUFFSIZE 10
#define PROCNUM 20
static int sem_id;
static void P()
{
struct sembuf buf;
buf.sem_num = 0;//下标为0的信号量
buf.sem_op = -1;//信号减1
buf.sem_flg = 0;//无特殊需求
while (semop(sem_id, &buf, 1) < 0)//操作信号量列表。总的又几个信号量,只有1个信号量
{
if (errno == EINTR || errno == EAGAIN)
continue;
perror("semop()");
exit(1);
}
}
static void V()
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;//信号加1
buf.sem_flg = 0;
while (semop(sem_id, &buf, 1) < 0)
{
if (errno == EINTR || errno == EAGAIN)
continue;
perror("semop()");
exit(1);
}
}
int main(int argc, char const *argv[])
{
FILE *fp;
char buf[BUFFSIZE];
int num;
int i;
pid_t pid;
//key_t key;
//key = ftok("/etc/services", 'a');//无亲缘关系的进程,需要获取一个key
//sem_id = semget(key, 1, 0660 | IPC_CREATE);//0660 | IPC_CREATE0660
sem_id = semget(IPC_PRIVATE, 1, 0660); //无亲缘关系的进程key设为IPC_PRIVATE,获取信号量列表
if (sem_id < 0)
{
perror("semget()");
exit(1);
}
if (semctl(sem_id, 0, SETVAL, 1) < 0)//初始化列表中下标为0的信号量,初始值为1。
{
perror("semctl()");
exit(1);
}
for (i = 0; i < PROCNUM; i++)//创建多个进程,对指定文件中的数据累加
{
pid = fork();
if (pid < 0)
{
perror("fork()");
exit(1);
}
if (pid == 0)
{
fp = fopen("/tmp/out", "r+");
if (fp == NULL)
{
perror("fopen()");
exit(1);
}
P();//获取信号量,信号量为0时阻塞
fgets(buf, BUFFSIZE, fp);
fseek(fp, 0, SEEK_SET);
num = atoi(buf);
fprintf(fp, "%dn", ++num);
fflush(fp);
sleep(1);
V();//归还信号量,信号量+1
fclose(fp);
exit(0);
}
}
for (i = 0; i < PROCNUM; i++)
wait(NULL);//回收子进程
semctl(sem_id, 0, IPC_RMID);
return 0;
}
执行结果:
hbj@ubuntu:~/develop/workspace/c/ipc/sem$ echo 10 > /tmp/out
shbj@ubuntu:~/develop/workspace/c/ipc/sem$ ./reciver
shbj@ubuntu:~/develop/workspace/c/ipc/sem$ cat /tmp/out
30
shbj@ubuntu:~/develop/workspace/c/ipc/sem$
5.共享存储
共享存储允许两个或多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC。
共享存储与内存映射的区别,共享存储没有相关的文件,共享存储段是内存的匿名段。
1.方法
1.创建共享存储。
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
size:存储区大小。
shmflg:权限位。
返回值:成功返回共享存储id,失败-1。
2.操作共享存储。
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
cmd:操作命令;IPC_STAT、IPC_SET、IPC_RMID
buf:cmd为IPC_STAT,将对应共享存储的shmid_ds结构,存储在buf指向的结构中;
cmd为IPC_SET,按照buf指向结构中的值,设置共享存储对应的shmid_ds结构。
3.共享存储连接到调用进程的哪个地址上。
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);//shmid_ds中的shm_nattach +1
int shmdt(const void *shmaddr);//shmid_ds中的shm_nattach -1
shmid:共享存储的id。
shmaddr:指定的地址,一般设为0,由系统自动分配。
shmflg:一般也设为0。
进程中,当共享存储段的操作结束时,调用shmdt与该段分离。注意,调用shmdt并不删除共享存储,只有当某个进程(一般是服务进程)调用了带IPC_RMID命令的shm_ctl特地删除它为止。
2.实例
1.使用共享存储实现不同进程间的数据传输。
#ifndef PROTO_H__
#define PROTO_H__
#define PATH "/etc/services"
#define PROJ_ID 'a'
struct stu_st
{
char name[20];
int math;
int chinese;
};
struct msg_stu
{
long mtype;
struct stu_st st;
};
#endif
//接收端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include "proto.h"
#define PATH "/etc/services"
#define SHM_SIZE 1024
int main(int argc, char const *argv[])
{
key_t key;
int shmid, err;
struct stu_st *stp;
key = ftok(PATH, 'a');
if (key < 0)
{
perror("ftok()");
exit(1);
}
shmid = shmget(key, SHM_SIZE, 0660 | IPC_CREAT);
if (shmid < 0)
{
perror("shmget()");
exit(1);
}
stp = shmat(shmid, 0, 0);
if (stp < 0)
{
perror("shmat()");
exit(1);
}
sleep(10);
printf("%s, %d, %dn", stp->name, stp->chinese, stp->math);
strcpy(stp->name, "lisi");
sleep(10);
err = shmdt(stp);
if (err < 0)
{
perror("shmdt()");
exit(1);
}
err = shmctl(shmid, IPC_RMID, NULL);
if (err < 0)
{
perror("shmctl()");
exit(1);
}
return 0;
}
//发送端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include "proto.h"
#define PATH "/etc/services"
#define SHM_SIZE 1024
int main(int argc, char const *argv[])
{
key_t key;
int shmid, err;
struct stu_st *stp;
key = ftok(PATH, 'a');
shmid = shmget(key, SHM_SIZE, 0660);
stp = (struct stu_st *)shmat(shmid, 0, 0);
strcpy(stp->name, "zhangsan");
stp->chinese = 90;
stp->math = 95;
sleep(10);
printf("%s, %d, %dn", stp->name, stp->chinese, stp->math);
err = shmdt(stp);
return 0;
}
2.改进1程序,实现两个进程间的数据同步,即同时只能有一个进程操作共享存储区。
采用信号量实现同步。
//同步代码
//proto.h
#ifndef PROTO_H__
#define PROTO_H__
#define PATH "/etc/services"
#define PROJ_ID 'a'
struct stu_st
{
char name[20];
int math;
int chinese;
};
struct msg_stu
{
long mtype;
struct stu_st st;
};
void sem_init();
void sem_destroy();
void P1();
void V1();
void P2();
void V2();
#endif
//proto.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include "proto.h"
static int semid;
void sem_init()
{
key_t key;
key = ftok(PATH, PROJ_ID);
if (key < 0)
{
perror("ftok()");
exit(1);
}
//判断信号量是否创建,创建两个信号量,0信号用于接收端,1信号用于发送端
if ((semid = semget(key, 2, 0660)) < 0)
{
semid = semget(key, 2, 0660 | IPC_CREAT);
}
if (semid < 0)
{
perror("semget()");
exit(1);
}
if (semctl(semid, 0, SETVAL, 0) < 0)//信号量初值都设为0
{
perror("semctl()");
exit(1);
}
if (semctl(semid, 1, SETVAL, 0) < 0)
{
perror("semctl()");
exit(1);
}
}
void sem_destroy(){
semctl(semid, 0, IPC_RMID);
}
//0信号量,初值为0,接收端进程会阻塞
void P1()
{
struct sembuf buf;
buf.sem_num = 0;//0信号
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
while (semop(semid, &buf, 1) < 0)
{
if (errno == EINTR || errno == EAGAIN)
continue;
perror("semop_p1()");
exit(1);
}
}
//0信号量+1,唤醒接收端进程
void V1()
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
while (semop(semid, &buf, 1) < 0)
{
if (errno == EINTR || errno == EAGAIN)
continue;
perror("semop_v1()");
exit(1);
}
}
//1信号量,初值为0,发送端进程会阻塞
void P2()
{
struct sembuf buf;
buf.sem_num = 1;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
while (semop(semid, &buf, 1) < 0)
{
if (errno == EINTR || errno == EAGAIN)
continue;
perror("semop_p2()");
exit(1);
}
}
//1信号量+1,唤醒发送端进程
void V2()
{
struct sembuf buf;
buf.sem_num = 1;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
while (semop(semid, &buf, 1) < 0)
{
if (errno == EINTR || errno == EAGAIN)
continue;
perror("semop_v2()");
exit(1);
}
}
接收端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <errno.h>
#include "proto.h"
#define SHM_SIZE 1024
int main(int argc, char const *argv[])
{
key_t key;
int shmid, err;
struct stu_st *stp;
key = ftok(PATH, 'a');
if (key < 0)
{
perror("ftok()");
exit(1);
}
shmid = shmget(key, SHM_SIZE, 0660 | IPC_CREAT);
if (shmid < 0)
{
perror("shmget()");
exit(1);
}
sem_init();
stp = shmat(shmid, 0, 0);
if (stp < 0)
{
perror("shmat()");
exit(1);
}
P1();
printf("%s, %d, %dn", stp->name, stp->chinese, stp->math);
strcpy(stp->name, "lisi");
sleep(5);
V2();
err = shmdt(stp);
if (err < 0)
{
perror("shmdt()");
exit(1);
}
P1();
err = shmctl(shmid, IPC_RMID, NULL);
if (err < 0)
{
perror("shmctl()");
exit(1);
}
sem_destroy();//接收端销毁信号量
return 0;
}
发送端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include "proto.h"
#define SHM_SIZE 1024
int main(int argc, char const *argv[])
{
key_t key;
int shmid, err;
struct stu_st *stp;
key = ftok(PATH, 'a');
shmid = shmget(key, SHM_SIZE, 0660);
stp = (struct stu_st *)shmat(shmid, 0, 0);
sem_init();
strcpy(stp->name, "zhangsan");
stp->chinese = 90;
stp->math = 95;
sleep(5);
V1();
P2();
printf("%s, %d, %dn", stp->name, stp->chinese, stp->math);
err = shmdt(stp);
V1();
return 0;
}
最后
以上就是完美信封为你收集整理的Linux进程间通信的全部内容,希望文章能够帮你解决Linux进程间通信所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复