进程间通信
一、主要函数:pipe();msgsnd();msgrcv();
二、
1 、管道:pipe()
一切设备皆文件
管道属于内核,是内核的一片内存,fork 不会有这样的内存
管道没有数据时会阻塞。一旦数据被从管道里读出来,那么管道里就没有这个数据了。管道的数据,读完了就没有了,不像文件。
管道pipe 要在fork 之前
2 、fifo
1) 用mkfifo 创建一个fifo 文件"test"
fifo 也是有大小限制的,最大为65536 ,如果没有数据也会阻塞
注:尽量少用fifo
2)/* 如果写端已关闭,那么读端不再阻塞,会跟着返回*/
/* 如果写一个读端已经关闭的管道,则直接出错? 会产生一个SIGPIPE 信号,这个信号默认在内核直接杀死进程。*/
3 、消息队列:
1) 用ftok 生成一个key:
2) 用msgget 取得或创建一个ipc 对象( 根据key 值):
3) 如果有必要则初始化ipc
4) 操作ipc: msgsnd,msgrcv:
注意:类型(mtype) 要一样才能接收
struct msqid_ds
qnum
IPC_RMID : 可用于消除msqrcv 阻塞
IPC_STAT : 可获得消息队列的信息
msgsnd();msgrcv();
client,server
4 、信号量:semget,semctl,semop
/* 信号量:
1 、semget: 若是父子进程之间通信,可用IPC_PRIVATE 作为key 值
semid = semget(IPC_PRIVATE, num, IPC_CREAT | 0600);
2 、semctl:
semctl(semid, i, SETVAL, semval);//i 表示第几个信号量
3 、semop: p 操作(-1),v 操作(+1);
semop(semid, op, num)
4 、p 操作能使进程阻塞,不够减的时候阻塞,v 操作不会阻塞,而且一般不会出错
5 、如迅雷下载只能有8 个任务同时进行,这就是信号量。。。。。。
*/
信号量常用的三种算法:
信号量(1): 互斥
信号量(2): 一个信号量,多个子进程
信号量(3): 栅栏同步,多个信号量
一个进程里的全局变量,另一个进程是看不到的。
进程之间要互斥的话,则要用同一信号量;信号量一般用于多线程。
5 、共享内存:shmget,shmctl,shmat,shmdt
共享内存,很适合于进程间通信,不同进程之间在一定条件下是可以看得到的
mmap 函数
三、例程
1 、
/*pipe.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd[2];
int ret = pipe(fd);
if (ret == -1) {
perror("pipe");
exit(1);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
close(fd[0]);
close(fd[1]);
exit(0);
}
if (pid == 0) {
char buf[32];
//close(fd[1]);
printf("read begin!/n");
ret = read(fd[0], buf, 32);
buf[ret-1] = '/0';
printf("%s/n", buf);
close(fd[0]);
exit(0);
}
if (pid > 0) {
//close(fd[0]);
sleep(3);
write(fd[1], "Hello world!", 12);
wait(NULL);
exit(0);
}
}
/* 确保管道一端写,一端读*/
2 、
/*endpipe.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd[2], ret;
char buf[32];
ret = pipe(fd);
if (ret == -1) {
perror("pipe");
exit(1);
}
pid_t pid = fork();
if (pid > 0) {
close(fd[1]);
printf("Begin read!/n");
ret = read(fd[0], buf, 32);
if (ret == -1) {
perror("read");
exit(1);
}
if (ret == 0) {
printf("The pipe is empty!/n");
}
close(fd[0]);
exit(0);
}
if (pid == 0) {
sleep(10);
exit(0);
}
}
/* 如果写端已关闭,那么读端不再阻塞,会跟着返回*/
3 、
/*brokenpipe.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void pipe_handler(int s)
{
}
int main(void)
{
int fd[2], ret;
char buf[32];
ret = pipe(fd);
if (ret == -1) {
perror("pipe");
exit(1);
}
signal(SIGPIPE, pipe_handler);
pid_t pid = fork();
if (pid > 0) {
close(fd[0]);
wait(NULL);
printf("Begin write!/n");
ret = write(fd[1], buf, 32);
if (ret == -1) {
perror("write");
exit(1);
}
if (ret == 0) {
printf("The pipe can not write!/n");
}
printf("father exit!/n");
close(fd[1]);
exit(0);
}
if (pid == 0) {
sleep(5);
exit(0);
}
}
/* 如果写一个读端已经关闭的管道,则直接出错? 会产生一个SIGPIPE 信号,这个信号默认在内核直接杀死进程。*/
4 、
5 、
/*repipe.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int ret, fd[2];
ret = pipe(fd);
if (ret == -1) {
perror("pipe");
exit(1);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
close(fd[0]);
close(fd[1]);
exit(1);
}
if (pid == 0) {
close(fd[1]);
dup2(fd[0], 0);
execlp("grep", "grep", "d", NULL);
exit(0);
}
if (pid > 0) {
close(fd[0]);
dup2(fd[1], 1);
execlp("ls", "ls", "-al", "./", NULL);
exit(0);
}
}
/* 相当于shell 命令行中的 ls -al ./ | grep d */
6 、
/*pipeseize.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
static int cnt = 0;
void alarm_handler(int s)
{
printf("Pipe max size is %d/n", cnt);
}
int main(void)
{
int fd[2];
int ret = pipe(fd);
if (ret == -1) {
perror("pipe");
exit(1);
}
signal(SIGALRM, alarm_handler);
ret = fcntl(fd[1], F_GETFL);
fcntl(fd[1], F_SETFL, ret | O_NONBLOCK);
while (1) {
ret = write(fd[1], "x", 1);
if (ret == -1) {
printf("Pipe max size is %d/n",
cnt);
break;
}
cnt += ret;
//alarm(1);
}
}
/* 测试管道的大小*/
7 、
/*fifo_write_end.c*//*fifo 的写端*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
printf("write end:before open!/n");
fd = open("test", O_WRONLY);
if (fd == -1) {
perror("open");
exit(1);
}
printf("write end:Open success!/n");
sleep(5);
write(fd, "Hello world!", 12);
close(fd);
exit(0);
}
/* 事先用mkfifo 创建一个fifo 文件"test"
fifo 也是有大小限制的,最大为65536 ,如果没有数据也会阻塞
注:尽量少用fifo
*/
8 、
/*fifo_read_end.c*//*fifo 的读端*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd, ret;
char buf[32];
printf("before open!/n");
fd = open("test", O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
printf("Open success!/n");
ret = read(fd, buf, 32);
buf[ret-1] = '/0';
printf("%s/n", buf);
close(fd);
exit(0);
}
9 、
/*msgsnd.c*//* 消息队列*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PATHNAME "/etc/passwd"
#define PROJID 123
struct stu {
char name[20];
float avg;
};
struct msgbuf {
long mtype;
struct stu student;
};
int main(void)
{
key_t key = ftok(PATHNAME, PROJID);
if (key == -1) {
perror("ftok");
exit(1);
}
int msgid = msgget(key, IPC_CREAT | 0600);
if (msgid == -1) {
perror("msgget");
exit(1);
}
struct msgbuf buf;// 封装要发送的数据
buf.mtype = 8;
strcpy(buf.student.name, "Allan");
buf.student.avg = 99.999;
int ret = msgsnd(msgid, &buf,
sizeof(buf) - sizeof(long), 0);
if (ret == -1) {
perror("msgsnd");
exit(1);
}
exit(0);
}
/* 消息队列:
1 、用ftok 生成一个key:
2 、用msgget 取得或创建一个ipc 对象( 根据key 值):
3 、如果有必要则初始化ipc
4 、操作ipc: msgsnd,msgrcv:
*/
10 、
/*msgrcv.c*//* 消息队列*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PATHNAME "/etc/passwd"
#define PROJID 123
struct stu {
char name[20];
float avg;
};
struct msgbuf {
long mtype;
struct stu student;
};
int main(void)
{
key_t key = ftok(PATHNAME, PROJID);
if (key == -1) {
perror("ftok");
exit(1);
}
#if 0
int msgid = msgget(key, IPC_CREAT | 0600);
if (msgid == -1) {
perror("msgget");
exit(1);
}
#endif
int msgid = 196608;
struct msqid_ds buffer;
msgctl(msgid, IPC_STAT, &buffer);
if (buffer.msg_qnum == 0) {
msgctl(msgid, IPC_RMID, NULL);
exit(0);
}
struct msgbuf buf;
int ret = msgrcv(msgid, &buf,
sizeof(buf) - sizeof(long), 8, 0);
if (ret == -1) {
perror("msgsnd");
exit(1);
}
printf("name=%s/n", buf.student.name);
printf("score=%f/n", buf.student.avg);
exit(0);
}
/*1 、if (buffer.msg_qnum == 0)
msgctl(msgid, IPC_RMID, NULL);
2 、msgrcv 比msgsnd 多一个参数
*/
11 、
/*2010-09-24*/
/* 信号量(1): 互斥*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int sem_init(int num,int semval)
{
int semid=semget(IPC_PRIVATE,num,IPC_CREAT | 0600);
if(semid==-1)
{
perror("semge");
return -1;
}
int i,ret;
for(i=0;i<num;i++)
{
ret=semctl(semid,i,SETVAL,semval);//i 为下标!
if(ret==-1)
{
perror("semctl");
return -1;
}
}
return semid;
}
int p(int semid,int num)
{
struct sembuf op[num];
int i;
for(i=0;i<num;i++)
{
op[i].sem_num=i;// 哪个信号量
op[i].sem_op=-1;// 执行什么样的操作,-1 表示减
op[i].sem_flg=0;// 一般写0 就OK 了
}
int ret;
while(ret=semop(semid,op,num));// 操作成功返回0, 则跳出while, 否则就停在这。也就是semop 出错返回-1 时不再往下走。
return ret;
}
void v(int semid,int num)
{
struct sembuf op[num];
int i;
for(i=0;i<num;i++)
{
op[i].sem_num=i;// 哪个信号量
op[i].sem_op=1;// 执行什么样的操作,-1 表示减
op[i].sem_flg=0;// 一般写0 就OK 了
}
semop(semid,op,num);
}
int main(void)
{
int semid=sem_init(1,1);
if(semid==-1)
exit(1);
pid_t pid=fork();
if(pid==-1)
{
perror("fork");
exit(1);
}
if(pid==0)
{
p(semid,1);
printf("I'm child!/n");
sleep(20);
printf("20 seconds passed!/n");
v(semid,1);
exit(0);
}
if(pid>0)
{
p(semid,1);
printf("I'm father!/n");
sleep(5);
v(semid,1);
exit(0);
}
}
/* 信号量:
1 、semget: 若是父子进程之间通信,可用IPC_PRIVATE 作为key 值
semid = semget(IPC_PRIVATE, num, IPC_CREAT | 0600);
2 、semctl:
semctl(semid, i, SETVAL, semval);//i 表示第几个信号量
3 、semop: p 操作(-1),v 操作(+1);
semop(semid, op, num)
4 、p 操作能使进程阻塞,不够减的时候阻塞,v 操作不会阻塞,而且一般不会出错
5 、如迅雷下载只能有8 个任务同时进行,这就是信号量。。。。。。
*/
12 、
/*2010-09-24*/
/* 信号量(2): 一个信号量,限制同时运行的子进程个数*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <signal.h>
#define BASE 30000
#define PROCESSNUM 5 // 允许5 个同时进行的子进程
int sem_init(int semval)
{
int semid=semget(IPC_PRIVATE,1,IPC_CREAT | 0600);// 只有一个信号量
if(semid==-1)
{
perror("semget");
return semid;
}
int ret=semctl(semid,0,SETVAL,semval);// 下标是从0 开始的
if(ret==-1)
{
perror("semctl");
return ret;
}
return semid;
}
void p(int semid)
{
struct sembuf op;
op.sem_num=0;
op.sem_op=-1;//-1 表示p 操作一次, 从semval 减1
op.sem_flg=0;
semop(semid,&op,1);
}
void v(int semid)
{
struct sembuf op;
op.sem_num=0;
op.sem_op=1;//1 表示v 操作一次, 从semval 加1
op.sem_flg=0;
semop(semid,&op,1);
}
void destroyzombie()
{
struct sigaction act;
act.sa_handler=SIG_IGN;// 不作处理
sigemptyset(&act.sa_mask);
act.sa_flags=SA_NOCLDWAIT;
sigaction(SIGCHLD,&act,NULL);
}
void doprime(int prime)
{
int i;
int flag=0;
for(i=2;i<prime;i++)// 从2 开始
{
if(prime%i==0)
{
flag=1;
break;
}
}
if(flag==0)
printf("%d is a prime!/n",prime);
else
printf("%d is not a prime!/n",prime);
}
int main(void)
{
int semid;
int i;
semid=sem_init(PROCESSNUM);//PROCESSNUM 为信号量的值
destroyzombie();
for(i=BASE;i<BASE+100;i++)
{
p(semid);// 当减不动的时候是否会产生阻塞
pid_t pid=fork();
if(pid==-1)
{
perror("fork");
exit(1);
}
if(pid==0)
{
doprime(i);
sleep(2);
v(semid);
exit(0);
}
}
}
13 、
/* 栅栏同步,5 个信号量,每个信号量的值为1*/
/*2010-09-24*/
/* 信号量(3): 栅栏同步*/
/* 让五个人同时开始跑,等所有人都跑完一圈以后再从重新让它们处于同一起跑线上*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define PRONUM 5
#define PATHNAME "/home/luo/advancedC/tongxin/test"
#define KEY_ID 123
int sem_init(int num,int semval)
{
key_t key=ftok(PATHNAME,KEY_ID);// 用ftok 取得key
if(key==-1)
{
perror("ftok");
return -1;
}
int semid=semget(key,num,IPC_CREAT | 0600);
if(semid==-1)
{
perror("semget");
return semid;
}
int i,ret;
for(i=0;i<num;i++)
{
ret=semctl(semid,i,SETVAL,semval);
if(ret==-1)
{
perror("semctl");
return -1;
}
}
return semid;
}
int p(int semid,int num)//num 表示第几个信号量
{
struct sembuf op;
op.sem_num=num;
op.sem_op=-1;
op.sem_flg=0;
int ret;
while(ret=semop(semid,&op,1));
return ret;
}
void v(int semid,int num)
{
struct sembuf op[num];
int i;
for(i=0;i<num;i++)
{
op[i].sem_num=i;
op[i].sem_op=1;
op[i].sem_flg=0;
}
semop(semid,op,num);// 刚才始写成了semop(semid,op,num);
}
int main(void)
{
int semid=sem_init(PRONUM,1);//5 个信号量,每个信号量的值为1
int i;
for(i=0;i<PRONUM;i++)
{
pid_t pid=fork();
if(pid==-1)
{
perror("fork");
exit(1);
}
if(pid==0)
{
while(1)
{
p(semid,i);
srand(getpid());// 播种
printf("pid=%d begin running!/n",getpid());
sleep(rand()%PRONUM + 3);// 产生随机数
printf("pid=%d run over!/n",getpid());
}
}
}
while(1)
{
for(i=0;i<PRONUM;i++)
{
while(semctl(semid,i,GETNCNT)==0);// 等所有的都跑完一圈
}
v(semid,PRONUM);// 从新让它们处于同一起跑线上
}
}
14 、
/*shared.c*//* 共享内存*/
/*2010-09-24*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
void destroy_shm(int shmid)
{
struct shmid_ds buf;
shmctl(shmid,IPC_STAT,&buf);// 读出信息放于buf
if(buf.shm_nattch==0)
{
shmctl(shmid,IPC_RMID,NULL);
}
}
int main(void)
{
int shmid=shmget(IPC_PRIVATE,32,IPC_CREAT | 0600);
if(shmid==-1)
{
perror("shmget");
exit(1);
}
pid_t pid=fork();
if(pid==-1)
{
perror("fork");
exit(1);
}
if(pid==0)
{
char* ptr=(char*)shmat(shmid,NULL,0);// 映射到共享内存,以操作它
strncpy(ptr,"hello world!",12);
shmdt(ptr);
exit(0);
}
wait(NULL);
sleep(1);
char* ptr=(char*)shmat(shmid,NULL,0);
printf("%s/n",ptr);
shmdt(ptr);
destroy_shm(shmid);
exit(0);
}
/* 共享内存,很适合于进程间通信
1 、shmget: 获得一个共享存储标识符shmid
2 、shmctl:
IPC_STAT, 取此段的shmid_ds 结构,并将它存放在由buf 指定的结构中
IPC_RMID, 从系统中删除该共享存储段
3 、shmat: 建立映射到所申请的地址空间
一般用法:shmat(shmid, NULL, 0)
4 、shmdt: 删除映射
建立,删除映射都会影响shmid_ds 结构中的shm_nattch
*/
15 、
/*mmap.c*/
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(void)
{
int fd, ret, fsize;
char *ptr;
struct stat buf;
fd = open("/etc/passwd", O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
fstat(fd, &buf);
fsize = buf.st_size;
ptr = mmap(NULL, fsize+1, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (((void *)(-1)) == ptr) {
perror("mmap");
close(fd);
exit(1);
}
ptr[fsize] = '/0';
printf("%s", ptr);
munmap(ptr, fsize);
close(fd);
exit(0);
}
/*mmap:
1 、若以AP_PRIVATE 映射,则不会修改原始文件,
私有映射,写时复制,对副本的操作是可读可写的
2 、若以MAP_SHARED 映射,用munmap 解除映射,解除映射,可以将内存中的内容写到磁盘里
3 、可用fstat 先获得文件的长度再映射
父子进程可mmap 共享内存,无关进程不可以??
以匿名方式申请内存,这个内存会自动被清零的:
mmap(NULL,32,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0)
4 、munmap() 解除映射。
*/
16 、
/*anonmous.c*/
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(void)
{
char *ptr;
pid_t pid;
ptr = (char *)mmap(NULL, 32, PROT_READ |
PROT_WRITE, MAP_PRIVATE |
MAP_ANONYMOUS, -1, 0);
if (((void *)(-1)) == ptr) {
perror("mmap");
exit(1);
}
pid = fork();
if (pid == -1) {
perror("fork");
munmap(ptr, 32);
exit(1);
}
if (pid == 0) {
strcpy(ptr, "Hello world!");
munmap(ptr, 32);
exit(0);
}
wait(NULL);
printf("%s/n", ptr);
munmap(ptr, 32);
exit(0);
}
/* MAP_PRIVATE | MAP_ANONYMOUS 以这种方式申请的内存有什么??
父子进程都munmap() 难道不会影响吗?
*/
16、
/*test_share_shm_write_int_type.c*/
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define TOKEN 'r'
void destroy_shm(int shmid)
{
struct shmid_ds buf;
shmctl(shmid, IPC_STAT, &buf);
if (buf.shm_nattch == 0)
shmctl(shmid, IPC_RMID, NULL);
}
int main(void)
{
int shmid = shmget(IPC_PRIVATE, 32, IPC_CREAT | 0600);
if (shmid == -1) {
perror("shmget");
exit(1);
}
pid_t pid = fork();
if (pid == -1) {
destroy_shm(shmid);
perror("fork");
exit(1);
}
if (pid == 0) {
int n=5;
int *ptr = (int *)shmat(shmid, NULL, 0);
*ptr=n;
shmdt(ptr);
int *rptr=(int *)shmat(shmid,NULL,0);
printf("child read n=%d/n",*rptr);
shmdt(rptr);
exit(0);
}
sleep(2);
int rea;
int *ptr = (int *)shmat(shmid, NULL, 0);
printf("father read n=%d/n",*ptr);
shmdt(ptr);
destroy_shm(shmid);
exit(0);
}
17、
/*pipe每得到的是未被使用的描述符*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int i;
for(i=0;i<3;i++)
{
int fd[2];
int ret = pipe(fd);
printf("fd[0]=%d/n",fd[0]);
printf("fd[1]=%d/n",fd[1]);
if (ret == -1) {
perror("pipe");
exit(1);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
close(fd[0]);
close(fd[1]);
exit(0);
}
if (pid == 0) {
char buf[32];
//close(fd[1]);
printf("read begin!/n");
ret = read(fd[0], buf, 32);
buf[ret-1] = '/0';
printf("%s/n", buf);
close(fd[0]);
exit(0);
}
if (pid > 0) {
//close(fd[0]);
sleep(3);
write(fd[1], "Hello world!", 12);
wait(NULL);
}
}
}