Linux 进程通信

Linux 进程通信

1.传统进程通信

1.1 信号

信号机制是在软件层次上对中断机制的一种模拟。
信号的捕获与处理也成为系统的“软中断”机制。

1.1.1 常用信号

每个信号都有一个编号和宏定义的名称,这些名字都已SIG开头。宏定义在signal.h头文件中。

1.1.2 信号的处理

缺省操作,默认的信号处理方式。SIG_DFL
忽略信号,进程忽略接收到的信号,但SIGKILL和SIGSTOP不能忽略。SIG_DFL
捕获信号,提供一个信号处理函数,而不是使用默认的信号处理方式。要求内核此时切换到用户态。

CUP的两种状态:内核态和用户态
点击这里有详细介绍

1.1.3 信号处理函数
  1. signal() 定义进程收到信号后的处理方法
#include<signal.h>
void (*signal(int signum, void(*func)(int)))(int);

signum: 信号名称,整数值
func:一个指针,指向信号处理函数。该参数还可以是SIG_DFL或SIG_IGN(前者为默认的缺省操作的函数,后者为默认的忽略信号的函数)
调用成功,返回原来的信号处理函数的指针;失败则返回SIG_ERR

  1. kill() 向进程或进程组发送一个信号
#include<signal.h>
int kill(pid_t pid, int sig);

pid: 接收信号的进程ID。当pid为0时,发送给与当前进程同组的所有进程。
sig: 信号名称
信号发送成功返回0;信号发送失败返回-1。

1.2 管道

连接两个进程的连接器,数据在其中单向流动。

1.2.1 普通管道

位于内存中。只能用于有亲缘关系进程(父子进程)之间的单向通信。
当一个管道建立后,将获得两个文件描述符,分别用来对管道进程读取和写入。通常将其称为管代的写入端和管道的读取端。

这里的写入端和读取端指的是对管道的写入和读取。

pipe() 创建一个普通管道

#include<unistd.h>
int pipe(int filedes[2])

filedes[0]: 读端口的文件描述符
filedes[1]: 写端口的文件描述符
调用成功返回0;调用失败返回-1。

普通管道读写规则:
1. 读规则
关闭管道的写端: close (fd[1])
读出: read(fd[0], buf, size); 从管道读端口fd[0]读出size个字符放到buf中
读出后关闭管道的读端: close(fd[0])
2. 写规则
关闭管道的读端: close(fd[0])
写入: write(fd[1], buf, size); 把buf中的长度为size的字符送到管道写端口fd[1]
写入后关闭管道的写端: close (fd[1])

两个进程通过一个管道只能实现单向通信,如果需要父子进程双向通信,就必须另打开一个管道。
在管道满时,写管道操作将被阻塞;在管道空时,读管道操作将被阻塞。

例子:

/*
 * 利用两个管道进行双向通信,实现父子进程协作把整数x1加到10
 */
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main()
{
    int x = 1;
    int pipe1[2], pipe2[2];
    pid_t val;
    pipe(pipe1);
    pipe(pipe2);
    if((val = fork()) <0 )
        exit(1);
    if(val == 0)
    {
        close(pipe1[1]);
        close(pipe2[0]);
        while(x<=9)
        {
            read(pipe1[0], &x, sizeof(int));
            printf("Child:%d, read:%d\n", getpid(), x++);
            write(pipe2[1], &x, sizeof(int));
        }
        close(pipe1[0]);
        close(pipe2[1]);
    }
    else
    {

        close(pipe2[1]);
        close(pipe1[0]);
        while(x<=10)
        {
            write(pipe1[1], &x, sizeof(int));
            read(pipe2[0], &x, sizeof(int));
            printf("Parent:%d, read%d\n", getpid(), x++);

        }
        close(pipe2[0]);
        close(pipe1[1]);
    }
    return 0;
}
1.2.2 命名管道

位于文件系统中。可以实现不同进程间的双向通信。

mkfifo() 创建命名管道,将产生一个FIFO文件

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

pathname:创建的FIFO文件名
mode:规定FIFO文件的读写权限
调用成功时返回0;调用失败返回-1

命名管道必须同时读和写,否则会阻塞

2. System V IPC 进程通信

2.1 IPC

进程间通信(Inter-Process Communication)

2.1.1 查看ipc对象的信息

ipcs [-asmq]
-a:all,查看全部的IPC对象信息
-s:signal,查看信号量集
-m:memory,查看共享内存
-q:queue,查看消息队列

IPC标识符由内核分配给IPC对象,在系统内部唯一,给系统看
IPC关键字由程序员选择,全局唯一,给人看,32字长
每个进程都可以建立一个键值为IPC_PRIVAE的私有IPC对象

ftok() 生成唯一的键值

#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(char *filename, int id);

filename:文件名,可以使用绝对路径或相对路径
id:整型变量。可通过指定相同文件的文件名和不同的整型变量,生成不同的键值。
调用成功返回所生成的键值;失败返回-1。

