Linux 学习记录27(进程线程篇)

本文介绍了Linux中的信号机制,包括概念、种类、API使用以及如何非阻塞地回收子进程资源。接着讨论了SystemVIPC中的消息队列,包括创建、发送和接收消息的方法。最后提到了共享内存的创建、映射和撤销映射的API。

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

Linux 学习记录27(进程线程篇)

在这里插入图片描述

一、信号

需要添加头文件:#include <signal.h>

1. 概念

所谓信号,就是软件模拟的中断的功能,信号是软件实现的,而中断是硬件实现的。
中断属于异步通讯的一种

同步通信:进程在要执行前一直阻塞等待其他进程结束
异步通信:进程在要执行前不需要一直阻塞等待其他进程结束

关于处理信号有三种情况:默认、忽略、捕获

2. 信号的种类及功能

可以使用kill -l查看所有信号
在这里插入图片描述

以下为常见的信号

信号名含义默认操作
SIGHUP该信号在用户终端关闭时产生,通常是发给和该终端关联的会话内的所有进程终止
SIGINT该信号在用户键入ITR字符(Ctrl-C)时产生,内核发送此信号送到当前终端的所有前台进程终止
SIGQUIT该信号和SIGIT类似,但由QUIT字符(通常是Ctrl-)来产生终止
SIGIL该信号在一个进程企图执行一条非法指令时产生终止
SIGSEV该信号在非法访问内存时产生,如野指针、缓冲区溢出终止
SIGPIPE当进程往一个没有读端的管道中写入时产生,代表“管道断裂”终止
SIGKILL该信号用来结束进程,并且不能被捕捉和忽略终止
SIGSTOP该信号用于暂停进程,并且不能被捕捉和忽略暂停进程
SIGTSTP该信号用于暂停进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号暂停进程
SIGCONT该信号让进程进入运行态继续运行
SIGALRM该信号用于通知进程定时器时间已到终止
SIGUSR1/2该信号保留给用户程序使用终止

3. 信号API

信号处理函数:typedef void (*sighandler_t)(int);
类型为函数指针,参数为整形的返回值为void类型的的信号处理函数,该参数就是要处理信号
返回值:
==============================================================
函数原型:sighandler t signal(int signum, sighandler t handler);
参数1:要处理的信号号
参数2:要处理的方式
	1. SIG_IGN:该信号被进程忽略
	2. SIG_DFL:该信号被默认处理,一般为终止进程
	3. 捕获信号,并执行信号处理函数
返回值:成功返回信号处理函数的首地址,失败返回SIG ERR,并置位错误码
示例:
/*信号处理函数*/
void handler(int signo)
{
    printf("获取的信号为: Ctrl+C\r\n");
}
int main(int argc, char const *argv[])
{
    /*忽略*/
    signal(SIGINT,SIG_IGN);
    /*默认*/
    signal(SIGINT,SIG_DFL);
    /*捕获*/
    signal(SIGINT,handler);
    while(1);
    return 0;
}

4. 使用信号以非阻塞方式回收子进程资源

当子进程结束时,父进程会拿到哟个SIGCHLD的信号,我们可以将该信号进行捕获,在该信号对应的信号处理函数中手动回收子进程的资源

/*信号处理函数*/
void handler(int signo)
{
    /*处理内容*/
    waitpid(-1,NULL,WNOHANG);
    printf("父进程以非阻塞状态回收子进程\r\n");

}


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

    pid = fork();

    if(pid < 0)
    {
        perror("Error");
        return -1;
    }else if(pid == 0)
    {//子进程
        printf("子进程\r\n");
        sleep(3);
        printf("子进程结束\r\n");
        exit(EXIT_SUCCESS);
    }else
    {//父进程
    
        printf("父进程\r\n");
        /*捕获*/
        if(signal(SIGHUP,handler) == SIG_ERR)
        {
            perror("signal");
        }
        while(1);
        exit(EXIT_SUCCESS);
    }

    // /*忽略*/
    // signal(SIGINT,SIG_IGN);
    // /*默认*/
    // signal(SIGINT,SIG_DFL);
    /*捕获*/
    // signal(SIGINT,handler);
    return 0;
}

5. 发信号相关的API(kill/raise)

(1. kill

函数原型:int kill(pid_t pid, int sig);
功能:给指定的进程发送信号
参数1:指定的进程
	1. 大于0:给指定的进程发送信号
	2. 等于0:给当前进程所在的组的所有进程发送信号
	3. 等于-1:给所有进程发送信号,须注意权限
	4. 小于-1:给pid的绝对值的进程所在的组下的所有进程发送信号
参数2:需要发送的信号
返回值:成功返回0,失败返回-1并置位错误码

(2. raise

函数原型:int raise(int sig);
功能:给自己发送信号
参数1:要发送的信号
返回值:成功返回0,失败返回非0错误码

6. 闹钟函数API

函数原型:unsigned int alarm(unsigned int seconds);
功能:在secnods秒后发送一个信号
参数1:毫秒数
返回值:该函数返回上次调用alarm函数余的毫秒数,如果之前没有调用过alarm函数或者,
	上一次alar函数用钟已经处理完毕,则返回0

当第二次调用alarm时,如果更新了秒数,则SIGALRM信号发射,根据新更改的秒数确定

二、system VIPC进程间通信

系统5又提供了三种进程间通讯

消息队列
共享内存
信号灯集

相关命令集

  1. ipcs:查看全部信息
    在这里插入图片描述
  2. ipcs -q:只查看消息队列
  3. ipcs -m:只查看共享内存
  4. ipcs -s:只查看信号量
  5. ipcs -想要删除的信息 信息ID:删除指定的信息

三、消息队列

1. ftok 获取key值

函数原型:key_t ftok(const char *pathname, int proj_id);
功能:获取打开消息队列的键值
参数1:已存在的文件路径
参数2:给定任意的值
返回值:成功返回key值,失败返回-1

2. msgget 创建IPC对象

函数原型:int msgget(key_t key, int msgflg);
功能:创建消息队列
参数1:IPC PRIVATE 或 ftok
参数2:创建共享内存的标志位
	IPC_CREAT|666:如果消息队列不存在就创建,如果存在就返回消息队列的ID
	IPC_CREAT|IPC_EXCL|666:如果消息队列不存在就创建,如果存在就报已存在错误
返回值:成功返回ID,失败返回-1
注意:
如果对应key值的IPC对象不存在,则创建,如果存在,直接返回IPC对象的ID

3. msgsnd 发消息

函数原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:发消息
参数1:消息队列ID
参数2:需要发送的消息存放的地址
参数3:消息正文的大小
参数4:发消息的标志位
	0:阻塞的方式发送
	IPC NOWAIT:非阻塞方式调用
返回值:成功返回@,失败返回-1 (队列的大小限制MSGMNB 16384)
==============================================================================
消息结构体定义:
typedef structf
{
	long msg_type;//消息类型必须在第一个位置
	char mtxt[1024];
	......
}
正文大小:sizeof(msg_t) - sizeof(long)

4. msgrcv 接收消息

函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:接收消息
参数1:消息队列ID
参数2:存放接收到的消息
参数3:正文大小
参数4:消息类型
	大于0:总是从消息队列中提取第一个消息
	小于0:指向消息的类型接收消息,如果类型不匹配,详细依然在消息队列中存放
	等于0:对消息的类型取绝对值
参数5:接收方式
	0:阻塞的方式接收
	IPC_NOWAIT:非阻塞方式
返回值:成功返回 接收消息正文大小,失败返回-1

5. msgctl 消息队列控制

函数原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:消息队列控制
参数1:消息队列ID
参数2:控制方式
	IPC_RMID(删除消息队列)
	IPC_SET(设置消息队列的属性信息)
	IPC_STAT(获取消息队列属性信息)
参数3:存放消息队列属性(如果cmd填写为IPC RMID这个参数缺省)
返回值:成功返回0,失败返回-1
===============================================================
struct msqid_ds {
struct ipc_perm msg_perm;     /* Ownership and permissions */
   time_t          msg_stime;    /* Time of last msgsnd(2) */
   time_t          msg_rtime;    /* Time of last msgrcv(2) */
   time_t          msg_ctime;    /* Time of last change */
   unsigned long   __msg_cbytes; /* Current number of bytes in
                                    queue (nonstandard) */
   msgqnum_t       msg_qnum;     /* Current number of messages
                                    in queue */
   msglen_t        msg_qbytes;   /* Maximum number of bytes
                                    allowed in queue */
   pid_t           msg_lspid;    /* PID of last msgsnd(2) */
   pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};
===============================================================
struct ipc_perm {
key_t          __key;       /* Key supplied to msgget(2) */
  uid_t          uid;         /* Effective UID of owner */
  gid_t          gid;         /* Effective GID of owner */
  uid_t          cuid;        /* Effective UID of creator */
  gid_t          cgid;        /* Effective GID of creator */
  unsigned short mode;        /* Permissions */
  unsigned short __seq;       /* Sequence number */
};

使用示例:


四、共享内存

所需头文件:#include <sys/shm.h>

2. 共享内存API

(1. 创建共享内存

函数原型:int shmget(key_t key, size_t size, int shmflg);
功能:功能:获取共享内存段的ID 
参数1:IPC_PRIVATE  或 ftok() 
参数2:申请的共享内存段大小 [4k的倍数]
参数3:创建消息队列的标志位
	IPC_CREAT|0666:如果共享内存不存在就创建,如果存在就返回共享内存的ID
	IPC_CREAT|IPC_EXCL|0666:如果共享内存不存在就创建,如果存在就报已存在错误
返回值:成功返回ID,失败返回-1 

(2. 映射内存

函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射内存
参数1:共享内存段ID 
参数2:映射到的地址
	NULL:系统自动完成映射 
参数3:共享内存操作的标志位
	SHM_RDONLY:只读
	0:读写
返回值:成功返回映射后的地址,失败返回(void *)-1 

(3. 撤销映射

注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。
只有当共享内存映射次数为0,才会删除共享内存对象

函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:根据命令控制共享内存 
参数1:共享内存的ID 
参数2:控制方式
	IPC_STAT  [获取属性],
    IPC_SET   [设置属性],
    IPC_RMID  [删除IPC对象]
参数3:存放消息队列属性 (如果cmd填写为IPC_RMID这个参数缺省)
返回值:成功返回0,失败返回 -1 

思维导图

在这里插入图片描述

练习

1.

#define MSG_MAX_SIZE (sizeof(msg_t)-sizeof(long))

/*定义一个存放到队列中的结构体*/
typedef struct
{
    long msg_type;//消息类型
    char msg_txt[1024];//数据域
}msg_t;
int main(int argc, char const *argv[])
{
    key_t key;//定义钥匙
    int msg_id;//定义消息队列ID
    msg_t msg={.msg_type = 100};//定义消息结构体

    /*获取键值*/
    if((key = ftok("./",'h')) == -1)
    {
        perror("ftok");
        return -1;
    }

    /*创建消息队列*/
    if((msg_id = msgget("./",'h')) == -1)
    {
        perror("msgget");
        return -1;
    }

    /*向消息队列子存放数据*/
    while (1)
    {
        /*提示输入消息正文*/
        printf("请输入消息体\n");
        fgets(msg.msg_txt, sizeof(msg.msg_txt), stdin);
        if(strcmp(msg.msg_txt, "over") == 0) break;
        msg.msg_txt[strlen(msg.msg_txt) - 1] = '\0';//消除回车
        /*将消息放入消息队列(阻塞输入)*/
        if(msgsnd(msg_id,&msg,MSG_MAX_SIZE,0) == -1)
        {
            perror("msgsnd");
            return -1;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值