简单进程和信号处理

来源:互联网 发布:qq飞车迈凯轮数据 编辑:程序博客网 时间:2024/05/21 22:35

进程的定义:一个其中运行有一个或多个线程的地址空间和线程要求使用的系统资源。简单而言,进程就是一个正在运行中的程序。
进程由程序代码、数据、变量、打开的文件(文件描述符)和一个环境组成。
每一个进程都拥有一个独一无二的PID,称之为进程标示码。这是一个正整数,范围为2-32768。进程号为1的永远是init进程。
UNIX/Lunix系统通过一个进程表的数据结构来管理进程。进程表包括进程的PID,进程的状态、命令字符串等。
除了init进程,其他进程都是有一个“父进程”的进程启动的,被父进程启动的进程叫做子进程。通过函数getppid()可以获得父进程的pid号

****************************************************************************************

由于Linux是一个多用户系统,同一时间由多个进程在运行,从而需要考虑cpu的分配方法。Linux通过时间片的方法来分配cpu。进程通过优先

级来区分,优先级高的进程得到时间片的可能性比较大。Linux通过一个进程调度器scheduler来决定下一个时间片分给哪个进程。
通过ps -l :
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S  5065 19220 19219  0  75   0 -  1151 wait   pts/1    00:00:00 bash
0 S  5065 23255 19220  0  86   0 -   502 -      pts/1    00:00:00 time.exe
0 R  5065 23257 19220  0  76   0 -   849 -      pts/1    00:00:00 ps
在NI这一列可以看到time.exe的优先级:0
可以在运行的时候使用nice命令设置命令的优先级,使用renice命令更改命令的优先级。
需要注意的是,renice命令只能增加命令的优先级,除非你有root权限,才可以降低命令的优先级。
$nice time.exe &
$ps -l
$F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
 0 S  5065 19220 19219  0  75   0 -  1151 wait   pts/1    00:00:00 bash
 0 S  5065 23255 19220  0  86  10 -   502 -      pts/1    00:00:00 time.exe
 0 R  5065 23257 19220  0  76   0 -   849 -      pts/1    00:00:00 ps
通过nice命令,time.exe优先级是10
$renice 20 23255
$ps -l
$F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
 0 S  5065 19220 19219  0  75   0 -  1151 wait   pts/1    00:00:00 bash
 0 S  5065 23255 19220  0  86  19 -   502 -      pts/1    00:00:00 time.exe
 0 R  5065 23257 19220  0  76   0 -   849 -      pts/1    00:00:00 ps
renice命令的格式如下:
 renice priority pid

*********************************************************************************

下面讲述如何启动新的进程
使用system函数可以让一个进程在另一个进程内部运行。
#include <stdlib.h>
int system(const char * cmd)
参数cmd为需要执行的命令。system函数将执行该命令并等待命令的完成。

system需要启动一个新的shell来运行新创建的进程,所以效率不是很高。
可以使用exec系列函数来启动新的进程,exec函数可以把当前进程替换成新的进程。
#include <unistd.h>
char ** environ;
int execl(const char * path,const char * arg0,...(char *)0);
,,,
替换后的进程的pid和原先的一样,优先级基数也不变。

如果像复制当前的进程,调用fork()函数。对于父进程,fork返回子进程的pid,对于子进程,fork返回0。出现错误返回-1。
#include <sys/types.h>
#include <unistd.h>
 pid_t fork();
在调用fork后,子进程可能和父进程结束的时间不一样。可以使用wait安排父进程等待子进程一起结束。
#include <wait.h>
 pid_t wait(int * stat_loc);
参数stat_loc返回子进程的推出状态信息。Linux实现定义了一些宏来解读该状态信息。
 WIFEXITED(stat_val)  如果子进程正常结束,它取非零值
 WEXITSTATUS(stat_val)  如果WIFEXITED非零,他返回子进程的推出码。
waitpid函数可以指定等待某一个子进程结束
 pid_t waitpid(pid_t pid,int * stat_loc,int options)
pid就是准备等待的子进程的PID,如果设为-1,那么waitpid将返回每一个子进程的信息。options常用WNOHANG,用来防止waitpid将调用者挂起


如果子进程尚未结束,返回0。如果已经结束,返回child_pid。操作失败返回-1并设置errno变量。errno包括ECHILD(不存在这个子进

程),EINTR(被信号中断),EINVAL(参数不合法)。

