嵌入式Linux进程与信号(学习笔记)

来源:互联网 发布:现金贷系统源码 编辑:程序博客网 时间:2024/06/05 01:15

知识点:

1.进程的概念

狭义定义:进程就是程序的执行过程。

进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

 

进程的特性:

1.动态性:进程的实质是进程实体的一次执行过程;

2.并发性:可以与其他进程在宏观上一并执行(实际上某一时刻只有一个进程在执行);

3.独立性:进程是一个可以独立运行,独立分配资源,独立调度的基本单位;

4.异步性:每个进程都以其相对独立,不可预知的速度向前推进;

 

结构特征:进程有程序,数据和进程控制块三部分组成.

 

2.Linux运行的第一个进程

initLinux系统执行的第一个进程,是系统所有进程的起点,主要用来执行一些开机初始化脚本和监视进程。Linux系统在完成内核引导以后就开始运行init程序,init程序需要读取配置文件/etc/inittab

 

3.进程的创建

a).ps命令 :查看进程命令

b).system函数:

头文件#include<stdlib.h>

int system(const char *string);

:system("ps");   //string即一个命令

system()函数执行了三步操作:

1.fork一个子进程;

2.在子进程中调用exec函数去执行string

3.在父进程中调用wait去等待子进程结束。

对于fork失败,system()函数返回-1

如果exec执行成功,也即string顺利执行完毕,则返回string通过exitreturn返回的值。

(注意,string顺利执行不代表执行成功,比如string"rm debuglog.txt",不管文件存不存在该string都顺利执行了)

如果exec执行失败,也即string没有顺利执行,比如被信号中断,或者string命令根本不存在,system()函数返回127.

如果stringNULL,则system()函数返回非0值,一般为1.

c).exec系列函数:

exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。

d).fork()函数:

头文件 #include<sys/types.h>

#include<unistd.h>

pid_t fork(void);

返回值:负数:如果出错,则fork()返回-1,此时没有创建新的进程。最初的进程仍然运行;

零:在子进程中,fork()返回0;

正数:在父进程中,fork()返回正的子进程的PID;

子进程PID可以用getpid()得到,父进程则可用getppid()来得到;

4.信号(kill,signal)(暂时写到这)

1.kill()函数

#include<sys/types.h>

#include<signal.h>

int kill(pid_t pid,int sig);

函数说明
kill()可以用来送参数sig指定的信号给参数pid指定的进程。参数
pid有几种情况
:
pid>0
将信号传给进程识别码为pid 的进程。
pid=0 将信号传给和目前进程相同进程组的所有进程
pid=-1 将信号广播传送给系统内所有的进程
pid<0 将信号传给进程组识别码为pid绝对值的所有进程

返回值:成功执行返回0,反之返回-1;

 

2.signal()函数

功能:设置某一信号的对应动作

#include<sys/types.h>

#include<signal.h>

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

或者:typedef void(*sig_t) ( int );  sig_t signal(int signum,sig_t handler);

第一个参数signum指明了所要处理的信号类型,它可以取除了SIGKILLSIGSTOP外的任何一种信号。

第二个参数handler描述了与信号关联的动作,它可以取以下三种值:  
1)一个返回值为正数的函数地址  此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler所指定的函数。这个函数应有如下形式的定义:  int func(int sig);  sig是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行。  
2SIGIGN  这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。  
3SIGDFL  这个符号表示恢复系统对信号的默认处理。

函数说明:  signal()会依参数signum指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,  如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断。  
返回值:返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。  

Signals:

Signal

Description

SIGABRT

由调用abort函数产生,进程非正常退出

SIGALRM

alarm函数设置的timer超时或setitimer函数设置的intervaltimer超时

SIGBUS

某种特定的硬件异常,通常由内存访问引起

SIGCANCEL

Solaris Thread Library内部使用,通常不会使用

SIGCHLD

进程TerminateStop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略

SIGCONT

当被stop的进程恢复运行的时候,自动发送

SIGEMT

和实现相关的硬件异常

SIGFPE

数学相关的异常,如被0除,浮点溢出,等等

SIGFREEZE

Solaris专用,Hiberate或者Suspended时候发送

SIGHUP

发送给具有TerminalControllingProcess,当terminaldisconnect时候发送

SIGILL

非法指令异常

SIGINFO

BSD signal。由Status Key产生,通常是CTRL+T。发送给所有ForegroundGroup的进程

SIGINT

Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGroundGroup的进程

SIGIO

异步IO事件

SIGIOT

实现相关的硬件异常,一般对应SIGABRT

SIGKILL

无法处理和忽略。中止某个进程

SIGLWP

Solaris Thread Libray内部使用

SIGPIPE

reader中止之后写Pipe的时候发送

SIGPOLL

当某个事件发送给Pollable Device的时候发送

SIGPROF

Setitimer指定的Profiling Interval Timer所产生

SIGPWR

和系统相关。和UPS相关。

SIGQUIT

输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程

SIGSEGV

非法内存访问

SIGSTKFLT

Linux专用,数学协处理器的栈异常

SIGSTOP

中止进程。无法处理和忽略。

SIGSYS

非法系统调用

SIGTERM

请求中止进程,kill命令缺省发送

SIGTHAW

Solaris专用,从Suspend恢复时候发送

SIGTRAP

实现相关的硬件异常。一般是调试异常

SIGTSTP

Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程

SIGTTIN

Background Group的进程尝试读取Terminal的时候发送

SIGTTOU

Background Group的进程尝试写Terminal的时候发送

SIGURG

out-of-band data接收的时候可能发送

SIGUSR1

用户自定义signal 1

SIGUSR2

用户自定义signal 2

SIGVTALRM

setitimer函数设置的Virtual Interval Timer超时的时候

SIGWAITING

Solaris Thread Library内部实现专用

SIGWINCH

Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程

SIGXCPU

CPU时间限制超时的时候

SIGXFSZ

进程超过文件大小限制

SIGXRES

Solaris专用,进程超过资源限制的时候发送

 

Linux的僵尸进程及其解决方法

 

1. 产生原因:

在LINUX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。通过ps命令查看其带有defunct的标志。僵尸进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程.

2.解决方法

  (1)父进程通过waitwaitpid等函数等待子进程结束,这会导致父进程挂起。执行wait()waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。

 

  (2)如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。

 

  (3)如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号

 

(4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。

 

Wait()函数

wait(等待子进程中断或结束)
表头文件

     #include<sys/types.h>
     #include<sys/wait.h>
定义函数
pid_t wait (int * status);
函数说明:

    wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
    如果在调用 wait()时子进程已经结束,则 wait()会立即返回子进程结束状态值。
    子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一起返回。
   如果不在意结束状态值,则参数 status 可以设成 NULL。
   子进程的结束状态值请参考
waitpid( )    
如果执行成功则返回子进程识别码(PID) ,如果有错误发生则返回返回值-1。失败原因存于 errno 中。

 

 

原创粉丝点击