linux学习:进程通信 信号

目录

例子1    sigaction 函数来设置信号处理函数,并接收从其他进程发来的信号和附带的数据

例子2    sigqueue 函数发送一个信号以及一个与信号相关的值到一个特定的进程

例子3  信号量进行进程间同步,包括创建、初始化、P操作、V操作以及删除信号量。这里特别展示了在父子进程之间的资源竞争和同步控制

例子4   忽略特定信号或为特定信号设置自定义处理函数

例子5    通过命令行参数接收信号编号和进程ID,并使用系统命令kill发送信号

例子6   通过信号量控制父子进程的执行顺序

例子 7   信号量的创建、初始化、基本操作(P 和 V 操作)


例子1    sigaction 函数来设置信号处理函数,并接收从其他进程发来的信号和附带的数据

#include <signal.h>  // 包含信号处理相关的函数和数据结构
#include <stdio.h>   // 包含标准输入输出函数库

// 自定义信号处理函数,使用了三个参数形式,以接收额外的信号信息
void handler(int signum, siginfo_t *info, void *context)
{
    printf("get signum %d\n", signum);  // 打印接收到的信号编号
    
    if(context != NULL) {  // 如果上下文指针不为空
        printf("from: %d\n", info->si_pid);  // 打印发送信号的进程ID

        // 下面的注释掉的代码展示了如何访问发送的数据
        // printf("get data=%d\n", info->si_int);  // 如果数据是整型
        // printf("get data=%d\n", info->si_value.sival_int);  // 如果数据是通过 sigqueue 发送的整型
        // printf("get data=%s\n", context);  // 如果数据是字符串
        printf("get data=%d\n", *(int *)(info->si_value.sival_ptr));  // 打印通过指针发送的数据
    }
}

int main()
{
    struct sigaction act;  // 定义 sigaction 结构体变量,用于指定信号处理的相关设置

    printf("pid = %d\n", getpid());  // 打印当前进程ID,便于其他进程向此进程发送信号

    act.sa_sigaction = handler;  // 设置信号处理函数
    act.sa_flags = SA_SIGINFO;   // 设置使用扩展信号处理函数,使函数能接收额外信息

    sigaction(SIGUSR1, &act, NULL);  // 设置 SIGUSR1 信号的处理函数为上面定义的 handler

    while(1);  // 让程序处于无限循环,等待接收信号

    return 0;
}

例子2    sigqueue 函数发送一个信号以及一个与信号相关的值到一个特定的进程

#include <stdio.h>   // 包含标准输入输出函数库
#include <signal.h>  // 包含信号处理相关的函数和数据结构

int main(int argc, char **argv)
{
    int signum;  // 用于存储信号编号
    int pid;     // 用于存储目标进程的进程ID
    
    // 从命令行参数获取信号编号和目标进程ID
    signum = atoi(argv[1]);  // 将命令行的第一个参数转换为整数,用作信号编号
    pid = atoi(argv[2]);     // 将命令行的第二个参数转换为整数,用作进程ID

    int a = 10;  // 定义一个整数变量a,并初始化为10
    union sigval value;  // 定义一个信号值的联合体

    // 使用信号值联合体的sival_ptr成员指向整数a的地址
    // 这样就可以向目标进程发送一个包含整数地址的信号
    value.sival_ptr = &a;

    // 使用sigqueue函数向目标进程发送信号及附加数据
    // 参数pid是目标进程的进程ID,signum是信号编号,value是要发送的信号值
    sigqueue(pid, signum, value);

    // 打印发送信号的进程的进程ID,确认信号已发送
    printf("%d, done\n", getpid());

    return 0;  // 程序正常结束
}

例子3  信号量进行进程间同步,包括创建、初始化、P操作、V操作以及删除信号量。这里特别展示了在父子进程之间的资源竞争和同步控制

#include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>

// 初始化信号量
int init_sem(int sem_id, int value)
{
	union semun sem_union;
	sem_union.val = value;  // 设置信号量的初值
	if(semctl(sem_id, 0, SETVAL, sem_union) == -1)  // 初始化信号量
	{
		perror("Init Semaphore Error");  // 错误处理
		return -1;
	}
	return 0;
}

// P操作: 等待信号量变为可用(减1操作)
int sem_p(int sem_id)
{
	struct sembuf sbuf;
	sbuf.sem_num = 0; // 信号量编号
	sbuf.sem_op = -1; // P操作
	sbuf.sem_flg = SEM_UNDO; // 确保程序未正常结束时恢复信号量状态

	if(semop(sem_id, &sbuf, 1) == -1)  // 执行P操作
	{
		perror("P operation Error");
		return -1;
	}
	return 0;
}

