让每个进程拥有独立进空间的好处:
1、对于编程人员来说,系统更容易捕获随意的内存读取和写入操作
2、对于用户来说,操作系统将变得更加健壮,因为一个应用程序无法破坏另一个进程或操作系统的运行(防止被攻击)
独立进程空间的缺点:
1、多任务实现开销较大
2、编写能够与其它进程进行通信,或者能够对其它进程进行操作的应用程序要困难的多
狭义上的真正的进程间通信:管道、信号、消息队列、共享内存、信息量、套接字
管道
无名管道 和 有名管道
**无名管道:**只适合在父子进程之间通信
内核开辟一个管道,通信的进程通过共享这个管道,从而实现通信
int pipe(int pipedf[2])
头文件 #include <unistd.h>
参数:缓存地址,缓存用于存放读写管道的文件描述符。从这个参数的样子可以看出,这个缓存就是一个拥有两个元素的int型数组。
1)元素[0]:里面放的是读管道的读文件描述符
2)元素[1]:里面放的是写管道的写文件描述符。
特别需要注意的是,这里的读和写文件描述符,是两个不同的文件描述符。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
int main()
{
pid_t pid;
int fd[2];
if(pipe(fd)<0)
{
perror("pipe error!");
exit(1);
}
char buffer [100];
pid=fork();
if(pid<0)
{
perror("fork error!");
exit(1);
}
if(pid==0)
{
while(1)
{
close(fd[1]);
read(fd[0],buffer,sizeof(buffer));
printf("recv:%s\n",buffer);
memset(buffer,0,sizeof(buffer));
sleep(1);
}
}
else if(pid>0)
{
while(1)
{
close(fd[0]);
memset(buffer,0,sizeof(buffer));
scanf("%s",buffer);
write(fd[1],buffer,strlen(buffer));
}
}
return 0;
}
无名管道特点:
1、会发生阻塞:读管道时,如果没有数据读操作会休眠(阻塞),写数据时,缓冲区写满会休眠(阻塞)
2、管道只允许具有血缘关系的进程间通信,如父子进程间的通信
3、管道只允许单项通信(通过以下方式可以实现双向)
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
int main()
{
pid_t pid;
int fd[2];
int fd1[2];
if(pipe(fd)<0)
{
perror("pipe error!");
exit(1);
}
if(pipe(fd1)<0)
{
perror("pipe1 error!");
exit(1);
}
char buffer[100];
pid=fork();
if(pid<0)
{
perror("fork error!");
exit(1);
}
if(pid==0)
{
while(1)
{
close(fd1[0]);
close(fd[1]);
read(fd[0],buffer,sizeof(buffer));
printf("recv:%s\n",buffer);
write(fd1[1],buffer,strlen(buffer));
memset(buffer,0,sizeof(buffer));
sleep(1);
}
}
else if(pid>0)
{
while(1)
{
close(fd1[1]);
close(fd[0]);
memset(buffer,0,sizeof(buffer));
scanf("%s",buffer);
write(fd[1],buffer,strlen(buffer));
sleep(1);
memset(buffer,0,sizeof(buffer));
read(fd1[0],buffer,sizeof(buffer));
printf("father recv:%s\n",buffer);
}
}
return 0;
}
有名管道:可以提供任意两个进程之间的通信
int mkfifo(const char *pathname, mode_t mode);
(1)功能
创建有名管道文件,创建好后便可使用open打开。
如果是创建普通文件的话,我们可以使用open的O_CREAT选项来创建,比如:
open("./file", O_RDWR|O_CREAT, 0664);
但是对于“有名管道”这种特殊文件,这里只能使用mkfifo函数来创建。
(2)参数
1)pathname:被创建管道文件的文件路径名。
2)mode:指定被创建时原始权限,一般为0664(110110100),必须包含读写权限。
(3)返回值:成功返回0,失败则返回-1,并且errno被设置。
int mkfifo(const char*pathname,mode_t mode)
第一个参数:被创建管道文件的文件路径名
第二个参数:指定被创建时原始权限,一般为0664,必须包含读写权限
返回值:成功返回0,失败返回-1,并且errno被设置
mkfifo1.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
int main()
{
pid_t pid;
int ret;
char buffer[100];
if((ret=mkfifo("./file",0644))<0)
{
if(ret==-1&&errno==EEXIST)
{
}
else
{
perror("make mkfifo error!");
exit(1);
}
}
//pid=fork();
/* if(pid<0)
{
perror("error");
exit(1);
}
if(pid==0)
{*/
int fd=open("./file",O_RDONLY);
while(1)
{
memset(buffer,0,sizeof(buffer));
read(fd,buffer,sizeof(buffer));
printf("recv:%s\n",buffer);
sleep(2);
}
/*
}
else if(pid>0)
{
int fd=open("./mkfifo",O_WRONLY);
while(1)
{
memset(buffer,0,sizeof(buffer));
scanf("%s",buffer);
write(fd,buffer,strlen(buffer));
}
}*/
return 0;
}
mkfifo2.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
int main()
{
pid_t pid;
int ret;
char buffer[100];
if((ret=mkfifo("./file",0644))<0)
{
if(ret==-1&&errno==EEXIST)
{
}
else
{
perror("make mkfifo error!");
exit(1);
}
}
/* if(mkfifo("./file",0644)<0)
{
perror("make mkfifo error!");
exit(1);
}*/
/* pid=fork();
if(pid<0)
{
perror("error");
exit(1);
}
if(pid==0)
{
int fd=open("./file",O_RDONLY);
while(1)
{
memset(buffer,0,sizeof(buffer));
read(fd,buffer,sizeof(buffer));
printf("recv:%s\n",buffer);
sleep(2);
}
}
else if(pid>0)
{*/
int fd=open("./file",O_WRONLY);
while(1)
{
memset(buffer,0,sizeof(buffer));
scanf("%s",buffer);
write(fd,buffer,strlen(buffer));
}
// }
return 0;
}
注意事项:
1、有名管道这种特殊文件,稚嫩用mkfifo函数创建
2、为了保证管道一定被创建,最好是两个进程都包含创建管道的代码,谁先运行就谁先创建,后运行的发现管道已经创建好了,那就直接open打开使用
3、不能以O_RDWR模式打开命名管道FIFO文件,否则其行为是未定义的,管道是单向的,不能同时读写
管道的最大特点:发送的是无格式的数据
消息队列
分类:System V的消息队列 Posix消息队列
区别:对Posix消息队列的读总是返回最高优先级的最早消息,对System V
消息队列的读则可以返回任意指定优先级的消息
组成:1、消息编号:识别消息用
2、消息正文:真正的信息内容
struct msgbuf
{
long mtype; /* 放消息编号,必须> 0 */
char mtext[msgsz]; /* 消息内容(消息正文) */
};
不会自动删除,重启时删除
使用msgget函数创建,返回唯一标识消息队列的标识符
int msgget(key_t key,int msgflg);
头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
key 有三种方式:
最多使用:使用ftok函数来动态生成key
key_t ftok(const char *pathname, int proj_id);
ftok通过指定路径名和一个整形数,就可以计算并返回一个唯一对应的key值,
只要路径名和整形数不变,所对应的key值就唯一不变的。
不过由于ftok只会使用整形数(proj_id)的低8位,因此我们往往会指定为一个ASCII码值,
因为ASCII码值刚好是8位的整形数。
int msgget(key_t key, int msgflg);
· msgflg
指定创建时的原始权限,比如0664
创建一个新的消息队列时,除了原始权限,还需要指定IPC_CREAT选项。
msgid = msgget(key, 0664|IPC_CREAT);
如果key值没有对应任何消息队列,就会创建一个新的消息队列,此时就会用到msgflg参数,
但是如果key已经对应了某个早已存在消息队列,就直接返回这个已存在消息队列的ID(标识符),
此时不会用到msgflg参数
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
用于读出消息队列的数据
1)函数原型
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
(a)功能:发送消息到消息队列上。
说白了就是将消息挂到消息队列上。
(b)返回值
· 成功:返回0,
· 失败:返回-1,errno被设置
(c)参数
· msqid:消息队列的标识符。
· msgp:存放消息的缓存的地址,类型struct msgbuf类型
这个缓存就是一个消息包(存放消息的结构体变量)。
struct msgbuf
{
long mtype; /* 放消息编号,必须 > 0 */
char mtext[msgsz]; /* 消息内容(消息正文) */
};
· msgsz:消息正文大大小。
· msgflg:
- 0:阻塞发送消息
也就是说,如果没有发送成功的话,该函数会一直阻塞等,直到发送成功为止。
- IPC_NOWAIT:非阻塞方式发送消息,不管发送成功与否,函数都将返回
也就是说,发送不成功的的话,函数不会阻塞。
函数原型
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
(a)功能:接收消息
说白了就是从消息队列中取出别人所放的某个编号的消息。
(b)返回值
成功:返回消息正文的字节数
失败:返回-1,errno被设置
(c)参数
· msqid:消息队列的标识符。
· msgp:缓存地址,缓存用于存放所接收的消息
类型还是struct msgbuf:
struct msgbuf
{
long mtype; /* 存放消息编号*/
char mtext[msgsz]; /*存放 消息正文内容 */
};
· msgsz:消息正文的大小
· msgtyp:你要接收消息的编号
· int msgflg:
- 0:阻塞接收消息
也就是说如果没有消息时,接收回阻塞(休眠)。
- IPC_NOWAIT:非阻塞接收消息
也就是说没有消息时,该函数不阻塞
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
struct msgbuf
{
long id;
char msg[1024];
};
int main()
{
int msgid;
open("./a",O_CREAT|O_RDWR);
key_t key=ftok("./a",'k');
if((msgid=msgget(key,0644|IPC_CREAT))<0)
{
perror("creat msg queue error!");
exit(1);
}
pid_t pid;
pid=fork();
if(pid<0)
{
exit(1);
}
if(pid==0)
{//msgid
struct msgbuf *p_msg=(struct msgbuf*)malloc(sizeof(struct msgbuf));
while(1)
{
memset(p_msg,0,sizeof(struct msgbuf));
msgrcv(msgid,p_msg,sizeof(struct msgbuf),4,0);
printf("recv:%s\n",p_msg->msg);
sleep(1);
}
}
else if(pid>0)
{//msgid
struct msgbuf *p_msg;
int i=0;
while(1)
{
p_msg=(struct msgbuf*)malloc(sizeof(struct msgbuf));
memset(p_msg,0,sizeof(struct msgbuf));
scanf("%s",p_msg->msg);
p_msg->id=i;
i=i+1;
msgsnd(msgid,p_msg,sizeof(struct msgbuf),0);
}
}
return 0;
}
特点:
传送有格式的消息流
多进程网状交叉通信时,消息队列是上上之选
能实现大规模数据的通信
msg2.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
struct msgbuf
{
long id;
char msg[1024];
};
int main()
{
int msgid;
open("./a",O_CREAT|O_RDWR);
key_t key=ftok("./a",'k');
if((msgid=msgget(key,0644|IPC_CREAT))<0)
{
perror("creat msg queue error!");
exit(1);
}
struct msgbuf *p_msg=(struct msgbuf*)malloc(sizeof(struct msgbuf));
while(1)
{
memset(p_msg,0,sizeof(struct msgbuf));
msgrcv(msgid,p_msg,sizeof(struct msgbuf),4,0);
printf("recv:%s\n",p_msg->msg);
sleep(1);
}
return 0;
}
msg3.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
struct msgbuf
{
long id;
char msg[1024];
};
int main()
{
int msgid;
open("./a",O_CREAT|O_RDWR);
key_t key=ftok("./a",'k');
if((msgid=msgget(key,0644|IPC_CREAT))<0)
{
perror("creat msg queue error!");
exit(1);
}
struct msgbuf *p_msg;
int i=0;
while(1)
{
p_msg=(struct msgbuf*)malloc(sizeof(struct msgbuf));
memset(p_msg,0,sizeof(struct msgbuf));
scanf("%s",p_msg->msg);
p_msg->id=i;
i=i+1;
msgsnd(msgid,p_msg,sizeof(struct msgbuf),0);
}
return 0;
}
信号量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/shm.h>
#include<signal.h>
#include<errno.h>
#include<unistd.h>
#define MAX_SIZE 1024
char *addr;
int shmid;
void my_exit(int signal)
{
shmdt(addr);
shmctl(shmid,IPC_RMID,0);
}
int main()
{
signal(SIGINT,my_exit);
char buffer[MAX_SIZE];
if(open("./shm",O_CREAT|O_RDWR)<0)
{
perror("openshm error!");
exit(1);
}
key_t shm_key=ftok("./shm",'b');
if(open("./sem",O_CREAT|O_RDWR)<0)
{
perror("opensem error!");
exit(1);
}
key_t sem_key=ftok("./sem",'c');
shmid=shmget(shm_key,MAX_SIZE,0644|IPC_CREAT);
int semid=semget(sem_key,1,0644|IPC_CREAT);
semctl(semid,0,1,SETVAL,1);
if(shmid==-1)
{
perror("shm get error!");
exit(1);
}
pid_t pid=fork();
if(pid<0)
{
perror("fork error!");
exit(1);
}
if(pid==0)
{
sleep(1);
addr=(char*)shmat(shmid,NULL,0);
struct sembuf sem_buf;
sem_buf.sem_num=0;
sem_buf.sem_flg=SEM_UNDO;
while(1)
{
sem_buf.sem_op=-1;
semop(semid,&sem_buf,1);
memset(buffer,0,sizeof(buffer));
if(strlen(addr)!=0)
{
strcpy(buffer,addr);
printf("recv:%s\n",buffer);
memset(addr,0,MAX_SIZE);
}
// sleep(2);
sem_buf.sem_op=1;
semop(semid,&sem_buf,1);
sleep(2);
}
}
else if(pid>0)
{
addr=(char*)shmat(shmid,NULL,0);
struct sembuf sem_buf;
sem_buf.sem_num=0;
sem_buf.sem_flg=SEM_UNDO;
while(1)
{
sem_buf.sem_op=-1;
semop(semid,&sem_buf,1);
memset(buffer,0,sizeof(buffer));
scanf("%s",buffer);
strcpy(addr,buffer);
sem_buf.sem_op=1;
semop(semid,&sem_buf,1);
sleep(2);
}
}
return 0;
}
信号
62个信号