进程信号生命周期详解

2023-11-12

信号和信号量半毛钱关系都没有!

每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2

查看信号的机制,如默认处理动作man 7 signal

SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump。

进程信号

kill命令可以给进程发送信号。kill -l查看信号列表,共61个信号,其中信号1-31号:普通信号,34-64号:实时信号。我们只需了解普通信号。信号是给进程发的,kill -信号编号 pid

进程本身是被程序员编写的属性和逻辑的集合—程序员编码完成的,当进程收到信号时,不一定立即处理。

信号及相关概念

信号是进程之间事件异步通知的一种方式,属于软中断

[yyq@VM-8-13-centos 2023_03_01_ProcessSignal]$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

ctrl+c:是一个组合键,OS会识别为kill -2 pid,是2号信号,用于终止前台进程。SIGINT的默认处理动作是终止进程。

  1. 实际执行信号的处理动作称为信号递达(Delivery)。

  2. 信号从产生到递达之间的状态,称为信号未决(Pending)。(信号产生了,但是没有被执行)

  3. 信号可以被进程选择性阻塞。

  4. 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

    注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作

信号的生命周期

预备

首先进程需要认识信号,知道信号的属性和对应要做的动作

信号产生

发送信号的本质就是更改对应进程PCB中的信号位图。PCB的管理者是OS,故信号也是由OS发送,因为只有OS有权利修改PCB里的内容。

无论有多少种发送信号的方式,本质都是通过OS向目标进程发送信号,故OS必须提供发送信号/处理信号的相关系统调用。

当信号来临时,进程不一定马上处理这个信号。异步:各自做着不一样的事情,进程继续执行自己的代码,而不执行信号。同步:进程先执行信号,停下自己的代码。由于信号可以随时产生,所以进程对信号是异步的。

常见的信号产生方式

键盘输入

ctrl+c ==> SIGINT-终止前台进程 ;ctrl+\ ==> SIGQUIT-终止进程

系统调用

kill() 给别人发任意信号、raise() 给自己发任意信号、abort() 给自己发SIGABRT信号

#include <sys/types.h>
#include <signal.h>
原型
	int kill(pid_t pid, int sig);
参数
	pid:
		pid>0,发送给指定进程; 
		pid==0,发送给调用进程的进程组中的每个进程; 
		pid==-1,发送给所有调用进程有权限发送信号的进程,除了进程1 (init); 
		pid<-1, 发送给进程组中ID为-pid的所有进程
	sig:信号编号
返回值
	成功返回0;失败返回-1
#include <signal.h>
原型
	int raise(int sig);// 相当于kill(getpid(), sig);
参数
	sig:信号编号
返回值
	成功返回0;失败返回非0
#include <signal.h>
原型
	void abort(void);// 相当于kill(getpid(), SIGABRT);

大多数信号的默认处理动作都是终止进程,信号的意义:信号不同代表不同的事件,对事件发生之后的处理动作可以一样。

硬件异常

1、如除0异常的8号信号SIGFPE

那操作系统如何得知下面这个程序在执行除0操作?

答:通过硬件-cpu内的状态寄存器。状态寄存器有个溢出标记位,当除0溢出标记位就变成1,此次运算结果无意义,CPU发生运算异常,OS作为软硬件资源的管理者,就知道是当前的进程导致的硬件异常,于是OS修改该进程的信号标记位。

#include <iostream>
#include <string>
#include <cstdio>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

void handler(int signo)
{
    std::cout << "捕捉到一个信号,编号是" << signo << std::endl;
} 

int main()
{
    signal(SIGFPE, handler);// 只是注册了该信号对应的动作
    int a = 10;
    a /= 0;
    while(true)
    {
        std::cout << "我是一个进程:" << getpid() << std::endl;        
        sleep(1);
    }
    return 0;
}

在执行这个程序后,会一直输出捕捉到一个信号,编号是8,不应该只输出1次,程序就被终止了么?