// V操作: 释放信号量(加1操作)
int sem_v(int sem_id)
{
	struct sembuf sbuf;
	sbuf.sem_num = 0; // 信号量编号
	sbuf.sem_op = 1;  // V操作
	sbuf.sem_flg = SEM_UNDO; // 确保程序未正常结束时恢复信号量状态

	if(semop(sem_id, &sbuf, 1) == -1)  // 执行V操作
	{
		perror("V operation Error");
		return -1;
	}
	return 0;
}

// 删除信号量集
int del_sem(int sem_id)
{
	union semun sem_union;
	if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)  // 删除信号量集
	{
		perror("Delete Semaphore Error");
		return -1;
	}
	return 0;
}

int main()
{
	int sem_id;  // 信号量集ID
	key_t key;  
	pid_t pid;

	// 获取key值
	if((key = ftok(".", 'z')) < 0)
	{
		perror("ftok error");
		exit(1);
	}

	// 创建信号量集,其中只有一个信号量
	if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
	{
		perror("semget error");
		exit(1);
	}

	// 初始化:初值设为0资源被占用
	init_sem(sem_id, 0);

	if((pid = fork()) == -1)
		perror("Fork Error");
	else if(pid == 0) /*子进程*/ 
	{
		sleep(2);  // 确保父进程先运行
		printf("Process child: pid=%d\n", getpid());
		sem_v(sem_id);  /*子进程释放资源*/
	}
	else  /*父进程*/
	{
		sem_p(sem_id);   /*父进程等待资源*/
		printf("Process father: pid=%d\n", getpid());
		sem_v(sem_id);   /*父进程释放资源*/
		del_sem(sem_id); /*只有父进程删除信号量集*/
	}
	return 0;
}

例子4   忽略特定信号或为特定信号设置自定义处理函数

#include <signal.h>  // 包含信号处理相关的头文件
#include <stdio.h>   // 包含标准输入输出函数库

// 自定义的信号处理函数
void handler(int signum)
{
    printf("get signum=%d\n", signum);  // 打印接收到的信号编号
    switch(signum){
        case SIGINT:  // 如果是中断信号
            printf("SIGINT\n");
            break;
        case SIGKILL:  // 如果是终止信号(注意,SIGKILL通常不能被捕捉或忽略)
            printf("SIGKILL\n");
            break;
        case SIGUSR1:  // 如果是用户定义信号1
            printf("SIGUSR1\n");
            break;
    }

    printf("never quit\n");  // 信号处理完毕后打印
}

int main()
{
    // 设置对SIGINT(通常是Ctrl+C发出的中断信号)的处理为忽略
    signal(SIGINT, SIG_IGN);
    // 设置对SIGKILL的处理为忽略,但实际上这个设置是无效的,因为SIGKILL信号不能被忽略或捕捉
    signal(SIGKILL, SIG_IGN);
    // 设置对SIGUSR1的处理为自定义的handler函数
    signal(SIGUSR1, handler);

    while(1);  // 使程序持续运行,等待信号

    return 0;  // 程序正常结束
}

例子5    通过命令行参数接收信号编号和进程ID,并使用系统命令kill发送信号

#include <signal.h>  // 包含信号处理相关的头文件
#include <stdio.h>   // 包含标准输入输出函数库
#include <sys/types.h>  // 包含数据类型,如pid_t
#include <stdlib.h> // 包含标准库函数,如atoi

int main(int argc, char **argv)
{
    int signum;  // 用于存储信号编号
    int pid;     // 用于存储进程ID

    char cmd[128] = {0};  // 创建一个字符数组用来存储系统命令

    // 使用atoi函数将命令行参数转换为整数
    signum = atoi(argv[1]);  // 第一个参数是信号编号
    pid = atoi(argv[2]);     // 第二个参数是目标进程ID

    // 打印接收到的信号编号和进程ID
    printf("num=%d, pid=%d\n", signum, pid);	

    // 使用sprintf函数构建kill命令,格式为"kill -信号编号 进程ID"
    sprintf(cmd, "kill -%d %d", signum, pid);

    // 调用system函数执行构建的命令
    system(cmd);

    // 打印发送信号成功的消息
    printf("send signal ok");	
    return 0;
}

例子6   通过信号量控制父子进程的执行顺序

#include <stdio.h>          // 包含标准输入输出函数库
#include <sys/types.h>      // 包含数据类型定义,如pid_t
#include <sys/ipc.h>        // 包含IPC(进程间通信)相关的头文件
#include <sys/sem.h>        // 包含System V信号量相关的函数声明和数据结构

// 用于semctl系统调用的联合体定义
union semun {
    int              val;    // Value for SETVAL
    struct semid_ds *buf;    // Buffer for IPC_STAT, IPC_SET
    unsigned short  *array;  // Array for GETALL, SETALL
};

