Linux系统编程——进程间通信的学习
学习参考博文:
- 进程间的五种通信方式介绍
- Linux 信号介绍
Linux系统编程学习相关博文
- Linux系统编程——文件编程的学习
- Linux系统编程——进程的学习
- Linux系统编程——线程的学习
- Linux系统编程——网络编程的学习
Linux系统编程——进程间通信的学习
- 一、概述
- 1. 无名管道和有名管道的区别
- 2. 当打开一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
- 3. 消息队列
- 4. 共享内存
- 5. 信号
- 6. 信号量
- 二、进程间通信API
- 三、API介绍
- 1. pipe函数
- 2. mkfifo函数
- 3. msgget函数
- 4. msgsnd函数
- 5. msgrcv函数
- 6. msgctl函数
- 7. shmget函数
- 8. shmat函数
- 9. shmdt函数
- 10. shmctl函数
- 11. signal函数
- 12. kill函数
- 13. sigaction函数
- 14. sigqueue函数
- 15. semget函数
- 16. semctl函数
- 17. semop函数
- 四、API的使用例子
- 1. pipe函数
- 2. mkfifo函数
- 3. msgget、msgsnd、msgrcv、msgctl函数
- 4. shmget、shmat、shmdt、shmctl函数
- 5. signal函数
- 6. kill函数
- 7. sigaction函数
- 8. sigqueue函数
- 9. semget、semctl、semop函数
一、概述
常规学习Linux系统编程的内容是复杂且繁多的,不推荐刚开始接触代码的朋友去学习,所以介绍Linux系统编程的目的主要是以应用开发为主。
进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。
1. 无名管道和有名管道的区别
- 无名管道是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间),它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
- 有名管道可以在无关的进程之间交换数据,与无名管道不同。
2. 当打开一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
-
若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
-
若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。
3. 消息队列
- 消息队列,是消息的链接表,存放在内核中。
- 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
4. 共享内存
- 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
- 因为多个进程可以同时操作,所以需要进行同步。
- 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
5. 信号
- 每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。
- 信号定义在signal.h头文件中,信号名都定义为正整数。
- 具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。
- 信号的处理有三种方法,分别是:忽略、捕捉和默认动作
6. 信号量
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
- 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。(P操作相当于拿钥匙,V操作相当于放钥匙)
- 支持信号量组。
二、进程间通信API
在Linux系统中,操作系统提供了一系列的API,详细看下图
1. 管道
创建无名管道 pipe()
创建有名管道 mkfifo()
2. 消息队列
创建或打开消息队列 msgget()
添加消息 msgsnd()
读取消息 msgrcv()
控制消息队列 msgctl()
3. 共享内存
创建或获取共享内存 shmget()
将共享内存映射到当前进程的虚拟地址空间 shmat()
断开与共享内存的连接 shmdt()
控制共享内存的相关信息 shmctl()
4. 信号
信号处理函数 入门版 signal() / 高级版 sigaction()
信号发送函数 入门版 kill() / 高级版 sigqueue()
5. 信号量
创建或获取信号量组 semget()
控制信号量的相关信息 semctl()
对信号量组进行操作 semop()
三、API介绍
1. pipe函数
#include <unistd.h>int pipe(int pipefd[2]);1. 函数功能:创建无名管道
2. 形参说明:
pipefd[2]:一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。
3. 返回值:成功返回0,失败返回-1
2. mkfifo函数
#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);1. 函数功能:创建有名管道
2. 形参说明:
pathname:文件路径名
mode:与open函数中的mode相同。一旦创建了一个FIFO,就可以用一般的文件I/O函数去操作它。(忘记的可以去文件编程学习篇章回顾下)
3. 返回值:成功返回0,失败返回-1
3. msgget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflg);1. 函数功能:创建或打开消息队列
2. 形参说明
key:键值key,可自己写死,也可用ftok函数生成(ftok函数具体看API使用例子了解)
msgflg:- 这个参数可以是单独的IPC_CREAT,也可以是IPC_CREAT|IPC_EXCL,但IPC_EXCL单独使用没有意义- 如果单独使用IPC_CREAT(记得附加权限),或者flg为0:创建一个消息队列,如果创建的消息队列已经存在,则返回已经存在的队列ID,不存在则创建 - IPC_CREAT | IPC_EXCL:如果不存在消息队列,则创建,如果已经有了消息队列,则返回-1
3. 返回值:成功返回队列ID,失败返回-1
4. msgsnd函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);1. 函数功能:添加消息
2. 形参说明:
msqid:队列ID
msgp:msgp参数是一个指针,指向调用者定义的结构体,其一般形式如下:struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[1]; /* message data */};mtext字段是一个数组(或其他结构),其大小由非负整数值msgsz指定。允许零长度的消息(即没有mtext字段)。mtype字段必须是严格的正整数值。接收进程可以使用此值进行消息选择
msgsz:指定msgp结构体里边mtext参数的大小
msgflg:默认是0
3. 返回值:成功返回0,失败返回-1
5. msgrcv函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);1. 函数功能:读取消息
2. 形参说明:
msqid:队列ID
msgp:msgp参数是一个指针,指向调用者定义的结构体,其一般形式如下:struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[1]; /* message data */};
msgsz:读取数据的大小
msgtyp:mtype字段必须是严格的正整数值。接收进程可以使用此值进行消息选择
msgflg:msgflg参数是一个位掩码,由以下零个或多个标志组合而成:· IPC_NOWAIT如果队列中没有请求类型的消息,则立即返回· MSG_EXCEPT当msgtyp大于0时,用于读取消息类型与msgtyp不同的队列中的第一条消息。· MSG_NOERROR如果消息文本长度超过msgsz字节,则截断消息文本。· 为0时是默认状态,当没有消息类型与msgtyp相同的队列发送信息时,则一直阻塞
3. 返回值:成功返回消息数据的长度,失败返回-1
6. msgctl函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf)1. 函数功能:控制消息队列
2. 形参说明:
msqid:队列ID
cmd:IPC_RMID————从内核中删除消息队列(其他参数请自行查阅)
buf:控制消息队列时数据处理的结构体(默认是NULL)
3. 返回值:成功返回0,失败返回-1
7. shmget函数
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);1. 函数功能:创建或获取共享内存
2. 形参说明:
key:键值key,可自己写死,也可用ftok函数生成(ftok函数具体看API使用例子了解)
size:当用shmget函数创建一段共享内存时,必须指定其 size;而如果引用一个已存在的共享内存,则将 size 指定为0 。
shmflg:- 这个参数可以是单独的IPC_CREAT,也可以是IPC_CREAT|IPC_EXCL,但IPC_EXCL单独使用没有意义- 如果单独使用IPC_CREAT(记得附加权限),或者flg为0:创建一个共享内存,如果创建的共享内存已经存在,则返回已经存在的共享内存ID,不存在则创建 - IPC_CREAT | IPC_EXCL:如果不存在共享内存,则创建,如果已经有了共享内存,则返回-1
3. 返回值:成功返回共享内存ID,失败返回-1
8. shmat函数
#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);1. 函数功能:将共享内存映射到当前进程的虚拟地址空间
2. 形参说明:
shmid:共享内存ID
shmaddr:该参数常用的是NULL。如果shmadr为NULL,系统选择一个合适的(未使用的)地址来附加该段
shmflg:该形参作用是权限附加,常用的是0。如果为0,系统默认映射进来的共享内存为可读可写权限
3. 返回值:成功返回指向共享内存的指针,失败返回-1
9. shmdt函数
#include <sys/types.h>
#include <sys/shm.h>int shmdt(const void *shmaddr);1. 函数功能:断开与共享内存的连接
2. 形参说明:
shmaddr:映射后返回共享内存的指针
3. 返回值:成功返回0,失败返回-1
10. shmctl函数
#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);1. 函数功能:控制共享内存的相关信息
2. 形参说明:
shmid:共享内存ID
cmd:命令参数。常用的是IPC_RMID(从系统中删除该共享内存)
buf:该参数是卸载共享内存后所返回的一些信息。不关心为NULL
3. 返回值:成功返回0,失败返回-1
11. signal函数
#include <signal.h>typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);1. 函数功能:信号处理函数
2. 形参说明:
signum:监听的信号(kill -l参看)
handler:收到信号后处理的函数函数原型:typedef void (*sighandler_t)(int);
12. kill函数
#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);1. 函数功能:信号发送函数
2. 形参说明:
pid:发送信号时对方的pid号
sig:要发送的信号(kill -l参看)
3. 返回值:成功返回0,失败返回-1
13. sigaction函数
#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);1. 函数功能:信号处理函数(带数据)
2. 形参说明:
signum:监听的信号(kill -l参看)
act:收到信号后处理的函数结构体原型:struct sigaction {void (*sa_handler)(int);void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;int sa_flags;void (*sa_restorer)(void);};上面结构体里第二个函数指针第二个struct siginfo结构体的相关信息siginfo_t {int si_signo; /* Signal number */int si_errno; /* An errno value */int si_code; /* Signal code */int si_trapno; /* Trap number that causedhardware-generated signal(unused on most architectures) */pid_t si_pid; /* Sending process ID */uid_t si_uid; /* Real user ID of sending process */int si_status; /* Exit value or signal */clock_t si_utime; /* User time consumed */clock_t si_stime; /* System time consumed */sigval_t si_value; /* Signal value */int si_int; /* POSIX.1b signal */void *si_ptr; /* POSIX.1b signal */int si_overrun; /* Timer overrun count; POSIX.1b timers */int si_timerid; /* Timer ID; POSIX.1b timers */void *si_addr; /* Memory location which caused fault */int si_band; /* Band event */int si_fd; /* File descriptor */}
oldact:该参数是数据备份。默认为NULL
3. 返回值:成功返回0,失败返回-1
14. sigqueue函数
#include <signal.h>int sigqueue(pid_t pid, int sig, const union sigval value);1. 函数功能:信号发送函数(带数据)
2. 形参说明:
pid:发送信号时对方的pid号
sig:要发送的信号(kill -l参看)
value:发送的数据联合体原型:union sigval {int sival_int;void *sival_ptr;};
3. 返回值:成功返回0,失败返回-1
15. semget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);1. 函数功能:创建或获取一个信号量组
2. 形参说明:
key:键值key,可自己写死,也可用ftok函数生成(ftok函数具体看API使用例子了解)
nsems:信号量个数
semflg:- 这个参数可以是单独的IPC_CREAT,也可以是IPC_CREAT|IPC_EXCL,但IPC_EXCL单独使用没有意义- 如果单独使用IPC_CREAT(记得附加权限),或者flg为0:创建一个共享内存,如果创建的共享内存已经存在,则返回已经存在的共享内存ID,不存在则创建 - IPC_CREAT | IPC_EXCL:如果不存在共享内存,则创建,如果已经有了共享内存,则返回-1
3. 返回值:成功返回信号量集ID,失败返回-1
16. semctl函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);1. 函数功能:控制信号量的相关信息
2. 形参说明:
semid:信号量集id
semnum:操控哪个信号量
cmd:命令参数。常用的是SETVAL这个函数有三个或四个参数,具体取决于cmd。当有四个时,第四个具有类型联合semur调用程序必须按如下方式定义这个联合体:union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */};3. 返回值:失败返回-1
17. semop函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semop(int semid, struct sembuf *sops, unsigned nsops)1. 函数功能:对信号量组进行操作
2. 形参说明:
semid:信号量集id
sops:sops所指向的数组中的每个nsop元素都指定了要在单个信号量上执行的操作。该结构体的元素类型为struct sembuf,包含以下成员:unsigned short sem_num; /* semaphore number */short sem_op; /* semaphore operation */short sem_flg; /* operation flags */
nsops:信号量集个数(决定第二个参数个数)该函数的使用例子:struct sembuf sops[2];int semid;/* Code to set semid omitted */sops[0].sem_num = 0; /* Operate on semaphore 0 */sops[0].sem_op = 0; /* Wait for value to equal 0 */sops[0].sem_flg = 0;sops[1].sem_num = 0; /* Operate on semaphore 0 */sops[1].sem_op = 1; /* Increment value by one */sops[1].sem_flg = 0;if (semop(semid, sops, 2) == -1) {perror("semop");exit(EXIT_FAILURE);}3. 返回值:成功返回0,失败返回-1
四、API的使用例子
1. pipe函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <unistd.h>6 7 int main()8 {9 // int pipe(int pipefd[2]);10 11 int pid = 0;12 int fd[2] = {0};13 char buf[128] = {0} ;14 15 if(pipe(fd) == -1){ //创建管道16 printf("create pipe failed\n");17 perror("why");18 }19 20 pid = fork(); //创建子进程21 22 if(pid < 0){ 23 printf("create child failed\n");24 perror("why");25 }26 else if(pid > 0){ //父进程27 sleep(1);28 29 printf("This is father\n");30 close(fd[0]); //关闭读端31 write(fd[1], "Hello World", strlen("Hello World")); //写入数据32 33 wait(NULL);34 }35 else{ //子进程36 printf("This is child\n");37 close(fd[1]); //关闭写端38 read(fd[0], buf, sizeof(buf)); //读取数据39 printf("read data: %s\n", buf);40 41 exit(0);42 }43 44 return 0;45 }
2. mkfifo函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <sys/types.h>6 #include <sys/stat.h>7 #include <errno.h>8 #include <fcntl.h>9 10 int main()11 {12 // int mkfifo(const char *pathname, mode_t mode);13 int fd = 0;14 15 if((mkfifo("./file", 0600) == -1) && errno != EEXIST){ //创建管道16 printf("create failed\n");17 perror("why");18 }19 20 fd = open("./file", O_RDONLY);21 22 return 0;23 }
3. msgget、msgsnd、msgrcv、msgctl函数
接收端demo
1 #include <stdio.h>2 #include <errno.h>3 #include <string.h>4 #include <stdlib.h>5 6 #include <sys/types.h>7 #include <sys/ipc.h>8 #include <sys/msg.h>9 10 // 1. int msgget(key_t key, int msgflg);11 // 2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);12 // 3. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);13 14 struct msgbuf {15 long mtype; /* message type, must be > 0 */16 char mtext[128]; /* message data */17 }18 19 int main()20 {21 int msgId;22 23 key_t key;24 25 struct msgbuf readBuf;26 struct msgbuf sendBuf = {988, "Welcome your coming"};27 28 key = ftok(".", 23); //获取key29 printf("key = 0x%x\n", key);30 31 msgId = msgget(key, IPC_CREAT|0777); //创建消息队列32 33 if(msgId == -1){34 printf("creat quene failed\n");35 perror("why");36 }37 38 msgrcv(msgId, &readBuf, sizeof(readBuf.mtext), 888, 0); //读取消息39 printf("get message from quene, content: %s\n", readBuf.mtext);40 41 msgsnd(msgId, &sendBuf, sizeof(sendBuf.mtext), 0); //发送消息42 43 msgctl(msgId, IPC_RMID, NULL); //删除消息队列44 45 return 0;46 }
发送端
1 #include <stdio.h>2 #include <errno.h>3 #include <string.h>4 #include <stdlib.h>5 6 #include <sys/types.h>7 #include <sys/ipc.h>8 #include <sys/msg.h>9 10 // 1. int msgget(key_t key, int msgflg);11 // 2. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);12 // 3. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);13 14 struct msgbuf {15 long mtype; /* message type, must be > 0 */16 char mtext[128]; /* message data */17 };18 19 int main()20 {21 int msgId;22 23 key_t key;24 25 struct msgbuf readBuf;26 struct msgbuf sendBuf = {888, "This is content from quene"};27 28 key = ftok(".", 23); //获取key29 printf("key = 0x%x\n", key);30 31 msgId = msgget(key, IPC_CREAT|0777); //创建消息队列32 33 if(msgId == -1){34 printf("creat quene failed\n");35 perror("why");36 }37 38 msgsnd(msgId, &sendBuf, sizeof(sendBuf.mtext), 0); //发送消息39 printf("Send over!\n");40 41 msgrcv(msgId, &readBuf, sizeof(readBuf.mtext), 988, 0); //读取消息42 printf("get message from msgGet, content: %s\n", readBuf.mtext);43 44 msgctl(msgId, IPC_RMID, NULL);45 46 return 0;47 }
4. shmget、shmat、shmdt、shmctl函数
接收端
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <sys/ipc.h>6 #include <sys/shm.h>7 #include <sys/types.h>8 9 // 1. int shmget(key_t key, size_t size, int shmflg);10 // 2. void *shmat(int shmid, const void *shmaddr, int shmflg);11 // 3. int shmdt(const void *shmaddr);12 // 4. int shmctl(int shmid, int cmd, struct shmid_ds *buf);13 14 int main()15 {16 int shmid;17 char *shmaddr;18 19 key_t key;20 21 key = ftok(".", 1); //获取key22 23 shmid = shmget(key, 1024*4, 0); //获取共享内存24 25 shmaddr = shmat(shmid, NULL, 0); //映射26 27 printf("read: %s\n", shmaddr);28 29 shmdt(shmaddr); //断开共享内存30 31 printf("quit\n");32 33 return 0;34 }
发送端
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <sys/ipc.h>6 #include <sys/shm.h>7 #include <sys/types.h>8 9 // 1. int shmget(key_t key, size_t size, int shmflg);10 // 2. void *shmat(int shmid, const void *shmaddr, int shmflg);11 // 3. int shmdt(const void *shmaddr);12 // 4. int shmctl(int shmid, int cmd, struct shmid_ds *buf);13 14 int main()15 {16 int shmid;17 char *shmaddr;18 19 key_t key;20 21 key = ftok(".", 1); //获取key22 23 shmid = shmget(key, 1024*4, IPC_CREAT|0666); //创建共享内存24 25 if(shmid == -1){26 perror("create shm failed");27 exit(-1);28 }29 30 shmaddr = shmat(shmid, NULL, 0); //映射31 32 strcpy(shmaddr, "Hello World"); //写入数据33 printf("write..\n");34 sleep(5);35 36 shmdt(shmaddr); //断开共享内存37 38 shmctl(shmid, IPC_RMID, NULL); //删除共享内存39 40 printf("quit\n");41 42 return 0;43 }
5. signal函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <signal.h>6 7 // typedef void (*sighandler_t)(int);8 // 1. sighandler_t signal(int signum, sighandler_t handler);9 10 void handler(int signum) //处理函数11 {12 switch(signum){13 case 2:14 printf("signum = %d\n", signum);15 printf("never quit\n");16 17 break;18 case 9:19 printf("signum = %d\n", signum);20 printf("never quit\n");21 22 break;23 case 10:24 printf("signum = %d\n", signum);25 printf("never quit\n");26 27 break;28 default:29 printf("signum = %d\n", signum);30 printf("never quit\n");31 32 break;33 }34 }35 36 int main()37 {38 signal(SIGHUP, handler); //捕获SIGHUP信号39 signal(SIGINT, handler); //捕获SIGINT信号40 signal(SIGKILL, handler); //捕获SIGKILL信号41 signal(SIGUSR1, handler); //捕获SIGUSR1信号42 43 while(1);44 45 return 0;46 }
6. kill函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <signal.h>6 #include <sys/types.h>7 8 // typedef void (*sighandler_t)(int);9 // 1. sighandler_t signal(int signum, sighandler_t handler);10 // 2. int kill(pid_t pid, int sig);11 12 int main(int argc, char **argv)13 {14 int pid;15 int signum;16 char cmd[32] = "/0";17 18 if(argc < 3){19 printf("please input three param\n");20 exit(-1);21 }22 23 signum = atoi(argv[1]); //将字符串转换成整型数(第二个参数)24 pid = atoi(argv[2]); //将字符串转换成整型数(第三个参数)25 26 sprintf(cmd, "kill -%d %d", signum, pid);27 //kill(pid, signum); //发送指令28 29 system(cmd); //发送指令30 31 return 0;32 }
7. sigaction函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <sys/types.h>6 #include <unistd.h>7 #include <signal.h>8 9 // 1. int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);10 11 void handler(int signum, siginfo_t *info, void *context) //处理函数12 {13 printf("\nsignum = %d\n", signum);14 15 if(context != NULL){16 printf("get data: %d\n", info->si_int);17 printf("get data: %d\n", info->si_value.sival_int);18 printf("from: %d\n",info->si_pid);19 }20 }21 22 int main()23 {24 struct sigaction act;25 26 printf("pid = %d\n", getpid());27 28 act.sa_sigaction = handler;29 act.sa_flags = SA_SIGINFO; //如果在sa_flags中指定了SA SIGINFO,则sa _sigaction(而不是sa_handler)指定signum的信号处理函数。30 31 sigaction(SIGUSR1, &act, NULL); 捕获SIGUSR1信号32 33 while(1);34 35 return 0;36 }
8. sigqueue函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <sys/types.h>6 #include <unistd.h>7 #include <signal.h>8 9 // 1. int sigqueue(pid_t pid, int sig, const union sigval value);10 11 int main(int argc, char **argv)12 {13 int pid;14 int signum;15 16 pid = atoi(argv[2]); //将字符串转换成整型数(第三个参数)17 signum = atoi(argv[1]); //将字符串转换成整型数(第二个参数)18 19 union sigval value;20 value.sival_int = 100; //发送的整形数据21 22 sigqueue(pid, signum, value); //发送函数23 24 printf("pid = %d\n", getpid()); //打印pid号25 26 return 0;27 }
9. semget、semctl、semop函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <sys/types.h>6 #include <sys/ipc.h>7 #include <sys/sem.h>8 #include <unistd.h>9 10 // 1. int semget(key_t key, int nsems, int semflg);11 // 2. int semctl(int semid, int semnum, int cmd, ...);12 // 3. int semop(int semid, struct sembuf *sops, unsigned nsops);13 14 union semun {15 int val; /* Value for SETVAL */16 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */17 unsigned short *array; /* Array for GETALL, SETALL */18 struct seminfo *__buf; /* Buffer for IPC_INFO19 (Linux-specific) */20 };21 22 void pGetKey(int semid) //拿钥匙23 {24 struct sembuf sop;25 26 sop.sem_num = 0; /* Operate on semaphore 0 */27 sop.sem_op = -1; /* Wait for value to equal 0 */28 sop.sem_flg = SEM_UNDO;29 30 semop(semid, &sop, 1); //操作信号量31 32 printf("Get key\n");33 }34 35 void vPutBackKey(int semid) //放钥匙36 {37 struct sembuf sop;38 39 sop.sem_num = 0; /* Operate on semaphore 0 */40 sop.sem_op = 1; /* Wait for value to equal 0 */41 sop.sem_flg = SEM_UNDO;42 43 semop(semid, &sop, 1); //操作信号量44 45 printf("Put back key\n");46 }47 48 int main()49 {50 int pid;51 int semid;52 key_t key;53 54 union semun value;55 56 key = ftok(".", 23); //获取key57 58 //创建/获取一个信号量59 semid = semget(key, 1, IPC_CREAT|0666); //创建信号量集60 61 value.val = 0; //初始时盒子里没钥匙62 63 //控制信号量集64 semctl(semid, 0, SETVAL, value); //信号量集初始化65 66 pid = fork(); //创建子进程67 68 if(pid > 0){ //父进程69 // 拿钥匙——如果盒子里没钥匙,父进程出于阻塞状态70 pGetKey(semid); //拿钥匙71 printf("This is father\n");72 // 放钥匙73 vPutBackKey(semid); //放钥匙74 }75 else if(pid == 0){ //子进程76 printf("This is child\n");77 // 放钥匙——因初始化时盒子里没钥匙,无论是不是父进程先运行它都会处于阻塞状态,等子进程把放入钥匙后父进程才能拿钥匙开门78 vPutBackKey(semid); //放钥匙79 }80 else{81 printf("fork error\n");82 }83 84 return 0;85 }
相关文章:
Linux系统编程——进程间通信的学习
学习参考博文: 进程间的五种通信方式介绍Linux 信号介绍 Linux系统编程学习相关博文 Linux系统编程——文件编程的学习Linux系统编程——进程的学习Linux系统编程——线程的学习Linux系统编程——网络编程的学习 Linux系统编程——进程间通信的学习 一、概述1. 无…...

人工智能AI 全栈体系(六)
第一章 神经网络是如何实现的 这些年神经网络的发展越来越复杂,应用领域越来越广,性能也越来越好,但是训练方法还是依靠 BP 算法。也有一些对 BP 算法的改进算法,但是大体思路基本是一样的,只是对 BP 算法个别地方的一…...

NPDP是什么?考了有用吗?
1)NPDP是什么? NPDP,全称为New Product Development Professional,即新产品开发专业人员。NPDP认证是由世界产品开发协会(PDMA)推出的一项专业认证。它旨在评估和认可个人在新产品开发领域的专业知识和技能…...

