进程间通讯

来源:互联网 发布:php exec 返回值126 编辑:程序博客网 时间:2024/06/05 18:59

一、进程间通讯: 两个进程之间能相互发送数据
二、进程间通讯的四种方式:管道 信号量 消息队列 共享内存
1.管道:
有名管道: 在文件系统目录中存在一个管道文件。
(1)管道文件:仅仅是文件系统中的标示,并不在磁盘上占据空间
在使用时,在内存上开辟空间,作为两个进程数据交互的通道
(2)管道文件的创建:
1) mkfifo 命令
2) mkfifo 函数 (在代码中使用其创建管道文件)
(3)有名管道的使用:
open 打开管道文件
如果一个进程以只读(只写)打开,那么这个进程会被阻塞到open,直到另一个进程以只写(只读)或者读写
read 读取内容
read读取普通文件,read不会阻塞
read读取管道文件,read会阻塞运行,直到管道中有数据或者所有的写端关闭
write 发送内容
close 关闭打开的文件
(4)练习题:
1)写端写入数据以后,读端不会立马读取内容:数据保持在管道中存在一段时间。管道文件的大小: 0
2)一个写端写入数据“Hello World”,A读端读取数据,B再去读取。(三个进程: 进程C 写端 进程A、B 为读端)

进程cvoid main(){    int fd=open("FIFO",O_WRONLY);    printf("打开管道成功:\n");    assert(fd!=-1);    write(fd,"hello world",11);    printf("写入成功");    close(fd);}进程bvoid main(){    int fd=open("FIFO",O_RDONLY);    char buff[128]={0};    assert(fd!=-1);    printf("打开管道成功\n");    read(fd,buff,5);    printf("读取管道信息成功:\n");    printf("%s",buff);    close(fd);}

结论1:管道通讯发送的数据没有指定进程接受,任何一个进程只要打开的是同一个管道文件,都有可能读到数据。
结论2: read读取管道中的数据,只要读过的数据就会被清空
无名管道: 仅能应用于父子进程之间不存在管道文件,实现依赖父子进程文件共享。打开一个文件,用两个文件符表示,一个为读端,一个为写端
(1)无名管道使用:
int pipe(int fd[2]); // 创建无名管道, 打开无名管道 ;fd[0]为读端,fd[1]为写端
注意:在内存开辟空间, 用两个文件描述符(fd[0], fd[1])指向内存空间。 父子进程之间文件共享。 pipe 必须在 fork 之前调用

例子:父进程创建管道,并在管道中写入数据,子进程从管道中读取void main(){    int fd[2];    pipe(fd);  //创建一个无名管道并打开;    pid_t pid=fork();    assert(pid!=-1);    if(pid==0)    {        close(fd[1]);            char buff[1024]={0};        read(fd[0],buff,1023);   //fd[0]读端        printf("child read data:%s\n",buff);    }    else    {        close(fd[0]);            printf("father will write\n");        write(fd[1],"hello word",11);    //fd[1]写端        printf("father over\n");    }}

2.信号量
计数器: 临界资源允许被几个进程同时访问。
作用: 进程同步控制
临界资源: 同一时刻仅能被一个进程访问的资源
临界区: 访问临界资源的代码区域
原子操作: 不能被中断的操作。
信号量操作:
1) 创建或者获取 int semget((key_t)key, int nsems, int flag);
2) P、 V 操作 int semop(int semid, struct sembuf sem[], int size);
struct sembuf
{
int sem_num;
int sem_op;
int sem_flg;
};
3) 初始化、 删除
int semctl(int semid, int num, int cmd, … /**/);
新定义信号量操作:
1、 获取 void sem_get(int key, int val);
2、 P 操作 void sem_p();
3、 V 操作 void sem_v();
4、 删除 void sem_del();
通过命令管理系统上所有信号量:
查看系统所有的信号量集: ipcs -s
删除系统中的信号量集: ipcrm -s semid
练习:
有两个进程 A, B。 A 进程负责接收用户输入, 当用户输入单个“OK” 的时候, B 进程输出“我准备好了”, 当 A 进程接受到用户输入的“走你”, 通知 B 进程可以开始运行,B 进程做计算 100 以内所有素数。
解决注意点:
1、 使用两个信号量, 但是一个信号量集。
2、 A 进程中当接收到“OK”时,对 sem1 进行 V 操作,当接收到“goto”,对 sem2 进程 V 操作
3、 B 进程启动后, 先对 sem1 进行 P 操作, 直到 A 对 sem1 进行 V 操作后, 输出” 我准备好了” , 然后对 sem2 进行 P 操作, 等 A 对 sem2进行 V 操作, 计算 100 以内所有素数。
void sem_get(int key, int val[], int len);
void sem_p(int semnum);
void sem_v(int semnum);
void sem_del();

一个信号量来完成,两个进程都进行P V操作进程Avoid main(){    int fd=open("FIFO",O_WRONLY);    assert(fd!=-1);    sem_get(1234,0);    printf("please input :");    fflush(stdout);    char buff[128]={0};    while(1)    {        fgets(buff,128,stdin);        if(!strncmp(buff,"ok",2))            {                sem_v();                sem_p();            }        else  if (!strncmp(buff,"走你",6))            {                sem_v();            }        else            {                printf("输入错误\n");            }    }}进程Bvoid main(){    int fd=open("FIFO",O_RDONLY);    int i,j;    assert(fd!=-1);    sem_get((int)1234,0);    sem_p();    printf("我准备好了\n");    sem_v();    sem_p();    printf("开始打印素数:\n");    for(i=1;i<100;i++)    {        int flag=1;        for(j=2;j<i;j++)        {            if(i%j==0)            {                flag=0;                break;            }        }        if(flag==1)        {            printf("%d ",i);        }    }}

3.消息队列
在内核中创建一个键值对, 用来维护消息队列的属性信息。
发送带有类型的数据。 进程可以仅仅获取特定类型的数据。 获取指定类型的数据的时候, 采用队列的特点: 先进先出。
int msgget(key_t key, int flag);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
int msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);
可以给指定进程发送数据。 如果 msgrcv 函数指定的读取大小 nbytes 比发送端发送的消息长度小, 则不会读取这个消息, 消息继续保留在消息队列中。
4.共享内存
共享内存属于临界资源, 对于其访问必须做访问控制, 保证同一时刻只有一个进程操作共享内存。
int shmget((key_t)key, int size, int flag);
void *shmat(int shmid, void *addr, int flag);
int shmdt(void *addr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

面试题:管道和消息队列的缺点?