答:首先我们自定义了收到8号信号的动作,就不会执行默认动作(退出)。其次,1、收到信号不一定会引起进程退出,2、进程没有退出就有可能还会被调度,3、cpu内部对应的寄存器只有一个,但是状态寄存器中的内容属于当前进程的上下文,但是用户没有能力去修改寄存器中的内容,4、当进程被切换的时候,就有无数次状态寄存器被保存和恢复的过程,所以每一次恢复的时候就会让OS识别到状态寄存器的溢出标志位,所以一直给进程发8号信号。

2、如访问野指针 11号信号SIGSEGV

int* p = nullptr;
*p = 100;//段错误

那操作系统如何得知下面这个程序访问野指针了?

答:咱们在程序里写的指针,本质是就是虚拟地址,而虚拟地址空间的0号地址,经过页表和MMU(在cou里))的映射,发现当前进程不允许访问0号地址,MMU这个硬件因为越界/野指针,进而发生异常,OS识别到了MMU异常,就将对应进程的信号位图的11位置为1.

软件条件

1、管道读端关闭,写端一直写==>OS直接终止当前进程,13号信号SIGPIPE

2、定时器alarm()函数 14号信号SIGALRM

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

可以通过alarm()的使用,我们可以明显观察到IO速度很慢

int main()
{
    alarm(1); // 1s内计算机能够将数据累加到144250
    int cnt = 0;
    while(true)
    {
        std::cout << cnt++ << std::endl;//这里要进行cnt次IO,速度很慢
        cnt++;
    }
    return 0;
}
---------------------------------------------
int cnt = 0;
void handler(int signo)
{
    std::cout << "cnt: " << cnt << std::endl;//只需要进行1次IO
}
int main()
{
    alarm(1); // 1s内计算机能够将数据累加到435882026
    signal(SIGALRM, handler);
    while(true)
    {
        cnt++;
    }
    return 0;
}

闹钟信号只会通知1次即只有1次输出,不会像硬件异常那样疯狂输出

int cnt = 0;
void handler(int signo)
{
    std::cout << "cnt: " << cnt << std::endl;//只需要进行1次IO
    alarm(1);//这样就可以实现每隔1s打印一次,简单的sleep(1)
}
int main()
{
    alarm(1); // 1s内计算机能够将数据累加到435882026
    signal(SIGALRM, handler);
    while(true)
    {
        cnt++;
    }
    return 0;
}

alarm(0)表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数。

任何一个进程都可以通过alarm系统调用在内核中设置闹钟,那么OS就要对其进行管理,先描述在组织。

struct alarm { 
	uint64_t when; //未来的时间 即时间戳+闹钟设定的时间
    int type; //闹钟类型一次性或周期性 
    task_struct *p; //对应的进程 
    struct alarm* next; //下一个闹钟
};

比如用最小堆来管理,那么堆顶的闹钟就是将来第一个要超时的闹钟,OS就可以周期性的检查堆顶,当操作系统的当前时间>alarm.when,就给对应的进程发送信号。

信号保存

在信号到来到信号被处理的这段时间,称为时间窗口,必须要保存这个信号。会被保存在进程PCBtask_struct{ unsigned int signal}这个结构体里,unsigned int有32个比特位,用来表示1-31号信号【比特位的位图结构】。比特位的位置,代表信号编号,比特位的内容,代表是否收到该信号,对应的值为0/1,收到对应的信号就置为1。

保存和处理中间的情况:信号阻塞、信号未决

被阻塞的信号将保持在未决(pending)状态,直到进程解除对信号的阻塞(block),才执行递达动作。普通信号在递达之前产生多次只计一次,因为对应二进制位非0即1。

task_struct {
	unsigned int pending = 0; //0000 0000 0000 0000 0000 0000 0000 0000 // 从右向左第1个比特位表示1号信号
	unsigned int block = 0;   //0000 0000 0000 0000 0000 0000 0000 0000
}

if ((1 << (signo - 1)) & pcb->block) 
{
	//被阻塞,那就不用递达了
}
else
{
	if((1 << (signo - 1)) & pcb->pending)
	{
		//未被阻塞,且信号未决,那就递达
	}
}

