Linux进程控制编程—进程间通信

1.  进程间通信的目的:

        ①数据传输:一个进程的数据发送给另一个进程;

        ②资源共享:多个进程之间共享同样的资源;

        ③通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件;

        ④进程控制:有些进程希望完全控制另一个进程的执行,此进程希望能够拦截另一个进程的全部操作,并能够及时直到它的状态发生了什么改变;

2.  Linux进程间通信(IPC)由三个部分发展而来:①Unix进程间通信;②基于system V系统进程间通信;③POSIX进程间通信;

        system V是贝尔实验室开发的一个Unix操作系统分支;

        POSIX全称可移植操作系统接口,由IEEE发布该标准,各主流操作系统均支持,目的是提高应用程序的可移植性;

3.  Linux在进程间通信的方式包括:

        ①无名管道(PIPE)、有名管道(FIFO):可能会装满、为空,即读写可能会阻塞,其收发消息都要经过缓冲区,当做特殊设备文件对待;同步通信特性;

        ②信号(signal):异步通信特性;

        ③消息队列:消息链表,比管道的容量大;

        ④共享内存:最快速,效率最高;

        ⑤套接字(socket)(大材小用):即可多机,也可以单机通信;

4.  管道通信

        (1)简介

        ①:管道本质是一个队列,是单向的、先进先出,一个进程在管道的尾部写入数据,另一个进程从管道的头部读出数据;

        ②数据被一个进程读出后,对应数据将从管道中删除;

        ③流控制机制:进程在读空管道时,进程将阻塞;进程在向满管道写入数据时,进程也会阻塞;

        ④配合进程使用时,需要在创建子进程前调用管道创建函数,将管道创建好,这样子进程才可以继承对应的管道;

        ⑤管道分为无名管道、有名管道,区别如下:

无名管道PIPE 有名管道FIFO
生命周期 随进程持续(不属于内核) 随内核持续
作用范围 用于父子进程之间的通信 用于任意两个进程之间通信

        (2)函数实现

无名管道创建

函数原型 int pipe(int filedis[2]);
示例

int pipe_fd[2];  //注意提前定义数组,一定要是2个元素;

pipe(pipe_fd);  //此条命令之后,2个元素分别代表了读写管道,可以把每个元素当做一个文件描述符:可以理解为它创建了两个文件,分别将文件描述符赋值给了2个元素,并且默认打开了这两个文件;

①pipe_fd[0]:read读管道

②pipe_fd[1]:write写管道

注意用完之后要分别关闭管道:

①close(pipe_fd[0]);

②close(pipe_fd[1]); 

参数 filedis[2]:提前定义好的数组(只有两个元素);
返回值

0:创建成功

-1:创建失败

头文件 #include<unistd.h>

        特性:①数据从一个管道读出后,该数据将被从管道中删除;

        ②管道空时,再读会阻塞;管道满时,再写会阻塞;若要更改为非阻塞,配合

        ③通常先在进程创建前生成管道,再创建子进程,这样子进程就可以继承管道,父子进程间就可以通信;这种通信是半双工方式;即fork()前用pipe()创建管道;

无名管道创建

函数原型 int mkfifo(const  char  *  pathname,  mode_t  mode);
示例

vim fifo.dada;  //提前创建一个fifo文件(空的,没有内容),就是普通的空文件;

mkfifo("./fifo.data",  0666);

if(fork>0)  //在父进程中

fd = open("./fifo.data",O_WRONLY);

write(fd,  "hello",  6);

else  //在子进程中

fd = open("./fifo.data",O_RDONLY);

read(fd,  buff,  10);

printf("%s",  buff);

详见下图

参数1

pathname:FIFO文件名

参数2 mode:属性(与open文件操作的属性一致)
返回值

0:创建成功

-1:创建失败

头文件

#include<sys/types.h>

#include<sys/stat.h>

 5.  信号通信

        (1)简介 

        ①信号(signal)机制是Unix中最老的进程间通信方式,信号的来源主要有:用户按键产生信号、硬件异常产生信号、进程用kill命令将信号发送给另一个进程等;

        ②信号类型,常用的几种信号:

SIGINT 来自键盘的中断信号(CTRL-C)
SIGKILL 该信号结束接收信号的进程
SIGTERM kill命令发出的信号
SIGCHLD 标识子进程停止或结束的信号
SIGSTOP 来自键盘(CTRL-Z)或调试程序的停止执行信号
SIGHUP 从终端上发出的结束信号

        ③信号的处理方式:三种

忽略此信号 SIGKILL、SIGSTOP不能被忽略,其他的默认忽略;
执行用户指定的动作 在用户函数中,执行用户希望的处理
执行系统默认动作 按照默认进行

        (2)函数实现:发送信号的函数有两个:kill、raise;区别就在于:

kill raise
既可以向自身进程发送信号,也可以向指定的进程发送信号 只可以可以向自身进程发送信号
函数原型

int  kill(pid_t  pid,  int  signo);

int  raise(int  signo);

示例 /
参数1

pid,指定的进程号,有4种情况:

①pid>0:将信号发送给进程id为pid的进程;

②pid==0:将信号发送给同组的进程;

③pid<0:将信号发送给其进程组id等于pid绝对值的进程;

④pid==-1:将信号发送给所有进程;

参数2 signo,信号种类
返回值

0:成功

-1:失败

头文件

#include<sys/types.h>

#include<signal.h>

alarm

        使用alarm可以设置一个闹钟时间,时间到了,产生SIGALRM信号,如果不捕捉该信号,默认动作是终止该进程;需要配合pause函数使用;每个进程只能有一个闹钟起作用;

函数原型

unsigned int  alarm(unsignde  int  seconds)

示例 /
参数1

seconds:指定的秒数;

返回值

0:成功

>0:被后一个闹铃覆盖后前一个闹铃剩余的时间;

头文件

#include<unistd.h>

pause

        作用是使调用该函数的进程挂起,直至捕捉到一个信号,只有执行了一个信号处理函数后,挂起才结束

函数原型

int  pause(void)

示例 /
参数

无参数;

返回值

0:成功

>0:被后一个闹铃覆盖后前一个闹铃剩余的时间;

头文件

#include<unistd.h>

        (3)信号处理函数,一般采用signal函数;

函数原型

void ( *signal ( int signo, void ( *func ) ( int ) ) ) ( int ) ;

示例

signal ( SIGINT,  myfunc);  //该进程得到SIGINT信号后去执行myfunc自定义函数,而SIGINT就是myfunc的参数;

详见下图;

参数1

signo:信号种类(如SIGINT等);

参数2 func:自定义函数的函数名;
返回值

0:成功

-1:失败

头文件

#include<signal.h>

6.  共享内存通信

        (1)简介

        ①共享内存是指被多个进程共享的一部分物理内存(存在于内核中,其地址映射到进程中),共享内存是进程间共享数据的一种最快的方法;

        ②实现分为4个步骤:创建共享内存(shmget函数),映射共享内存(shmat函数)、解除映射(shmdt函数)、关闭共享内存(shmctl函数);

        (2)函数实现(课件中的shm_comm.c较全面)

shmget:创建共享内存

函数原型

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

示例

/

参数1

key,标识共享内存的键值,可以是指2种情况:

①IPC_PRIVATE:此时由内部分配一块新的共享内存;

②0,那么第三个参数shmflg也要设置为IPC_PRIVATE,那么等同于第一个参数直接设置为IPC_PRIVATE;

参数2

size,需要的共享内存大小,以字节为单位;

系统内核实际分配会以一页(4k)个字节为单位分配;

参数3

shmflg,通常设置为IPC_CREAT,设置共享内存的文件属性权限,举例:

0666|IPC_CREAT;

返回值

>0:成功,返回共享内存id,称为共享内存标识符,就像文件描述符一样;

-1:失败

头文件

#include<shm.h>

shmat:映射共享内存

函数原型

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

示例

/

参数1

shmid,就是shmget返回的共享内存标识符;

整体上就是把第一个参数的共享内存地址映射到第二个参数,即进程;

参数2 shmaddr,当前进程的内核地址,一般不指定,直接写NULL;
参数3

flag,以什么方式来确定映射的地址,一般写0;

很少写SHM_RDONLY,表示只读打开;

返回值

