【Linux】Linux进程间通信的步骤
【Linux】Linux进程间通信的方法Linux进程间通信的方法概述为以下七种:1、管道(pipe)2、有名管道(named pipe)3
【Linux】Linux进程间通信的方法
Linux进程间通信的方法概述为以下七种:
1、管道(pipe)
2、有名管道(named pipe)
3、信号量
4、消息队列
5、信号
6、共享内存
7、套接字
-------------------------------------------
linux进程间通信
1. 管道。
在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为: · 1) 限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。 · 2)读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。 注意:从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。 我们可以使用管道符|来连接进程.在Linux系统中,由管道连接起来的进程可以自动运行,就如同在他们有一个数据流一样.在下面的这个例子中,我们要使用sort命令来排序ps的输出.而如果我们不使用管道,我们就要分几步来完成: $ ps > psout.txt $ sort psout.txt >pssort.out 一个更好的办法就是可以用管道来处理: $ ps | sort > pssort.out 因为我们要在屏幕上看到他们,我们要使用第三个进程: $ ps | sort | more 使用的管道数并没有一个量的限制.如果我们要看到正在运行的除了shell以外的不同名字的进程,我们可以用下面的命令: $ ps -xo comm | sort | uniq | grep -v sh | more 在这个命令中,使用了ps的输出,将这个输出以字母的顺序进行排序,使用uniq来解压进程,使用grep -v sh来移除名为sh的进程,最后在屏幕上显示结果. 在这里我们就可以看到,这样的方式式要比单个执行的命令好得多.在这里我们要注意的一点点就是,在这个命令中我们不要两次使用同一个文件.如下面的命令: $ cat mydate.txt | sort | uniq | >mydate.txt 这样我们就会得到一个空文件,因为在我们读取这个之前已经改写了这个文件.
#include <unistd.h> 函数原型: int pipe(int fd[2]) unix中欧你多大许多命令被设计为过滤器 从标准输入中读取输入 将输出传送到 标准输出 bash用“|” 在两个命令之间创建管道
2. FIFO特别文件
有名管道,作为特别文件存储于文件系统中。有名管道一旦建立就存在于文件系统中,除非显示的unlink
#include<sys/tpes.h>
#include<sys/stat.h>
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);
path:创建有名管道的全路径名
mod:创建有名管道的模式,指存取权限
dev:设备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到
注意:有名管道创建后就可以使用了,有名管道和管道的使用方法基本是相同的。只是使用有名管道的时候必须先调用open()将其打开
因为有名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件
3. 消息队列、信号量和共享内存
函数 msgget semget shmget msgctl semctl shmctl
消息队列、信号量和共享内存都是ipdc资源
每个ipc资源有两个唯一的标志与其相连 关键字和标识(关键字类似于文件名,标志类似于文件描述字用来访问资源的操作)
关键字获得函数
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char * pathname,int proj_id);
ipc_perm{ //结构,当进程创建ipc资源时,内核存储了ipc资源的属主和组id和访问权限等信息。
uid_t uid //当前有效用户id
gid_t gid //当前有效组id
cuid_t cuid //创建用户id
cgid_t cgid //创建组id
mode_t mode //访问权限
};
ipc资源创建之后,可以长期存储在文件系统中,直到被删除或者系统重启。
shell命令:ipcs -q -s -m ipcrm
A、消息队列
struct msqid_ds { //消息队列相连的数据结构
struct ipc_perm msg_perm; //该消息队列的属主和访问权限
struct msg* msg_first; //指向第一个消息的指针
struct msg* msg_last; //指向最后一个消息的指针
msgqnum_t msg_qnum; //当前消息的个数
msglen_t msg_qytes; //最大容量字节数
pid_t msg_lspid; //最后一个调用msgsnd的进程
pid_t msg_lrpid; //最后一个调用msgrcv的进程
time_t msg_stime; //最后发送时间
time_t msg_rtime; //最后接收时间
time_t msg_ctime; //最后修改时间
}
struct msg {
struct msg* mas_next; 下个消息的指针
long msg_type; 消息的类型
caddr_t msg_sport; 消息正文地址
sort msg_ts; 消息正文的大小
}
int msgget(key_t key, int msgflg);
key:为ftok函数的返回值
msgflag:标志参数,可取值为IPC_CREATE,IPC_EXCL
IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)
int msgctl(int msgid, int cmd, struct msqid_ds* buf);
控制消息队列cmd
IPC_STAT 获得msgid的消息队列数据结果到buf中
IPC_SET 设置buf中ipc_perm为消息队列的新值
IPC_RMID 删除消息队列
int msgsnd(int msgid, const void* msg, size_t size, int msgflag);
mysgid:消息队列标识符
msg:指向发送的消息
size:要发送消息的大小,不包含消息类型占用的4个字节
msgflag:操作标志位,可以设置为0或者IPC_NOWAIT
0:当消息队列已满的时候,msgsnd将会阻塞,直到消息可以写进消息队列
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回。
msgsnd()为阻塞函数,当消息队列容量满或者消息个数满会阻塞,如果消息队列被删除,则EIDRM错误,被信号中断E_INTR。
如果设置IPC_NOWAIT会返回-1并且置EAGAIN错误
ssize_t msgrcv(int msgid, void* msg, size_t size, long int msgtyp, int msgflag);
size实际上是指正文段的大小,总大小应该是size+4字节,接收是同样的意思
msgtyp:请求读取的消息类型。
msgflag: 操作标志位,可以为IPC_NOWAIT,IPC_EXCEPT,IPC_NOERROR
IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG;
IPC_EXCEPT:与msgtyp配合使用返回队列中第一个类型不为msgtyp的消息
IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将被丢弃
long int msgtyp if == 0 接收第一个消息
if > 0 接收类型等于msgtyp的第一个消息
if < 0 接收类型等于或者小于msgtyp绝对值的第一个消息
B、信号量
struct semid_ds {
struct ipc_perm sem_perm; 包含信号量资源的属主和访问权限
struct sem *sem_base; 指向信号量集合的指针
unsigned short int sem_nsems; 集合中信号的个数
time_t sem_otime; 最后一次操作的时间
time_t sem_ctime; 最后一次修改的时间
}
struct sem {
unsigned short semval; 当前信号量值
pid_t sempid; 最后修改信号量的进程
unsignde short semcnt; 等待进行p操作的进程数
unsigned short semzcnt; 等待semval为0的进程数
}
int semget(key_t key, int semnum, int flag)
key:ftok函数的返回值
semnum:要创建的信号集包含的信号个数,如果只是打开信号集,其值为0即可
flag:标志参数,可取值为IPC_CREATE,IPC_EXCL,IPC_CREATE|IPC_EXCL
IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)
信号量操作
int semop(int semid, struct sembuf* buf, int bufsize);
semid:信号集的标志符
buf:指向进行操作的结构体数组首地址
bufsize:指出将要进行操作的信号的个数
struct sembuf {
short semnum; 信号量集合中的信号量编号
short val; val>0进行v操作加val,表示进程释放控制的资源;
val<0进行p操作减val,若没有设置IPC_NOWAIT,则调用进程阻塞,直到资源可用;否则进程直接返回EAGAIN
val=0时阻塞等待信号量为0;若没有设置IPC_NOWAIT,调用进程进入睡眠状态,直到信号值为0;否则进程不会睡眠,直接返回EAGAIN
short flag; IPC_NOWAIT IPC_UNDO当执行pv操作之后,进程突然终止没有进行相应的vp操作
}buf;
semctl(int semid, int semnum, int cmd, union semun arg) 可以为信号量初始值,删除信号量设置权限等
semid:信号集标志符
semnum: 为信号量集合中的某个信号量操作
cmd: SETVAL,IPC_STAT,IPC_SET,SETALL,GETALL,IPC_INFO,IPC_RMID
union semun {
short val; 设置信号量的值SETVAL
struct semid_ds* buf; 设置或者获得semid_ds结构IPC_STAT,IPC_SET
unsigned short* array; 指向信号量的数组,用于集体初始化SETALL,GETALL
struct seminfo *buf; 为控制IPC_INFO提供的缓存
} arg;
IPC_SET:对信号集的属性进行设置
IPC_RMID:把semid指定的信号集从系统中删除
GETPID:返回最后一个执行semop操作的进程的ID
GETVAL:返回信号集中semnum指定信号的值
GETALL:返回信号集中所有信号的值
GETNCNT:返回正在等待资源的进程的数量
GETZCNT:返回正在等待完全空闲资源的进程数量
SETVAL:设置信号集中semnum指定的信号的值
SETALL:设置信号集中所有信号的值
C、共享存储
shmget(key_t key, int size, int flag)
key:ftok返回的值
size:以字节为单位指定内存的大小
flag:标志参数,可取值为IPC_CREATE,IPC_EXCL,IPC_CREATE|IPC_EXCL
IPC_CREATE:当系统不存在和key相连的消息队列时,就创建一个key为关键字的消息队列(用于创建一个新的队列)
IPC_EXCL: 和IPC_CREAT一起使用时,当已经存在队列时,则返回失败(防止关键字重复)
size为n时表示获得或者创建这么大的共享段
size为0时表示直接获得整个共享段
size大于创建的共享段时,返回错误
(创建时指定n, 获得时一般直接为 0)
struct shmid_ds {
struct ipc_perm shm_perm;
size_t shm_segsz 大小
pid_t shm_pid 最后操作的进程id
pid_t shm_cpid 创建的进程id
shmatt_t shm_nattch 当前连接数
time_t shm_atime 最后调用shmat的时间
time_t shm_dtime 最后调用shmdt的时间
time_t shm_ctime 最后调用shmctl的时间
}
void* shmat(int shmid, const void* shmaddr, int flag);
shmid:共享内存区的标志符
shmaddr一般为0,内核自动为程序一个连接到共享内存段的地址返回给进程;共享内存的附加点
flag SHM_RDONLY SHM_RND(当shmaddr设置为自己的值时,shm_rnd标志设置后,将会对这个地址做适当的截断以适合系统的对齐)
参数shmaddr不同取值的含义如下:
如果为空,则由内核选择一个空闲的内存区;如果非空,返回地址取决于调用者是否给flag参数指定了SHM_RND值,如果没有指定,则共享内存区附加到由shmaddr指定
的地址;否则附加地址为shmaddr向下舍入一个共享内存低端边界地址后的地址(SHMLBA,一个常址)
通常将参数shmaddr设置为NULL
int shmdt(const void* shmaddr); //断开与共享内存区的联系
shmaddr:shmat的返回值
只有当shm_nattch为0后,即没有任何进程再使用该共享内存区,共享内存区才在内核中被删除,一般来说,当一个进程终止时,他所附加的共享内存区都会自动脱离
shmctl(int shmid, int cmd, struct shmid_d* buf);
shmid:共享内存区的标志符
cmd:IPC_STAT IPC_SET IPC_RMID SHM_LOCK SHM_UNLOCKroot用户专用
buf:指向shmid_ds结构体的指针