一.无名管道
无名管道:只能用在具有亲缘关系的进程间通信(父子进程)
操作流程:
1.创建并打开管道:pipe();
2.读管道:read();
3.写管道:write();
4.关闭管道:close();
int pipe(int pipefd[2]);功能:在内核区创建并打开一个无名管道(即缓冲区),父子共用内核区(在创建父子进程之前,否则在内核区创建两个独立管道)
参数:
pipefd:管道读端和写端的文件描述符
pipefd[0] 读端
pipefd[1] 写端
返回值:
成功:0
失败:-1
管道的特点:
管道本质是内核中的一段缓冲区,默认64K字节,遵循队列存储方式(FIFO),数据在管道中被读取后,会自动从内核缓冲区中移除(出队)。
1.写阻塞:当管道的读写端都存在时,管道写满数据时,发生写阻塞。
2.读阻塞:当管道的读写端都存在时,管道为空时,读管道发生读阻塞。
3.读返回0:当管道没有写端(close(pipefd[1]),只有读端时,管道中有数据则读出数据,无数据时,不再读阻塞,读管道直接返回0字节。
4.管道破裂:当管道没有读端(close(pipefd[2]),向管道中写数据,发生管道破裂异常。
二.有名管道
有名管道:可以用在同一主机,任意进程间通信
1.创建有名管道文件:mkfifo();
int ret = mkfifo("myfifo2",0664); if(ret == -1&& errno != EEXIST) { perror("mkfifo2 error"); return NULL; }2.打开管道文件:open();
int fd = open("myfifo2",O_WRONLY); if(fd == -1) { perror("open error"); return NULL; }3.写管道文件:write():
4.读管道文件:read();
5.关闭管道文件:close();
三.信号
信号:进程间通知机制(不携带数据)--异步通信
信号:软中断
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
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+1247) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
2) SIGINT:ctrl + c
让一个进程被打断
3) SIGQUIT:ctrl + \
让一个进程结束
9) SIGKILL:
强制让一个进程结束
11)SIGSEGV:
让一个进程结束(段错误)
13)SIGPIPE:
让一个进程结束(管道破裂)
14)SIGALRM:
让一个进程结束(定时时间到达)
17)SIGCHLD:
子进程结束时发送给父进程
18)SIGCONT:
让停止态的进程继续执行
19)SIGSTOP:
让运行态的进程进入停止态(暂停)强制停止
20)SIGTSTP:
ctrl + z 让进程进入暂停态,后台进程
来自终端的停止信号
10 SIGUSR1
12 SIGUSR2
专门预留给程序员使用的未定义信号。
1.注册信号
信号处理方式:
1.忽略:不处理
2.缺省:按照操作系统默认方式处理
3.捕获:按照自定义方式处理
sighandler_t signal(int signum, sighandler_t handler);
功能:注册一个信号
参数:
signum :要注册的信号的编号
handler:对信号的处理方式
SIG_IGN : 按忽略方式处理
SIG_DFL :按照缺省方式处理
自定义的函数:按照捕获方式处理
返回值:
成功:handler
失败:SIG_ERR
注意:
1. 如果信号不注册,自动按照默认(缺省)方式处理;大多数信号默认处理方式是结束一个进程。
2. 信号尽量早注册。
2.发送信号
int kill(pid_t pid, int sig);
功能:给进程发送一个信号,与signal注册信号搭配使用
参数:
pid:接收信号的进程
sig:信号的编号
返回值:
成功:0
失败:-1
3.pause()
void pause(void);
功能:挂起一个进程(让进程进入休眠)
可以通过一个信号唤醒pause挂起的进程,但是这个信号必须被捕获(先注册)。
4.alarm
unsigned int alarm(unsigned int seconds);
功能:定时若干秒之后发送一个SIGALRM的信号,可以注册自定义实现方式
pause与alarm实现sleep功能
void handler(int signum) { printf("%d signal is coming ,end\n",signum); } void mysleep(int n) { signal(SIGALRM,handler); alarm(5); int cnt = 4; while(cnt--) { printf("sleeping\n"); sleep(1); } pause(); } //定时5秒闹钟后,执行4秒的任务,任务结束后陷入pause,第五秒结束时alarm发送SIGALRM信号,执行handler函数,执行结束后pause返回。信号处理函数(handler)一定先执行,然后 pause() 才返回。四.消息队列
system V : IPC对象
消息队列与管道的区别:
1. 将进程间通信的数据封装成消息体发送;
2. 增加消息类型,可以根据消息的类型设置数据的优先级。
1. 获取IPC的key值:ftok();
2. 创建消息队列 :msgget();
3. 发送消息队列消息: msgsnd();
4. 接收消息队列消息 :msgrcv();
5. 删除消息队列 : msgctl();
struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[MSG_LEN]; /* message data */ }; //系统定义的消息体结构,消息正文大小可自定义1.ftok
key_t ftok(const char *pathname, int proj_id);
功能:产生一个IPC对象key值
参数:
pathname:路径名
proj_id:项目id
返回值:
成功:key值
失败:-1key_t key = ftok(".", '?'); if (-1 == key) { perror("ftok error"); return -1; } printf("key = %#x\n", key);
2.msgget
int msgget(key_t key, int msgflg);
功能:根据key值创建一个消息队列,如果存在返回消息队列ID
参数:
key: IPC key值
msgflg:IPC_CREAT|0664
返回值:
成功:消息队列ID
失败:-1int msgid = msgget(key, IPC_CREAT|0664); if (-1 == msgid) { perror("msgget error"); return -1; } printf("msgid = %d\n", msgid);
3.msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:发送消息队列的消息
参数:
msqid:消息队列ID
msgp:要发送的消息体
struct msgbuf {
long mtype; /* message type, must be > 0 */ 消息类型
char mtext[1]; /* message data */ 消息正文
};
msgsz:消息正文大小
msgflg :标志位:0:默认方式发送
返回值:
成功:0
失败:-1while (1) { msg.mtype = MSG_TYPE_CHAT; fgets(msg.mtext, MSG_LEN, stdin);//读取用LEN最大长度 int ret = msgsnd(msgid, &msg, strlen(msg.mtext), 0);//发送用实际读取的长度 if (-1 == ret) { perror("msgsnd error"); return -1; } }
4.msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
功能:从消息队列中接收消息
参数:
msqid:消息队列ID
msgp :要接受的消息的存储位置
msgsz:消息正文的大小
msgtyp:消息类型
msgflg:标志位:0:默认方式(阻塞方式)
返回值:
成功:返回实际消息正文的大小
失败:-1while (1) { memset(&msg, 0, sizeof(msg)); ssize_t ret = msgrcv(msgid, &msg, MSG_LEN, MSG_TYPE_CHAT, 0); if (-1 == ret) { perror("msgrcv error"); return -1; } printf("msg = %s\n", msg.mtext); }
5.msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:操作IPC对象对应的消息队列
参数:
msqid:消息队列的id
cmd:操作方法:
IPC_STAT:获取状态操作
IPC_SET: 设置
IPC_RMID:删除操作
buf:
IPC_RMID :buff-》NULL
返回值:
成功:0
失败:-1msgctl(msgid, IPC_RMID, NULL);
6.IPC对象命令
ipcs -a
查看系统中的IPC对象
ipcrm -q msgid
ipcrm -Q msgkey
删除消息队列
ipcrm -m shmid
ipcrm -M shmkey
删除共享内存
五.共享内存
共享内存:进程间效率最高的通信方式
利用内存映射技术,避免了数据的反复拷贝
1.获取IPC对象的key值:ftok();
2.创建共享内存:shmget();
3.共享内存和用户进程空间建立内存映射关系:shmat():
4.向用户空间写入/读出数据:
5.解除内存映射关系:shmdt();
6.删除共享内存:shmctl();
1.shmget
int shmget(key_t key, size_t size, int shmflg);
功能:创建共享内存并获得共享内存的id
参数:
key :IPC对象key值
size:共享内存的大小(PAGE_SIZE(4096字节)整数倍)
shmflg: IPC_CREAT | 0664
返回值:
成功:共享内存的ID
失败:-1int shmid = shmget(key, 4096, IPC_CREAT | 0664); if (-1 == shmid) { perror("shmget error"); return -1; } printf("shmid = %d\n", shmid);
2.shmat
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:建立共享内存和用户空间的内存映射
参数:
shmid:共享内存id
shmaddr:映射的用户空间首地址
NULL :由操作系统选择用户空间并以返回值的方式返回空间首地址
shmflg :
SHM_RDONLY :只读
!SHM_RDONLY :读写
返回值:
成功:映射的用户空间首地址
失败:(void *)-1void *pmem = shmat(shmid, NULL, !SHM_RDONLY); if ((void *)-1 == pmem) { perror("shmat error"); return -1; }
3.shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:操作共享内存
参数:
shmid:共享内存id
cmd:操作指令
IPC_RMID: 删除操作
buf: NULL
返回值:
成功:0
失败:-1shmdt(pmem);//解除内存映射 shmctl(shmid, IPC_RMID, NULL);