首页 > 系统相关 >进程之信号

进程之信号

时间:2024-09-17 09:21:02浏览次数:11  
标签:函数 int signal pid 信号 进程

文章目录

进程信号

信号是Linux系统中的一种通信机制,常见的通信机制还有文件锁、管道、消息队列、共享内存等。

中断的概念

  • 硬件中断

    • 在单片机中常常能看到有硬件中断,中断说白了就是一种机制,中断的存在允许当程序在运行的时候能够转而去执行别的操作,在单片机中,当发生硬件中断的时候,CPU首先会停下当前的工作,然后将寄存器等状态保存在堆栈中,然后转而去执行中断服务程序。当程序执行完以后,继续返回之前被中断的地方继续执行。
  • 软件中断

    • 软件中断就是通过一种信号来出发它的中断,例如这里的信号。信号作为一种软件中断它的原理是,当一个进程给另外一个进程发送信号的时候,另外一个进程停下当前正在做的事情,然后转而去处理信号函数。但是这种是通过信号机制来中断当前正在执行的程序,而非是由于硬件中断所引起的,所以信号是一种软件中断

信号是异步事件

  • 异步事件:异步事件就是说当进程1发送信号给进程2以后不管去管对方到底有没有接收到,进程1都要往下执行,这种异步操作能够通过减少等待时间来提高程序的响应性和性能。
  • 同步事件:以这里的信号为例,如果是同步事件的话,当进程1给进程2发信号以后必须收到来自进程2的回复进程1才能够继续往下执行。

异步事件和同步事件对比:经过对比发现异步事件比同步实践的执行效率更好,但是可能执行精度上较低于同步事件。同步事件虽然资源利用效率低下,但是能够保持较高的精度,一旦出错以及停止执行然后进行相应的处理。所以对于两种方式的选择要进行合理的选择才能达到预期的效果。

Linux信号

在Linux系统中的信号都是继承了Unix系统的信号,在Linux系统中可以使用kill -l来查看系统中的所有信号。

image-20240909165055152

信号发生的来源

  • 硬件来源:当按下键盘的Ctrl+CCtrl+Z的时候会产生SIGINTSIGTSTP信号,这些信号都是由硬件驱动程序产生,当内核的驱动程序捕捉到硬件按下对应的按键以后从而触发对应的信号。
    • 软件来源:最常用发送信号的系统函数是kill()raise()alarm()setitimer()等函数,软件来源还包括一些非法运算等操作(如除零操作、段错误),软件设置条件(如:gdb调试),信号由内核产生。

信号的处理方式

  • 忽略信号

    • SIGKILLSIGSTOP永远不能忽略
    • 进程启动时SIGUSR1SIGUSR2两个信号被忽略
    • 这里的忽略信号需要使用一个宏SIG_IGN来将指定的信号忽略
  • 执行默认操作

    • 每个信号都有默认动作,大部分信号的默认动作是终止进程
    • 执行默认操作使用SIG_DFL宏来将指定的信号执行默认操作
  • 捕获信号

    • 使用signal()函数来将指定的信号捕获,然后转而去执行自己定义的信号处理函数
    • SIGKILLSIGSTOP不能被捕获

    SIGKILLSIGSTOP信号不能被忽略或捕获的原因是:它们两个相当于一种保护机制,当Ctrl+C(SIGINT)Ctrl+Z(SIGTSTP)等信号被捕获转而去执行用户自定义的函数时,必须保证有一个信号能够将此进程杀死或者停止,所以这里SIGKILLSIGSTOP的底层就写好了它们不能被忽略也不能被捕获。

signal函数

上边说的几种信号的处理方式可以通过Linux系统提供的一个函数来实现:

#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);

//功能:向内核注册信号处理函数,当发生对应的信号时做出相应的操作
//参数1:要捕获的信号
//参数2:函数指针,指向用户自定义的信号处理函数
//返回值:如果成功执行,将返回一个指向先前信号处理函数的指针。若执行失败,则返回值为SIG_ERR

