Linux:进程间通信


进程间通信(IPC)

1 管道

  • 通过在内存中创建管道文件实现进程间的通信
  • 大小为0,磁盘不存储内容
  • 文件操作符,使用和普通文件操作一样
    进程打开:0标准输入,1标准输出,2标准错误输出
  • 创建管道文件命令
mkfifo 管道文件名
  • 有名管道:任意进程通信
  • 无名管道:只能父子进程通信
    • 半双工:某一时刻只能进行单向操作

单工:只能单向操作
全双工:同时进行双方操作

#include <unistd.h>
int pipe(int pipefd[2]);
//pipe()创建一个管道
//数组pipefd用于返回两个指向管道末端的文件描述符
//pipefd[0]:读端
//pipefd[1]:写端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<fcntl.h>

int main()
{
    int fd[2];
    pipe(fd);
    pid_t pid = fork();
    assert(pid!=-1);

    if(pid == 0)
    {
        close(fd[1]);//关闭子进程的写操作
        char buf[128] = {};
        read(fd[0],buf,127);
        printf("%s\n",buf);
        close(fd[0]);
    }
    else
    {
        close(fd[0]);//关闭父进程的读操作
        write(fd[1],"hello",5);
        close(fd[1]);
    }
}

管道必须是双端操作
读端关闭,写端接收到SIGPIPE信号,终止写入。

2 信号量

  • 临界资源:某一时刻只允许一个进程进行访问的资源
  • 临界区:访问临界资源的代码段
  • 特殊变量
    • 原子增值,p:等待,获取资源(当信号量为0时,进程堵塞)
    • 原子减值,v:发送信号,释放资源

原子内存操作,底层CPU支持,硬件加速

  • 二值信号量:0,1(可访问 / 不可访问)
  • 计数信号量:可获取资源计数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//创建一个信号量或取得信号量的key
int semget(key_t key, int nsems, int semflg);
//key: 唯一key值,不同进程通过key值访问同一个信号量
//nsems: 指定创建信号量的个数
//semflg: 标志位,信号量的权限,联合使用IPC_CREAT和IPC_EXCL确保创建唯一的新信号量
//返回值:-1,创建失败;>0,成功,返回信号量标识符semid
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//改变信号量的值,实现pv操作
int semop(int semid, struct sembuf *sops, size_t nsops);
//semid: semget返回的信号量标识符
//struct sembuf *sops
//{
//	unsigned short sem_num;//信号量编号
//	short          sem_op;//操作信号量的数值;-1,p操作;+1,v操作
//	short          sem_flg;//标志,SEM_UNDO当进程终止时,该操作将自动撤销,用于释放资源
//}
//nsops: sops结构体个数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//控制信号量信息
int semctl(int semid, int semnum, int cmd, ...);
//semid: semget返回的信号量标识符
//semnum: 信号量编号,唯一一个信号量时为0,
//cmd: 命令参数,SETVAL设置信号量初始值,IPC_RMID删除信号量
//...:SETVAL需要第四个参数,union semun结构体
//union semun//需要外部自己定义联合体
//{
//	int val;//SETVAL使用该成员,将其设置为信号量的初始值
//	struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
//	unsigned short  *array;  /* Array for GETALL, SETALL */
//	struct seminfo  *__buf;  /* Buffer for IPC_INFO
//                              (Linux-specific) */
//};
ipcs  #查看消息队列、共享内存、信号量通信情况
------ Message Queues --------//消息队列
key        msqid      owner      perms      used-bytes   messages    

------ Shared Memory Segments --------//共享内存
key        shmid      owner      perms      bytes      nattch     status           

------ Semaphore Arrays --------//信号量
key        semid      owner      perms      nsems       
0x000002c4 458752     root       600        1

ipcs -s  #查看信号量
ipcrm -s semid  #删除信号量
ipcs -q  #查看消息队列
ipcs -m  #查看共享内存
gcc -o PrinterA PrinterA.c sem.c