①PCB进程控制块中有信号屏蔽状态字(block),信号未决状态字(pending),还有是否忽略标识
②信号屏蔽状态字(block):1代表阻塞,0代表不阻塞;信号未决状态字(pending):1代表未决,0代表信号递达
③向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,如果信号屏蔽状态字阻塞,信号未决状态字(pengding)相应位置1;
若阻塞解除,信号未决状态字(pending)相应位置0,表示信号可以递达了。
④block状态字,pending状态都是64bit,分别代表Linux系统中的64个信号。例如SIGINT是2号信号,对应block状态字中的第二位
⑤block状态字用户可以读写,pending状态字用户只能读,这是信号的设计机制。

注意:即使一个信号没有产生,它仍然可以先被设置为阻塞状态。进程能识别信号,是因为PCB结构中有相应的数据结构(block

位图、pending位图、handler_t表)

信号处理

typedef void(*handler_t)(int signo);//函数指针-表示操作方法
handler_thandler[32] = 0;//函数指针数组,存储32个信号对应的处理方法 数组的下标+1表示信号编号,数组下标对应的内容表示对应信号的处理方法

收到信号后一般会有3个动作。

默认动作

大多数信号的默认处理动作都是终止进程,信号的意义:信号不同代表不同的事件,对事件发生之后的处理动作可以一样。term,core,ign,stop,cont

进程终止时核心转储问题

当进程出现异常的时候,我们将进程对应时刻在内存中的有效数据转储到磁盘

First the signals described in the original POSIX.1-1990 standard.
//表中第一列是各信号的宏定义名称,第二列是各信号的编号,第三列是默认处理动作
Signal     Value     Action   Comment
──────────────────────────────────────────────────────────────────────
SIGHUP        1       Term    Hangup detected on controlling terminal
or death of controlling process
SIGINT        2       Term    Interrupt from keyboard
SIGQUIT       3       Core    Quit from keyboard
SIGILL        4       Core    Illegal Instruction
SIGABRT       6       Core    Abort signal from abort(3)
SIGFPE        8       Core    Floating point exception
SIGKILL       9       Term    Kill signal
SIGSEGV      11       Core    Invalid memory reference
SIGPIPE      13       Term    Broken pipe: write to pipe with no
readers
SIGALRM      14       Term    Timer signal from alarm(2)
SIGTERM      15       Term    Termination signal
SIGUSR1   30,10,16    Term    User-defined signal 1
SIGUSR2   31,12,17    Term    User-defined signal 2
SIGCHLD   20,17,18    Ign     Child stopped or terminated
SIGCONT   19,18,25    Cont    Continue if stopped
SIGSTOP   17,19,23    Stop    Stop process
SIGTSTP   18,20,24    Stop    Stop typed at terminal
SIGTTIN   21,21,26    Stop    Terminal input for background process
SIGTTOU   22,22,27    Stop    Terminal output for background process
  • Term表示终止当前进程,OS正常结束该进程

  • Core表示终止当前进程并且Core Dump(Core Dump 用于gdb调试)。在命令行输入ulimit -a查看core file size核心转储功能,云服务器默认是0(关闭该功能),命令行输入ulimit -c 1024,打开云服务器的核心转储功能并设置大小为1024字节,此时再执行会发生段错误的程序,就会输出Segmentation fault (core dumped),且当前目录下会生成一个文件core.引起core问题的进程ID,给该文件在编译时带上-g选项,在进行调试的时候,输入core-file core.pid就可以把对应的核心转储文件打开,就可以看到详细的报错信息。

  • Ign表示忽略该信号

  • Stop表示停止当前进程

  • Cont表示继续执行先前停止的进程

捕捉(自定义动作)

用户自定义信号处理函数称为捕捉9号信号无法捕捉。信号产生不一定被立即处理,而是在合适的时候(由内核态返回用户态的时候)处理。如果是默认动作或忽略动作的信号则可以直接在内核态执行,不用等到回用户态再执行。

内核态:由用户/进程发起的,但实际执行系统调用的身份是内核

用户态:平时自己写的代码是运行在用户态的,而我们的代码难免访问2种资源【OS资源(getpid, waitpid),硬件资源(printf, write, read)】,用户为了访问内核和硬件资源,必须通过系统调用来完成对应的访问。

往往系统调用比较费时间,要进行角色转换。CPU内有寄存器,有个current寄存器指向当前正在运行进程的PCB的起始地址,CR3寄存器表征当前进程的运行级别(0表示内核态,3表示用户态)

