嵌入式Day27--进程间通信机制(IPC) 进程间空间独立不能直接通信数据共享需要使用IPC机制进程间通信的方法同一主机间进程通信有名管道无名管道信号共享内存消息队列信号量集不同主机间进程间通信网络套接字无名管道无名管道只能用在具有亲缘关系的进程间通信有名管道可以用在任意进程间通信int pipe(int pipefd[2]); 功能创建一个无名管道并获得两个文件描述符 参数 pipefd保存读写端对应的文件描述符 pipefd[0] ---读端 pipefd[1] ---写端 返回值 成功0 失败-1 读管道read(); 写管道write(); 关闭管道close(); 注意 1. 管道在使用时要确定它的方向是一个单向的数据通道 2. 管道中的数据一旦被读走直接剪切走数据 3. 管道中的数据遵循先进先出的特点FIFO、 4. 管道默认大小65536字节64k。 5. 管道的读端和写端不能交换。管道的特点读阻塞当读写端都存在时从管道中读数据管道为空时发生读阻塞写阻塞当管道读写段都存在时向管道中写数据管道满时发生写阻塞管道破裂当读端关闭向管道中写入数据时发生管道破裂异常读返回0当写端关闭读管道时管道中有数据则读出数据没有数据则读不发生阻塞直接返回0示例#include stdio.h #include head.h // pipefd[0] --- read // pipefd[1] --- write int main(int argc, const char *argv[]) { int pipefd[2]; int ret pipe(pipefd); if (ret ! 0) { perror(pipe error); return -1; } pid_t pid fork(); if (pid 0) { close(pipefd[0]); write(pipefd[1], hello world, 11); wait(NULL); close(pipefd[1]); } else if (0 pid) { char buff[512] {0}; close(pipefd[1]); ssize_t size read(pipefd[0], buff, sizeof(buff)); printf(size %ld,buff %s\n, size, buff); close(pipefd[0]); } else { perror(fork error); } return 0; }有名管道有名管道同一主机任意进程间通信。通过创建一个管道文件实现p。int mkfifo(const char *pathname, mode_t mode); 功能创建一个管道文件 参数 pathname管道文件名称 mode管道文件的读写执行权限 返回值 成功0 失败-1 打开管道文件open 读写管道文件read/write 关闭管道文件close头文件 #ifndef __HEAD_H__ #define __HEAD_H__ #include sys/types.h #include unistd.h #include stdio.h #include stdlib.h #include sys/wait.h #include string.h #include sys/fcntl.h #include sys/stat.h #include error.h #include errno.h #endif//写管道 #include head.h int main(int argc, char **argv) { int ret mkfifo(pipe, 0664); if (ret 0 errno ! EEXIST) { perror(mkfifo error); return -1; } int fd open(pipe, O_WRONLY); if (fd 0) { perror(open pipe error); return -1; } ssize_t size write(fd, hello world, 11); printf(size is %ld\n, size); close(fd); return 0; }//读管道 #include head.h int main(int argc, char **argv) { int ret mkfifo(pipe, 0664); if (ret 0 errno ! EEXIST) { perror(mkfifo error); return -1; } int fd open(pipe, O_RDONLY); if (fd 0) { perror(open error); return -1; } char buff[1024] {0}; ssize_t size read(fd, buff, sizeof(buff)); printf(size is %ld,buff is %s\n, size, buff); close(fd); remove(pipe); return 0; }信号信号进程间的通知机制异步通信信号软中断。CPU正在执行的任务可以被一个信号打断中断后执行新的任务新的任务结束继续执行之前的任务。信号的操作发送信号 killkill()注册信号signal()信号的处理方式忽略不处理缺省按照操作系统默认方式处理捕获按照自定义方式处理kill -l 查看信号 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 此处省略部分信号 2 SIGINT:ctrl c 让一个进程被打断 3 SIGQUIT:ctrl \ 让一个进程结束 9 SIGKILL: 强制让一个进程结束 11SIGSEGV: 让一个进程结束(段错误) 13SIGPIPE: 让一个进程结束(管道破裂) 14SIGALRM: 让一个进程结束(定时时间到达) *17SIGCHLD: 子进程结束时发送给父进程隐含的发送 18SIGCONT: 让停止态的进程继续执行 19SIGSTOP: 让运行态的进程进入停止态暂停强制停止 20SIGTSTP:ctrl z 让进程进入暂停态后台进程来自终端的停止信号 10 SIGUSR1 12 SIGUSR2 9 SIGKILL 19 SIGSTOP 不能被忽略和捕捉typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 功能注册一个信号 参数 signum 需要注册的信号的编号 handler信号的处理方式 SIG_IGN : 以忽略方式处理 SIG_DFL 以缺省默认方式处理 handler 以捕获方式处理自定义自定的信号任务处理函数 返回值 成功返回handler 失败SIG_ERR 注意 1. 信号只需要注册一次 2. 信号能早注册就早注册 3. 信号函数尽可能的快、短、小尽可能避免长延时、阻塞、休眠等耗时操作 4. 使用sigaction替代signal可以屏蔽同源信号防止产生信号触发嵌套 pause() 功能让一个进程挂起休眠 可以使用一个信号将pause唤醒该信号必须被捕获。 unsigned int alarm(unsigned int seconds); 功能设置一个闹钟闹钟时间到时可以发送一个SIGALRM信号 参数 seconds设置的闹钟的秒数示例防止信号触发嵌套#include signal.h #include sys/types.h #include head.h void handler(int signum) { int cnt 3; while (1) { cnt--; printf(Son is doing homework\n); fflush(stdout); sleep(1); if (0 cnt) { break; } } } int main(int argc, char **argv) { pid_t pid fork(); if (pid 0) { int cnt 4; while (cnt--) { printf(Father is working\n); sleep(1); } kill(pid, SIGUSR1); while (1) { } wait(NULL); } else if (0 pid) { struct sigaction act; act.sa_handler handler; sigemptyset(act.sa_mask); //执行handler期间屏蔽SIGUSR1杜绝嵌套触发 sigaddset(act.sa_mask, SIGUSR1); act.sa_flags 0; sigaction(SIGUSR1, act, NULL); // signal(SIGUSR1,handler); while (1) { printf(Son is playing\n); sleep(1); } } else { perror(fork error); } return 0; }#include head.h void handler(int signum) { printf(%d signal comming\n, signum); } int main(int argc, const char *argv[]) { signal(SIGALRM, handler); alarm(5); printf(hello world\n); pause(); printf(how are you\n); return 0; }消息队列System V提供的IPC对象用于进程间通信。消息队列可以发送指定消息类型的消息可用于设置消息的优先级查看IPC对象的命令 ipcs -a 查询共享内存信号量集消息队列 ipcrm -s 删除信号量集 -m 删除共享内存 -q 删除消息队列 ipcrm -m shmid ipcrm -M shmkey ipcrm -q msgid ipcrm -Q msgkey获取IPC对象key值ftok()key_t ftok(const char *pathname, int proj_id); 功能获取IPC对象的key值 参数 pathname路径名 proj_id工程id 返回值 成功key值 失败-1创建消息队列msgget()int msgget(key_t key, int msgflg); 功能:创建消息队列并获得一个消息队列的id 参数 keyIPC对象key值 msgflg操作方式 IPC_CREAT | 0664 返回值 成功id 失败-1发送消息msgsnd()int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 功能向消息队列发送消息 参数 msqid消息队列id msgp发送的消息体 msgsz消息体中的正文大小 msgflg0 默认方式发送 成功 成功0 失败-1 struct msgbuf { long mtype; /* message type, must be 0 */ char mtext[1]; /* message data */ };接收消息msgrcv()ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); 功能从消息队列接收消息 参数 msqid消息队列id msgp存放消息体的空间地址 msgsz消息体正文大小 msgtyp接收的消息类型 0 按顺序收 0 : mtype 接收指定mtype的消息 0 :接收|mtype |的消息:-100 100 msgflg0 默认方式接收 返回值 成功返回实际收到的消息体正文的大小 失败-1删除消息队列msgctl()int msgctl(int msqid, int cmd, struct msqid_ds *buf); 功能对消息队列执行操作 参数 msqid消息队列id cmd IPC_STAT :获取状态 IPC_SET:设置状态 IPC_RMID删除 buf 删除NULL 返回值 成功0 失败-1示例//头文件 #ifndef __HEAD_H__ #define __HEAD_H__ #include errno.h #include error.h #include stdio.h #include stdlib.h #include string.h #include sys/fcntl.h #include sys/ipc.h #include sys/msg.h #include sys/stat.h #include sys/types.h #include sys/wait.h #include unistd.h #endif//发送端 #include head.h struct msgbuf { long mtype; char mtext[128]; }; int main(int argc, char **argv) { key_t keyftok(.,!); if(key0) { perror(ftok error); return -1; } printf(KEY is %x\n,key); int msgidmsgget(key,IPC_CREAT|0664); if(msgid0) { perror(msgget error); return -1; } printf(msgid is %d\n,msgid); struct msgbuf data; data.mtype10; strcpy(data.mtext,hello world); int retmsgsnd(msgid,data,strlen(data.mtext),0); if(ret0) { perror(msgsnd error); return -1; } //msgctl(); return 0; }//接收端 #include head.h #include stdio.h struct msgbuf { long mtype; char mtext[128]; }; int main(int argc, char **argv) { key_t keyftok(.,!); if(key0) { perror(ftok error); return -1; } printf(key is %x\n,key); int msgidmsgget(key,IPC_CREAT|0664); if(msgid0) { perror(msgget error); return -1; } printf(msgid is %d\n,msgid); struct msgbuf data; ssize_t sizemsgrcv(msgid,data,sizeof(data.mtext),0,0); if(size0) { perror(msgrcv error); return -1; } printf(mtype is %ld,mtext is %s\n,data.mtype,data.mtext); //msgctl(msgid,IPC_RMID,NULL); return 0; }共享内存共享内存进程间效率最高的通信方式原因使用内存映射技术减少了读写数据时反复数据拷贝带来的时间消耗。创建共享内存shmgetint shmget(key_t key, size_t size, int shmflg); 功能创建共享内存并获得共享内存的id 参数 keyIPC对象的key值 size创建的共享内存的大小向上取整到PAGE_SIZE4096整数倍 shmflg 创建方式 IPC_CREAT | 0664 返回值 成功共享内存的id 失败-1建立内存映射shmatvoid *shmat(int shmid, const void *shmaddr, int shmflg); 功能建立共享内存和用户进程空间的映射 参数 shmid共享内存id shmaddr映射的用户空间首地址 NULL由操作系统自己分配用户空间 shmflg对共享内存的读写可执行权限 SHM_EXEC 可执行权限 SHM_RDONLY只读权限 !SHM_RDONLY : 读写权限 返回值解除内存映射shmdtint shmdt(const void *shmaddr); 功能解除共享内存和用户空间的映射关系 参数 shmaddr用户空间首地址 返回值 成功0 失败-1删除共享内存shmctlint shmctl(int shmid, int cmd, struct shmid_ds *buf); 功能操作共享内存 参数 shmid共享内存的id cmd IPC_STAT :获取状态 IPC_SET:设置状态 IPC_RMID删除 buf 删除NULL 返回值 成功0 失败-1示例//头文件 #ifndef __HEAD_H__ #define __HEAD_H__ #include errno.h #include error.h #include stdio.h #include stdlib.h #include string.h #include sys/fcntl.h #include sys/ipc.h #include sys/msg.h #include sys/shm.h #include sys/stat.h #include sys/types.h #include sys/wait.h #include unistd.h #endif//发送端 #include sys/types.h #include head.h int main(int argc, char** argv) { key_t key ftok(., ); if (key 0) { perror(ftok error); return -1; } int shmid shmget(key, 4096, IPC_CREAT | 0664); if (shmid 0) { perror(shmget error); return -1; } printf(shmid is %d\n, shmid); void* pmem shmat(shmid, NULL, !SHM_RDONLY); if ((void*)-1 pmem) { perror(shmat error); return -1; } //*((int*)pmem)100; // strcpy((char*)pmem,hello world); pid_t other_pid *((pid_t*)pmem); while (1) { fgets((char*)pmem, 4096, stdin); kill(other_pid, SIGUSR1); } shmdt(pmem); return 0; }//接收端 #include head.h #include sys/types.h void handler(int signum) { } int main(int argc, char **argv) { signal(SIGUSR1,handler); key_t keyftok(.,); if(key0) { perror(ftok error); return -1; } int shmidshmget(key,4096,IPC_CREAT|0664); if(shmid0) { perror(shmget error); return -1; } printf(shnid is %d\n,shmid); void* pmemshmat(shmid,NULL,!SHM_RDONLY); if((void*)-1 pmem) { perror(shmat error); return -1; } (*(pid_t*)pmem)getpid(); while(1) { pause(); printf(shm is %s\n,(char*)pmem); } shmdt(pmem); //shmctl(shmid,IPC_RMID,NULL); return 0; }信号量集信号量数组信号量集实现进程间同步。了解即可