linux:信号

文章目录

    • 1、信号的实现机制
    • 2、发送信号
      • 2.1、发送信号的原因
      • 2.2、发送信号的机制
        • kill 函数
    • 3、接收信号
      • 3.1、处理信号
        • signal 函数
        • sigaction 函数
      • 3.2、信号阻塞 | 解除
        • sigset_t 信号集合
        • sigpending 函数
        • sigprocmask 函数
        • sigsuspend 函数
    • 4、定时器
      • 4.1、睡眠函数
        • sleep 函数
        • pause 函数
        • alarm 函数
      • 4.2、定时器
        • setitimer 函数
    • 5、例:四窗口聊天

信号:消息,通知进程系统中发生了某种类型的事件,每种类型的信号对应某种系统事件。

信号是进程间通信中唯一的异步通信机制。在进程运行过程中,经常会产生一些事件,这些事件的产生和进程的执行往往是异步的,信号提供了一种在软件层面上的异步处理事件的机制。在硬件层面上的异步处理机制是中断,信号是中断的软件模拟。

每个信号用一个整型常量宏表示,以 SIG开头,在头文件中定义

# 查看信号列表
kill -l 
man 7 signal

1、信号的实现机制

发送信号到目的进程是由发送信号和接收信号两个步骤组成。

  • 发送信号:内核通过更新目的进程上下文中的某个状态,发送一个信号给目的进程。
  • 接收信号:目的进程被内核强迫以某种方式对信号的发送做出反应。

linux:信号_第1张图片

待处理信号(pending signal):一个发出而没有接收的信号,又称为挂起信号或未决信号。

在任何时候,一种类型的信号至多只会有一个待处理信号。也就是说,如果一个进程有一个类型为 k 的待处理信号,那么任何接下来发送到这个进程的类型为 k 的信号都不会排队等待,它们只是被简单丢弃。一个进程可以选择性地阻塞接收某种信号,当一种信号被阻塞,它仍可以被发送,但是产生的待处理信号不会被接收,直到进程取消对这种信号的阻塞。

一个待处理信号最多只能被接收一次。内核为每个进程,在位图 pending 中维护着待处理信号的结合,在位图 blocked 集合(信号屏蔽字)中维护着被阻塞的信号集合。只要发送了一个类型为 k 的信号,内核就会设置 pending中的第 k 位,只要接收了一个类型为 k 的信号,内核就会清楚 pending中的第 k 位。

2、发送信号

2.1、发送信号的原因

  • 内核检测到系统事件,例除零错误等
  • 进程调用 kill 函数

2.2、发送信号的机制

unix 系统提供了大量向进程发送信号的机制,这些机制都是基于进程组的。每个进程拥有自己的 pid,每个进程属于一个进程组 pgid

硬件来源:键盘。硬件触发中断,操作系统切换到内核态执行中断处理程序,中断处理程序发送信号,进程接收信号并处理。

ctrl + c: SIGINT,表示终止该进程
ctrl + z: SIGTSTP,表示挂起该进程

软件来源:/bin/kill 程序,kill 函数,定时器函数。

kill 函数

进程通过 kill 函数发送信号给其他进程(包括自己)。

/*
返回值:成功返回 0,失败返回 -1

参数
- pid: pid > 0,发送 sig 信号给进程 pid
	   pid = 0, 发送 sig 信号给调用进程所在进程组中的每个进程(包括调用进程自己)
	   pid < 0,发送 sig 信号给进程组 |pid| 中的每个进程
- sig: 信号类型
*/
int kill(pid_t pid, int sig);

3、接收信号

进程接收到信号,有三种处理方法:

  • 默认处理:signal(SIGINT,SIG_DFL)

    - Term:终止当前进程
    - Ign:忽略该信号
    - Core:终止当前进程,并且产生core dump
    - Stop:停止(挂起)一个进程
    - Cont:使当前停止的进程,继续运行
    
  • 忽略信号:signal(SIGSEGV,SIG_IGN)

  • 捕捉信号:自定义信号处理函数。用户自定义信号处理函数的目的就是实现进程的有序退出。

自定义信号处理函数设置原则

  • 程序尽可能简单。例:处理程序简单设置全局标志并返回,所有与接收信号相关的处理由主程序执行。
  • 函数内部只调用信号异步安全函数

安全函数:可重入的(例只访问局部变量),不能被信号处理程序中断。

3.1、处理信号

signal 函数

进程可以通过signal函数修改和信号相关联的默认行为。注意:SIGKILLSIGSTOP这两个信号既不能被忽略也不能被捕捉,即进程接收到这两个信号后,只能接受系统的默认处理,即终止进程。

# include  
// 信号回调函数,信号处理
typedef void (*sighandler_t)(int);

/*
功能:捕获信号
返回值:成功返回前次处理程序的指针,失败返回 SIG_ERR
参数:
- signum:要捕捉的信号值,
- handler:函数处理函数。SIG_DFL 默认处理; SIG_IGN,忽略信号; 其他,执行信号回调函数
*/ 
sighandler_t signal(int signum, sighandler_t handler); 
sigaction 函数

signal函数处理机制在多信号处理的场景下

  • 收到不同类型的信号:中断当前信号处理,优先处理新的信号,处理完后返回继续处理当前信号。
  • 收到相同类型的信号:执行完当前信号处理,然后只执行新的相同信号一次,重复的信号被忽略。
  • 当前进程阻塞在系统调用上,收到一个信号后,中断系统调用,执行信号处理函数。

不同的系统有不同的信号处理语义,但 signal函数的处理过程是固定的,无法调整。因此,Posix 标准定义了sigaction函数,允许用户自定义这些场景下进程的行为。

/*
返回值:成功返回0,失败返回-1。
参数:
- signum:要捕捉的信号值 
- act:自定义行为
- oldact:保存原来信号的回调函数,通常传入空指针
*/
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) 

其中 sigaction 结构体

struct sigaction {    
    // 联合体,新旧处理函数只有一个可以生效  
    union { 
        // 旧类型的信号处理函数 
        __sighandler_t _sa_handler; 
        // 新类型的信号处理函数,siginfo_t 保存信号的相关信息
        void (*_sa_sigaction)(int, struct siginfo *, void *);
    } _u;

    // 阻塞信号的结合 
    sigset_t sa_mask; 

    // 信号处理的方式,旧类型填 0。其他例如:
    // SA_SIGINFO,选择回调函数 sa_sigaction; 
    unsigned long sa_flags; 

    // 保留不用
    void (*sa_restorer)(void);
};

3.2、信号阻塞 | 解除

Linux 提供阻塞信号的机制

  • 隐式阻塞机制:内核默认阻塞任何当前处理程序正在处理信号类型的待处理信号。
  • 显式阻塞机制: sigprocmask 函数
sigset_t 信号集合
#include  

typedef struct { 
    unsigned long int __val[(1024/(8*sizeof(unsigned long int)))]; 
} __sigset_t; 

// sigset_t的本质就是一个位图,共有1024位
typedef __sigset_t sigset_t; 

// 初始化信号集,清除所有信号 
int sigemptyset(sigset_t *set); 
// 初始化信号集,包括所有信号
int sigfillset(sigset_t *set);  
// 增加信号 
int sigaddset(sigset_t *set, int signum); 
// 删除信号 
int sigdelset(sigset_t *set, int signum); 
// 检查信号是否处于信号集之中
int sigismember(const sigset_t *set, int signum); 
sigpending 函数

获取当前待处理信号的集合。通常在回调函数当中使用的,用于检查当前是否阻塞了某个信号

/*
返回值:成功返回0,失败返回-1
参数 set: 要检测的信号
*/
int sigpending(sigset_t *set);
sigprocmask 函数

改变当前阻塞的信号集合(位图 blocked

/*
返回值:成功返回0, 失败返回-1

参数
- how:
	SIG_BLOCK: 把集合 set 中的信号加入到阻塞集合 blocked 当中 (blocked |= set)
    SIG_UNBLOCK: 把集合 set 从阻塞集合 blocked 中删除 (blocked = blocked & ~set)
    SIG_SETMASK: 把集合 set 替换阻塞集合 blocked (blocked = set)
- set:阻塞集合
- 参数3:原有的集合
*/
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
sigsuspend 函数

使用 sigprocmask 函数可以实现信号保护临界区。在临界区执行代码的时候,此时产生的信号将会被阻塞,临界区结束的位置只需要再使用 sigprocmask 即可。pause 系统调用可以唤醒一个阻塞进程,直到被一个信号唤醒。若采用 pause 函数捕捉临界区的信号,信号解除阻塞后,立即执行信号处理函数,无法捕捉信号。

为了捕捉临界区的信号,将解除阻塞和等待信号合并成一个原子操作,就是sigsuspend函数

int sigsuspend(const sigset_t *mask);

4、定时器

4.1、睡眠函数

sleep 函数
#include 
// 休眠 sec 秒
unsigned int sleep(unsigned int seconds);
// 休眠 usec 秒
int usleep(useconds_t usec);
pause 函数

阻塞一个进程,直到某个信号被递送时,进程会解除阻塞,然后终止进程或者执行信号处理函数

int pause(void);
alarm 函数

进程调用 alarm 函数向它自己发送 SIGALRM信号。

/*
返回值:前一次闹钟剩余的秒数,若以前没有设定闹钟,则为 0
参数:seconds: 闹钟的时间间隔
*/
unsigned int alarm(unsigned int seconds);

alarm 函数安排内核在 secs 秒后发送一个 SIGALRM 信号给调用进程。在任何情况下,对 alarm 的调用都将取消任何待处理的闹钟,并且返回待处理的闹钟在被发送前还剩下的秒数,若没有代理处理的闹钟,返回 0。

4.2、定时器

setitimer 函数

setitimer 系统调用负责调整间隔定时器。间隔定时器在创建的时候,就会设置一个时间间隔,定时器到达时间间隔时,调用进程会产生一个信号,随后定时器被重置。

定时器的分类

  • 真实计时器:程序实际运行的时间(时钟时间),时间到发送 SIGALARM信号
  • 虚拟计时器:程序在用户态模式下的 CPU 时间,时间到发送SIGVTALARM信号,
  • 实用计时器:程序在用户态和内核态所占用的 CPU 时间,时间到发送 SIGPROF信号

使用 fork 的时候子进程不会继承父进程的定时器,使用 exec 时候,定时器不会销毁。

/*
返回值:成功返回0,失败返回-1.

参数:
- which:设置定时器的种类:真实计时器 SIGALARM、虚拟计时器 SIGVTALARM、实用计时器 SIGPROF
- new_value:定时器的初始时间  
- old_value: 定时器的间隔时间    
*/
int getitimer(int which, struct itimerval *curr_value)
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) 
  
struct itimerval { 
    struct timeval it_interval; // 时间间隔 
    struct timeval it_value;    // 初始时间
}; 
struct timeval {
    time_t tv_sec;       // 秒 
    suseconds_t tv_usec; // 微秒
};

例:使用真实计时器,实用计时器,统计程序执行时间,在实用计时器及虚拟计时器设定计时后,先睡眠,再让程序处于while(1)

#include 

void sigFunc(int sigNum) {
    time_t now;
    time(&now);
    printf("now time = %s\n", ctime(&now));
}

int main(int argc,char*argv[]) {
    //真实计时器,信号SIGALRM
    signal(SIGALRM, sigFunc);

    struct itimerval  Time;
    memset(&Time, 0, sizeof(Time));
    Time.it_value.tv_sec = 1;    // 1s后启动
    Time.it_interval.tv_sec = 2; // 间隔2s

    time_t now;
    time(&now);
    printf("now time = %s", ctime(&now));
	// ITIMER_REAL
    setitimer(ITIMER_REAL, &Time, NULL);
    // ITIMER_PROF
    // setitimer(ITIMER_PROF, &Time, NULL);

    printf("before sleep\n");
    sleep(3);
    printf("after sleep\n");
    
    while(1);
    return 0;
}

5、例:四窗口聊天

在四个终端上分别启动A、A1、B、B1四个进程。

  • A 和 B 负责通信,A1和B1负责显示信息。
  • A 和 B 使用有名管道进行通信
  • A 和 A1 进程通信采用共享内存(信号量同步),B 和 B1 进程通信采用消息队列
  • 退出方式:ctrl + ckill 命令,任意进程收到信号后,给所有进程发送 10 号信号,有序退出。有序退出要做的主要有:关闭管道,解除对共享内存的映射,删除共享内存,删除信号量,删除消息队列。

showA

#include 

// 保存各个进程的pid
typedef struct pidnums{
    int pidWriteA;
    int pidWriteB;
    int pidShowA;
    int pidShowB;
} pidNums_t, *pPidNums_t;

typedef struct package{
    int flag;
    char buf[64];
}Package_t, *pPackage_t;

pPidNums_t pidsets; 
pPackage_t p;

// 2 信号处理函数
void sigFunc2(int signum,siginfo_t *p,void *p1){
    printf("%d is coming\n",signum);
    kill(pidsets->pidWriteA, 10);
    kill(pidsets->pidWriteB, 10);
    kill(pidsets->pidShowB, 10);
    kill(pidsets->pidShowA, 10);
}

// 10 信号处理函数:
void sigFunc10(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    shmdt(pidsets);
    shmdt(p);
    exit(0);
}

int main(int argc, char *argv[]) {
    // 2 号信号
    struct sigaction act2;
    bzero(&act2, sizeof(act2));
    act2.sa_flags = SA_SIGINFO;
    act2.sa_sigaction = sigFunc2;
    int ret = sigaction(SIGINT, &act2, NULL);
    ERROR_CHECK(ret, -1, "sigaction 2");
	
    // 10 号信号
    struct sigaction act10;
    bzero(&act10, sizeof(act10));
    act10.sa_flags = SA_SIGINFO;
    act10.sa_sigaction = sigFunc10;
    ret = sigaction(10, &act10, NULL);
    ERROR_CHECK(ret, -1, "sigaction 10");

    // 创建共享内存
    int shmid = shmget(1000, 1024, IPC_CREAT|0600);
    ERROR_CHECK(shmid,-1,"shmget");

    // 映射地址空间
    p = (pPackage_t)shmat(shmid,NULL,0);
    ERROR_CHECK(p, (pPackage_t)-1, "shmat");
    int semArrId = semget(1000, 1, IPC_CREAT|0600);
    ERROR_CHECK(semArrId, -1, "semget");
	
    ret = semctl(semArrId, 0, SETVAL, 1);
    ERROR_CHECK(ret, -1, "semctl");
    struct sembuf sopp, sopv;
    sopp.sem_num = 0;
    sopp.sem_op = -1;
    sopp.sem_flg = SEM_UNDO;
    sopv.sem_num = 0;
    sopv.sem_op = 1;
    sopv.sem_flg = SEM_UNDO;

    int pid = getpid();
    int shmidPid = shmget(2000, 4096, IPC_CREAT|0600);
    ERROR_CHECK(shmidPid,- 1, "pid shmget");
    pidsets = (pPidNums_t)shmat(shmidPid, NULL, 0);
    ERROR_CHECK(pidsets, (pPidNums_t) - 1, "pid shmat");
    pidsets->pidShowA = pid;
    while(1){
        semop(semArrId, &sopp, 1);
        if(p->flag == 1){
            puts(p->buf);
            printf("\n");
            memset(p->buf, 0, sizeof(p->buf));
            p->flag = 0;
        }
        if(p->flag == 2){
            printf("%*s%s\n", 10, "", p->buf);
            memset(p->buf, 0, sizeof(p->buf));
            p->flag = 0;
        }
        semop(semArrId, &sopv, 1);
    }
    return 0;
}

write A

#include 

typedef struct package{
    int flag;
    char buf[64];
}Package_t, *pPackage_t;

typedef struct pidnums{
    int pidWriteA;
    int pidWriteB;
    int pidShowA;
    int pidShowB;
} pidNums_t, *pPidNums_t;

pPidNums_t pidsets; 

int fdr = 0,fdw = 0;
int shmid = 0;
int semArrId = 0;
int shmidPid = 0;
pPackage_t p;

void sigFunc2(int signum,siginfo_t *p,void *p1){
    printf("%d is coming\n",signum);
    kill(pidsets->pidWriteB, 10);
    kill(pidsets->pidShowA, 10);
    kill(pidsets->pidShowB, 10);
    kill(pidsets->pidWriteA, 10);
}

void sigFunc10(int signum,siginfo_t *p,void *p1){
    printf("%d is coming\n", signum);
    semctl(semArrId, 0, IPC_RMID);
    shmdt(p);
    shmdt(pidsets);
    shmctl(shmid, IPC_RMID, NULL);
    shmctl(shmidPid, IPC_RMID, NULL);
    close(fdr);
    close(fdw);
    exit(0);
}

int main(int argc,char *argv[]) {
    ARGS_CHECK(argc, 3);
    struct sigaction act2;
    bzero(&act2, sizeof(act2));
    act2.sa_flags = SA_SIGINFO;
    act2.sa_sigaction = sigFunc2;
    int ret = sigaction(SIGINT, &act2, NULL);
    ERROR_CHECK(ret, -1, "sigaction 2");

    struct sigaction act10;
    bzero(&act10, sizeof(act10));
    act10.sa_flags = SA_SIGINFO;
    act10.sa_sigaction = sigFunc10;
    ret=sigaction(10, &act10, NULL);
    ERROR_CHECK(ret, -1, "sigaction 10");

    //A和B的读写管道
    fdr=open(argv[1], O_RDONLY);
    ERROR_CHECK(fdr, -1, "open1");
    fdw=open(argv[2], O_WRONLY);
    ERROR_CHECK(fdw, -1, "open2");
    printf("i am chat1, fdr=%d fdw=%d\n",fdr,fdw);

    char buf[128] = {0};
    fd_set rdset;

    // A和showA之间的共享内存
    shmid=shmget(1000, 1024, IPC_CREAT|0600);
    ERROR_CHECK(shmid, -1, "shmget");
    p=(pPackage_t)shmat(shmid, NULL, 0);
    ERROR_CHECK(p, (pPackage_t) - 1, "shmat");
    p->flag = 0;

    // A和showA之间的互斥锁
    semArrId = semget(1000, 1, IPC_CREAT|0600);
    ERROR_CHECK(semArrId, -1, "semget");
    ret=semctl(semArrId, 0, SETVAL, 1);
    ERROR_CHECK(ret, -1, "semctl");
    struct sembuf sopp, sopv;
    sopp.sem_num = 0;
    sopp.sem_op = -1;
    sopp.sem_flg = SEM_UNDO;
    sopv.sem_num = 0;
    sopv.sem_op = 1;
    sopv.sem_flg = SEM_UNDO;

    // pid存储的共享内存
    int pid = getpid();
    shmidPid = shmget(2000, 4096, IPC_CREAT|0600);
    ERROR_CHECK(shmidPid, -1, "pid shmget");
    pidsets=(pPidNums_t)shmat(shmidPid, NULL, 0);
    ERROR_CHECK(pidsets, (pPidNums_t) - 1, "pid shmat");
    pidsets->pidWriteA = pid;

    while(1){
        FD_ZERO(&rdset);
        FD_SET(STDIN_FILENO, &rdset);
        FD_SET(fdr, &rdset);
        ret = select(fdw + 1, &rdset, NULL, NULL, NULL);
        ERROR_CHECK(ret, -1, "select");
        printf("ret = %d\n", ret);

        if(FD_ISSET(STDIN_FILENO,&rdset)){
            memset(buf, 0, sizeof(buf));
            ret=read(STDIN_FILENO, buf, sizeof(buf));
            //        if(0==ret){
            //            printf("主动断开\n");
            //            break;
            //        }
            if(strcmp(buf,"\n")!=0){
                write(fdw,buf,strlen(buf)-1);
                ret = semop(semArrId,&sopp,1);
                ERROR_CHECK(ret, -1, "semop1");
                p->flag=2;
                strcpy(p->buf,buf);
                ret = semop(semArrId,&sopv,1);
                ERROR_CHECK(ret, -1, "semop2");
            }
        }

        if(FD_ISSET(fdr, &rdset)){
            memset(buf, 0, sizeof(buf));
            ret=read(fdr, buf, sizeof(buf));
            ERROR_CHECK(ret, -1, "read");
            printf("ret = %d\n", ret);
            //        if(0==ret){
            //            printf("连接已断开\n");
            //            break;
            //        }
            ret = semop(semArrId, &sopp, 1);
            ERROR_CHECK(ret, -1, "semop1");
            p->flag = 1;
            strcpy(p->buf, buf);
            ret = semop(semArrId, &sopv, 1);
            ERROR_CHECK(ret, -1, "semop2");
            puts(buf);
        }
    }
    return 0;
}

showB

#include 

typedef struct pidnums{
    int pidWriteA;
    int pidWriteB;
    int pidShowA;
    int pidShowB;
} pidNums_t,*pPidNums_t;

typedef struct mymsgbuf{
    long mtype;
    char mtext[64];
}MSG_t;

pPidNums_t pidsets;

void sigFunc2(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    kill(pidsets->pidWriteA, 10);
    kill(pidsets->pidWriteB, 10);
    kill(pidsets->pidShowA, 10);
    kill(pidsets->pidShowB, 10);
}

void sigFunc10(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    shmdt(pidsets);
    exit(0);
}

int main(int argc,char *argv[]) {
    struct sigaction act2;
    bzero(&act2,sizeof(act2));
    act2.sa_flags =S A_SIGINFO;
    act2.sa_sigaction = sigFunc2;
    int ret =s igaction(SIGINT,&act2,NULL);
    ERROR_CHECK(ret, -1, "sigaction 2");

    struct sigaction act10;
    bzero(&act10, sizeof(act10));
    act10.sa_flags = SA_SIGINFO;
    act10.sa_sigaction = sigFunc10;
    ret=sigaction(10, &act10, NULL);
    ERROR_CHECK(ret, -1, "sigaction");
    int msgid=msgget(1000, IPC_CREAT|0600);
    ERROR_CHECK(msgid, -1, "msgget");
   
    struct mymsgbuf msgInfo;
    
    int pid = getpid();
    int shmidPid = shmget(2000, 4096, IPC_CREAT|0600);
    ERROR_CHECK(shmidPid, -1, "pid shmget");
    pidsets=(pPidNums_t)shmat(shmidPid, NULL, 0);
    ERROR_CHECK(pidsets, (pPidNums_t) - 1, "pid shmat");
    pidsets->pidShowB = pid;

    while(1){
        bzero(&msgInfo, sizeof(msgInfo));
        ret = msgrcv(msgid, &msgInfo, sizeof(msgInfo), 0, 0);
        ERROR_CHECK(ret, -1, "msgrcv");
        if(msgInfo.mtype == 1){
            printf("%s\n\n", msgInfo.mtext);
        }
        if(msgInfo.mtype == 2){
            printf("%*s%s\n", 10, "", msgInfo.mtext);
        }
    }
    return 0;
}

wirteB

#include 

typedef struct pidnums{
    int pidWriteA;
    int pidWriteB;
    int pidShowA;
    int pidShowB;
}pidNums_t, *pPidNums_t;

