Linux网络编程--进程间通信(一)

进程间通信简介(摘自《Linux网络编程》p85)

  AT&T 在 UNIX System V 中引入了几种新的进程通讯方式,即消息队列( MessageQueues),信号量( semaphores)和共享内存( shared memory),统称为 System V IPC。在Linux 系统编程中,它们有着广泛的应用。
  System V IPC 的一个显著的特点,是它的具体实例在内核中是以对象的形式出现的,我们称之为 IPC 对象。每个 IPC 对象在系统内核中都有一个唯一的标识符。通过标识符内核可以正确的引用指定的 IPC 对象.。需要注意的是,标识符的唯一性只在每一类的 IPC 对象内成立。比如说,一个消息队列和一个信号量的标识符可能是相同的,但绝对不会出现两个有相同标识符的消息队列。

  标识符只在内核中使用, IPC 对象在程序中是通过关键字( key)来访问的。和 IPC 对象标识符一样,关键字也必须是唯一的。而且,要访问同一个 IPC 对象, Server 和 Client必须使用同一个关键字。因此,如何构造新的关键字使之不和已有的关键字冲突,并保证Server 和 Client 使用的关键字是相同的,是建立 IPC 对象时首先要解决的一个问题。(具体在后边的msg通信中详解)

通信方法还有:半双工管道pipe,命名管道fifo,消息队列,信号量,共享内,socket套接字等,下面一一介绍:

①半双工管道:

  int pipe(int filedes[2]);

  管道是将两个进程之间的标准输入输出相互对接的机制

  linux命令中使用的管道 |  : ls -l | grep *.c  //显示文件(输入端)-(|)-(输出端)>找到.c结尾文件

实现:因为半双工缘故,所以只能实现一段输入,一段输出,而不能双向通信。所以:实现为,通过管道连接进程,一端开放读文件描述,一端开放写文件描述

//管道的性质就是,一个进程的输出作为另一个进程的输入
//那么我们可以关闭一个进程读端使之作为输入端,
//另一个进程关闭写端,读取数据,接收数据作为管道输出端

//FIFO命名管道
//文件系统中,命名管道是特殊文件的方式存在的
//不同进程可以通过命名管道共享数据

//命名管道一直是阻塞方式的,且必须是显示的通过open建立连接到管道的通道
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

#include<sys/types.h>

int main()
{
    int result = 1;
    int fd[2];
    pid_t pid;
    int *write_fd = &fd[1];        //写文件描述
    int *read_fd = &fd[0];        //读文件描述

    int nbytes;
    char str[] = "管道,你好\n";
    char readBuffer[80];
    memset(readBuffer,0,sizeof(readBuffer));

    result = pipe(fd);        //创建管道
    if(-1==result)
    {
        printf("管道创建失败!\n");
        return -1;
    }

    pid = fork();            //进程创建分叉程序
    if(-1 == pid)
    {
        printf("fork失败");
        return -1;
    }

    if(0==pid)            //子进程关闭读端,写入字符
    {
        close(*read_fd);
        result = write(*write_fd,str,strlen(str));
        printf("写入%d个数据\n",result);
    }
    else                //父进程关闭写端,读取数据
    {
        close(*write_fd);
        nbytes = read(*read_fd,readBuffer,sizeof(readBuffer));
        printf("接收到%d个数据,内容为%s",nbytes,readBuffer);
    }
    return 0;
}

②命名管道

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

  类似于普通管道,只是

  a.在文件系统中以设备特殊文件的形式存在

  b.不同进程之间可以通过命名管道共享数据

操作区别于普通管道:FIFO中必须显式通过open建立连接到管道的通道,且总是处于阻塞状态的

③消息队列

  消息队列是内核地址空间的内部链表,通过内核在各个进程之间传递内容。每个消息队列通过唯一IPC标识符标识,不同队列相对独立。

  

//file: msg.h/* message buffer for msgsnd and msgrcv calls */
struct msgbuf {
    __kernel_long_t mtype;          /* type of message */
    char mtext[1];                  /* message text */
};