这里对signal函数的原型进行详解:

  • signal函数的第一个参数sig是要捕捉的信号,例如SIGINT SIGTSTP等,然后对捕捉到的信号进行相应的操作,这个操作函数就是它的第二个参数。这里的sig可以用编号来表示也可以用它具体的信号来表示(如SIGINT信号对应的编号是2)
  • signal函数的第二个参数void (*func)(int)是一个函数指针,它是使用typedef定义的类型别名,这个函数指针指向了一个函数,该函数是一个无返回值,具有一个整型参数的函数。
  • 它的第二个参数还可以是两个宏:SIG_INGSIG_DFL分别表示忽略信号和执行该信号的默认操作
  • signal函数的返回值是一个函数指针,它指向了一个函数,该函数是一个无返回值,具有一个整型参数的函数。实际上它的返回值就是信号处理函数。
示例–使用signal函数对常见的信号进行捕获、执行默认操作、忽略三种方式处理
#include "header.h"

void sig_handler(int signum)
{
    switch(signum)
    {
        case 1:
            printf("process %d catches signal is SIGHUP\n",getpid());
            break;
        case 2:
            printf("process %d catches signal is SIGINT\n",getpid());
            break;
        case 9:
            printf("process %d catches signal is SIGKILL\n",getpid());
            break;
      	case 10:
            printf("process %d catches signal is SIGUSR1\n",getpid());
            break;
        case 12:
            printf("process %d catches signal is SIGUSR2\n",getpid());
            break;
        case 19:
            printf("process %d catches signal is SIGSTOP\n",getpid());
            break;
        default:
            printf("there is no such signal\n");
    }
}

int main(void)
{
	printf("process pid is %d\n",getpid());

    //向内核登记信号处理函数以及需要捕获的信号
    if(signal(SIGINT, sig_handler) == SIG_ERR)
    {
        perror("signal SIGINT error");
    }
    
    if(signal(SIGHUP, sig_handler) == SIG_ERR)
    {
        perror("signal SIGHUP error");
    }
    
    if(signal(SIGUSR1, sig_handler) == SIG_ERR)
    {
        perror("signal SIGUSR1 error");
    }
    
    if(signal(SIGUSR2, sig_handler) == SIG_ERR)
    {
        perror("signal SIGUSR2 error");
    }
    
    if(signal(SIGKILL, sig_handler) == SIG_ERR)
    {
        perror("signal SIGKILL error");
    }
    
    if(signal(SIGSTOP, sig_handler) == SIG_ERR)
    {
        perror("signal SIGSTOP error");
    }
    
    while(1) sleep(1);
    
    return 0;
}

image-2024091019742

通过编译执行,发现signal函数将SIGHUPSIGINT信号捕获,而且执行了用户自定义的函数,这里由于SIGHUP函数不能通过键盘等硬件方式去向内核发送信号,所以采用kill指令去发送信号。这里使用kill给进程发送编号为3的信号,程序退出,由此也验证了大部分信号的默认处理机制是将程序结束。

在这里插入图片描述

当使用signal函数去捕获SIGKILLSIGSTOP信号的时候,可以看到这里在运行的时候就直接报错了,显示非法的参数。并且使用SIGKILLSIGSTOP信号还能杀死和停止进程。SIGUSR1SIGUSR2信号虽然在进程启动的时候被忽略了,但是可以使用signal函数对它们进行捕获执行相应的函数。

如果这里将signal函数的第二个参数设置为SIG_IGNSIG_DFL的话,它们分别会将捕获的信号忽略和执行默认操作,例如这里将SIGINT信号设置为默认的操作,它就会将正在运行的进程中断。如果将signal函数设置为SIG_IGN它的处理是将信号忽略不做任何处理。

image-20240910171827116

image-20240910171954422

SIGCHLD信号

