设置信号相应方式——signal
#include <unistd.h> #include <signal.h> void (*signal(int signo, void (*handler)(int)))(int);//函数指针定义 typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);
成功时返回原先的信号处理函数,失败时返回SIG_ERR
signo 要设置的信号类型
handler 指定的信号处理函数: SIG_DFL代表缺省方式; SIG_IGN 代表忽略信号;
示例代码:
#include <signal.h> #include <stdio.h> void handler(int sig){ if(SIGINT == sig){ printf("I got sigint signal\n"); }else{ printf("I got sighup signal\n"); } } int main(){ signal(SIGINT, handler); signal(SIGHUP, handler); while(1){ sleep(1); } return 0; }
应用:按ctrl +c一次继续执行,两次继续执行,第三次停止
#include <stdio.h> #include <signal.h> typedef void (*signt)(int); signt oldhandle; int count = 0; void functh(int sig){ ++count; printf("I catch ctrl+c!!!\n"); if(count == 2){ signal(SIGINT, oldhandle);//signal(SIGINT, SIG_DEL);功能类似 } } int main(){ oldhandle = signal(SIGINT,functh); while(1) sleep(1); }
子进程结束信号
SIGCHLD
可以用来处理子进程退出的僵尸
示例代码:
#include <stdio.h> #include <signal.h> #include <stdlib.h> void functh(int sig){ wait(NULL); } int main(){ pid_t pid; signal(SIGCHLD, functh); pid = fork(); if(pid < 0 ){ perror("fork"); return -1; }else if(pid == 0){ printf("this child \n"); sleep(10); exit(0); } while(1){ printf("I am working!!!\n"); sleep(1); } return 0; }
共享内存
System V IPC
IPC 对象包含: 共享内存、消息队列和信号灯集
每个IPC对象有唯一的ID
IPC对象创建后一直存在,直到被显式地删除
每个IPC对象有一个关联的KEY
ipcs / ipcrm可以查看和删除这个
System V IPC – ftok
#include <sys/types.h> #include <sys/ipc.h> key_t ftok(const char *path, int proj_id);
成功时返回合法的key值,失败时返回EOF
path 存在且可访问的文件的路径
proj_id 用于生成key的数字,范围1-255。
共享内存
共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活
由于多个进程可同时访问共享内存,因此需要同步和互斥机制配合使用
使用步骤
创建/打开共享内存
映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
读写共享内存
撤销共享内存映射
删除共享内存对象
创建 - shmget
#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, int size, int shmflg);
成功时返回共享内存的id,失败时返回EOF
key 和共享内存关联的key,IPC_PRIVATE 或 ftok生成
shmflg 共享内存标志位 IPC_CREAT|0666
共享内存映射 – shmat
#include <sys/shm.h> void *shmat(int shmid, const void *shmaddr, int shmflg);
成功时返回映射后的地址,失败时返回(void *)-1
shmid 要映射的共享内存id
shmaddr 映射后的地址, NULL表示由系统自动映射
shmflg 标志位 0表示可读写;SHM_RDONLY表示只读
共享内存映射 – shmat
#include <sys/ipc.h> #include <sys/shm.h> int shmdt(void *shmaddr);
成功时返回0,失败时返回EOF
不使用共享内存时应撤销映射
进程结束时自动撤销
共享内存控制 – shmctl
#include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf);
成功时返回0,失败时返回EOF
shmid 要操作的共享内存的id
cmd 要执行的操作 IPC_STAT IPC_SET IPC_RMID
buf 保存或设置共享内存属性的地址
示例代码
send
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <string.h> int main(){ key_t key; int shmid; char *addr; key = ftok(".", 23); if(key == -1){ perror("ftok"); return -1; } shmid = shmget(key, 1024, IPC_CREAT|0666); if(shmid == -1){ perror("shmget"); return -1; } addr = shmat(shmid, NULL, 0); strcpy(addr, "this my share memeory"); shmdt(addr); }
write
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <string.h> int main(){ key_t key; int shmid; char *addr; key = ftok(".", 23); if(key == -1){ perror("ftok"); return -1; } shmid = shmget(key, 1024, IPC_CREAT|0666); if(shmid == -1){ perror("shmget"); return -1; } addr = shmat(shmid, NULL, 0); printf("get shar mem=%s\n",addr); shmdt(addr); }
删除队列留给大家自己学习,当然gitee库里面有对应的示例代码。
消息队列
消息队列是System V IPC对象的一种
消息队列由消息队列ID来唯一标识
消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等
消息队列可以按照类型来发送/接收消息
使用步骤
打开/创建消息队列 msgget
向消息队列发送消息 msgsnd
从消息队列接收消息 msgrcv
控制消息队列 msgctl
消息创建/打开 - msgget
#include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);
成功时返回消息队列的id,失败时返回EOF
key 和消息队列关联的key IPC_PRIVATE 或 ftok
msgflg 标志位 IPC_CREAT|0666
消息发送 – msgsnd
#include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int msgid, const void *msgp, size_t size, int msgflg);
成功时返回0,失败时返回-1
msgid 消息队列id
msgp 消息缓冲区地址
通常如下
struct msgbuf{
long mtype;//消息类型
char mtext[1];///消息正文
}
size 消息正文长度不包含消息类型指针变量
msgflg 标志位 0 或 IPC_NOWAIT
消息接收 – msgrcv
#include <sys/ipc.h> #include <sys/msg.h> int msgrcv(int msgid, void *msgp, size_t size, long msgtype, int msgflg);
成功时返回收到的消息长度,失败时返回-1
msgid 消息队列id
msgp 消息缓冲区地址
size 指定接收的消息长度
msgtype 指定接收的消息类型
0:接收消息队列中第一个
大于0:接收消息队列中第一个类型为msgtype的消息
小于0:接收队列中第一个类型值不小于msgtype绝对值且类型最小的消息
msgflg 标志位 0 或 IPC_NOWAIT
MSG_NOERROR:返回消息比msgsz字节多,则消息会截断,且不提示。
IPC_NOWAIT:无消息立即返回
0: 阻塞等到直接接收一条相应类型的消息
控制消息队列 – msgctl
#include <sys/ipc.h> #include <sys/msg.h> int msgctl(int msgid, int cmd, struct msqid_ds *buf);
成功时返回0,失败时返回-1
msgid 消息队列id
cmd 要执行的操作 IPC_STAT / IPC_SET / IPC_RMID
buf 存放消息队列属性的地址
示例代码
send
#include <stdio.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> typedef struct{ long type; char txt[64]; }MSG; #define LEN sizeof(MSG) - sizeof(long) int main(){ key_t ipkey; int msgid; MSG msg_t; ipkey = ftok(".",23); if(ipkey == -1){ perror("ftok"); return -1; } msgid = msgget(ipkey, IPC_CREAT|0666); if(msgid == -1){ perror("msgget"); return -1; } msg_t.type = 1; strcpy(msg_t.txt, "msg type one"); msgsnd(msgid, &msg_t, LEN, 0); return 0; }
read:
#include <stdio.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> typedef struct{ long type; char txt[64]; }MSG; #define LEN sizeof(MSG) - sizeof(long) int main(){ int re; key_t ipkey; int msgid; MSG msg_t; ipkey = ftok(".",23); if(ipkey == -1){ perror("ftok"); return -1; } msgid = msgget(ipkey, IPC_CREAT|0666); if(msgid == -1){ perror("msgget"); return -1; } while(1){ re = msgrcv(msgid, &msg_t, LEN, 0, 0); printf("receive msg = %s\n",msg_t.txt); if(re < 0){ break; } } return 0; }
信号灯
信号灯也叫信号量,用于进程/线程同步或互斥的机制
信号灯的类型
Posix 无名信号灯(前面线程学过)
Posix有名信号灯
System V 信号灯
信号灯的含义
计数信号灯
信号灯特点
System V 信号灯是一个或多个计数信号灯的集合
可同时操作集合中的多个信号灯
申请多个资源时避免死锁
使用步骤
打开/创建信号灯 semget
信号灯初始化 semctl
P/V操作 semop
删除信号灯 semctl
信号灯创建/打开 – semget
#include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg);
成功时返回信号灯的id,失败时返回-1
key 和消息队列关联的key IPC_PRIVATE 或 ftok
nsems 集合中包含的计数信号灯个数
semflg 标志位 IPC_CREAT|0666 IPC_EXCL
信号灯初始化 – semctl
#include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, …);
成功时返回0,失败时返回EOF
semid 要操作的信号灯集id
semnum 要操作的集合中的信号灯编号
cmd 执行的操作 SETVAL IPC_RMID
union semun 取决于cmd
信号灯P/V操作 – semop
#include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sembuf, unsigned nsops);
成功时返回0,失败时返回-1
semid 要操作的信号灯集id
sembuf 描述对信号灯操作的结构体(数组)
nsops 要操作的信号灯的个数
信号灯操作 – sembuf
struct sembuf { short sem_num; short sem_op; short sem_flg; };
semnum 信号灯编号
sem_op -1:P操作 1:V操作
sem_flg 0 / IPC_NOWAIT
使用示例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <errno.h> #include <sys/shm.h> #include <sys/sem.h> #include <string.h> #define SEM_READ 0 #define SEM_WRITE 0 union semun{ int Val; }; void poperation(int index, int semid){ struct sembuf sop; sop.sem_num = index; sop.sem_op = -1; sop.sem_flg = 0; semop(semid, &sop,1); } void voperation(int index, int semid){ struct sembuf sop; sop.sem_num = index; sop.sem_op = 1; sop.sem_flg = 0; semop(semid, &sop,1); } int main(){ key_t key; int semid,shmid; char *shmaddr; pid_t pid; key = ftok(".",123); semid = semget(key, 2, IPC_CREAT|0777); if(semid < 0){ perror("semget"); return -1; } shmid = shmget(key, 256, IPC_CREAT|0777); if(shmid < 0){ perror("shmget"); return -1; } union semun myun; myun.Val = 0; semctl(semid, SEM_READ, SETVAL, myun); myun.Val = 1; semctl(semid, SEM_WRITE, SETVAL, myun); pid = fork(); if(pid < 0){ perror("fork"); return -1; }else if(pid == 0){ shmaddr = (char *)shmat(shmid, NULL, 0); while(1){ poperation(SEM_READ, semid); printf("getshm:%s",shmaddr); voperation(SEM_WRITE, semid); } }else{ shmaddr = (char *)shmat(shmid, NULL, 0); while(1){ poperation(SEM_WRITE, semid); printf("please input char to shm:"); fgets(shmaddr, 32, stdin); voperation(SEM_READ, semid); } } }
写在最后
今天讲完了进程相关的内容,这部分内容比较枯燥,感觉过于复杂,想要重构,所有文件我都放在了gitee哦,需要自取,我尽量一天一更,大家和我一起变强呀!明天开始进入网络编程!最后三连即可提高学习效率!!!
另外我在更新的就是算法笔记的一些例题笔记,这个系列是用于提高我的算法能力,如果有兴趣对算法领域感兴趣找不到合适的入门文章也可以追更,如果我更新的太慢了请大家点赞收藏,一键三连才能更有更新的动力呀0.0