进程通信 —— 信号

本文深入讲解了信号的概念、来源及其在Linux系统中的应用。探讨了如何使用signal和sigaction函数进行信号处理,并通过实例展示了sigqueue函数的具体用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是信号

信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。通常信号是由一个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程。一个信号的产生叫生成,接收到一个信号叫捕获。

在Linux中,每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO”、“SIGCHLD”等等。信号定义在signal.h头文件中,信号名都定义为正整数。具体的信号名称可以使用kill-l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。

信号来源

1.硬件
如:ctrl+c等或硬件故障
2.软件
最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。

处理方式: 信号的处理有三种方法,分别是:忽略、捕捉和缺省(默认)动作

如何使用信号

如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。

linux主要有两个函数实现信号的安装:signal()、sigaction()。

signal

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

signum:为信号编号
handler:描述了与信号关联的动作
它可以取以下三种值:
SIG_IGN 这个符号表示忽略该信号。
SIG_DFL 这个符号表示恢复对信号的系统默认处理。不写此处理函数默认也是执行系统默认操作。
sighandler_t类型的函数指针 当接收到一个类型为sig的信号时,就执行handler 所指定的函数。

返回值:返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。

sigaction

int sigaction(int sig,const struct sigaction *act,struct sigaction *oact);

参数:

signum:要操作的信号。如:SIGUSR1
act:要设置的对信号的新处理方式。
oldact:原来对信号的处理方式。

返回值:0 表示成功,-1 表示有错误发生

struct sigaction {
(1) void (*sa_handler)(int);
sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数//
(2) void (*sa_sigaction)(int, siginfo_t *, void *);
//sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的
更详细的信息。当sa_flags成员的值包含了SA_SIGINFO标志时,系统将使用sa_s igaction函数作为信号处理函数,否则使用sa_handler作为信号处理//
(3) sigset_t sa_mask;
// 用来指定在信号处理函数执行期间需要被屏蔽的信号//
(4) int sa_flags;
//sa_flags 成员用于指定信号处理的行为//
(5) void (*sa_restorer) (void);
//sa_restorer已经废弃勿用//
};

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 Linux 2.6.32) */
               void    *si_lower;     /* Lower bound when address violation
                                         occurred (since Linux 3.19) */
               void    *si_upper;     /* Upper bound when address violation
                                         occurred (since Linux 3.19) */
               int      si_pkey;      /* Protection key on PTE that caused
                                         fault (since Linux 4.6) */
               void    *si_call_addr; /* Address of system call instruction
                                         (since Linux 3.5) */
               int      si_syscall;   /* Number of attempted system call
                                         (since Linux 3.5) */
               unsigned int si_arch;  /* Architecture of attempted system call
                                         (since Linux 3.5) */
           }

发送信号

sigqueue

功能

新的发送信号系统调用,主要是针对实时信号提出的,支持信号带有参数,与函数sigaction()配合使用。

函数原型

int sigqueue(pid_t pid, int sig, const union sigval value);

第1个参数是指定接收信号的进程id
第2个参数确定即将发送的信号
第3个参数是一个联合数据结构union sigval,指定了信号传递的参数,即通常所说的4字节值。

typedef union sigval
           { 
                int sival_int; 
                void *sival_ptr; 
}sigval_t; 

例子

sigSEND.c

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>


// union sigval {
  //             int   sival_int;
    //           void *sival_ptr;
      //     };
int main(int stu,char **arg)
{
        int pid;
        int signum;

        signum=atoi(arg[1]);

        pid=atoi(arg[2]);
        union sigval value;
        value.sival_int=100;
        sigqueue(pid,signum,value);

        printf("进程ID:%d\n",getpid());
        printf("信号(%d)发送成功\n",signum);
        return 0;
}

sigRCV.c

#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void handler(int pt,siginfo_t *information,void *text)
{
        if(text!=NULL)
        {
        printf("信号编号:%d\n",pt);
        printf("发送来的信息%d\n",information->si_value.sival_int);
        printf("from(来自):%d\n",information->si_pid);
        }
}

int main()
{
        struct sigaction act;

        act.sa_flags=SA_SIGINFO;
        act.sa_sigaction=handler;

        sigaction(SIGUSR1,&act,NULL);

        printf("该进程ID为:%d\n",getpid());
        while(1);
        return 0;
}

最后

以后使用到出现问题或涉及其他会补充更新
以后使用到出现问题或涉及其他会补充更新
以后使用到出现问题或涉及其他会补充更新

信号量是一种用于进程间通信和同步的机制。它是一个计数器,用于保证在共享资源上的互斥访问。在Linux系统中,可以使用信号量来实现进程间的同步和互斥。以下是信号量的基本概念: - 计数器:信号量的值是一个计数器,它可以被多个进程共享。 - P操作:当一个进程需要访问共享资源时,它必须执行P操作,该操作会将信号量的值减1。如果信号量的值为0,则进程将被阻塞,直到信号量的值大于0。 - V操作:当一个进程使用完共享资源后,它必须执行V操作,该操作会将信号量的值加1。如果有进程正在等待该信号量,则唤醒其中一个进程继续执行。 在ZUCC中,可以使用信号量来实现进程的同步和互斥。首先,需要使用semget函数创建一个信号量集合,并使用semctl函数对信号量进行初始化。然后,可以使用semop函数执行P和V操作。例如,下面是一个简单的示例程序,用于演示信号量的使用: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sem.h> #define SEM_KEY 1234 union semun { int val; struct semid_ds *buf; unsigned short *array; }; int main() { int semid, pid; union semun arg; struct sembuf sb; // 创建信号量集合 semid = semget(SEM_KEY, 1, IPC_CREAT | 0666); if (semid == -1) { perror("semget"); exit(EXIT_FAILURE); } // 初始化信号量 arg.val = 1; if (semctl(semid, 0, SETVAL, arg) == -1) { perror("semctl"); exit(EXIT_FAILURE); } // 创建子进程 pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程执行P操作 sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop P"); exit(EXIT_FAILURE); } printf("Child process\n"); // 子进程执行V操作 sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop V"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } else { // 父进程执行P操作 sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop P"); exit(EXIT_FAILURE); } printf("Parent process\n"); // 父进程执行V操作 sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop V"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } return 0; } ``` 在上述代码中,创建了一个信号量集合,并将其初始化为1。然后,创建了一个子进程和一个父进程,它们分别执行P和V操作。由于信号量的初始值为1,因此父进程和子进程都可以顺利地执行。如果将信号量的初始值改为0,那么父进程和子进程都将被阻塞,直到有一个进程执行V操作为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值