pPidNums_t pidsets; 
int fdr,fdw;
int msgid;

typedef struct mymsgbuf{
    long mtype;
    char mtext[64];
} Msg_t,*pMsg_t;

void sigFunc2(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    kill(pidsets->pidWriteA, 10);
    kill(pidsets->pidShowA, 10);
    kill(pidsets->pidShowB, 10);
    kill(pidsets->pidWriteB, 10);
}

void sigFunc10(int signum, siginfo_t *p, void *p1){
    printf("%d is coming\n", signum);
    close(fdr);
    close(fdw);
    shmdt(pidsets);
    msgctl(msgid, IPC_RMID,NULL);
    exit(0);
}

int main(int argc,char *argv[]) {
    ARGS_CHECK(argc, 3);

    struct sigaction act2;
    bzero(&act2, sizeof(act2));
    act2.sa_flags = SA_SIGINFO;
    act2.sa_sigaction =s igFunc2;
    int ret=sigaction(SIGINT, &act2, NULL);
    ERROR_CHECK(ret, -1, "sigaction 2");

    struct sigaction act10;
    bzero(&act10, sizeof(act10));
    act10.sa_flags =S A_SIGINFO;
    act10.sa_sigactio n= sigFunc10;
    ret=sigaction(10, &act10, NULL);
    ERROR_CHECK(ret, -1, "sigaction");

    fdw = open(argv[1], O_WRONLY);
    fdr = open(argv[2], O_RDONLY);
    printf("i am chat2 , fdr=%d fdw=%d\n", fdr, fdw);
    // B 和 showB 的消息队列
    char buf[128] = {0};
    fd_set rdset;
    msgid = msgget(1000, IPC_CREAT|0600);
    ERROR_CHECK(ret, -1, "msgget");
    struct mymsgbuf msgInfo;
    // pid 共享内存
    int pid = getpid();
    int shmidPid = shmget(2000, 4096, IPC_CREAT|0600);
    ERROR_CHECK(shmidPid, -1, "pid shmget");
    pidsets = (pPidNums_t)shmat(shmidPid, NULL, 0);
    ERROR_CHECK(pidsets, (pPidNums_t) - 1, "pid shmat");
    pidsets->pidWriteB = pid;

    while(1){
        FD_ZERO(&rdset);
        FD_SET(STDIN_FILENO, &rdset);
        FD_SET(fdr, &rdset);
        ret=select(fdr + 1, &rdset, NULL, NULL, NULL);
        ERROR_CHECK(ret, -1, "select");
        if(FD_ISSET(STDIN_FILENO, &rdset)){
            memset(buf, 0, sizeof(buf));
            ret = read(STDIN_FILENO, buf, sizeof(buf));
            //     if(0==ret){
            //         printf("主动断开\n");
            //         break;
            //     }
            if(strcmp(buf, "\n") != 0){
                write(fdw,buf,strlen(buf) - 1);
                msgInfo.mtype = 2;
                strcpy(msgInfo.mtext, buf);
                ret = msgsnd(msgid, &msgInfo, strlen(msgInfo.mtext), 0);
                ERROR_CHECK(ret,-1,"msgsnd");
            }
        }

        if(FD_ISSET(fdr, &rdset)){
            memset(buf, 0, sizeof(buf));
            ret=read(fdr, buf, sizeof(buf));
            ERROR_CHECK(ret, -1, "read");
            //     if(0==ret){
            //         printf("连接已断开\n");
            //         break;
            //     }
            if(strlen(buf) != 0){
                puts(buf);
                msgInfo.mtype = 1;
                strcpy(msgInfo.mtext, buf);
                ret = msgsnd(msgid, &msgInfo, strlen(msgInfo.mtext), 0);
                ERROR_CHECK(ret, -1, "msgsnd");
            }
        }
    }
    close(fdr);
    close(fdw);
    return 0;
}

你可能感兴趣的:(linux_计算机系统,linux,服务器)