在这里插入图片描述

合适的时候:从内核态返回用户态的时候,要返回的话,曾经肯定进入了内核态(系统调用、进程切换等),此时要从内核态切换回去,OS会先通过PCB看看每个信号的block表是否被阻塞,没被阻塞的再看对应的pending表是否未决,未决的信号让它递达。【block1,不管;block0&&pending0,不管;block0&&pending==1,查handler表对应的方法并执行】,默认和忽略这两个动作很方便,而自定义动作需要切换回用户态才能执行【不可以在内核态直接执行】,然后再回到内核态获取当前进程的上下文,才能回到用户态跳转的位置继续执行后续代码。

普通信号的自定义捕捉流程:用户态–>内核态【执行系统调用,以及信号检测】–>用户态【执行自定义动作】–>内核态【获取进程上下文】–>用户态。

忽略动作

函数接口

signal()函数 信号捕捉

#include <signal.h>
功能 信号处理器,即可以收到特定信号时,执行自定义动作

原型
	typedef void (*sighandler_t)(int); // 函数指针,传入的int表示信号的编号
    sighandler_t signal(int signum, sighandler_t handler);//其实就是拿着signum去操作方法表里修改对应信号的操作方法
// 设置了收到2号信号的自定义动作
void handler(int signo)
{
    std::cout << "捕捉到2号信号" << signo << std::endl;
}

// 这里是对signal函数的调用,而不是对handler的调用
// handler对应的方法一般不会执行,除非收到对应的信号
signal(2, handler);// 只是注册了该信号对应的动作
//要想触发该动作,需要按ctrl+c组合键 SIGINT的默认处理动作是终止进程
//2号的默认动作是退出进程,当我们给这个进程的2号信号设置了自定义动作,就会执行自定义动作

sigaction()函数 信号捕捉

#include <signal.h>
功能 信号处理器,即可以收到特定信号时,执行自定义动作

原型
	int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
参数
    signo:信号编号
    act输入型参数:
		非nullptr:根据act修改对应信号的处理动作
	oact输出型参数:
		nullptr:不保存原来的操作方法
        非nullptr:保存原来的操作方法
返回值
    调用成功则返回0,出错则返回-1
struct sigaction {
               void     (*sa_handler)(int);//sa_handler==SIG_IGN,表示忽略;sa_handler==SIG_DFL,表示执行系统默认动作;sa_handler==用户自定义,表示捕捉
               void     (*sa_sigaction)(int, siginfo_t *, void *);//实时信号的处理函数
               sigset_t   sa_mask;//表示当前进程正在处理某一种信号时,额外需要屏蔽的信号集【正在递达的信号自动放进当前进程的信号屏蔽字里】。当信号处理函数返回时,自动恢复原来的信号屏蔽字【不会再屏蔽额外添加的这个信号集】 sigaddset(&act.sa_mask, signo);
               int        sa_flags;//设为0即可
               void     (*sa_restorer)(void);//已不再使用
           };
sa_handler和sa_sigaction两个参数不共存!

附:1-31号信号产生原因

  1. SIGHUP:当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程
  2. SIGINT:当用户按下了组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止里程
  3. SIGQUIT:当用户按下组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信 号。默认动作为终止进程
  4. SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件
  5. SIGTRAP:该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件
  6. SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件
  7. SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件
  8. SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件
  9. SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可 以杀死任何进程的方法
  10. SIGUSE1:用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程
  11. SIGSEGV:指示进程进行了无效内存访问。默认动作为终止进程并产生core文件
  12. SIGUSR2:这是另外一个用户自定义信号 ,程序员可以在程序中定义并使用该信号。默认动作为终止进程
  13. SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
  1. SIGALRM:定时器超时,超时的时间 由系统调用alarm设置。默认动作为终止进程

  2. SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行 shell命令Kill时,缺省产生这个信号。默认动作为终止进程

  3. SIGSTKFLT:Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程

  4. SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号

  5. SIGCONT:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为终止进程

  6. SIGTTIN:后台进程读终端控制台。默认动作为暂停进程

  7. SIGTSTP:停止进程的运行。按下组合键时发出这个信号。默认动作为暂停进程

  8. SIGTTOU:该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程

  9. SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号

  10. SIGXFSZ:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程

  11. SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程

  12. SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程

  13. SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程

  14. SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号

  15. SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略

  16. SIGPWR:关机。默认动作为终止进程

  17. SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件

    31号SIGRTMIN~64号SIGRTMAX:LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