/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct msqid_ds {
    struct ipc_perm msg_perm;
    struct msg *msg_first;        /* first message on queue,unused  */
    struct msg *msg_last;        /* last message in queue,unused */
    __kernel_time_t msg_stime;    /* last msgsnd time */
    __kernel_time_t msg_rtime;    /* last msgrcv time */
    __kernel_time_t msg_ctime;    /* last change time */
    unsigned long  msg_lcbytes;    /* Reuse junk fields for 32 bit */
    unsigned long  msg_lqbytes;    /* ditto */
    unsigned short msg_cbytes;    /* current number of bytes on queue */
    unsigned short msg_qnum;    /* number of messages in queue */
    unsigned short msg_qbytes;    /* max number of bytes on queue */
    __kernel_ipc_pid_t msg_lspid;    /* pid of last msgsnd */
    __kernel_ipc_pid_t msg_lrpid;    /* last receive pid */
};
//filename/* Obsolete, used only for backwards compatibility and libc5 compiles */
struct ipc_perm
{
    __kernel_key_t    key;  //函数msgget()使用的键值  
    __kernel_uid_t    uid;  //用户UID
    __kernel_gid_t    gid;  //用户GID
    __kernel_uid_t    cuid;  //创建者UID
    __kernel_gid_t    cgid;  //创建者GID
    __kernel_mode_t    mode;   //权限
    unsigned short    seq;  //序列号
};

  内核中的消息队列

注:结构list_head 形成一个链表,结构msg_msg之中的m_list使得消息形成链表,查找,插入时,对m_list域进行偏移找到位置

相关函数:

  键值构建 key_t ftok(const char* pathname,int proj_id);

  获取消息 int msgget(key_t key,int msgflg);

  发送消息 int msgsnd(int msqid, const void * msgp,size_t msgsz,int msgflg);

  接收消息 ssize_t msgrcv(int msqid, void * msgp, size_t msgsz, long msgtype, int msgflg);

  消息控制 int msgctl(int msqid, int cmd, struct msqid_ds *buf);  //向内核发送cmd命令判断进行何种操作

一个简单例子

④信号量

  信号量是一种计数器,用来控制对多个进程共享的资源所进行的访问。常用作锁机制(生产者消费者模型是个典型使用)

  信号量结构

//filename sys/sem.h/* arg for semctl system calls. */
union semun {
    int val;            /* value for SETVAL */
    struct semid_ds *buf;    /* buffer for IPC_STAT & IPC_SET */
    unsigned short *array;    /* 数组结构 */
    struct seminfo *__buf;    /* 信号量内部结构 */
    void *__pad;
};

  相关函数

  新建信号量 int semget(key_t key, int nsems, int semflg);

  //key 来自于ftok()

  信号量操作函数 int semop(int semid,struct sembuf* sops, unsigned nsops);

  //信号量的P,V操作通过向已经建立好的信号量发送命令完成

  控制信号量参数

  int semctl(int semid, int semnum ,int cmd,.....);

  //用于在信号量集合上执行控制操作

#include<stdio.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>

typedef int sem_t;
union semun
{
    int val;
    struct semid_ds * buf;
    unsigned short *array;
}arg;

sem_t CreateSem(key_t key, int value)
{
    union semun sem;
    sem_t semid;
    sem.val = value;

    semid = semget(key,0,IPC_CREAT);
    if(-1 == semid)
    {
        printf("create semaphore error\n");
        return -1;
    }

    semctl(semid,0,SETVAL,sem);

    return semid;
}

int Sem_P(sem_t semid)
{
struct sembuf sops = {0,+1,IPC_NOWAIT};
return (semop(semid,&sops,1));
}

int Sem_V(sem_t semid)
{
    struct sembuf sops = {0,-1,IPC_NOWAIT};
    return (semop(semid,&sops,1));
}

void SetvalueSem(sem_t semid , int value)
{
    union semun sem;
    sem.val = value;
    semctl(semid,0,SETVAL,sem);
}

int GetvalueSem(sem_t semid)
{
    union semun sem;
    return semctl(semid,0,GETVAL,sem);
}

void DestroySem(sem_t semid)
{
    union semun sem;
    sem.val = 0;
    semctl(semid,0,IPC_RMID,sem);
}
int main()
{
    key_t key;
    int semid;
    char i;
    int value = 0;
    key = ftok("/ipc/sem",‘a‘);

    semid = CreateSem(key,100);
    for( i = 0;i <= 3;++i)
    {
        Sem_P(semid);
        Sem_V(semid);
    }
    value = GetvalueSem(semid);    

    DestroySem(semid);
    return 0;
}