关于安卓SVGA浅尝(二)加载数据
关于安卓SVGA浅尝(二)加载数据 相关链接 SVGA官网 SVGA-github说明文档 背景 项目开发,都会和动画打交道,动画的方案选取,就有很多选择。如Json动画,svga动画,gif等等。各有各的优势。目前项…...

使用matlab产生二维动态曲线视频文件具体举例
使用matlab产生二维动态曲线视频文件举例 在进行有些函数变化过程时候,需要用到直观的动态显示,本博文将举例说明利用Matlab编程进行二维动态曲线的生成视频文件。 一、问题描述 利用matlab编程实现 y 1 s i n ( t ) , y 2 c o s ( t ) , y 3 s i …...

Selenium自动化测试框架常见异常分析及解决方法
01 pycharm中导入selenium报错 现象: pycharm中输入from selenium import webdriver, selenium标红 原因1: pycharm使用的虚拟环境中没有安装selenium, 解决方法: 在pycharm中通过设置或terminal面板重新安装selenium 原因2: 当前项目下有selenium.py,和系统包名冲突导致, …...

[TI] [Textual Inversion] An image is worth an word
自己的理解: 根据几个图像,找出来一个关键字可以代表它们,然后我们可以再用这个关键字去生成新的东西。 提出关键字 1 Introduction word->token->embedding Textual Inversion过程 需要: ① a fixed, pre-trained text…...
remote: The project you were looking for could not be found
git拉取公司项目时报错: remote: The project you were looking for could not be found 发生这个问题的原因,在于git账号可能并未真正登录。 我们可以通过打开电脑的凭据管理器,查看git当前的登录是否正常。 参考链接:参考...

