信号的原理:
在进程创建初期,会为进程创建一个信号函数表:
信号的处理方式:
1.忽略:指的是信号到来了,不采取热呢措施,如:SIGCHLD SIGCHLD:子进程结束后给父进程发送一个信号 SIGKILL和SIGSTOP不能被忽略 2.捕捉:指的是信号到来之前,将信号函数表中信号所对应的默认函数指针修改成指向自己定义的函数----为我所用 SIGKILL和SIGSTOP不能被捕捉 3.默认:指定的是信号到来之后,去执行进程创建初期信号函数表中的默认操作
3. 信号的相关的函数
1.signal
头文件:#include 原型:typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 功能: 注册一个信号函数,一般在进程刚开始的时候 参数: signum:信号号 handler:信号的处理方式 1.忽略:SIG_IGN 2.默认:SIG_DFL 3.捕捉:指向自定义函数的指针,函数指针,指向一个返回值:void,参数:int类型的函数指针 signal函数会将该signum信号号和函数指针相绑定 返回值: 成功:返回一个函数指针指向上一次所执行的函数,保留下一个 失败:返回 SIG_ERR
#include <stdio.h> #include <signal.h> #include <unistd.h> void myfun(int signum) { printf("哈哈,关不掉我吧!\n"); } int main(int argc, char const *argv[]) { //注册信号函数 //进行信号捕捉,将SIGINT信号的处理方式改成自己的处理方式 //去执行我自己的功能 if(signal(SIGINT,myfun) == SIG_ERR) { perror("signal"); return -1; } while(1) { printf("主线程在干自己的事情\n"); sleep(1); } return 0; } 练习:利用signal函数去完成最佳回收僵尸进程的方式 #include <stdio.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> void myfun(int signum) { pid_t pid = wait(NULL); printf("回收成功,进程ID为 %d\n",pid); } int main(int argc, char const *argv[]) { //注册信号 if(signal(SIGCHLD,myfun) == SIG_ERR) { perror("signal"); return -1; } //创建一个进程 pid_t pid = fork(); if(-1 == pid) { perror("fork"); return -1; } else if(0 == pid) { //子进程 sleep(4); exit(0); } else if(pid > 0) { while(1) { printf("父进程在做自己的事情\n"); sleep(1); } } return 0; }
2. alarm
头文件:#include 原型:unsigned int alarm(unsigned int seconds); 功能:给自己发送一个闹钟信号 ----》SIGALRM 默认终止进程 参数: 定时seconds秒后发送信号 unsigned int ret = alarm(5); //5秒之后发送一个信号,代码正常向下执行 返回值: 成功返回上一次alarm剩余的秒数 0代表定时器时间到 注意: 如果调用alarm后再次调用alarm函数会刷新定时器的事件,打断了上一次alarm的定时,上一次的alarm不会再发送闹钟信号,会将上一次alarm剩余的描述返回回来。
#include <stdio.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> void myfun(int signum) { printf("自动出牌了\n"); alarm(5); } int main(int argc, char const *argv[]) { if(SIG_ERR == signal(SIGALRM,myfun)) { perror("signal"); return -1; } //开始出牌 alarm(5); while(1) { char ch; printf("请出牌\n"); scanf("%c",&ch); //用它来模拟出牌 getchar(); alarm(5); } return 0; }
4. IPC通信linux命令
ipc通信共有三个:消息队列,共享内存,信号灯集
linux查看所有IPC对象的命令: ipcs
linux删除ipc对象的命令:
ipcrm -s ID号 :信号灯集
ipcrm -q ID号: 消息队列
ipcrm -m ID号:共享内存
5. IPC通信之消息队列
消息队列:是实现进程间通信的一种方式,是利用内核空间完成的,并且是一种全双工的通信方式,实质就是管道在内核空间的集合。
5.1消息队列的函数接口
1.ftok
头文件:#include #include 原型:key_t ftok(const char *pathname, int proj_id); 功能:根据pathname和proj_id这两个参数生成一个key_t类型的数据, 按照内部自己的算法,pathname和proj_id这两个参数给的一样, 生成的key_t类型的数据就是一样的。 参数: pathname:文件路径,路径必须存在 proj_id:只用这个int类型的数据的低八位,所以说我们一把在使用时都给其字符型 返回值: 成功返回生成好的键值 失败: -1
2. msgget
头文件:#include #include #include 原型:int msgget(key_t key, int msgflg); 功能:打开或者创建一个消息队列 参数: KEY:通过秘钥键值创建一个消息队列 返回一个msgid消息队列的ID号,同一个key值生成的ID号一样 IPC_PRIVATE:直接用于亲缘间关系 自定义KEY值: ftok:生成一个key_t类型的变量---键值 msgflg:打开消息队列的方式 IPC_CREAT:如果消息队列不存在,则创建消息队列 如果消息队列存在,则打开消息队列 例如:IPC_CREAT | 0664 IPC_EXCL:如果文件存在则报错返回 IPC_CREAT|IPC_EXCL | 0664 //如果文件不存在则创建文件 返回值: 成功:返回一个消息对垒ID号(非负数) 失败 :-1
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/ipc.h> #include <stdio.h> int main(int argc, char const *argv[]) { //创建一个key_t的键值 key_t mykey = ftok("/home/jsetc",'a'); if(-1 == mykey) { printf("生成键值失败\n"); return -1; } //打开或者创建消息队列 int msgid = 0; msgid = msgget(mykey,IPC_CREAT | 0664); if(-1 == msgid) { perror("msgget"); return -1; } printf("创建消息队列成功!\n"); return 0; }
3. msgsnd
头文件:#include #include #include 原型: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 非阻塞发送:IPC_NOWAIT 返回值: 成功:返回 0 失败 :-1
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/ipc.h> #include <stdio.h> #include <string.h> struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data */ }; struct msgbuf mybuf; int main(int argc, char const *argv[]) { //创建一个key_t的键值 key_t mykey = ftok("/home/jsetc",'a'); if(-1 == mykey) { printf("生成键值失败\n"); return -1; } //打开或者创建消息队列 int msgid = 0; msgid = msgget(mykey,IPC_CREAT | 0664); if(-1 == msgid) { perror("msgget"); return -1; } printf("创建消息队列成功!\n"); //开始发送数据 while(1) { //发送数据 fgets(mybuf.mtext,1024,stdin); mybuf.mtext[strlen(mybuf.mtext) - 1] = '\0'; if(-1 == msgsnd(msgid,&mybuf,strlen(mybuf.mtext),0)) { perror("msgsnd"); return -1; } } return 0; }
4. msgrcv
头文件:#include #include #include 原型:int ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 功能:从消息队列中读取数据 参数: msqid:目标消息队列的ID号 msgp:存放消息的地址,结构体要求和发送端一致 struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data */ }; msgsz:要发送消息正文的大小 msgtyp:消息类型,可以等于0,如果写0,从消息队列开头读取 msgflg:发送消息的方式 阻塞发送:0 非阻塞发送:IPC_NOWAIT 返回值: 成功:成功接收到正文的大小 失败 :-1
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/ipc.h> #include <stdio.h> #include <string.h> struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1024]; /* message data */ }; struct msgbuf mybuf; int main(int argc, char const *argv[]) { //创建一个key_t的键值 key_t mykey = ftok("/home/jsetc",'a'); if(-1 == mykey) { printf("生成键值失败\n"); return -1; } //打开或者创建消息队列 int msgid = 0; msgid = msgget(mykey,IPC_CREAT | 0664); if(-1 == msgid) { perror("msgget"); return -1; } printf("创建消息队列成功!\n"); //开始接收数据 ssize_t ret = msgrcv(msgid,&mybuf,sizeof(mybuf.mtext),1,0); if(-1 == ret) { perror("msgrcv"); return -1; } printf("mybuf.text = %s\n",mybuf.mtext); return 0; }
5. msgctl
头文件:#include #include #include 原型:int msgctl(int msqid,int cmd,struct msqid_ds *buf) 功能:控制消息队列 参数:
msqid:目标消息队列 cmd:如何控制,控制的方式 IPC_STAT:获取消息队列的信息,可以获取到所有msqid_ds的信息 IPC_SET:设置消息队列信息,只能设置msgid_ds里面的msg_perm结构体 IPC_RMID:删除消息队列,第三个参数直接填写NULL buf: struct msqid_ds { struct ipc_perm msg_perm; /* Ownership and permissions */ time_t msg_stime; /* Time of last msgsnd(2) */ time_t msg_rtime; /* Time of last msgrcv(2) */ time_t msg_ctime; /* Time of last change */ unsigned long __msg_cbytes; /* Current number of bytes in queue (nonstandard) */ msgqnum_t msg_qnum; /* Current number of messages in queue */ msglen_t msg_qbytes; /* Maximum number of bytes allowed in queue */ pid_t msg_lspid; /* PID of last msgsnd(2) */ pid_t msg_lrpid; /* PID of last msgrcv(2) */ }; The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET): struct ipc_perm { key_t __key; /* Key supplied to msgget(2) */ uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */ uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective GID of creator */ unsigned short mode; /* Permissions */ unsigned short __seq; /* Sequence number */ }; 返回值:
6. IPC通信之共享内存
共享内存是利用地址映射的方式完成进程间通信,实质在内核建立一个共享的区域,然后将这片区域的地址,映射到用户空间,用户空间就有了一个映射出来的地址,
操作用户空间,就相当于直接操作了内核空间。共享内存相对来说简单一些。还有就是共享内存是IPC通信中能够效率最高的一种也是速度最快的一种。但是共享内存数据拿走不会消失。并且写入数据时会覆盖掉之间的数据。
共享内存一般用于数据的实时采集上传,本身不具备进程同步的功能。
6.1 共享内存的接口
1.shmget
头文件:#include #include 原型:int shmget(key_t key, size_t size, int shmflg); 功能:打开或者创建一个共享内存 参数:key: 两种方式打开或者创建共享内存 IPC_PRIVATE:用于亲缘间进程 自定义key使用ftok函数 size:创建共享内存的阿晓,如果已经存在了则无效 注意:size一般采取4K的倍数,如果不采取4K的倍数,在真实的共享内存中, 会向上近似等于4K的倍数,但是IPCS不会显示出来 shmflg:打开的方式 IPC_CREAT:如果共享内存存在,则打开,不存在则创建 例如:IPC_CREAT | 0664 IPC_EXCL:如果存在则报错返回,如果不存在配合IPC_CREAT创建 返回值: 成功返回共享内存的ID号 失败返回-1
2. shmat
头文件:#include #include 原型:void *shmat(int shmid, const void *shmaddr, int shmflg); 功能:地址映射,将shmid所对应的共享内存的首地址映射到用户空间shmaddr地址上 参数: shmid:目标共享内存 shmaddr:映射到用户空间的地址 默认入口:如果填写NULL。操作系统会自动分配地址 会通过返回值提供给用户 shmflg: 操作映射完的地址的权限 0:默认操作 读写权限 SHM_RDONLY:只读权限操作内存 返回值: 成功:会返回用户空间映射的地址 失败:返回(void *)-1
3.shmdt
头文件:#include #include 原型:int shmdt(const void *shmaddr); 功能:取消地址映射 参数: shmaddr:映射到用户空间的首地址 返回值: 成功:返回0 失败:返回-1 4.shmctl 头文件:#include #include 原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf); 功能:控制共享内存 参数: shmid:目标共享内存ID号 cmd:如何控制,控制的方式 IPC_STAT:获取共享内存属性 IPC_SET:设置共享内存属性 IPC_RMID:删除共享内存,如果说有进程正在使用,它会记录一下,等该进程断开连接后 删除
buf: The buf argument is a pointer to a shmid_ds structure, defined in <sys/shm.h> as follows: struct shmid_ds { struct ipc_perm shm_perm; /* Ownership and permissions */ size_t shm_segsz; /* Size of segment (bytes) */ time_t shm_atime; /* Last attach time */ time_t shm_dtime; /* Last detach time */ time_t shm_ctime; /* Last change time */ pid_t shm_cpid; /* PID of creator */ pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */ shmatt_t shm_nattch; /* No. of current attaches */ ... }; The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET): struct ipc_perm { key_t __key; /* Key supplied to shmget(2) */ uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */ uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective GID of creator */ unsigned short mode; /* Permissions + SHM_DEST and SHM_LOCKED flags */ unsigned short __seq; /* Sequence number */ }; 返回值: IPC_STAT:成功返回ID号 其它情况:返回0 失败返回-1
//read.c #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> int main(int argc, char const *argv[]) { //生成一个共享内存使用的key值 key_t mykey = ftok("/home/jsetc/jsetc/208/",'a'); if(-1 == mykey) { perror("生成键值失败"); return -1; } //创建共享内存 int shmid = shmget(mykey,4096,IPC_CREAT | 0664); if(-1 == shmid) { perror("shmget"); return -1; } printf("创建或者打开共享内存成功\n"); //地址映射 char *buf = (char *)shmat(shmid,NULL,0); if((char *)-1 == buf) { perror("shmat"); return -1; } //开始操作共享内存 while(1) { printf("buf = %s\n",buf); sleep(1); } return 0; }
#include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <stdio.h> int main(int argc, char const *argv[]) { //生成一个共享内存使用的key值 key_t mykey = ftok("/home/jsetc/jsetc/208/",'a'); if(-1 == mykey) { perror("生成键值失败"); return -1; } //创建共享内存 int shmid = shmget(mykey,4096,IPC_CREAT | 0664); if(-1 == shmid) { perror("shmget"); return -1; } printf("创建或者打开共享内存成功\n"); //地址映射 char *buf = (char *)shmat(shmid,NULL,0); if((char *)-1 == buf) { perror("shmat"); return -1; } //开始操作共享内存 scanf("%s",buf); //取消地址映射,断开与共享内存的连接,避免误操作 shmdt(buf); //删除共享内存 shmctl(shmid,IPC_RMID,NULL); return 0; }