当前位置: 首页 > >

Linux进程间通信机制

发布时间:

1. 信号 Linux 系统预定义(早先 32 种)64 种信号,一个信号和一个信号值(整型)对应,一个信 号即是一种事件,当信号发出时,触发事件处理函数,系统为预定义信号定义了默认事件处 理函数。 SIGALRM 超时告警 SIGINT 终端中断 (Ctrl+C 将产生该信号) SIGKILL *终止进程 SIGUSR1 *用户自定义信号 1 SIGUSR2 *用户自定义信号 2 SIGCHLD 子进程已停止或退出 SIGCONT *让暂停的进程继续执行 SIGSTOP *停止执行(即“暂停") 注意:SIGKILL 和 SIGSTOP 不能被捕获,即,这两种信号的事件处理函数不能被用户重新定 义。 (1) 信号的捕获,即信号和时间处理函数的链接实现 包含头文件#include <signal.h> Signal --help man 2 signal typedef void (*sighandler_t)(int);//定义函数指针 sighandler_t signal(int signum, sighandler_t handler);//参数 1 是信号值,参数 2 是信号的事件 响应函数 (可以取特殊值: SIG_IGN 忽略信号 SIG_DFL 恢复默认行为) 一般不判定返回值。 , Sigaction --help man 2 sigaction int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 结构 struct sigaction struct sigaction { void (*sa_handler)(int); /* 信号的响应函数 */ sigset_t sa_mask; /* 是一个信号集,该进程将 */ int sa_flags; /* 当 sa_flags 中包含 SA_RESETHAND 时,接受到该信号并调用 指定的信号处理函数执行之后,把该信号的响应行为重置为默认行为 SIG_DFL */ ... } 信号集的概念:用来表示包含多个信号的集合,用 sigset_t 类型表示,实质是一个无符号长 整形。位运算实现操作。 信号集的基本操作 sigemptyset 把信号集清空 sigfillset 把所有已定义的信号填充到指定信号集 sigdelset 从指定的信号集中删除指定的信号 sigaddset 从指定的信号集中添加指定的信号 sigismember 判断指定的信号是否在指定的信号集中如果是,返回 1 如果不是,返回 ,信号无效, 返回-1 进程的信号屏蔽字概念: 进程的 “信号屏蔽字” 是一个信号集, 当该信号集中的信号产生时, 该进程不会接受到! int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 参数:how: SIG_BLOCK 把参数 set 中的信号添加到信号屏蔽字中 SIG_UNBLOCK 把参数 set 中的信号从信号屏蔽字中删除

SIG_SETMASK 把参数 set 中的信号设置为信号屏蔽字 Oldest 返回原来的信号屏蔽字 获取未处理的信号:当进程的信号屏蔽字中信号发生时,这些信号不会被该进程接受到,可 通过 sigpending 函数(man 2 sigpending)获取这些已经发生了但是没有被处理的信号. (2) 信号的发送 Kill --help man 2 kill #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); alarm –help man 2 alarm #include <unistd.h> unsigned int alarm(unsigned int seconds); 信号使用简单,但只能传递信号值本身。 2.管道 管道只能在具有亲戚关系的进程之间实现,管道的读写端对应管道描述符数组:fd[0]->读, fd[1]->写。 管道的创建: Pipe –help man 2 pipe #include <unistd.h> int pipe(int filedes[2]); 3.命名管道 FIFO 命名管道以普通文件的形式 (在文件系统中有一个确定的路径和文件名) 存在, 无名管道 (即 管道) ,是通过文件描述符的形式使用。 管道的创建: #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 成功返回 0,失败返回-1. 命名管道可以向普通文件一样进行读和写,在非阻塞模式下,一个进程的非正常结束,可能 造成另一个进程读或者写的阻塞。 4.共享内存 两个进程之间共享一段物理内存。不同进程可以对这段物理内存进行读和写。 创建共享内存: int shmget (key_t key, size_t size, int shmflg); 参数:1)key 共享内存的键值 特殊键值:IPC_PRIVATE,该共享内存该创建进程所独享,仅能用于父子进程间的 通信。在消息队列和信号量中同样适用键值这个概念。 2)size 共享内存的大小,单位为字节 3)标志,和 open 的 mode 类似 特殊标志:IPC_CREAT 创建共享内存,多次“创建”不是错误 返回值:成功: 返回一个非负整数,作为共享内存的标识符。失败:返回-1 共享内存的链接,返回一个地址指针,进程通过返回的指针,对共享内存读和写: void *shmat(int shmid, const void *shmaddr, int shmflg);

参数 1:即为 shmget 的返回值。 参数 2:指定关联到该进程的什么位置,一般取 0,让系统自动选择。 参数 3:如果使用 SHM_RDONLY 则该段共享内存是只读的,即使访问权限允许写操作,一 般取 0 返回:返回一个本进程空间内的虚拟指针,该指针关联到共享内存。 读写结束共享内存后,分离地址指针和进程之间的联系,即释放这个地址,下一个进程使用 的时候,再次通过连接获得一个地址。 int shmdt(const void *shmaddr); 参数:即为 shmat 的返回值 注意:仅分离,并不删除该共享内存。 返回值:成功,返回 0,失败,返回-1 共享内存的控制: #include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf) 参数: cmd: IPC_STAT 获取当前状态,返回给参数 3 IPC_SET 设置共享内存(如果权限允许) IPC_RMID(常用) 删除该段共享内存 5. 信号量 信号量不能在进程之间实现数据交换,可以实现临界区的互斥访问。 实现原理: 进入临界区之前, 执行 P 操作, 检测信号量的值, 若大于 0, 则进入临界区执行, 若等于 0,则进程阻塞挂起,执行完临界部分后,执行 V 操作,先检测队列中是否有进程处 于睡眠状态,若有,则唤醒该进程,若没有,则信号量的值加 1. 信号量的获取: #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); 参数 1 是键值, 参数 2 是获取信号量的个数, 参数 3 标志: IPC_CREAT,如果该信号量未存在, 则创建该信号量,成功返回大于 0 的信号量的值,失败返回-1. 信号量的操作: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, unsigned nsops);对信号执行 P 操作或者是 V 操作 struct sembuf { short sem_num; 取 0 , 只 获 取 一 个 信 号 量 short sem_op; // -1, 表示 P 操作, 1, 表示 V 操作 short sem_flg; // SEM_UNDO : 如果进程在终止时,没有释放信号量, 则自动释 放该信号量 } 信号量的控制: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h int semctl(int semid, int semnum, int cmd, ...);

初始化信号量或者是删除信号量,Semnum 你需要控制的信号编号,取 0,若命令为初始化 信号量:SETVAL,则第四个参数 Semun,若系统为定义需要自己定义 #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) #else union semun { int val; //初始值,设为 1,临界区每次值允许一个进程访问, struct semid_ds *buf; unsigned short int *array; struct seminfo *__buf; }; #endif 若命令为 IPC_RMID 则删除信号量。 5.消息队列 消息队列独立于进程的存在,即进程结束以后,消息队列依然可以存在。发送消息的进程只 负责将消息发送到队列中。队列的程度即消息的个数和每个消息的长度读收到系统限制。 消息队列的获取: #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);参数 2 和获取信号量以及获取共享内存一样,IPC_CREAT。成 功返回消息队列的标志符,失败返回-1. 消息的发送和接收: #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); msqid: 获取的消息队列的标志符 msgp:存放需要发送的消息或是接收消息的地址,需自己定义结构体 struct msg_struct { long int msg_type;//是必须的,指定消息类型, char buffer[MSG_SIZE];//自由定义 }; Msgsz:实际消息的大小 sizeof(struct msg_struct)-sizeof(long int) Msgflg:取 0 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); msgtyp:需要接受的消息类型 0: 从消息队列中获取第一个消息,以实现顺序接受(先发先收) >0:从消队列中获取相同类型的第一个消息 <0:从消息队列中获取消息类型<=(msgtyep 的绝对值)的第一个消息 Msgflg:如果包含 IPC_NOWAIT, 则当消息队列中没有指定类型的消息时,立即返回-1,如 果不包含:IPC_NOWAIT,则当消息队列中没有指定类型的消息时,挂起本进程,直到收到指 定类型的消息 返回值:成功,返回接收到的消息的长度(不包含第一个成员 msg_type),失败,返回-1

消息队列的控制: #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf); 删除消息队列:msgctl(msgid,IPC_RMID,0);成功返回 0,失败返回-1.




友情链接: