进程间通信(一)

本文详细介绍了进程间通信的重要性及其四种主要方式:管道通信、共享内存、消息队列和信号通信。重点讲解了管道通信和共享内存的具体实现过程,包括popen、pclose函数的使用方法及管道创建、共享内存创建和映射的步骤。
摘要由CSDN通过智能技术生成

为什么进程间要通信?
1、 数据传输
2、 资源共享
3、 通知事件
4、 进程控制

进程间通信方式:
管道通信、
共享内存、
消息队列、
信号通信、


一、管道通信
管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道尾部写入数据,另一个进程(读进程)从管道的头部读出数据。
(1)popen
两个程序之间传递数据的一种简单方法是使用popen和pclose。

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名和相应的参数。type必须是”r”或”w”。
如果type是”r”,被调程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE *文件流指针,可以读取被调程序的输出;如果type是”w”,调用程序就可以向被调程序发送数据,而被调程序可以在自己的标准输入上读取这些数据。
pclose函数只在popen启动的进程结束后才返回。如果调用pclose时它仍在运行,pclose将等待该进程的结束。

#include <stdio.h>

#define SIZE 1024*100

int main()
{
    FILE *fp = popen("ps -ef", "r");
    if (fp == NULL)
    {
        perror ("popen");
        return -1;
    }

    char buf[SIZE] = {0};

    int ret = fread(buf, sizeof(char), SIZE-1, fp);

    // printf ("读到的数据:\n %s\n", buf);


    FILE *fp2 = popen("grep a.out", "w");
    if (fp2 == NULL)
    {
        perror ("popen");
        return -1;
    }

    fwrite (buf, sizeof(char), ret, fp2);
    printf ("写入完成\n");

    pclose (fp);
    pclose (fp2);

    return 0;
}

(2)管道
管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。

①无名管道
无名管道由pipe()函数创建:

int pipe(int filedis[2]);

当一个管道建立时,它会创建两个文件描述符:
filedis[0]用于读管道,filedis[1]用于写管道。
管道用于不同进程间通信。通常先创建一个管道,在通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道描述符。
注意事项:
必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符。

注意:如果管道所有的读端都被关闭,继续写数据系统默认的操作是使程序退出

父子进程通过管道进行通信

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

#define SIZE 1024

// 子进程通过管道从父进程接收数据
void child_do(int *fd)
{
    // 将管道的写端关闭
    close (fd[1]);

    char buf [SIZE];

    while (1)
    {
        // 从父进程读取数据
        int ret = read (fd[0], buf, SIZE-1);
        if (ret == -1)
        {
            perror ("read");
            break;
        }
        buf[ret] = '\0';
        printf ("子进程读到 %d 字节数据: %s\n", ret, buf);
    }

    // 关闭读端
    close (fd[0]);
}

// 父进程通过管道向子进程发送数据
void father_do(int *fd)
{
    // 将管道读端关闭
    close (fd[0]);

    char buf[SIZE];

    while (1)
    {
        fgets (buf, SIZE, stdin);

        // 向子进程发送数据
        int ret = write (fd[1], buf, strlen(buf));
        printf ("父进程发送了 %d 字节数据\n", ret);
    }

    // 关闭写端
    close (fd[1]);
}

int main()
{
    int fd[2];

    // 创建管道
    int ret = pipe(fd);
    if (ret == -1)
    {
        perror ("pipe");
        return -1;
    }

    // 创建子进程
    pid_t pid = fork();

    switch (pid)
    {
        case -1:
            perror ("fork");
            break;
        case 0:   // 子进程
            child_do(fd);
            break;
        default:
            father_do(fd);
            break;
    }

    return 0;
}

②命名管道
命名管道(FIFO)和无名管道基本相同,但也有不同点:无名管道只能由父子进程使用;但是通过命名管道,不相关的进程也能交换数据。
创建:

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

    pathname: FIFO文件名
    mode:属性(同文件操作)

一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。
当打开FIFO时,非阻塞标识(O_NONBLOCK)将对以后的读写产生影响:
1、没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如果试图读取空的FIFO,将导致进程阻塞。
2、使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回。errno是ENXIO。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>

#define SIZE 1024

int main()
{
    int fd = open("/home/mkfifo", O_WRONLY);
    if (fd== -1)
    {
        perror ("mkfifo");
        return -1;
    }

    //写端
    char buf[SIZE];
    while (1)
    {
        fgets (buf, SIZE, stdin);
        write (fd, buf, strlen(buf));
    }

    //读端
    char buf[SIZE];
    while (1)
    {
        int ret = read (fd, buf, SIZE);
        buf[ret] = '\0';
        printf ("读到 %d 字节: %s\n", ret, buf);
    }

    return 0;
}

二、共享内存
共享内存实现分两个步骤:
1、创建共享内存,使用shmget函数
2、映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数。

①创建:

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

key:
1、0/IPC_PRIVATE:当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key取值为0,而参数shmflg中又设置IPC_PRIVATE这个标志,则同样会创建一块新的共享内存。
2、大于0的32位整数:视参数shmflg来确定操作。

size:
1、大于0的整数:新建的共享内存大小,以字节为单位0:
2、只获取共享内存时指定为0

shmflg:模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定共享内存的存取权限
1、0:取共享内存标识符,若不存在则函数会报错
2、IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
3、IPC_CREAT|IPC_EXCL:如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错

返回值:如果成功,返回共享内存标识符;如果失败,返回-1。

②映射:

void* shmat(int shmid, char *shmaddr, int flag)

参数:
第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。

返回值:
如果成功,则返回共享内存映射到进程中的地址;如果失败,则返回-1。

③解除映射:当一个进程不再需要共享内存时,需要把它从进程地址空间中脱离

int shmdt(char *shmaddr);

参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.

该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。

④共享内存控制

int shmctl(int shm_id, int command, struct shmid_ds *buf);  

第一个参数,shm_id是shmget函数返回的共享内存标识符。

第二个参数,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段

第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。

shmid_ds结构至少包括以下成员:

struct shmid_ds
{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
};

创建共享内存

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string.h>

typedef struct _shm
{
    int flag;
    char msg[256];
}SHM;

int main()
{
    // 1、创建或者获取一个共享内存
    int shmid = shmget((key_t)1234, sizeof(SHM), 0666 | IPC_CREAT);
    if (shmid == -1)
    {
        perror ("shmget");
        return -1;
    }

    // 2、将共享内存映射到当前的进程空间
    SHM* pshm = (SHM*)shmat(shmid, NULL, 0);
    if(pshm == (SHM*)-1)
    {
        perror ("shmat");
        return -1;
    }

    strcpy (pshm->msg, "hello");

    // 解除共享内存映射,解除是值当前进程不能再使用共享内存
    shmdt(pshm);


    shmctl(shmid, IPC_RMID, NULL);


    return 0;
}
weixin063传染病防控宣传微信小程序系统的设计与实现+springboot后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值