⑤共享内存(最快捷的方法)没有中间过程,管道等

  在多个进程之间共享内存区域的一种进程间通信方式,在多个进程之间对内存段进行映射的方式实现内存共享。

  相关函数

  创建共享内存函数 int shmget(key_y key, size_t size, int shmflg);

  获得共享内存地址void * shmat(int shmid,const void* shmaddr, int shmflg);

  删除共享内存函数 int shmdt(const void* shmadddr);

  共享内存控制函数 int shmctl(int shmid ,int cmd, struct shmid_ds * buf);

⑥信号

  用于在一个或多个进程之间传递异步信号。

  相关函数

  信号截取 sighandler signal(int signum ,sighandler handler);

  发送信号 int kill(pid_t pid, int sig);

       int raise(int sig);

  

  

  

  

时间: 2024-08-02 06:49:30

Linux网络编程--进程间通信(一)的相关文章

很全的linux网络编程技巧

注:作者王晓,本人认为总结得很好,故记之,绝无侵权之意. 1. LINUX网络编程基础知识 1 1.1. TCP/IP协议概述 1 1.2. OSI参考模型及TCP/IP参考模型 1 1.3. TCP协议 3 1.4. UDP协议 5 1.5. 协议的选择 6 2. 网络相关概念 6 2.1. socket概念 7 2.2. socket类型 8 2.3. socket信息数据结构 8 2.4. 数据存储优先顺序的转换 8 2.5. 地址格式转化 9 2.6. 名字地址转化 10 3. sock

Linux网络编程(一)

1.socket     int socket(int domain, int type,int protocol) domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等). AF_UNIX只能够用于单一的Unix系统进程间通信,而AF_INET是针对Internet的,因而可以允许在远程 主机之间通信(当我们 man socket时发现 domain可选项是 PF_*而不是AF_*,因为glibc是posix的实现 所以用PF代替了AF,不过我们都可以使用的

Linux网络编程入门 (转载)

http://www.cnblogs.com/RascallySnake/archive/2012/01/04/2312564.html (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件

嵌入式 Linux网络编程(一)——Socket网络编程基础

嵌入式 Linux网络编程一--Socket网络编程基础 一.Socket简介 1.网络中进程间通信 本机进程使用进程号区别不同的进程进程间通信方式有管道.信号.消息队列.共享内存.信号量等.网络中进程间的通信首先需要识别进程所在主机在网络中的唯一标识即网络层的IP地址主机上的进程可以通过传输层的协议与端口号识别. 2.Socket原理 Socket是应用层与TCP/IP协议族通信的中间软件抽象层是一种编程接口.Socket屏蔽了不同网络协议的差异支持面向连接(Transmission Cont

Linux网络编程入门

(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍 客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端 在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一 个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序. 服务端 和客户端相对应的程序即为服务端程序.被动的等待外面的程序来和自己通

Linux网络编程:原始套接字的魔力【上】

基于原始套接字编程 在开发面向连接的TCP和面向无连接的UDP程序时,我们所关心的核心问题在于数据收发层面,数据的传输特性由TCP或UDP来保证: 也就是说,对于TCP或UDP的程序开发,焦点在Data字段,我们没法直接对TCP或UDP头部字段进行赤裸裸的修改,当然还有IP头.换句话说,我们对它们头部操作的空间非常受限,只能使用它们已经开放给我们的诸如源.目的IP,源.目的端口等等. 今天我们讨论一下原始套接字的程序开发,用它作为入门协议栈的进阶跳板太合适不过了.OK闲话不多说,进入正题. 原始

[转] - Linux网络编程 -- 网络知识介绍

(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序. 服务端        和客户端相

Linux网络编程视频 百度网盘

Linux网络编程(总共41集)讲解Linux网络编程知识,分以下四个篇章.Linux网络编程之TCP/IP基础篇Linux网络编程之socket编程篇Linux网络编程之进程间通信篇Linux网络编程之线程篇Linux网络编程之TCP/IP基础篇01TCPIP基础(一)ISO/OSI参考模型TCP/IP四层模型基本概念(对等通信.封装.分用.端口)02TCPIP基础(二)最大传输单元(MTU)/路径MTU以太网帧格式ICMPARPRARP03TCPIP基础(三)IP数据报格式网际校验和路由04

linux网络编程_1

本文属于转载,稍有改动,以利于学习. (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程