前边进程那一章有讲过当子进程优于父进程退出的时候,子进程的资源无法完全释放,在内核中还留有进程表项。所以当时为了避免子进程成为僵尸进程共有三种方法:

  • 在父进程中使用wait函数等待子进程退出,然后让父进程通知内核去回收子进程的进程表项;
  • 当运行起来的时候,将父进程杀死。使子进程变成一个孤儿进程然后由initsystemd进程收留,最后由它们去释放子进程的资源;
  • 当子进程退出的时候会产生SIGCHLD信号,此时如果父进程捕获到子进程发送此信号就表明子进程退出了,调用wait函数去回收子进程的资源。

通过前边的案例不难发现当父进程调用wait函数以后父进程就处于阻塞状态了,所以父进程一直在等待子进程退出,中间是不能做任何事情的,这其实是资源的浪费。所以这里介绍一些应该怎么使用SIGCHLD信号来避免子进程变成僵尸进程,通过在使用SIGCHLD信号的时候父进程也不是处于阻塞状态,父进程可以干自己的事,相比之前直接使用wait函数等待子进程退出效果更好一点。

示例–使用SIGCHLD信号来避免子进程退出
#include "header.h"

void sig_handler(int signum)
{
    printf("process %d receive a signal and the number is %d\n",getpid(),signum);
    wait(NULL);
}

void count_func(int num)
{
    int i;
    
    for(i=0;i<num;i++)
    {
        printf("process: %d i:%d\n",getpid(),i);
        sleep(1);
    }
}

int main(void)
{
    //向内核登记捕获SIGCHLD信号,一旦产生这个信号就去执行相应的信号处理函数
    if(signal(SIGCHLD, sig_handler) == SIG_ERR)
    {
        perror("signal SIGCHLD error");
    }
    
    pid_t pid;
    if((pid = fork()) < 0)
    {
        perror("fork error");
        exit(EXIT_FAILURE);
    }
    else if(pid == 0)
    {
        printf("this is child process and the pid is %d\n",getpid());
        count_func(10);
    }
    else
    {
        printf("this is parent process and the pid is %d\n",getpid());
        count_func(40);
    }
    
    return 0;
}

image-20240910175255586

通过编译执行可以发现,在子进程退出的时候会发送一个SIGCHLD信号,在此时调用wait函数可以将子进程的资源进行回收,使用ps指令去查看子进程状态也没有变成僵尸进程,说明子进程成功被回收。

kill函数

在上边的终端里使用kill指令发送信号给特定的进程,在Linux系统里又一个kill函数和kill指令的功能是一样的。

#include <signal.h>

int kill(pid_t pid, int sig);

//功能:向指定进程发送信号
//参数1:指定进程的PID号
//参数2:向指定进程发送的信号
//返回值:如果成功执行返回0,否则返回非零值并设置errno

raise函数

#include <signal.h>

int raise(int sig);

//功能:向执行此函数的当前进程发送指定的信号
//参数:要发送的信号
//返回值:如果成功执行返回0,否则返回非零值并设置errno
示例–使用kill函数给指定进程发送信号
#include "header.h"