进程信号生命周期详解 的相关文章

  • Raspberry 交叉编译 - 执行程序以“分段错误”结束

    我有一个自己编写的程序 我想从我的 x86 机器上为 Raspberry Pi 构建它 我正在使用 eclipse 生成的 makefile 并且无法更改此内容 我已经阅读了 CC for raspi 的教程 Hackaday 链接 htt
  • 无法在 64 位 Linux 上从汇编 (yasm) 代码调用 C 标准库函数

    我有一个函数foo以汇编语言编写 并在 Linux Ubuntu 64 位上使用 yasm 和 GCC 编译 它只是使用以下命令将消息打印到标准输出puts 如下所示 bits 64 extern puts global foo secti
  • 如何将后台作业的输出分配给 bash 变量?

    我想在 bash 中运行后台作业并将其结果分配给一个变量 我不喜欢使用临时文件 并且希望同时运行多个类似的后台任务 root root var echo hello world root root echo var hello world
  • linux下写入后崩溃

    如果我使用 write 将一些数据写入磁盘上的文件会发生什么 但我的应用程序在刷新之前崩溃了 如果没有系统故障 是否可以保证我的数据最终会刷新到磁盘 如果您正在使用write 并不是fwrite or std ostream write 那
  • 如何访问 mmaped /dev/mem 而不导致 Linux 内核崩溃?

    我有一个简单的程序 尝试访问用户空间中的物理内存 其中内核存储第一个结构页 在 64 位机器上 该地址是 内核虚拟地址 ffffea0000000000 物理地址 0000620000000000 我正在尝试通过用户空间中的 mmap 访问
  • 从c调用汇编函数

    我试图从 c 调用汇编函数 但我不断收到错误 text globl integrate type integrate function integrate push ebp mov esp ebp mov 0 edi start loop
  • 码头无故停止

    我需要经验丰富的码头用户的建议 我在负载均衡器 亚马逊云 后面维护着 2 台 Linux 机器 使用 Jetty 9 0 3 有时我的 Jetty 容器会被 Thread 2 无故关闭 同时地 显示以下日志并且容器无故停止 没有错误 没有例
  • 未找到 Gem 命令

    我已经在 Ubuntu 10 10 32 位上安装了 gem apt get install gem y 但当我尝试跑步时 gem install something gem 我收到未找到命令的错误 bash gem command not
  • 如何从 Linux 内核模块获取使用计数?

    我对正在开发的内核模块的使用计数有疑问 我想打印它以进行调试 如何从模块代码中获取它 有问题的内核版本 Linux 2 6 32 module refcount http lxr linux no linux v2 6 34 1 inclu
  • 查看 Linux 上的多核或多 CPU 利用率

    我有一个在 Linux 上运行的程序 我需要确定它如何利用所有 CPU 内核 有没有什么程序可以查看这些信息 跑过 top 命令并按下 1 查看各个核心
  • 如何设置Java线程的CPU核心亲和力?

    我搜索了以前关于类似主题的帖子 但找不到合适的答案 因此提出这个问题 非常感谢您帮助回答 我知道在 Linux 中通过任务集命令设置进程与特定 CPU 核心的关联性 但我想设置 Java 线程与特定 cpu 核心的亲和力 以便属于同一进程的
  • 将node.js +expressjs应用程序的NODE_ENV设置为ubuntu下的守护进程

    我按照这些说明让守护进程正常工作 http kevin vanzonneveld net techblog article run nodejs as a service on ubuntu karmic http kevin vanzon
  • 使用 ProcessBuilder 运行 shell 脚本

    我正在尝试使用 Java 和 ProcessBuilder 运行脚本 当我尝试运行时 我收到以下消息 error 2 没有这样的文件或目录 我不知道我做错了什么 但这是我的代码 ps 我尝试只执行不带参数的脚本 错误是相同的 String
  • 使用c在linux上分块读写

    我有一个 ASCII 文件 其中每一行都包含一个可变长度的记录 例如 Record 1 15 characters Record 2 200 characters Record 3 500 characters Record n X cha
  • 如何反汇编、修改然后重新组装 Linux 可执行文件?

    无论如何 这可以做到吗 我使用过 objdump 但它不会产生我所知道的任何汇编器都可以接受的汇编输出 我希望能够更改可执行文件中的指令 然后对其进行测试 我认为没有任何可靠的方法可以做到这一点 机器代码格式非常复杂 比汇编文件还要复杂 实
  • 完整的 C++ i18n gettext()“hello world”示例

    我正在寻找完整的 i18ngettext 你好世界的例子 我已经开始了一个基于的脚本使用 GNU gettext 的本机语言支持教程 https web archive org web 20130330233819 http oriya s
  • 如何在文件夹中的 xml 文件中 grep 一个单词

    我知道我可以使用 grep 在这样的文件夹中的所有文件中查找单词 grep rn core 但我当前的目录有很多子目录 我只想搜索当前目录及其所有子目录中存在的所有 xml 文件 我怎样才能做到这一点 我试过这个 grep rn core
  • Bash - 比较 2 个文件列表及其 md5 校验和

    我有 2 个列表 其中包含带有 md5sum 检查的文件 即使文件相同 列表也具有不同的路径 我想检查每个文件的 md5 和 我们正在讨论数千个文件 这就是为什么我需要脚本来仅显示差异 第一个列表是普通列表 第二个列表是文件的当前状态 我想
  • Visual Studio - X11:缺少 DISPLAY 环境变量

    我正在使用 Visual Studio 2019 Enterprise 开发跨平台 Windows Linux x64 GUI 应用程序 在这个 2019 版本中 我们可以使用 Visual Studio调试平台 Windows 本机 和
  • 了解 Linux oom-killer 日志

    我的应用程序被 oom killer 杀死了 它是在实时 USB 上运行的 Ubuntu 11 10 无需交换 PC 具有 1 Gig 的 RAM 唯一运行的应用程序 除了所有内置的 Ubuntu 东西 是我的程序 flasherav 请注