// P操作 (等待/锁定资源)
void Phandle(int id)
{
    struct sembuf sops;
    sops.sem_num = 0;        // 操作信号量的编号
    sops.sem_op = -1;        // 信号量减1操作,阻塞等待信号量变为0
    sops.sem_flg = SEM_UNDO; // 确保如果进程异常退出,信号量操作会被撤销

    semop(id, &sops, 1);     // 执行P操作
}

// V操作 (释放/解锁资源)
void Vhandle(int id)
{
    struct sembuf sops;
    sops.sem_num = 0;        // 操作信号量的编号
    sops.sem_op = 1;         // 信号量加1操作
    sops.sem_flg = SEM_UNDO; // 确保如果进程异常退出,信号量操作会被撤销

    semop(id, &sops, 1);     // 执行V操作
}

int main()
{
    key_t key;
    key = ftok(".",1);  // 生成一个基于文件的唯一键

    // 创建或获取信号量集,只包含一个信号量
    int semid = semget(key, 1, IPC_CREAT|0666);

    // 初始化信号量的值为0
    union semun setVal;
    setVal.val = 0;
    semctl(semid, 0, SETVAL, setVal);

    int pid = fork(); // 创建子进程

    if(pid > 0){
        // 父进程
        Phandle(semid); // 等待信号量变为0
        printf("this is father\n");   
        Vhandle(semid); // 信号量值加1
        semctl(semid, 0, IPC_RMID, NULL); // 删除信号量集
    } else if(pid == 0){
        // 子进程
        printf("this is child\n");
        Vhandle(semid); // 信号量值加1
    } else {
        // fork失败
        printf("fork error\n");
    }

    return 0;
}

例子 7   信号量的创建、初始化、基本操作(P 和 V 操作)

#include <stdio.h>          // 包含标准输入输出函数库
#include <stdlib.h>         // 包含标准库函数,如exit()
#include <sys/sem.h>        // 包含System V信号量相关的函数声明和数据结构
#include <unistd.h>         // 提供access()函数和其他有用的POSIX函数和符号

// 初始化信号量
int init_sem(int sem_id, int value)
{
    union semun sem_union;  // 必须定义一个联合体用于semctl操作
    sem_union.val = value;  // 设置信号量的值
    if (semctl(sem_id, 0, SETVAL, sem_union) == -1)  // 初始化信号量
    {
        perror("Init Semaphore Error");
        return -1;
    }
    return 0;
}

// P操作 (信号量减1,即等待操作)
int sem_p(int sem_id)
{
    struct sembuf sbuf;
    sbuf.sem_num = 0; // 信号量编号
    sbuf.sem_op = -1; // P操作,等待并减小信号量的值
    sbuf.sem_flg = SEM_UNDO; // 在进程终止时撤销当前操作,防止死锁

    if (semop(sem_id, &sbuf, 1) == -1)  // 执行P操作
    {
        perror("P operation Error");
        return -1;
    }
    return 0;
}

// V操作 (信号量加1,即释放操作)
int sem_v(int sem_id)
{
    struct sembuf sbuf;
    sbuf.sem_num = 0; // 信号量编号
    sbuf.sem_op = 1;  // V操作,释放信号量,增加其值
    sbuf.sem_flg = SEM_UNDO; // 在进程终止时撤销当前操作,防止死锁

    if (semop(sem_id, &sbuf, 1) == -1)  // 执行V操作
    {
        perror("V operation Error");
        return -1;
    }
    return 0;
}

// 删除信号量集
int del_sem(int sem_id)
{
    if (semctl(sem_id, 0, IPC_RMID) == -1)  // 删除信号量集
    {
        perror("Delete Semaphore Error");
        return -1;
    }
    return 0;
}

int main()
{
    int sem_id;  // 信号量集ID
    key_t key;   // IPC键
    pid_t pid;   // 进程ID

    // 获取key值,用于信号量的唯一标识
    if ((key = ftok(".", 'z')) < 0)
    {
        perror("ftok error");
        exit(1);
    }

    // 创建信号量集,其中只有一个信号量
    if ((sem_id = semget(key, 1, IPC_CREAT | 0666)) == -1)
    {
        perror("semget error");
        exit(1);
    }

    // 初始化信号量:设置初值为0,表示资源被占用
    init_sem(sem_id, 0);

    // 创建子进程
    if ((pid = fork()) == -1)
        perror("Fork Error");
    else if (pid == 0) /* 子进程 */
    {
        sleep(2);  // 等待确保父进程先行
        printf("Process child: pid=%d\n", getpid());
        sem_v(sem_id);  // 释放资源
    }
    else  /* 父进程 */
    {
        sem_p(sem_id);   // 等待资源,此处将阻塞直到子进程释放信号量
        printf("Process father: pid=%d\n", getpid());
        sem_v(sem_id);   // 再次释放资源
        del_sem(sem_id); // 删除信号量集
    }
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值