int main(int argc, char **argv)
{
	if(argc < 3)
	{
		fprintf(stderr,"usage: %s [pid] [signum]\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	//从外部传入要发送进程的PID号和信号编号
	pid_t pid = atoi(argv[1]);
	int signum = atoi(argv[2]);

	if((kill(pid,signum)) != 0)
	{
		perror("kill perror");
		exit(EXIT_FAILURE);
	}

	return 0;
}

在这里插入图片描述

经过代码编译执行发现kill函数的使用和kill指令无异,都可以使用它给指定进程发送信号。

示例–使用kill函数和raise函数向当前进程发送信号
#include "header.h"

void sig_handler(int signum)
{
    printf("process %d receive a signal is %d\n",getpid(),signum);

    switch (signum)
    {
    case 1:
        printf("receive signal is SIGHUP\n");
        break;
    case 2:
        printf("receive signal is SIGINT\n");
        break;
    case 10:
        printf("receive signal is SIGUSR1\n");
        break;
    case 12:
        printf("receive signal is SIGUSR2\n");
        break;
    default:
        printf("no sign to kernel\n");
        break;
    }
}

int main(int argc, char **argv)
{
    if(argc < 2)
    {
        fprintf(stderr,"usage: %s [signum]\n",argv[0]);
        exit(EXIT_FAILURE);
    }

    int signum = atoi(argv[1]);

    if(signal(SIGUSR1, sig_handler) == SIG_ERR)
    {
        perror("signal SIGUSR1 error");
    }

    if(signal(SIGUSR2, sig_handler) == SIG_ERR)
    {
        perror("signal SIGUSR2 error");
    }

    if(signal(SIGHUP, sig_handler) == SIG_ERR)
    {
        perror("signal SIGHUP error");
    }

    if(signal(SIGINT, sig_handler) == SIG_ERR)
    {
        perror("signal SIGINT error");
    }

    //将当前进程的PID号传进去给当前进程发信号
    if(kill(getpid(), signum) != 0)
    {
        perror("kill perror");
        exit(EXIT_FAILURE);
    }

    if(raise(signum) != 0)
    {
        perror("raise perror");
        exit(EXIT_FAILURE);
    }
    
    return 0;
}

13f5d04b8a676d58dc79d33b8aee0a0

通过编译执行可以发现kill函数可以向当前进程发送信号,kill(getpid(), signum)这个等同于raise(signum),它们两个都可以向当前进程发送信号。

这里有一个比较有意思的案例:父进程使用fork函数创建子进程,在子进程中调用信号处理函数,然后在父进程中使用kill函数向子进程发送信号

示例–使用killsignal函数练习
#include "header.h"

void sig_handler(int signum)
{
    printf("pid:%d signum:%d\n",getpid(),signum);
}

int main()
{
    pid_t pid;
    
    if((pid = fork()) < 0)
    {
        perror("fork error");
        exit(EXIT_FAILURE);
    }
	else if(pid == 0)	//子进程向内核注册信号处理函数
	{
		printf("this is child process,pid:%d ppid:%d\n",getpid(),getppid());

		if(signal(SIGHUP, sig_handler) == SIG_ERR)
		{
			perror("signal error");
		}
		
		int i = 0;
		while(i < 15)
		{
			printf("pid:%d i = %d\n",getpid(),i);
			i++;
			sleep(1);
		}
	}
	else		//父进程向子进程发送信号
	{
		printf("parent process,pid:%d child's pid:%d\n",getpid(),pid);
		if(kill(pid, SIGHUP) != 0)
		{
			perror("kill error");
			exit(EXIT_FAILURE);
		}
		wait(NULL);
	}
}

image-20240911180952696

通过编译执行,发现并没有想象的父进程向子进程发送信号,然后子进程捕获对应的信号执行相应的操作。实际的运行效果是父进程执行,子进程直接退出了。这里先说一下原因:当父进程先运行的时候,就会打印父进程和子进程的pid,然后通过kill函数给子进程发送信号。通过上边的内容可知信号是一种异步机制,所以这里父进程给子进程发送信号已经发送成功了,只不过这时候子进程可能还没有调用signal函数向内核登记信号处理函数,所以父进程向子进程发送的信号执行它的默认操作。由于大部分信号的默认操作都是结束该进程,所以这里的子进程直接被结束掉了,这也就是子进程为什么没有运行的原因。所以这里要想让子进程收到来自父进程的信号就要让子进程先开始运行,让父进程后运行,保证子进程已经向内核登记了信号处理函数以后才让父进程发送信号给子进程。只需在父进程开始时加一句sleep(1)就可以实现这个功能,修改后的执行结果如下:

image-20240911182500727

定时器

在Linux系统中有一种定时器信号SIGALRM,用于通知进程一个定时器已经到期。当设置了一个定时器(使用setitimer()alarm()函数),并且时间到达时,操作系统会向进程发送SIGALRM信号。进程可以选择忽略这个信号,或者捕获它并执行相应的处理程序。

alarm函数

#include <unistd.h>

unsigned alarm(unsigned seconds);

//功能:用于在指定的时间后由内核向调用进程发送SIGALRM信号
//参数:以秒为单位的要设定的时间
//返回值:返回0或以前设置的定时器时间余留秒数
//注意:alarm这个函数是一次性的,调用一次只产生一次alarm信号,如果想要产生周期性的信号,要在信号处理函数里再次调用
示例–使用alarm函数设置一个定时器
#include "header.h"

void sig_handler(int signum)
{
	if(signum == 14)
		printf("receive a signal is SIGALRM,time out\n");
	alarm(5);		//当进入到这个函数里就说明触发了SIGALRM信号,此时要再次设置定时时间来产生周期性的定时时间
}

int main(void)
{
	pid_t pid;

	//向内核登记SIGALRM信号处理函数,如果产生了这个信号就去执行相应的信号处理函数
	if(signal(SIGALRM, sig_handler) == SIG_ERR)
	{
		perror("signal error");
		exit(EXIT_FAILURE);
	}

	if((pid = fork()) < 0)
	{
		perror("fork error");
		exit(EXIT_FAILURE);
	}
	else if(pid == 0)
	{
		printf("child's pid:%d ppid:%d\n",getpid(),getppid());
	
		int i = 0;

		while(i < 15)
		{
			printf("pid:%d i = %d\n",getpid(),++i);
			sleep(1);
		}	
	}
	else
	{
		printf("parent's pid:%d child's pid:%d\n",getpid(),pid);
		alarm(5);		//设定5秒后产生SIGALRM信号
		wait(NULL);
	}

	return 0;
}

image-20240911201157312

通过编译执行,可以发现通过alarm()函数可以来设置一个定时器,功能和单片机的硬件定时器类似。到时间以后就产生SIGALRM信号,由于之前使用signal函数向内核登记了该信号和信号处理函数,所以当该信号产生的时候就去信号处理函数里进行相应的操作。

示例–使用定时器每隔一个定时时间翻转引脚状态
#include "header.h"
#include <wiringPi.h>

#define PIN 1

void sig_handler(int signum)
{
	static int i = 0;
	if(signum == 14)
		printf("receive a signal is SIGALRM,time out\n");
	alarm(5);		//当进入到这个函数里就说明触发了SIGALRM信号,此时要再次设置定时时间来产生周期性的定时时间

	if(++i % 2 == 0)		//当i对2取余等于0的时候,将引脚设置为高电平反之设置为低电平
		digitalWrite(PIN,HIGH);
	else
		digitalWrite(PIN,LOW);
}

int main(void)
{
	pid_t pid;

	//向内核登记SIGALRM信号处理函数,如果产生了这个信号就去执行相应的信号处理函数
	if(signal(SIGALRM, sig_handler) == SIG_ERR)
	{
		perror("signal error");
		exit(EXIT_FAILURE);
	}

	if(wiringPiSetup() == -1)		//初始化wiringPi库
	{
		perror("init wiring error");
		exit(EXIT_FAILURE);
	}

	//将引脚配置为输出模式
	pinMode(PIN,OUTPUT);

	if((pid = fork()) < 0)
	{
		perror("fork error");
		exit(EXIT_FAILURE);
	}
	else if(pid == 0)
	{
		printf("child's pid:%d ppid:%d\n",getpid(),getppid());
	
		int i = 0;

		while(i < 15)
		{
			printf("pid:%d i = %d\n",getpid(),++i);
			sleep(1);
		}	
	}
	else
	{
		printf("parent's pid:%d child's pid:%d\n",getpid(),pid);
		alarm(5);		//设定5秒后产生SIGALRM信号
		wait(NULL);
	}

	return 0;
}

image-20240911203806593

由于这里用到了wiringPi库,所以要将-lwiringPi选项,执行的时候要用超级用户权限

image-20240911203912909image-20240911203945014image-20240911204014066

这里使用gpio readall这个指令来查看引脚的输入输出模式以及引脚的电平状态,通过对比发现它每五秒切换一次引脚号为1的电平状态,通过alarm函数实现了定时器的用法。

setitimer函数

#include <sys/time.h>

int setitimer(int which, const struct itimerval *restrict value,struct itimerval *restrict ovalue);

struct itimerval {
               struct timeval it_interval; /* Interval for periodic timer */
               struct timeval it_value;    /* Time until next expiration */
           };
struct timeval {
               time_t      tv_sec;         /* seconds */
               suseconds_t tv_usec;        /* microseconds */
           };

//功能:用于设置定时器,以实现延时和定时功能。该函数可以代替alarm函数,并具有更高的精度,支持微秒级的定时控制,它的工作机制是在指定的时间后发送特定的信号(如SIGALRM、SIGVTALRM或SIGPROF)给进程。
//参数1:which参数用于指定定时器的类型,
	//ITIMER_REAL:真实时间计时器,以系统实时时间为准,一旦时间到达就发送信号。
	//ITIMER_VIRTUAL:虚拟时间计时器,以进程在用户态消耗的时间为准。
	//ITIMER_PROF:CPU时间计时器,以进程在用户态和内核态所消耗的总时间为准。
//参数2:新的定时器设置值(value):这是一个指向struct itimerval结构体的指针,该结构体包含了定时器的间隔时间和总时间。
	//it_value:首次触发定时器前的等待时间。(定时器开始工作前需要的时间)
	//it_interval:定义了两次触发之间的时间间隔。如果设置为0,则定时器只触发一次。(要设定的时间)
	//在struct itimerval中又嵌入了一个结构体,这两个结构体的成员分别是tv_sec和tv_usec,用来设置秒和微秒。
//参数3:旧的定时器设置值(old_value):这也是一个指向struct itimerval结构体的指针,用来存储当前定时器的设置值。如果不需要获取当前值,可以将其设置为NULL。
//返回值:setitimer函数的返回值0表示成功,返回-1表示出错,出错时可以通过errno变量获取具体的错误信息。
示例–使用setitimer函数设置定时器
#include "header.h"
#include <wiringPi.h>
#include <sys/time.h>

#define PIN 1

void sig_handler(int signum)
{
	static int i = 0;
	if(signum == 14)
		printf("receive a signal is SIGALRM,time out\n");

	if(++i % 2 == 0)		//当i对2取余等于0的时候,将引脚设置为高电平反之设置为低电平
		digitalWrite(PIN,HIGH);
	else
		digitalWrite(PIN,LOW);
}

int main(void)
{
	pid_t pid;

	//向内核登记SIGALRM信号处理函数,如果产生了这个信号就去执行相应的信号处理函数
	if(signal(SIGALRM, sig_handler) == SIG_ERR)
	{
		perror("signal error");
		exit(EXIT_FAILURE);
	}

	if(wiringPiSetup() == -1)		//初始化wiringPi库
	{
		perror("init wiring error");
		exit(EXIT_FAILURE);
	}

	//将引脚配置为输出模式
	pinMode(PIN,OUTPUT);

	if((pid = fork()) < 0)
	{
		perror("fork error");
		exit(EXIT_FAILURE);
	}
	else if(pid == 0)
	{
		printf("child's pid:%d ppid:%d\n",getpid(),getppid());
	
		int i = 0;

		while(i < 15)
		{
			printf("pid:%d i = %d\n",getpid(),++i);
			sleep(1);
		}	
	}
	else
	{
		printf("parent's pid:%d child's pid:%d\n",getpid(),pid);

		struct itimerval timer;
		timer.it_interval.tv_sec = 5;		//设定定时时间,支持微秒级
		timer.it_interval.tv_usec = 0;
		timer.it_value.tv_sec = 5;			//设定定时器在启动前经过的时间
		timer.it_value.tv_usec = 0;
		if(setitimer(ITIMER_REAL,&timer,NULL) == -1)		//设置定时器参数,使用真实时间计时器进行定时
		{
			perror("setitimer error");
			exit(EXIT_FAILURE);
		}
		
		wait(NULL);
	}

	return 0;
}

image-20240911213636185

image-20240911213715782image-20240911213738375image-20240911213809585

通过上边的编译结果可以看出setitimer函数也可以用来设置定时器,相比alarm函数来说,它还支持毫秒级的定时,使用起来更加精确一点。

标签:函数,int,signal,pid,信号,进程
From: https://blog.csdn.net/m0_52867657/article/details/142178305

相关文章

  • PostgreSQL的walsender和walreceiver进程介绍
    PostgreSQL的walsender和walreceiver进程介绍在PostgreSQL中,WAL(Write-AheadLogging)是一种用于确保数据库事务日志安全可靠的机制。WAL是PostgreSQL进行数据库恢复、复制等操作的基础。walsender和walreceiver是PostgreSQL内部两个非常重要的进程,它们负责主......
  • 小林coding学习笔记(进程调度算法)
    //进程调度算法进程调度算法是CPU通过进程调度算法决定某个时刻去调用哪个进程到CPU上运行的算法1、先来先服务调度算法每次从就绪队列的队头调度到CPU上运行,直到进程退出或被阻塞,才会继续从队列中调度进程运行。特点:对短作业不利,对长作业有利,无法平衡短作业与长作业。2、最......
  • Linux下通过命名管道实现进程间通信
    引入上一篇文章介绍了Linux中通过pipe创建匿名管道,并实现父子进程间通信的功能;当时我就提到了Linux中的另一种管道通信方式——命名管道,下面就来详细介绍一下;命名管道什么是命名管道命名管道(NamedPipe),也叫FIFO(FirstInFirstOut),是一种用于进程间通信(IPC)的机制。与匿名管......
  • 我与C++的爱恋:进程状态(RSDT 阻塞 僵尸 孤儿)
    ​​......
  • 阅读周·深入浅出的Node.js | Node应用,产品化实践摸索,玩转进程
    背景去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。没有计划的阅读,收效甚微。新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。这个“玩法”虽然常见且板正,但是有效。已读完书籍:《架构简洁之道》。当前阅读......
  • 操作系统的心脏:进程调度的基本过程
    欢迎浏览高耳机的博客希望我们彼此都有更好的收获感谢三连支持!进程调度是操作系统中至关重要的组成部分,它负责决定哪个进程应该在何时使用CPU。了解计算机进行进程调度的过程和原理,对我们了解计算机以及进行多线程编程大有帮助!在操作系统中,进程是系统分配资源的最小......
  • 详解 Linux 系统下的进程(下)
    目录一.进程控制1.进程创建a.Linux系统中,如何创建一个进程?b.进程创建成功后,Linux底层会为其做些什么?2.进程终止a.什么是进程终止?b.进程终止的方法有哪些?c.exit 与_exit的区别3.进程等待a.什么进程等待? b.为什么要进程等待?c.如何进行进程等待?①wait②waitp......
  • Linux下使用pipe进行父子进程间通信
    引入之前我们介绍了多进程以及创建进程的函数fork,下面我们将继续深入,讨论一下多进程间的通信问题;pipe管道谈论多进程通信,就离不开pipe(管道),这是一个系统调用,用于在UNIX和类UNIX系统(如Linux)上创建一个管道(pipe),实现进程间通信。它创建了一个双向的通信通道,允许一个进程向另一......
  • NAS安装及应用supervisor进程守护
    学会安装并使用supervisor进程守护,再也不用担心进程无故消失/退出朗读全文Yourbrowserdoesnotsupporttheaudioelement.背景/有什么用NAS中进程守护的实践应用(Debian中也一样)前文讲了在群晖NAS的后台中运行如下命令来实现推流nohupffmpeg-re-i"rtsp://admi......
  • php和go进行跨进程fifo通信
    fifo的文件需要提前建立好,或者用程序处理php<?php$fifoName="my_fifo";//打开命名管道进行读取$fifo=fopen($fifoName,"r+");if($fifo===false){die("UnabletoopenFIFO:$fifoName");}//读取数据$data=fread($fifo,1024);echo"Re......