Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1706866
  • 博文数量: 607
  • 博客积分: 10031
  • 博客等级: 上将
  • 技术积分: 6633
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-30 17:41
文章分类

全部博文(607)

文章存档

2011年(2)

2010年(15)

2009年(58)

2008年(172)

2007年(211)

2006年(149)

我的朋友

分类: LINUX

2008-09-01 16:46:37

进程和信号构成了LINUX操作环境最基础的部分。
它们控制了LINUX系统几乎所有的活动。

LINUX环境中进程如何被处理,如何准确地发现计算机在干什么,怎样启动或停止其他的进程,怎么使进程收发消息,如何避免僵尸进程?在这一章都会给出答案。

特别地,我们需要了解:
1.进程结构,类型和调度;
2.开始新的进程的不同方式;
3.父,子,僵尸进程;
4.什么是信号,如何被使用。

进程:是一个地址空间,带着若干个同时运行的线程,以及这些线程所需要的资源。

多任务操作系统会让许多程序一起跑,每个程序就构成1个进程。

进程结构
每个进程会被分配一个唯一的标识符 PID (范围 2--32768, init进程就是1)
#include
int getpid(void);   //取得当前进程的PID
int getppid(void);  //取得当前进程的父进程的PID

/proc目录下放了特定的进程文件

LINUX系统有虚拟存储系统,可以把超出物理存储空间以外的进程装进物理内存空间。

进程表
$ps -af
$ps -ax

进程调度
LINUX调度器基于优先级决定哪个进程该跑。高优先级的跑得更频繁,低优先级的会因为高优先级进程作好了准备而根本没法跑。

启动新的进程
1.system 函数

2.exec 系列函数 :代替当前执行的线程
int execv(const char* pathname, char *const argv[])

3.fork 函数 复制新的进程
在进程表中创建1个新的process entry,跑同样的代码,与当前进程有很多相同的属性,但是有自己的数据空间,环境和文件描述符。
#include
#include
pid_t fork(void);

Initial Process
      |
Fork() ----------------------------
      |                                                                      |
return a new PID                                        return 0
      |                                                                      |
Original Process continue                      New Process

fork失败,会返回-1,这通常是由于子进程数量的限制CHILD_MAX,相应的errno是EAGAIN. 如果没有足够的空间资源,errno会是ENOMEM

当fork被调用时,当前进程会分成2个进程,区分父进程的方式是返回非零值(即新创建的子进程的PID)。而子进程则是返回0。


4.等待进程
#include
#include
pid_t wait(int* stat_loc);

wait会导致父进程阻塞,直到它的1个子进程结束或者父根本没有子进程。这个调用会返回该子进程的PID。如果有错误,返回-1,错误码保存在errno中。

用宏WIFEXITED(stat_loc)判断子进程是否正常退出;
用宏WEXITSTATUS(stat_loc)返回子进程的退出码。


#include
#include
#include
#include
int main()
{
    pid_t pid;
    char *message;
    int n;
    int exit_code;
    printf("fork program starting\n");
    pid = fork();
    switch(pid)
    {
    case -1:
        perror("fork failed");
        return 1;
    case 0:
        message = "This is the child";
        n = 5;
        exit_code=37;
        break;
    default:
        message = "This is the parent";
        n = 3;
        exit_code=0;
        break;
    }
    for(; n > 0; n--) {
        puts(message);
        sleep(1);
    }
    if(pid!=0){
        printf("child PID= %d\n", pid);
        int stat_val;
        pid_t child_pid;
        child_pid = wait(&stat_val);
       if(-1==child_pid){
          printf("errno: %d\n", errno);
       }
        printf("child process has finished: PID=%d\n", child_pid);
        if(WIFEXITED(stat_val)){
                printf("xxx %d\n", stat_val);
                printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
                printf("xxx %d\n", stat_val);

        }
        else
                printf("Child terminated abnormally\n");
    }
    return exit_code;
}


父进程创建子进程后,子进程一般要执行不同的程序.为了调用系统程序,我们可以使用系统调用exec族调用.