如果子进程在父进程结束之前结束,那么子进程将成为“僵进程”,他所占有的资源得不到释放,仍然停留在系统中。只有父进程结束后或者

父进程调用wait命令后,子进程才会得到释放。如果父进程非正常结束,子进程将自动的将PID为1的进程,也就是init进程作为父进程。

下面关注一下信号signal。
信号是UNIX相应某些状况而产生的事件,类似于windows中的MESSAGE。进程可以生成、捕捉并响应信号或屏蔽信号。
信号都在signal.h中定义,重要的有下面一些:
 SIGALRM  警告钟
 SIGHUP  系统挂断
 SIGINT  终端中断
 SIGQUIT  终端退出
 SIGPIPE  向没有读者的管道写数据
 SIGCHID  子进程已经停止或退出,默认情况下被屏蔽
 SIGCONT  如果被停止则继续执行
在SHELL下可以使用KILL命令向进程发送信号
 KILL <SIGNAL> <PID>
可以使用signal库函数,让程序能够捕捉并处理信号:
 #include <signal.h>
 typedef void (*sighandler_t)(int);
 sighandler_t signal(int signum,sighandler_t handler);
参数sig表示要捕捉的信号,func表示处理信号的函数。
func还可以去下面两个特殊的值:
 SIG_IGN  屏蔽该信号
 SIG_DFL  恢复默认行为
程序还可以调用kill函数向其他进程发送信号。需要注意的,用户必须拥有其他进程的权限。
 #include <sys/types.h>
 #include<signal.h>
 int kill(pid_t pid,int sig);
成功时返回0,失败返回-1,并设置errno变量。权限不足时设为EPERM。指定的进程不存在设为ESRCH。信号无效设为EINVAL。
x/open规范为我们推荐了一个比较健壮的信号设计接口:
 #include <signal.h>
 int sigaction(int sig,const struct sigaction * act,struct sigaction * oact);
sig:捕捉的信号
act:接收到信号后的操作动作,包括以下三个成员:
 void(*) (int) sa_hander //操作函数,SIG_IGN,SIG_DFL
 sigset_t sa_mask //signals to block in sa_handler
 int sa_flags  //signal action modifiers
sa_mask是信号处理函数执行时应该被屏蔽的信号。
sa_flags可设为0。
关于信号集,也就是sigset_t,有以下操作函数,其意义通过名称就可以看出:
 int sigaddset(sigset_t *set,int signo);
 int sigdelset(sigset_t *set,int signo);
 int sigemptyset(sigset_t *set);
 int sigfillset(sigset_t *set);
 int sigismember(sigset_t *set,int signo);//是,返回1,不是,返回0。
信号掩码的设置可以由sigprocmask函数完成,进程将收不到信号集里的信号。
 int sigprocmask(int how,const sigset_t *set,sigset_t * oset);
how的取值可以如下:
 SIG_BLOKC  将set信号添加到信号掩码里 
 SIG_SETMASK  将set信号设置为信号掩码
 SIG_UNBLOCK  从信号掩码里删除set里的信号
如果一个信号被一个进程阻塞,既不会到达这个进程,但是会停留在待处理的状态。程序可以通过下面这个函数察看有哪些信号停留在待处理

状态
 int sigpending(sigset_t *set);
程序可以通过sigsuspend函数将自身挂起,直到有信号到达为止
 int sigsuspend(const sigset_t * sigmask);
参数sigmask用来代替之前设置的信号掩码。

一个简单的例子:

#include <signal.h>
#include <sys/types.h>
#include <stdio.h>

void func(int sig)
{
        printf("%d/n",sig);
}

int main()
{
        sigset_t maskset;

        pid_t id;

        struct sigaction act;

        act.sa_handler=func;
        act.sa_flags=0;
        sigemptyset(&act.sa_mask);

        id=fork();
        switch(id){
        case -1:printf("fork failed./n");exit(1);
        case 0:sleep(3);kill(getppid(),SIGINT);exit(0);
        default:break;
        }

        sigemptyset(&maskset);
        sigaddset(&maskset,SIGQUIT);

        sigsuspend(&maskset);

        sigaction(SIGINT,&act,0);

        exit(0);
}
 

原创粉丝点击