知识梳理------进程(二)

一、进程间通信
每个进程都有各自不同的用户地址空间,任何一个进程的变量在另一个进程中都是不可见的,即进程之间要交换数据必须要通过内核。(在内核中开辟一块缓冲区,进程A把数据从用户空间拷贝到内核空间缓冲区中,进程B在从内核缓冲区中把数据读走)

<1> pipe管道 (使用队列实现,一般时环形队列,默认阻塞)
管道是一种基本的IPC机制,实现就是在内核中开辟缓冲区。它只能在有亲缘关系的进程间通信。
1) 特点:
- 管道是半双工的,数据只能单行流动,若需要双向通信,则必须创建两个管道;
- 只能用于父子进程、兄弟进程见的通信;
- 管道对于其两端的进程而言,就是一个文件,但它不属于普通文件,不属于某个文件系统,并且只存在与内核中;
- 一个进程向管道的一端写数据,数据在另一端被读取;
2) 管道的创建

#inlcude <unistd.h>
int pipe(int pipefd[2]);

管道是基于文件描述符的通信方式,当一个管道被创建时,它会创建两个文件描述符pipefd[0]和pipefd[1],其中pipefd[0]用于读,pipefd[1]用于写。在使用管道时,非常重要的一步就是,确定通信方向,防止通信混乱。即 :
父写子读:关父读,关子写。
子写父读:关子读,关父写。
其流程是:父进程创建管道—>父进程fork出子进程—>确定通信方向,关闭相应的描述符.
3) 管道读写
- 写关闭后,读端读完管道的内容后,再次读,会返回0,相当于读到EOF.
- 写端未关闭且写端暂无数据,读端读完管道中的内容后,再次读就阻塞
- 读端关闭,写端写管道,将产生SIGPIPE信号,此时写端默认终止进程
- 读端没有读出管道内的数据,当写端写满管道后,再次写就阻塞

<2> 有名管道FIFO
主要用于无血缘关系的进程之间进行通信。
1) 有一个路径名与之关联,以FIFO的文件形式存在与系统中。即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能彼此间通过FIFO进行通信。
2) 有名管道的创建

#inlcude <sys/types.h>
#inlcude <sys/stat.h>
int mkfifo(const char* pathname, mode_t mode);

<3> 信号 (进程间唯一的异步通信方式)
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。它可以直接进行用户空间进程和内核空间进程之间的交互。若该进程当前并未处在执行状态,则信号就会被内核保存,直到该进程回复执行再递送给它。若一个信号被设为阻塞,则该信号的递送将被延迟,直到其阻塞被取消。(信号的详细内容请参见 “知识梳理——信号” )

<4> 共享存储
共享存储允许两个或者多个进程共享一个给定的存储区,它是最快的一种IPC。一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不再涉及内核。但是,往该共享存储区存放信息或者从中取走信息的进程间通常需要某种形式的同步。
1) mmap
把一个文件或者一个Posix共享内存区对象映射到进程地址空间,其目的在于:
- 使用普通文件以提供内存映射I/O;
- 使用特殊文件以提供匿名内存映射;
- 使用shm_open以提供无亲缘关系进程间的Posix共享内存区;

#inlcude <sys/mman.h>
void* mmap(void *addr, size_t len, int port, int flags, int fd, off_t offset);
//成功返回被映射区的起始地址,出错时返回MAP_FAILED
int munmap(void *addr, size_t len);
// addr   表示文件映射到内存的什么位置(通常为NULL,表示由OS自动分配)
// len    表示映射长度
// port   设定映射内存区的读写权限(PROT_READ(可读)、PROT_WRITE(可写)、PROT_EXEC(可执行)、PROT_NONE(不可访问))
// flags  表示映射方式
// -----> MAP_SHARD(共享) 调用进程对映射数据所做修改对共享该对象的所用进程都可见,且也改变其底层支撑对象。
// -----> MAP_PRIVATE(私有) 调用进程对映射数据的修改只对该进程可见,且不改变其底层支撑对象
// fd     指定用于映射的文件 
// offset 指定从文件的什么位置开始映射 (必须是4096的整数倍)

注意:用于进程间通信时,一般设计成结构体的形式来传输数据。
注意:若要打开的文件不存在,则必须要创建,同时使用sleek扩大其大小。因为刚创建的文件大小是0,所以必须要将其扩大到要映射的长度,否则会出现“总线错误”。
注意:当一个进程先调用mmap,然后再fork时,子进程会共享父进程的内存映射关系。

lseek(fd, 0x1000-1,     SEEK_SET);    //设定文件大小为4096
unlink(fd);   //将文件设为临时的,当其引用计数减为0时自动删除
  • 例子:在父、子进程中持续给共享内存中的计数器加1
#include <sys/mman.h>
#include <stdio.h>
#include <sys/sem.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define SEM_NAME "mysem"

int main(int argc, char *argv[])
{
    if(argc != 3){
        printf("Usage: %s PATH_NAME LOOPS.\n", basename(argv[0]));
        return 1;
    } 
    int nloop = atoi(argv[2]);
    fd = open(argv[1], O_RDWR | O_CREAT, 0666);
    if(fd == -1){
        perror("open failed.\n");
    }
    int count = 0;
    int n = sizeof(int)
    if(write(fd, &count, n) != n){
        perror("write failed.\n");
    }
    int *ptr = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARD, fd, 0);
    close(fd);
    sem_t *mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0666, 1);
    sem_unlink(SEM_NAME);
    setbuf(stdout, NULL);
    if(fork() == 0){
        for(int i = 0; i < nloop; i++){
            sem_wait(mutex);
            printf("child: %d\n", (*ptr)++);
            sem_pose(mutex);
        }
        exit(0);
    }
    for(int i = 0; i < nloop; i++){
        sem_wait(mutex);
        printf("parent: &d\n", (*ptr)++);
        sem_post(mutex);
    }
    exit(0);
}

2) system共享内存
内核为每个共享内存区维护一个结构,它定义在 sys/shm.h头文件中。
* shmget函数
创建一个新的共享内存区,或者访问一个已存在的共享内存区。

#include <sys/shm.h>
int shmget(key_t key, size_t size, int flag);
// 返回值是一个称为共享内存区标识符的整数,
// key 可以是ftok的返回值,也可以是IPC_PRIVATE.
// size 以字节为单位,指定内存区大小。若是创建一个新的共享内存区,则必须是一个大于0的整数;若是访问一个已存在的共享内存区,则值为0.
//flag 用于设置权限
  • shmat函数
    将一个共享内存区链接到进程的地址空间。
#include <sys/shm.h>
void* shmat(int shmid, const void* addr, int flag);
// 成功时返回一个指向共享内存的指针,失败时返回-1
// shmid 是shmget返回的共享内存的标识符
// addr 表示共享内存链接到进程的哪个地址上
// ----> 若addr为0,则链接的地址由OS指定。
//-----> 若addr不为0,且没有设定SHM_RND,则链接到addr指定的地址
//-----> 若addr不为0,但设置了SHM_RND,则链接到(addr - (addr mod SHMLBA))所表示的地址。(SHM_RND-->取整,SHMLBA-->低边界地址倍数)
// 
  • shmctl函数
    对共享内存区进行多种操作。
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
// cmd 表示要进行的操作
//--->  IPC_RMID 删除共享内存
//--->  IPC_SET    改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内
//--->  IPC_STAT   得到共享内存的状态,并存到buf中
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值