S5.5:Unix进程通讯

管道

在这里插入图片描述

1.无名管道

#include <unistd.h>
int pipe(int pfd[2]);
  • 成功返回0,失败-1
    pfd包含两个元素的整形数组用来保存文件描述符
    pfd[0]用于读管道,pfd[1]用于写管道

代码案例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>

int main(int argc, char const *argv[])
{
    int pfd[2];
    int re;
    pid_t pid;

    re = pipe(pfd);
    if (re == -1)
    {
        perror("pipe");
        return -1;
    }

    pid = fork();
    if (pid < 0)
    {
        perror("pid");
        return -1;
    }
    //子线程
    else if (pid == 0)
    {
        char buf[64] = "haha,i am child,pipe message!\n";
        while (1)
        {
            write(pfd[1], buf, strlen(buf));
            sleep(1);
        }
    }
    else
    {
        char buf[64];
        while (1)
        {
            memset(buf,0,64);
            re = read(pfd[0], buf, 64);
            if (re > 0)
            {
                printf("msg from child %s\n", buf);
            }
            else
            {
                break;
            }
        }
    }

    return 0;
}

2.有名管道

  • 提供一个路径名与之相关
#include <unistd.h>
#include <fcntl.h>
int mkfifo(const char *path, mode_t mode);
  • 成功返回0,失败返回-1
    path创建管道的路径
    mode管道文件的权限,例0666

问题及解决方案(转载)

mkfifo: Operation not permitted

3.信号简介

  • 软件层次上对中断机制的一种模拟,是一种异步通信方式
  • Linux内核通过信号通知用户进程,不同的信号类型代表不同的事件

信号响应方式:

  1. 缺省方式(执行默认程序)
  2. 忽略方式
  3. 捕捉方式(执行自定义程序员)

常见信号

  • kill -l 查看常见信号

kill [-signal] pid

  1. sig可指定信号
  2. pid指定发送对象

killall [-u user|prog]

  1. prog 指定进程名
  2. user 指定用户名
信号名含义
sighup终端关闭时,发给终端关联所有进程
sigint用户输入ctl+c后,内核发送该信号给当前终端的所有前台进程
sigquitctrl+\,同sigint
sigll进程执行非法指令,进程终止
sigsev非法访问内存,缓存区溢出
sigpipe当进程往一个没有读端的管道中写入时产生,代表"管道破裂"
sigkill用于结束进程,无法捕捉和忽略
sigstop用于暂停进程,无法捕捉和忽略
sigtstp用于暂停进程,ctrl+z
sigcint进程进入运行状态
sigalrm信号用于通知进程定时器时间已到
sigusr1/2用户自定义信号,用于终止进程

4.程序处理信号

信号发送

#include <unistd.h>
#include <signal.h>
int kill(pid_t pid,int sig);
int raise(int sig);//给自己发信号
  • 成功返回0,是被返回EOF
    pid 接收进程的进程号
    0代表同组进程,-1代表所有进程
    sig 信号类型

信号相关函数 alarm/pause

int alarm(unsigned int seconds);
  • 成功返回上个定时器的剩余时间,失败返回EOF
  • seconds 定时器时间
  • 一个进程只能设置一个定时器,时间到产生sigalrm信号
int pause(void)
  • 使进程一直处于阻塞状态,直到被信号中断
  • 被信号中断后产生-1,errno为eintr

代码案例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>

int main(int argc, char const *argv[])
{
    alarm(3);//alaem信号时间到后会终止程序
    pause();
    printf("I have been waken up!\n");//不会被打印
    return 0;
}

设置信号响应方式 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,sighndler_t handler);
  • 成功返回原先的信号处理函数,失败返回SIG_ERR
  • signo 要设置的信号类型
  • handler 指定的信号处理函数,SIG_DFL代表缺省方式
  • SIG_IGN代表忽略信号

代码案例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <signal.h>

//定义了一个指向(返回值为void)(参数为int)的函数指针
typedef void (*sight)(int);
sight oldhandle;
int count;
void handlerfunc();

int main(int argc, char const *argv[])
{
    //signal返回值是原先(默认)的信号处理函数
    oldhandle = signal(SIGINT, handlerfunc);
    oldhandle = signal(SIGHUP, handlerfunc);
    while (1)
    {
        sleep(1);
    }
    return 0;
}

void handlerfunc(int sig)
{
    count++;
    if (SIGINT == sig)
    {
        printf("i got sigint\n");
    }
    else if (SIGHUP == sig)
    {
        printf("i got sighub\n");
    }
    //ctrl+c两次后退出程序
    if (count == 2)
    {
        //SIG_DFL:默认信号处理程序
        //signal(SIGINT, SIG_DFL);
        signal(SIGINT, oldhandle);
    }
}
  • 使用ctrl+c后程序不会终止,而是执行handlerfunc函数

子进程结束信号

  • SIGCHID
  • 用于处理子进程的僵尸进程

5.共享内存

  1. 共享内存是一种搞笑的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
  2. 共享内存存在内核空间创建,可以被进程映射到用户空间访问,使用灵活
  3. 由于多个进程可同时访问共享内存,因此需要同步互斥机制配合

共享内存使用步骤

  • 创建,共享内存空间,读写,撤销内存映射,删除共享内存对象

IPC对象

  • 共享内存
  • 队列消息
  • 信号灯集
  • IPC对象创建后一直存在,直到被显式删除
  • 每个IPC对象有一个关联的key
  • ipcs(查看)/ipcrm(删除)
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *path,int proj_id);
  • path 存在并可访问的路径
  • proj_id 用于生成key的数字,范围1-255