https跳过SSL认证时是不是就是不加密的,相当于http?
https跳过SSL认证时是不是就是不加密的,相当于http?,其实不是,HTTPS跳过SSL认证并不相当于HTTP,也不意味着没有加密。请注意以下几点: HTTPS(Hypertext Transfer Protocol Secure)本质上是在HTTP的基础上…...

linux下链接
linux下链接用法 ln链接格式与介绍 linux下链接用法一、链接的使用格式二、链接的介绍 一、链接的使用格式 链接: 格式: ln 源文件 链接文件 硬链接 ln -s 源文件 链接文件 软连接 硬链接文件占磁盘空间 但是删除源文件不会影响硬链接文件 软链接文件不…...

OpenCV项目开发实战--主成分分析(PCA)的特征脸应用(附C++/Python实现源码)
什么是主成分分析? 这是理解这篇文章的先决条件。 图 1:使用蓝线和绿线显示 2D 数据的主要组成部分(红点)。 快速回顾一下,我们了解到第一个主成分是数据中最大方差的方向。第二主成分是空间中与第一主成分垂直(正交)的最大方差方向,依此类推。第一和第二主成分红点(2…...

多层感知机——MLP
源代码在此处:https://github.com/wepe/MachineLearning/tree/master/DeepLearning Tutorials/mlp 一、多层感知机(MLP)原理简介 多层感知机(MLP,Multilayer Perceptron)也叫人工神经网络(ANN&…...
HttpClientr入门
HttpClientr入门 介绍 HttpClient是Apache Jakarta Common下的子项目,可以用来提供高效的,最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议的版本和建议。 依赖导入 <dependency><groupId>org.apache.…...

网关-开放API接口签名验证方案
接口安全问题 请求身份是否合法?请求参数是否被篡改?请求是否唯一? AppId&AppSecret 请求身份 为开发者分配AppId(开发者标识,确保唯一)和AppSecret(用于接口加密,确保不易被…...

Linux知识点 -- 网络基础 -- 传输层
Linux知识点 – 网络基础 – 传输层 文章目录 Linux知识点 -- 网络基础 -- 传输层一、传输层协议1.端口号2.网络相关bash命令 二、UDP协议1.UDP报文的解包与交付2.理解UDP报文3.UDP协议的特点4.UDP应用层IO类接口5.UDP的缓冲区6.UDP使用注意事项7.基于UDP的应用层协议 三、TCP协…...

计算机视觉与深度学习-经典网络解析-AlexNet-[北邮鲁鹏]
这里写目录标题 AlexNet参考文章AlexNet模型结构AlexNet共8层:AlexNet运作流程 简单代码实现重要说明重要技巧主要贡献 AlexNet AlexNet 是一种卷积神经网络(Convolutional Neural Network,CNN)的架构。它是由Alex Krizhevsky、Il…...
Django学习笔记-实现联机对战(下)
笔记内容转载自 AcWing 的 Django 框架课讲义,课程链接:AcWing Django 框架课。 CONTENTS 1. 编写移动同步函数move_to2. 编写攻击同步函数shoot_fireball 1. 编写移动同步函数move_to 与上一章中的 create_player 同步函数相似,移动函数的同…...

一文了解什么SEO
搜索引擎优化 (SEO) 是一门让页面在 Google 等搜索引擎中排名更高的艺术和科学。 一、搜索引擎优化的好处 搜索引擎优化是在线营销的关键部分,因为搜索是用户浏览网络的主要方式之一。 搜索结果以有序列表的形式呈现,网站在该列表中的排名越高&#x…...

SpringBoot+Jpa+Thymeleaf实现增删改查
SpringBootJpaThymeleaf实现增删改查 这篇文章介绍如何使用 Jpa 和 Thymeleaf 做一个增删改查的示例。 1、pom依赖 pom 包里面添加Jpa 和 Thymeleaf 的相关包引用 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.…...

最快的包管理器--pnpm创建vue项目完整步骤
1.用npm全局安装pnpm npm install -g pnpm 2.在要创建vue项目的包下进入cmd,输入: pnpm create vue 3.输入项目名字,选择Router,Pinia,ESLint,Prettier之后点确定 4.cd到创建好的项目 ,安装依赖 cd .\刚创建好的项目名称\ p…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...