成功:返回该进程中被映射到的地址,可以设置一个char* 变量shm_addr接收;而后就可以当做字符串进行处理了,例如strcpy等;

-1:失败,需要强制转换(char*),再错误处理;

头文件

#include<shm.h>

shmdt:解除映射

函数原型

int  shmdt  ( char * shmaddr );

示例

/

参数1

shmaddr,就是映射函数shmat的映射返回值

返回值

0:成功

-1:失败

头文件

#include<shm.h>

shmctl:关闭共享内存

函数原型

int  shmctl  (int shmid,  int cmd, struct shmid_t *buff );

示例

/

参数1

shmid,对应的共享内存标识符;

参数2 cmd,设置为IPC_RMID;
参数3 直接写NULL;
返回值

0:成功

-1:失败

头文件

#include<shm.h>

7.  消息队列通信

        (1)简介

        ①本质是一个消息的链表,具有特定的格式,消息被读走后,队列中将没有该条记录;

        ②声明周期是随内核持续,需要人工删除或者关机重启内核才行;

        ③目前广泛使用的是“系统V”型的消息队列;

        ④每个消息队列对应唯一的键值;

        (2)函数实现

ftok:键值创建

函数原型

int  ftok  (char* pathname,  char  proj );

功能

返回文件名对应的键值

参数1

pathname,文件名

参数2 proj,项目名(不为0即可)
返回值

0:成功

-1:失败

头文件

#include<sys/types.h>

#include<sys/ipc.h>

msgget:打开、创建消息队列(整体类似于shmget)

函数原型

int  msgget  (key_t  key,  int  msgflg );

功能

返回与键值对应的消息队列描述字,类似于文件描述符;

参数1

key,上述ftok返回的键值;

参数2

msgflg,标志位,类似于shmget中的shmflg,都是表示以什么样的方式打开对应的消息队列:

①IPC_CREAT:创建新的消息队列

②IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误,用 ‘ | ’ 隔开;

③IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞,不常用;

返回值

成功:与键值对应的消息队列描述字,类似于文件描述符;

-1:失败

头文件

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

msgsnd:发送消息

函数原型

int  msgsnd  (int msqid, struct  msgbuf* msgp, int msgsz, int  msgflg );

功能

向消息队列中发送一条消息;

参数1

msqid,已打开的消息队列id;

参数2

msgp,存放消息的结构;

struct  msgbuf

{

    long  mtype;    //消息类型

    char  mtext [1];    //消息数据的首地址
}

参数3 msgsz,消息数据长度;
参数4

msgflg,发送标志,通常设置为

①0:阻塞;

②IPC_NOWAIT,非阻塞;

返回值

0:成功

-1:失败

头文件

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

msgrcv:接收消息

函数原型

int  msgrcv  (int msqid, struct  msgbuf* msgp, int msgsz, long msgtyp, int  msgflg );

功能

从msqid代表的消息队列中读取一个msgtyp类型的消息,并把消息存储在msgp指向的msgbuf结构中。

且读取消息后,队列中的这条消息将自动被删除;

参数1

msqid,已打开的消息队列id;

参数2

msgp,存放消息的结构;

struct  msgbuf

{

    long  mtype;    //消息类型

    char  mtext [ n ];    //接收消息数据的首地址,可根据具体情况改动;
}

参数3 msgsz,消息数据长度;
参数4 msgtyp,和发送消息中存放消息数据中的消息类型保持一致;
参数5

msgflg,发送标志,通常设置为

①0:阻塞;

②IPC_NOWAIT,非阻塞;

返回值

0:成功

-1:失败

头文件

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

8.  信号量

        (1)信号量又称信号灯,主要用途是保护临界资源;进程可以根据它判定是否能够访问某些共享资源。处理用于访问控制,还可以用于进程同步;

        (2)二值信号灯只能取0或者1,类似于互斥锁,但是两个不同:信号灯强调共享资源,只要共享资源可以用,其他进程同样可以修改信号灯的值;而互斥锁更强调进程,占用资源的进程使用完资源后,必须有进程本省来解锁;

        (3)计数信号灯的值可以取任意非负值;

猜你喜欢

转载自blog.csdn.net/m0_72814368/article/details/130261199