创建共享内存

#include <sys/types.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

共享内存映射

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid,const void *shmaddr,intshmflg);
  • 成功返回映射后的地址,失败返回(void *)-1;
  • shmid 要映射的共享内存id
  • shmaddr 映射后的地址,NULL 表示由系统自动选择映射
  • shmflg 标志位 0表示可读写,SHM_RDONLY

共享内存读写

addr=(char *)shmat(shmid,NULL,0);
...
fget(addr,N,stdin);

共享内存撤销

#include <sys/types.h>
#include <sys/shm.h>
int shmdt(void *shmaddr);
  • 成功返回0,失败-1
  • 不使用共享内存时撤销
  • 进程结束时自动撤销

共享内存控制

#include <sys/types.h>
#include <sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
  • 成功0 ,失败-1
  • shmid 要操作的共享内存的id
  • crmd 要执行的操作 IPC_RMID(删除)
  • buf 保存或设置共享内存属性的地址

代码案例(写)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <sys/ipc.h>
#include <sys/shm.h>

int main(int argc, char const *argv[])
{
    key_t key;
    int shmid;
    char *addr;

    key=ftok("/mnt/e/LV5_day6",23);
    if(key==-1){
        perror("fork");
        return -1;
    }

    shmid=shmget(key,1024,IPC_CREAT|0666);
    if (shmid==-1)
    {
        perror("shmget");
        return -1;
    }
    
    addr=shmat(shmid,NULL,0);
    strcpy(addr,"this is share memeory");

    shmdt(addr);
    
    return 0;
}

代码案例(读)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <sys/ipc.h>
#include <sys/shm.h>

int main(int argc, char const *argv[])
{
    key_t key;
    int shmid;
    char *addr;

    key=ftok("/mnt/e/LV5_day6",23);
    if(key==-1){
        perror("fork");
        return -1;
    }

    shmid=shmget(key,1024,IPC_CREAT|0666);
    if (shmid==-1)
    {
        perror("shmget");
        return -1;
    }
    
    addr=shmat(shmid,NULL,0);
    printf("get share mem=%s\n",addr);

    //是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存
    shmdt(addr);

    //删除共享内存
    shmctl(shmid,IPC_RMID,NULL);
    
    return 0;
}

6.消息队列

创建/打开消息队列

#include <sys/ipc.h>
#incluee <sys/msg.h>
int msgget(key_t key,int msgflg);
  • 成功队列id,失败EOF;
  • key 与共享内存关联的key,IPC_PRIVATE或ftok生成

发送消息队列 msgsnd

#include <sys/ipc.h>
#incluee <sys/msg.h>
int msgsnd(int msgid,const void *msgp,size_t size,int msgflg);
  • 成功0,失败-1
  • msgid 消息队列id
  • msgp 消息缓冲区地址
  • size 消息正文长度
  • msgflg 标志位0或IPC_NOWAIT

消息队列结构体

  • 消息双方使用定义好的统一格式
struct msgbuf
{
	long mtype;//消息类型
	char mtext[1];//消息正文
}

消息接收 msgrcv

#include <sys/types.h>
#include <sys/ipc.h>
#incluee <sys/msg.h>
int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int msgflg);
  • msgtype 指定接收消息的类型
  • 0:接收消息队列中第一个消息
  • 大于0:第一个msgtype类型的消息
  • 小于0:第一个类型值不小于msgtype绝对值且类型值最小的消息
  • msgflg : MSG_NOERROR,IPC_NOWWAIT,0

控制消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#incluee <sys/msg.h>
int msgctl(int msgid,int cmd,struct msqid_ds *buf);
  • cmd 要执行的操作
  • buf 存放消息队列属性的地址

代码案例

#include <sys/msg.h>
#include <sys/ipc.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define LEN sizeof(MSG) - sizeof(long)

typedef struct
{
    long type;
    char txt[64];
} MSG;

int main(int argc, char const *argv[])
{
    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, "message one");
    msgsnd(msgid, &msg_t, LEN, 0);

    msg_t.type = 2;
    strcpy(msg_t.txt, "message two");
    msgsnd(msgid, &msg_t, LEN, 0);
}
#include <sys/msg.h>
#include <sys/ipc.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define LEN sizeof(MSG) - sizeof(long)

typedef struct
{
    long type;
    char txt[64];
} MSG;

int main(int argc, char const *argv[])
{
    key_t ipkey;
    int msgid;
    int re;

    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("recrive type:%ld/t txt:%s\n",msg_t.type,msg_t.txt);
        if (re < 0)
        {
            break;
        }
    }
}

7.信号灯(system V)

  • system v信号灯是一个或多个技术信号灯的集合
  • 可同时操作集合中多个信号灯
  • 申请多个资源避免锁死

打开/创建信号灯 semget

#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key,int nsms,int semglg);
nsems 集合中包含的计数信号灯个数
semflg 标志位 IPC_CREAT|0666 IPC_EXCL

信号灯初始化/删除信号灯 semctl

#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid,int sennum,int cmd,...);
sennum 要操作的集合中信号灯编号
cmd 要执行的操作 SETVAL IPC_RMIC
第四个参数(联合体union semun)是否存在取决于cmd

P/V操作 semop

#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid ,struct sembuf *sops,unsigned nsops);
sops:描述对信号灯操作的结构体(数组)
nsops:要操作的信号灯个数
struct sembuf
{
	short sem_num;
	short sem_op;
	short sem_flg;
}
semnum:信号灯编号
sem_op:-1 p操作,1 v操作
sem_flg:0/IPC_NOWAIT
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值