zombie
zombie进程又叫defunct进程
“僵尸”进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。所以,defunct进程不仅占用系统的内存资源,影响系统的性能,而且如果其数目太多,还会导致系统瘫痪。

我们知道,每个Unix进程在进程表里都有一个进入点(entry),核心程序执行该进程时使用到的一切信息都存储在进入点。当用ps命令察看系统中的进 程信息时,看到的就是进程表中的相关数据。当以fork()系统调用建立一个新的进程后,核心进程就会在进程表中给这个新进程分配一个进入点,然后将相关 信息存储在该进入点所对应的进程表内。这些信息中有一项是其父进程的识别码。当这个进程走完了自己的生命周期后,它会执行exit()系统调用,此时原来 进程表中的数据会被该进程的退出码(exit code)、执行时所用的CPU时间等数据所取代,这些数据会一直保留到系统将它传递给它的父进程为止。由此可见,defunct进程的出现时间是在子进 程终止后,但是父进程尚未读取这些数据之前。

线程
进程虽然可以通过共享内存或片段来完成一定的协作,但是本质上是分割的空间,不能共享变量。
线程可以,详细以后再讨论。


信号Signal
信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断。从它的命名可以看出,它的实质和使用很象中断。所以,信号可以说是进程控制的一部分。

信号只是用来通知某进程发生了什么某种异步事件,并不给该进程传递任何数据
信号的操作有raise,catch两种。

在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。

进程之间可以互相通过系统调用kill发送软中断信号。
#include
#include
int kill(pid_t pid, int sig);
将信号sig传送给进程pid

void (*signal(int sig, void (*handler)(int)))(int);
设置信号sig的处理函数handler.当信号发生,handler被执行后,系统会恢复原来的预设处理函数。

#include
int pause(void);
令当前进程暂停进入睡眠状态,直到信号发生

信号类型
  1. 与进程终止相关的信号。当进程退出,或者子进程终止时,发出这类信号。
  2. 与进程例外事件相关的信号。如进程越界,或企图写一个只读的内存区域(如程序正文区),或执行一个特权指令及其他各种硬件错误。
  3. 与在系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前系统资源又已经耗尽。
  4. 与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用。
  5. 在用户态下的进程发出的信号。如进程调用系统调用kill向其他进程发送信号。
  6. 与终端交互相关的信号。如用户关闭一个终端,或按下break键等情况。
  7. 跟踪进程执行的信号
POSIX1.1中的信号包括
信号   值  处理动作 发出信号的原因

SIGHUP 1     A  终端挂起或者控制进程终止
SIGINT 2    A  键盘中断(如break键被按下)
SIGQUIT 3     C  键盘的退出键被按下
SIGILL 4     C  非法指令
SIGTRAP 5 C 跟踪/断点捕获
SIGABRT 6     C  由abort(3)发出的退出指令
SIGBUS 7 C 总线错误(错误的内存访问)
SIGFPE 8     C  浮点异常
SIGKILL 9   AEF  Kill信号
SIGSEGV 11     C  无效的内存引用
SIGALRM 14    A  由alarm(2)发出的信号
SIGTERM 15    A  终止信号

动作A: terminate进程
动作B: 忽略
动作C: terminate进程并转储coredump


#include
#include
#include
#include

static int alarm_fired = 0;
void ding(int sig)
{
  alarm_fired = 1;
}
int main()
{       pid_t pid;
    printf("alarm app starting\n");
    pid=fork();
    switch(pid){
    case -1:
        perror("fork failed");
        return 1;
    case 0:
        sleep(5);
        kill(getppid(), SIGALRM);
        return 0;
    }


    printf("waiting for alarm to go off\n");
    (void) signal(SIGALRM, ding);
    pause();
    if (alarm_fired) printf("Ding!\n");

    printf("done\n");
    return 0;
}

#include
int alarm(int seconds);
经过指定的秒数后raise信号SIGALRM

#include
#include
void handler(int sig) {
    printf(“hello\n”);
}
main()
{
   int i;
   signal(SIGALRM,handler);
   alarm(5);

   for(i=1;i<7;i++){
      printf(“sleep %d ...\n”,i);
      sleep(1);
   }
}


阅读(956) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~