迅为嵌入式Linux学习笔记5——进程间通信
管道通信
无名管道
无名管道只能实现有亲缘关系的进程之间的通信,比如父子进程。
pipe函数:
#include <unistd.h>
int pipe(int pipefd[2]);//创建管道
参数:是我们得到的文件描述符
- fd[0]: 读端
- fd[1]: 写端
返回值:
- 成功创建返回0
- 创建失败返回-1.
例程1
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
int fd[2];
char buf[32] = {0};
// 在fork函数之前创建管道
pipe(fd);
printf("fd[0] is %d\n", fd[0]);
printf("fd[1] id %d\n", fd[1]);
pid = fork();
if (pid < 0) {
printf("fork error\n");
return -1;
}
// 父进程
if (pid > 0) {
int status;
close(fd[0]);
write(fd[1], "hello", 5);
close(fd[1]);
wait(&status);
exit(0);
}
// 子进程
if (pid == 0) {
close(fd[1]);
read(fd[0], buf, 32);//阻塞读
printf("buf is %s\n", buf);
close(fd[0]);
exit(0);
}
}
有名管道
有名管道可以实现两个互不相关的进程间通信。
mkfifo 函数
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:是我们得到的文件描述符
- pathname: 路径
- mode: 读写模式
返回值:
- 成功创建返回0
- 创建失败返回-1.
例程2
fifo_read.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buf[32] = {0};
int fd;
if (argc < 2)
{
printf("Usage: %s <fifo name>\n", arg[0]);
return -1;
}
fd = open(argv[1], O_RDONLY);
while (1)
{
sleep(1);
read(fd, buf, 32);
printf("buf is $s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
return 0;
}
fifo_write.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int ret;
int fd;
if (argc < 2)
{
printf("Usage: %s <fifo name>\n", argv[0]);
return -1;
}
if (access(argv[1], F_OK) == -1) // 判断argv[1]传入的文件是否存在
{
ret = mkfifo(argv[1], 0666);
if (ret == -1)
{
printf("mkfifo error\n");
return -2;
}
printf("mkfifo ok\n");
}
fd = open(argv[1], O_WRONLY);
while (1)
{
sleep(1);
write(fd, "hello", 5);
}
close(fd);
return 0;
}
信号通信
可以通过命令* " kill -l " *查看系统中有哪些信号。
信号发送函数
kill函数:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
raise函数:
#include <signal.h>
int raise(int sig);
raise函数等价于kill(getpid(), sig);
alarm函数:
设置一个时间值,当设置的时间到了就产生一个信号。
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
例程3
// 9号信号是“不能被捕获的进程终止信号”,进程接收到信号后默认操作是“终止进程”。
// 这个程序只会打印 "raise before\n"
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(void)
{
printf("raise before\n");
raise(9);
printf("raise after\n");
return 0;
}
例程4
kill.c
// 用法与“kill 命令用法相同”
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
pid_t pid;
int sig;
if (argc < 3)
{
printf("Usage:%s <pid_t> <signal>\n", argv[0]);
return -1;
}
sig = atoi(argv[2]);
pid = atoi(argv[1]);
kill(pid, sig);
return 0;
}
test.c 一秒钟打印一句helloworld
// 先运行test.c
// 使用 " ps aux | grep ./test " 查看pid号
// 运行kill,发送信号到 test进程。
#include <stdio.h>
#include <unistd.h>
int main(void)
{
while(1)
{
printf("hello world\n");
sleep(1);
}
return 0;
}
例程5
// 设置时间3秒,时间值到了以后,发送信号"SIGALRM",默认操作是终止该进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#incldue < stdlib.h>
int main(int argc, char *argv[])
{
int i;
alarm(3);
while (1)
{
sleep(1);
i++;
printf("i = %d\n", i);
}
return 0;
}
信号通信
信号接收函数
如果一个进程要接收信号,需要这个进程不停止。一共有三种方式实现:
- while 循环
- sleep 函数
- pause 函数
pause函数:
#include <unistd.h>
int pause(void);
// pause() 会令目前的进程暂停(进入睡眠状态),直到被信号(signal)所中断。
// 返回值:只返回-1
信号的处理
信号处理的三种方式:系统默认,忽略,捕获
signal函数:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
// 可以简化成:
// signal(参数1,参数2);
// 参数1:我们要进行处理的信号,系统的信号我们可以在终端键入 kill -l 查看。
// 参数2:处理的方式(是系统默认还是忽略还是捕获)。
三个例子:
“SIGINT” 信号由“ctrl+c”产生。
- signal(SIGINT, SIG_IGN);
- SIG_IGN,代表忽略,也就是忽略SIGNT信号
- signal(SIGINT, SIG_DFL);
- SIG_DFL代表执行系统默认操作,大多数信号的系统默认动作是终止该进程
- signal(SIGINT, myfun);
- 捕捉SIGINT这个信号,然后执行myfun函数里面的代码。myfun由我们自己定义。
例程6
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
signal(SIGINT, SIG_IGN); //忽略2号信号,ctrl+c无法终止该进程
//signal(SIGINT, SIG_DFL);//系统默认的处理方式
while (1)
{
printf("wait signal\n");
sleep(1);
}
return 0;
}
例程7
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void myfun(int sig)
{
if (sig == SIGINT) // 接收到“ctrl+c”打印“get signal\n”
{
printf("get signal\n");
}
}
int main(void)
{
signal(SIGINT, myfun);// 接收到2号信号,执行myfun函数
// 接收到其他信号,不会忽略,也不会执行myfun函数
while (1)
{
printf("wait signal\n");
sleep(1);
}
return 0;
}
共享内存
- shmget函数,创建共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
- 参数:
- key_t key: IPC_PRIVATE或者是ftok函数的返回值
- size_t size: 共享内存的大小
- int shmflg: 权限
- 返回值:
- 成功返回共享内存的标识符,失败返回-1
例程8
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(void) {
int shmid;
shmid = shmget(IPC_PRIVATE, 1024, 0777);
if (shmid < 0) {
printf("shmget is error\n");
return -1;
}
printf("shmget is ok and shmid is %d\n", shmid);
return 0;
}
可以通过命令 ipcs -m 查看是否创建成功
可以使用命令 ipcrm -m ${shmid} 删除
ftok函数
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
- 参数:
- const char *pathname 文件路径以及文件名
- int proj_id 字符
- 返回值
- 成功返回key值,失败返回-1
例程9
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
int main(void) {
int shmid;
key_t key;
key = ftok("./a.c", 'a');
shmid = shmget(key, 1024, 0777 | IPC_CREAT);// 使用ftok函数返回值时,需要加上IPC_CREAT权限,否则创建失败
if (shmid < 0) {
printf("shmget is error\n");
return -1;
}
printf("shmget is ok and shmid is %d\n", shmid);
return 0;
}
两种创建方式的区别,如果使用IPC_PRIVATE作为第一个参数,创建的共享内存,key值是0,如果使用ftok函数返回值创建,key值不是0。
shmat函数,创建共享内存用户空间的地址映射
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
- 参数:
- int shmid: 共享内存的标识符,也就是shmget函数的返回值
- const void *shmaddr: 映射到的地址,一般写NULL,NULL为系统自动帮我们完成映射
- int shmflg: 通常为0,表示共享内存可读可写,或者为SHM_RDONLY,表示共享内存只读
- 返回值:
- 成功返回共享内存映射到进程中的地址,失败返回-1
shmdt函数,将进程里的共享内存地址映射删掉
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
- 参数:
- const void *shmaddr: 共享内存映射后的地址
- 返回值:
- 成功返回0,失败返回-1
注意:shmdt函数是将进程中的地址映射删除,也就是说当一个进程不需要共享内存的时候,就可以使用这个函数将他从进程地址空间中脱离,并不会删除内核里面的共享内存对象。
shmctl函数,删除共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds buf);
- 参数:
- int shmid: 要操作的共享内存的标识符
- int cmd: IPC_STAT(获取对象属性)IPC_SET(设置对象属性) IPC_RMID(删除对象)
- struct shmid_ds *buf: 指定 IPC_STAT(获取对象属性) IPC_SET(设置对象属性)时用来保存或者设置的属性
- 返回值:
- 成功返回0,失败返回-1
例程10
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
int shmid;
key_t key;
pid_t pid;
char *s_addr, *p_addr;
key = ftok("./a.c", 'a');
shmid = shmget(key, 1024, 0777 | ICP_CREATE);
if (shmid < 0)
{
printf("shmget is error\n");
return -1;
}
printf("shmget is ok and shmid is %d\n", shmid);
pid = fork();
if (pid > 0)
{
p_addr = shmat(shmid, NULL, 0);
strncpy(p_addr, "hello", 5);
wait(NULL);
exit(0);
}
if (pid == 0)
{
sleep(2);
s_addr = shmat(shmid, NULL, 0);
printf("saddr is %s\n", s_addr);
exit(0);
}
return 0;
}
消息队列
- 创建消息队列
- 向消息队列发送消息
- 接收消息队列中的消息
msgget函数:创建消息队列
#include <sys/types.h>
#Include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
- 参数:
- key_t key: 和消息队列相关的key值
- int msgflg: 访问权限
- 返回值:
- 成功返回消息队列的ID,失败返回-1
msgctl函数:删除消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
- 参数:
- int msqit: 消息队列的ID
- int cmd:
- IPC_STAT: 读取消息队列的属性,然后把它保存在buf指向的缓冲区。
- IPC_SET: 设置消息队列的属性,这个值取自buf参数
- IPC_RMID: 删除消息队列
- struct msqit_ds *buf: 消息队列的缓冲区
- 返回值:
- 成功返回0,失败返回-1
例程11
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(void)
{
int msgid;
key_t key;
key = ftok("./a.c", 'a');
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid < 0)
{
printf("msgget is error\n");
}
printf("msgget is ok and msgid is %d\n", msgid);
return 0;
}
可以使用命令 tpcs -q 查看消息队列是否创建成功
msgsend函数:向消息队列发送消息
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsend(int msqid, const void *msgp, size_t msgsz, int msgflg);
- 参数:
- int msqid: 消息队列ID
- const void *msgp: 指向消息类型的指针
- size_t msgsz: 发送的消息的字节数
- int msgflg:
- 如果为0,直到发送完成函数才返回,即阻塞发送
- IPC_NOWAIT: 消息没有发送完成,函数也会返回,即非阻塞发送
- 返回值:
- 成功返回0,失败返回-1
消息结构体,上文所述的发送的消息的字节数,仅包含消息内容‘mtext’的长度
struct msgbuf {
long mtype;//消息类型
char mtext[1]; //消息内容
}
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);
- 参数:
- int msqid: 消息队列ID
- void *msgp: 接收消息的缓冲区
- size_t msgsz: 接收消息的字节数
- long msgtyp: 接收消息的标识,0表示接收消息队列中的第一个消息
- int msgflg:
- 0表示阻塞读取
- IPC_NOWAIT表示非阻塞读取
- 返回值:
- 成功返回接收到的消息长度,失败返回-1
例程12
消息队列写
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf {
long mtype;
char mtext[128];
};
int main(void)
{
int msgid;
key_t key;
struct msgbuf msg;
key = ftok("./a.c", 'a');
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid < 0)
{
printf("msgget is error\n");
}
printf("msgget is ok and msgid is %d\n", msgid);
msg.mtype = 1;
strncpy(msg.mtext, "hello", 5);
msgsnd(msqid, &msg, strlen(msg.mtext), 0);
return 0;
}
消息队列读
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf {
long mtype;
char mtext[128];
};
int main(void)
{
int msgid;
key_t key;
struct msgbuf msg;
key = ftok("./a.c", 'a');
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid < 0)
{
printf("msgget is error\n");
}
printf("msgget is ok and msgid is %d\n", msgid);
msgrcv(msgid, &msg, 128, 0, 0);
printf("msg.mtype is %ld\n", msg.mtype);
printf("msg.mtext is %s\n", msg.mtext);
return 0;
}
信号量
信号量不以传输数据为主要目的,他的主要作用是保护共享资源。
semget函数,获取信号量的ID
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
- 参数:
- key_t key: 信号量的键值。
- int nsems: 信号量的数量
- int semflg: 标识(权限)
- 返回值:
- 成功返回信号量的ID
- 失败返回-1
semctl函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, union semun arg);
- 参数:
- int semid: 信号量ID
- int semnum: 信号量编号
- int cmd:
- IPC_STAT 获取信号量的属性
- IPC_SET 设置信号量的属性
- IPC_RMID 删除信号量
- SETVAL 设置信号量的值
- arg: union semun {
- int val;
- struct semid_ds *buf;
- unsigned short *array;
- struct seminfo * __buf;
- 返回值:
- 成功返回0,失败返回-1
semop函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
- 参数:
- int semid: 信号量ID
- struct sembuf *sops: 信号量结构体数组
- size_t nsops 要操作信号量的数量
sturct sembuf {
unsigned short sem_num; //要操作的信号量的编号
short sem_op; //P/V操作,1为V操作,释放资源。-1为P操作,分配资源。0为等待,直到信号量的值变成0
short sem_flg;//0表示阻塞,IPC_NOWAIT表示非阻塞
}
例程13
#include <stdio.h>
#include <sys/types.h>
#include <ipc.h>
#include <sys/sem.h>
#include <unistd.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *buf;
};
int main(void)
{
int semid;
int key;
union semun semun_union;
pid_t pid;
struct sembuf sem;
key = ftok("./a.c", 0666);
semid = semget(key, 1, 0666 | IPC_CREAT);
semun_union.val = 0;
semctl(semid, 0, SETVAL, semun_union);
pid = fork();
if (pid > 0)// 父进程
{
sem.sem_num = 0;
sem.op = -1;
sem.flg = 0;
semop(semid, &sem, 1);
printf("This is parents\n");
}
if (pid == 0)// 子进程
{
sleep(2);
sem.sem_num = 0;
sem.op = 1;
sem.flg = 0;
semop(semid, &sem, 1);
printf("This is son\n");
}
return 0;
}