随机推荐

  • Axure rp9 学习笔记-进度笔记以及问题整理

    Axure rp9 学习笔记 进度笔记以及问题整理 4 15 iphone 原型设计 遇到问题 1 苹果手机左按键画的过程中没有弧度 corner 选项为灰 没法选 2 将绘制好的iphone 原型图设置成母版以后 却无法更改颜色 我们对M
  • Rust vs Go:常用语法对比(八)

    题目来自 Golang vs Rust Which Programming Language To Choose in 2023 1 141 Iterate in sequence over two lists Iterate in seq
  • 【LeetCode 热题 HOT 100-002】两数相加(python)

    题集链接 https leetcode cn problem list 2cktkvj 题目链接 https leetcode cn problems add two numbers favorite 2cktkvj 一 题目 二 代码 解
  • C# winform Panel 获取滚轮事件

    使用 Panel 做为控件容器时 设置 Panel AutoScroll true时 在适当的时候将会出现滚动条 但是只能通过拖动滚动条来调整滚动条的位置 如果想要用鼠标中间键来控制滚动条的位置 可以通过下面几步来完成 1 在构造函数中加上
  • DCGAN、WGAN、WGAN-GP、LSGAN、BEGAN原理总结及对比

    GAN系列学习 2 前生今世 本文已投稿至微信公众号 机器学习算法工程师 欢迎关注 本文是GAN系列学习 前世今生第二篇 在第一篇中主要介绍了GAN的原理部分 在此篇文章中 主要总结了常用的GAN包括DCGAN WGAN WGAN GP L
  • 记一次Vulnstack靶场内网渗透(五)

    实验环境搭建 VMware新建网卡VMnet14 选择仅主机模式 并将网段IP设置为192 168 138 0 然后将Windows 7和Windows 2008绑在这个VMnet14上 除此之外 还需要给Windows 7 新增一个网卡
  • C#实现俄罗斯方块游戏

    还记得小时候经常拿袖珍电子游戏机或者小霸王玩过最多的就是俄罗斯方块 冒险岛 超级玛丽还有魂斗罗之类的 这些游戏由于其中简单易上上手的特点 曾经风靡了全世界 俄罗斯方块的基本规则是移动 旋转和摆放游戏自动输出的各种方块 使之排列成完整的一行或
  • Linux开机&关机

    走进Linux系统 开机登录 开机会启动许多程序 它们在Windows叫做 服务 service 在Linux就叫做 守护进程 daemon 开机成功后 它会显示一个文本登录界面 这个界面就是我们经常看到的登录界面 在这个登录界面会提示用户
  • 关于String内存分配的深入探讨

    2019独角兽企业重金招聘Python工程师标准 gt gt gt public class Test public static final String MESSAGE taobao public static void main St
  • Sqli-labs之Less-34

    Less 34 基于错误 POST 单引号 字符型 addslashes 宽字节注入 这一关是POST型的注入 同样的将post传递过来的内容进行了转义处理 过滤了单引号 反斜杠 有之前的例子我们可以看到 df可以将转义的反斜杠给吃掉 而G
  • 数据结构 十进制和十六进制进制间的相互转换

    一 十进制转十六进制 例题 输入十进制数 654321 输出十六进制数 9FBF1 解题步骤 十进制数对16取余 因为最终结果是从下往上依次书写 说以我们可以利用栈的特性 先进 的后出 将余数存入栈中依次弹出 再将弹出的数进行拼接输出即可
  • C++迭代器-------array的基本用法总结

    基本用法中主要总结有 遍历和比较大小 注意 加上头文件 include
  • 利用python来制作动态二维码

    前言 为什么要学习python 是因为不仅很多工作需要用到python 同时我们可以利用python做很多好玩儿的事儿 今天就来教大家如何利用python制作动态二维码 代码说明 我们以小猪佩奇gif图片为例 如果我们利用的背景图是gif动
  • 工厂实施MES系统可以带来哪些效益?

    众多工厂生产现状 1 设施设备先进 但是管理方式落后 手工管理模式的存在 造成数据的不准确和不完全 没有完全实现信息化管理 2 生产计划协调性差 作业调度困难 生产作业计划主要依据调度员的经验制定 设备利用率低 任务进度监控难 紧急插单普遍
  • C++虚基类

    问题引出 问题 A中数据 在D中保存了两份 虚继承 虚继承的目的是让某个类做出声明 承诺愿意共享它的基类 其中 这个被共享的基类就称为虚基类 Virtual Base Class 虚派生只影响从指定了虚基类的派生类中进一步派生出来的类 它不
  • 扎心!为何HR看了你的简历却不通知面试?

    还只是老老实实地写简历 投简历 默默地等待面试通知 那只有两种可能 你太天真 或者是 你真的很久没有 求职 了 如果你细心观察会发现 当你完成一份简历之后 它的状态也会有变化 然而 却有很多求职者并没有搞清楚这些 状态 到底代表着什么 但小
  • idea 配置 JavaWeb 项目的 tomcat

    目录 第一步 单击 idea 靠右上部位的 添加配置 Add Config Run Config 第二步 点击 添加新 或者图中箭头指向的任意一个地方 第三步 选择 Tomcat 服务器 本地 不是 TomEE 第四步 若以前从未配置 To
  • 使用SARIMA做季节时间序列预测全流程(附MATLAB代码)

    在之前的专栏中我们用ARIMA的方法做了时间序列的趋势性预测 不过我们经常还会遇到一种情况 即某些时间序列中存在明显的周期性变化 这种周期是由于季节性变化 季度 月度等 引起的 如下图所示 为1949年到1960年每月国际航空公司的乘客人数
  • C# pdf文件加数字证书,防篡改

    C 指定文件夹内新创建pdf文件加签数字证书 代码实现 引用 Spire Pdf string files Directory GetFiles Config pdfPath 需加数字证书pdf存放文件夹地址 string filesLis
  • 进程信号生命周期详解

    信号和信号量半毛钱关系都没有 每个信号都有一个编号和一个宏定义名称 这些宏定义可以在signal h中找到 例如其中有定 义 define SIGINT 2 查看信号的机制 如默认处理动作man 7 signal SIGINT的默认处理动作