/*
 *
 利用消息队列实现父子进程的通信。
 父进程创建消息队列,并向消息队列发送消息;子进程接受消息;
 父进程等待子进程接收消息后删除消息队列。
 *
 */
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/msg.h>
#include<string.h>
struct msgbuf{
    long msgtype;
    char msgtext[255];
};

int create_queue(int key)
{
    int qid = -1;
    if(key == -1)
        perror("ftok");
    else if( (qid= msgget(key, IPC_CREAT|0666)) < 0)
        perror("msgget");
    else return qid;
}

void  send_message(int qid, struct msgbuf *msg, char *message)
{
    strcpy(msg->msgtext, message);
    if(msgsnd(qid, msg, strlen(msg->msgtext), 0) < 0)
        perror("msgsnd");
}

void recive_message(int qid, struct msgbuf *msg)
{
    memset(msg->msgtext, 0, 255);
    if(msgrcv(qid, msg, sizeof(msg->msgtext), msg->msgtype, 0) < 0)
        perror("msgrcv");
}

void distory_queue(int qid)
{
    struct msqid_ds qds;
    if(msgctl(qid, IPC_RMID, &qds) < 0)
        perror("msgctl");
}

int main()
{
    struct msgbuf msg;
    int qid;
    pid_t pid;
    key_t key = ftok(".", 1);   //生成唯一的键值
    msg.msgtype = 1;
    qid = create_queue(key);
    pid = fork();
    if(pid < 0)
        perror("fork");
    else if(pid > 0)
    {
        send_message(qid, &msg, "This is the input!");
        printf("Parent: Send to the message queue successfully!\n");
        printf("The message sent is :%s\n", msg.msgtext);
        wait(NULL);
        distory_queue(qid);
    }
    else
    {
        recive_message(qid, &msg);
        printf("Clild: Receiving from the message queue:%s\n", msg.msgtext);
    }


    return 0;
}

2.2 消息队列

以异步方式为通信频繁但数据量少的进程通信提供服务
存储消息得的线性表,按照先进先出的原则,对数据输入输出。

系统中消息队列最多个数:256
每条消息的最大字节数:8KB
每个队列能容纳的最大字节数:16KB

2.2.1 消息结构模板msgbuf

include/linux/msg.h

struct msgbuf
{
    long msgtype;   //消息类型
    char mtext[1];  //消息内容,可自行定义消息的长度
};
2.2.2 基本操作:
  1. 创建或打开消息队列:msgget()
#inclide<sys/types.h>
#include<sys/msg.h>
int msgget(key_t, int flags);

key:键值
flags:标识和权限信息的组合。若标识部分为0,则获取一个已存在的消息队列的标识符;若为IPC_CREAT:如果消息队列不存在则创建,存在则引用。
调用成功,返回消息队列的标识符;否则,返回-1。

  1. 向消息队列发送消息:msgsnd()
#inclide<sys/types.h>
#include<sys/msg.h>
int msgsnd(int msgid, struct msgbuf *msgp, size_t size, int flag);

msgid:消息队列的标识符
msgp:指向消息结构的指针
size:消息内容的长度
flag:发送消息可选标志。若flag为0,则当消息队列满时,发送操作阻塞;若为IPC_NOWAIT时,当消息队列满时,立即返回-1。
调用成功,返回0;否则,返回-1。

  1. 从消息队列读出消息:msgrcv()
#inclide<sys/types.h>
#include<sys/msg.h>
int msgrcv(int msgid, struct msgbuf *msgp, \
              size_t size, long type, int flag);

msqid:消息队列的标识符
msgp:消息结构指针
size:要读取消息的长度
type:要读取消息的类型
flag:接收消息可选标志。若flag为0,当消息队列为空时,进程阻塞;若flag为IPC_NOWAIT,当消息队列空时,进程不阻塞,立即返回-1;若flag为MSG_NOERROR,当消息长度大于接收缓冲区长度,截断消息返回,小于等于则不接受该消息,出错返回。
调用成功,返回实际读取到的消息数;否则,返回-1。

  1. 删除消息队列:msgctl()
#include <sys/msg.h>
int msgctl(int msgid, int cmd, struct msgid_ds *buf);

msgid:消息队列的标识符
buf:指向msgid_ds结构的指针
cmd:控制命令。若cmd为IPC_RMID, 则删除消息队列;若为IPC_STAT,则获取消息队列的结构,保存在buf所指向的缓冲区中;若为IPC_SET,则按照buf指向的结构来设置该消息队列的结构。
调用成功,返回0;否则,返回-1。
获取和修改消息队列属性信息
查询消息队列描述符
修改消息队列许可权
删除该队列等

2.3 共享主存

2.4 信号量集

远程通信

猜你喜欢

转载自blog.csdn.net/neverwa/article/details/80214302