迅为嵌入式Linux学习笔记5——进程间通信

迅为嵌入式Linux学习笔记5——进程间通信

管道通信

无名管道

无名管道只能实现有亲缘关系的进程之间的通信,比如父子进程。

pipe函数:

#include 
int pipe(int pipefd[2]);//创建管道

参数:是我们得到的文件描述符

  • fd[0]: 读端
  • fd[1]: 写端

返回值:

  • 成功创建返回0
  • 创建失败返回-1.

例程1

#include 
#include 
#include 
#include 
#include 
#include 

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 
#include 

int mkfifo(const char *pathname, mode_t mode);

参数:是我们得到的文件描述符

  • pathname: 路径
  • mode: 读写模式

返回值:

  • 成功创建返回0
  • 创建失败返回-1.

例程2

fifo_read.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    char buf[32] = {0};
    int fd;

    if (argc < 2)
    {
        printf("Usage: %s \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 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int ret;
    int fd;

    if (argc < 2)
    {
        printf("Usage: %s \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 
#include 
int kill(pid_t pid, int sig);

raise函数:

#include 
int raise(int sig);

raise函数等价于kill(getpid(), sig);

alarm函数:
设置一个时间值,当设置的时间到了就产生一个信号。

#include 
unsigned int alarm(unsigned int seconds);

例程3

// 9号信号是“不能被捕获的进程终止信号”,进程接收到信号后默认操作是“终止进程”。
// 这个程序只会打印 "raise before\n"
#include 
#include 
#include 
#include 
#include 

int main(void)
{
	printf("raise before\n");
	raise(9);
	printf("raise after\n");
	return 0;
}

例程4

kill.c

// 用法与“kill 命令用法相同”
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    pid_t pid;
    int sig;
    if (argc < 3)
    {
        printf("Usage:%s  \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 
#include 

int main(void)
{
	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	return 0;
}

例程5

// 设置时间3秒,时间值到了以后,发送信号"SIGALRM",默认操作是终止该进程
#include 
#include 
#include 
#include 
#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 
int pause(void);
// pause() 会令目前的进程暂停(进入睡眠状态),直到被信号(signal)所中断。
// 返回值:只返回-1

信号的处理

信号处理的三种方式:系统默认,忽略,捕获

signal函数:

#include 
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 
#include 
#include 

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 
#include 
#include 

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 
#include 

int shmget(key_t key, size_t size, int shmflg);
  • 参数:
    • key_t key: IPC_PRIVATE或者是ftok函数的返回值
    • size_t size: 共享内存的大小
    • int shmflg: 权限
  • 返回值:
    • 成功返回共享内存的标识符,失败返回-1

例程8

#include 
#include 
#include 

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 
#include 

key_t ftok(const char *pathname, int proj_id);
  • 参数:
    • const char *pathname 文件路径以及文件名
    • int proj_id 字符
  • 返回值
    • 成功返回key值,失败返回-1

例程9

#include 
#include 
#include 
#include 

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 
#include 

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 
#include 

int shmdt(const void *shmaddr);
  • 参数:
    • const void *shmaddr: 共享内存映射后的地址
  • 返回值:
    • 成功返回0,失败返回-1

注意:shmdt函数是将进程中的地址映射删除,也就是说当一个进程不需要共享内存的时候,就可以使用这个函数将他从进程地址空间中脱离,并不会删除内核里面的共享内存对象。

shmctl函数,删除共享内存

#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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;
}

消息队列

  1. 创建消息队列
  2. 向消息队列发送消息
  3. 接收消息队列中的消息

msgget函数:创建消息队列

#include 
#Include 
#include 

int msgget(key_t key, int msgflg);
  • 参数:
    • key_t key: 和消息队列相关的key值
    • int msgflg: 访问权限
  • 返回值:
    • 成功返回消息队列的ID,失败返回-1

msgctl函数:删除消息队列

#include 
#include 
#include 

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 
#include 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 

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 
#include 
#include 

int semget(key_t key, int nsems, int semflg);
  • 参数:
    • key_t key: 信号量的键值。
    • int nsems: 信号量的数量
    • int semflg: 标识(权限)
  • 返回值:
    • 成功返回信号量的ID
    • 失败返回-1

semctl函数

#include 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 
#include 
#include 

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;
}

你可能感兴趣的:(嵌入式Linux,linux,学习,嵌入式)