Linux进程通信——信号、信号传输消息、信号量以及DMEO

信号

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
int kill(pid_t pid, int sig);
  • 利用指令kill -l 可以查看所有信号,其中信号2是我们常用的ctrl+c,信号9为杀死进程此信号不可转换和忽略,SIG_IGN为无视信号的宏,而信号9不可被无视
  • signum为信号写数字也行写名称也行,handler为函数指针将其信号的处理转化为handler指向的服务函数

DEMO

#include <signal.h>
#include "stdio.h"


int main(int argc,char **argv)
{
        int signum;
        int pid;

        char cmd[128]={0};

        signum=atoi(argv[1]);
        pid = atoi(argv[2]);

        printf("num=%d,pid=%d\n",signum,pid);

//      kill(pid,signum);

        sprintf(cmd,"kill -%d %d",signum,pid);

        system(cmd);

        printf("send signal ok\n")
        return 0;
}
       
void handler(int signum)
{
        printf("get signum=%d\n",signum);
        switch(signum){
                case 2:
                        printf("SIGINT\n");
                        break;
                case 9:
                        printf("SIGKILL\n");
                        break;
                case 10:
                        printf("SIGUSR1\n");
                        break;

        }
        printf("never quit\n");
}

int main()
{

        signal(SIGINT,SIG_IGN);  //无视信号的宏
        signal(SIGKILL,handler);
        signal(SIGUSR1,handler);

        while(1); //等待信号接受,不让进程退出
        return 0;
}    

信号带消息

int sigqueue(pid_t pid, int sig, const union sigval value);
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
  • sigqueue函数参数一为发送进程的pid,参数而为发送的信号,参数三为携带的数据如以下的共用体
union sigval_t {
    int   sival_int;
    void *sival_ptr;
};
  • sigaction函数中参数一为接受的信号,参数二是一个已定义的结构体指针形参,结构体struct sigaction
  • 参数三oldact参数输出先前信号的处理方式(如果不为NULL的话)
struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}  //其中变量一为信号转换函数,如果设置了相当于signal函数差不多,设置了sigaction函数指针相当于携带消息的服务函数,sa_mask默认表示阻塞这里不设置表示以默认设置,sa_flags一般设置为SA_SIGINFO表示有消息
  • SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
    SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用 SA_NODEFER :一般情况下,当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号

  • 如果设置sa_sigaction指针变量,其处理函数有三个形参,第一个形参为接受的信号,第二个为接受消息的结构体指针,第三个形参当其不为NULL表示有消息

siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               int      si_trapno;   /* Trap number that caused
                                        hardware-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 */
               long     si_band;     /* Band event (was int in
                                        glibc 2.3.2 and earlier) */
               int      si_fd;       /* File descriptor */
               short    si_addr_lsb; /* Least significant bit of address
                                        (since kernel 2.6.32) */
           }

DEMO

#include <signal.h>
#include "stdio.h"

//       int sigaction(int signum, const struct sigaction *act,
  //                   struct sigaction *oldact);

void handler(int signum, siginfo_t *info, void *context)
{
        printf("get signum %d\n",signum);

        if(context!=NULL){
                printf("get data=%d\n",info->si_int);
//              printf("get str=%s\n",(char *)info->si_value.sival_ptr);
                printf("get data=%d\n",info->si_value.sival_int);
                printf("from pid=%d\n",info->si_pid);
        }

}



int main()
{

        struct sigaction act;

        printf("pid = %d\n",getpid());
        act.sa_sigaction=handler;  //带消息的函数
        act.sa_flags=SA_SIGINFO;	//表示有消息

        sigaction(SIGUSR1,&act,NULL);  //SIGUSR1为信号10的宏

        while(1);
        return 0;
}
int main(int argc,char **argv)
{

        int signum;
        int pid;

        signum=atoi(argv[1]);
        pid=atoi(argv[2]);

        union sigval value;
//      value.sival_ptr="xiaoma";
        value.sival_int=100;

        sigqueue(pid,signum,value);

        printf("%d,done\n",getpid());

        return 0;

}

信号量

1 #include <sys/sem.h>
2 // 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
3 int semget(key_t key, int num_sems, int sem_flags);
4 // 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
5 int semop(int semid, struct sembuf *sops, unsigned nsops);
6 // 控制信号量的相关信息
7 int semctl(int semid, int sem_num, int cmd, ...);
  • semget函数第二参数为必须指定集合中信号量的个数(即num_sems),通常为1, 如果是引用一个现有的集合,则将num_sems指定为 0 。函数第三个参数如果没有就利用IPC_CREAT创建并赋予权限
  • semop函数第二个参数指向一个结构体结构体参数如下,sem_num为第几个信号量,像数组标号一下,写0为第一个,对于第二个sem_op一次操作中的信号量的改变量;第三个参数nsops 这个是每次操作的工作(struct sembuf)数量,由于我们操作一个因此写1
unsigned short sem_num;  /* semaphore number */
short          sem_op;   /* semaphore operation */
short          sem_flg;  /* operation flags */
  • 若sem_op > 0,表示进程释放相应的资源数,将 sem_op 的值加到信号量的值上。如果有进程正在休眠等待此信号量,则换行它们。 若sem_op < 0,请求 sem_op 的绝对值的资源。
    如果相应的资源数可以满足请求,则将该信号量的值减去sem_op的绝对值,函数成功返回。
    当相应的资源数不能满足请求时,这个操作与sem_flg有关。
    sem_flg
    指定IPC_NOWAIT,则semop函数出错返回EAGAIN。 sem_flg
    没有指定IPC_NOWAIT,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:
    当相应的资源数可以满足请求,此信号量的semncnt值减1,该信号量的值减去sem_op的绝对值。成功返回;
    此信号量被删除,函数smeop出错返回EIDRM;
    进程捕捉到信号,并从信号处理函数返回,此情况下将此信号量的semncnt值减1,函数semop出错返回EINTR 若sem_op ==
    0,进程阻塞直到信号量的相应值为0: 当信号量已经为0,函数立即返回。 如果信号量的值不为0,则依据sem_flg决定函数动作:
    sem_flg指定IPC_NOWAIT,则出错返回EAGAIN。
    sem_flg没有指定IPC_NOWAIT,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:
    信号量值为0,将信号量的semzcnt的值减1,函数semop成功返回; 此信号量被删除,函数smeop出错返回EIDRM;
    进程捕捉到信号,并从信号处理函数返回,在此情况将此信号量的semncnt值减1,函数semop出错返回EINTR
    一般指定sem_flg为SEM_UNDO
  • 对于semctl函数,其中第一而参数为第几个信号量,写0代表第一个信号量,对于第三个参数cmd一般指定为SETVAL设定初值,第三个参数为一个结构体,在man手册中需要我们去定义这个结构体
  • SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
  • IPC_RMID:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。

DEMO

#include "stdio.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

//       int semget(key_t key, int nsems, int semflg);
 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) */
       };
//       int semop(int semid, struct sembuf *sops, unsigned nsops);

void pGetKey(int id)
{
        struct sembuf set;

        set.sem_num=0; //信号量的编号
        set.sem_op=-1;
        set.sem_flg=SEM_UNDO; //锁住或者解锁

        semop(id,&set,1);
        printf("get key\n");
}

void vPutBackKey(int id)
{
       struct sembuf set;

        set.sem_num=0;
        set.sem_op=1;
        set.sem_flg=SEM_UNDO;

        semop(id,&set,1);
        printf("put back the key\n");
}


int main()
{

        key_t key;
        int semid;
        key=ftok(".",2);
						//信号量集合中有一个信号量
        semid=semget(key,1,IPC_CREAT|0666);//获取/创建信号量

        union semun initsem;
        initsem.val = 0;
					//控制第0个信号量
        semctl(semid,0,SETVAL,initsem);//初始信号量的值
					//SETVAL设置信号量的值 设置为inisem
        int pid=fork();
        if(pid>0){
                pGetKey(semid);
                printf("this is father\n");
                vPutBackKey(semid);
                semctl(semid,0,IPC_RMID);
        }else if(pid==0){
                printf("this is child\n");

                vPutBackKey(semid);
        }else{
                printf("fork error\n");
        }
        return 0;
}
**
  • 先将信号量初值设置为0,到子进程去把钥匙释放位置前,父进程都拿不到钥匙,因此执行结果为
this is son
put the key
get the key
this is father
put the key
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值