gcc -c sem.c
gcc PrinterA.c sem.o -o PrinterA
1  0  0
A  B  C
p1 p2 p3
v2 v3 v1

3 共享内存

  • 不同进程共享同一个物理内存块
#include <sys/ipc.h>
#include <sys/shm.h>
//创建或获取共享内存
int shmget(key_t key, size_t size, int shmflg);
//key: 唯一key值,不同进程通过key值访问同一个内存
//size: 指定创建共享内存的大小
//shmflg: 标志位,共享内存的权限,IPC_CREAT获取共享内存,不存在则创建
//返回值:-1,创建失败;>0,成功,返回共享内存的ID,shmid
#include <sys/types.h>
#include <sys/shm.h>
//将共享内存映射到进程的虚拟地址空间
void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmid: shmget返回的共享内存的ID
//shmaddr: 映射的虚拟地址,一般为NULL,有系统自动选择
//shmflg: 标志位;0,默认可读可写;SHM_EDONLY设置为只读模式
//返回值:共享内存的首地址,或失败返回NULL
#include <sys/types.h>
#include <sys/shm.h>
//断开共享内存和进程虚拟地址空间之间的映射
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
//shmaddr:映射的虚拟地址
//返回值:0,成功;-1,失败
#include <sys/ipc.h>
#include <sys/shm.h>
//控制共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//shmid: shmget返回的共享内存的ID
//cmd: 命令参数,IPC_RMID销毁共享内存
//struct shmid_ds *buf
//返回值:0,成功;-1,失败

4 消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//创建或获取一个消息队列
int msgget(key_t key, int msgflg);
//唯一key值,不同进程通过key值访问同一个消息队列
//msgflg: 标志位,消息队列的权限,IPC_CREAT获取共享内存,不存在则创建
//返回值:-1,创建失败;>0,成功,返回消息队列的ID,msqid
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
//msqid: msgget返回的消息队列的ID
//msgp:消息结构体指针
struct msgbuf
{
	long mtype;//消息类型,必须 > 0
	char mtext[1];//消息数据
};
//msgsz:消息数据的大小
//msgtyp:接收消息的类型,0无类型,默认接收所有类型的消息
//msgflg:标志位,默认0

5 套接字

套接字地址:IP+Port

#include <sys/socket.h>
//通用套接字地址结构
struct sockaddr 
{
	sa_family_t sa_family;//地址族,IPV4(AF_INET),IPV6(AF_INET6)
	char sa_data[]; /* Socket address */
};
//专用,IPV4
struct sockaddr_in
{
	sa_family_t sa_family;//地址族,AF_INET
	u_int16_t sin_port;//端口号
	struct in_addr sin_addr;//IP地址
	//struct in_addr
	//{
	//u_int32_t s_addr;
	//}
};
//专用,IPV6
struct sockaddr_in6
{
	sa_family_t sa_family;//地址族,AF_INET6
	u_inet16_t sin6_port;//端口号
	u_int32_t sin6_flowinfo;//流信息
	struct in6_addr sin6_addr;//IP地址
	//struct in6_addr
	//{
	//unsigned char sa_addr[16];
	//}
};

网络字节序:大端(htons)
1024(root) ~ 4096(保留)~ 临时端口

#include <arpa/inet.h>
//长整型,主机转网络
uint32_t htonl(uint32_t hostlong);
//短整型,主机转网络
uint16_t htons(uint16_t hostshort);

//长整型,网络转主机
uint32_t ntohl(uint32_t netlong);
//短整型,网络转主机
uint16_t ntohs(uint16_t netshort);

IP地址转换函数
十进制字符串表示IPV4

 #include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//字符串转网络字节序
in_addr_t inet_addr(const char *cp);
//网络字节序转字符串
char *inet_ntoa(struct in_addr in);

typedef uint32_t in_addr_t;//无符号整型
struct in_addr 
{
	in_addr_t s_addr;
};


参与评论 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页

打赏作者

执行x

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值