Linux程序设计--进程

来源:互联网 发布:mac是什么意思啊 编辑:程序博客网 时间:2024/06/05 03:12

1. 什么是进程

一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源

2. 进程的结构

传递给程序的字符串以变量的形式出现在每个进程的数据区中。他们之间是分离的。通常不能被其它进程读取。进程通过各自的文件描述符来访问文件。

3. 系统进程

你可以把init进程看成操作系统的进程管理器,它是其它所有进程的祖先进程。其它进程要么被init启动的,要么被init启动的进程所启动的。

4. 进程调度

Linux内核用进程调度器来决定下一个时间片应该分配给哪个进程。它判断的依据是根据进程的优先级。

5. 启动新进程

  1. 我们可以在一个程序的内部启动另一个程序,从而创建一个新进程。这个工作可以通过库函数system来完成。
    #include <stdio.h>int system(const char * string);
    system函数的作用是,执行一个shell命令并等待该命令的完成。类同于在shell执行sh -c string
    返回值:0表示成功,非0则为失败。
    总结:system函数很有用,但它也有局限性,因为程序等待system函数启动的进程退出后才能继续,当时我们不能立刻执行其它任务。
  2. 替换进程映像。
    exec函数可以把当前进程替换为一个新的进程,新进程由path或file参数指定。你可以使用exec程序将程序的执行从一个程序切换到另一个程序。

     #include <unistd.h> int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...);

    两个函数都能实现由一个程序切换到另一个程序的作用。区别为execlp函数通过搜索PATH环境变量来查找新程序的可执行文件的路径。如果不在PATH定义的路径中,我们就需要把可执行文件的绝对路径的位置作为参数传递给函数。execl不具备搜索PATH环境变量的作用,需要填写可执行文件的绝对路径。

    当exec函数出现错误时,exec函数将返回-1,并且会设置错误变量errno。另外,通过perror来打印错误的信息。

  3. 复制进程映像
    要想要进程同时执行多个函数,我们可以使用线程或从原程序中创建一个完全分离的进程。使用fork函数

     #include <sys/types.h> #include <unistd.h> pid_t fork(void);

    fork函数的作用在于创建子进程。根据fork函数的返回值,返回值是0表明是子进程。大于0是父进程。失败为-1.
    自fork函数调用之后,程序中就存在两个进程交互运行。即,fork后的语句会被父、子进程分别执行一次。
    等待一个进程
    wait系统调用将暂停父进程直到它的子进程结束为止。这个调用返回子进程的PID,它通常是已经结束运行的子进程的PID。

     #include<sys/wait.h> pid_t wait(int *stat_loc);

    父进程用wait系统调用将自己的进程挂起,直到子进程的状态信息出现为止。这将发生在子进程调用exit的时候。父进程然后继续运行,通过测试wait调用的返回值来判断子进程是否正常终止。

  4. 僵尸进程
    子进程终止时,它与父进程之间的关联还会保持,直到父进程也正常终止或父进程调用wait函数。因此,进程表中代表子进程的表项不会被立刻释放。虽然子进程已经不再运行,但它仍然存在于系统中,因为它的退出码还需要保存起来,以备父进程今后的wait调用使用。这是它将使一个死(defunct)进程或僵尸(zombie)进程。
    僵尸进程将一直保留在进程表中直到被init进程发现并释放。进程表越大,这一过程就越慢。
    除了wait函数外,还有一个waitpid函数。可以用来等待某个特定进程的结束.函数原型pid_t waitpid(pid_t pid, int *status, int options);

    • pid参数指定需要等待子进程的PID,如果它的值为-1,waitpid将返回任一子进程的信息。
    • 与wait一样,如果stat_loc不是空指针,waitpd将把状态信息写到它指向的位置。
    • option参数可用来改变waitpid的行为,其中最有用的一个选项来查找是否有子进程已经结束,如果没有,程序将继续执行。其它的选项和wait调用选项相同。
    • 因此,如果想让父进程周期性地检查某个特定的子进程是否已终止,就可以使用如下的调用方式:
      waitpid(child_pid, (int*)0,WNOHANG);如果子进程没有结束或意外终止,它就返回child_pid。如果waitpid失败,就返回-1并设置errno
  5. 信号
    信号是 Unix 和 Linux 系统响应某些条件而产生的一个事件。接收到该信号的进程会相应地采取一些行动。它们还可以作为在进程间传递信息或修改行为的一种方式,明确地由一个进程发送给另一个进程。
    信号可以被生成、捕获、响应或忽略(只对部分信号)。
    如果想发送一个信号给进程,而该进程并不是当前的前台进程,就需要用到 kill 命令。该命令需要有一个可选的信号代码或信号名称和一个接收信号的目标进程的 PID。
    进程可以通过调用 kill 库函数向包括它本身在内的其它进程发送一个信号。定义如下:

    #include <sys/types.h>#include <signal.h>int kill(pid_t pid,int arg);

    kill 函数把参数 sig 给定的信号发送给给有参数指定 PID 所给出的进程号。成功是返回 0,失败时返回 -1,并设置 errno 变量。

  6. 发送信号
    一个健壮的信号接口:sigaction
    函数原型:int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
    sigaction 结构定义在 signal.h 中,它的作用是定义在接受到参数 sig 指定的信号后应该采取的行动。该结构至少有以下成员:

    void (*) (int) sa_handler;    /* function, SIG_DFL or SIG_IGN */sigset_t sa_mask    /* signal to block in sa_handler */int sa_flags     /* signal action modifiers */

    sigaction 函数设置与信号 sig 关联的动作。成功返回 0,失败时返回 -1.如果给出的信号无效或试图对一个不允许扑获或忽略的信号进行扑获或忽略,错误变量 errno 将被设置为 EINVAL。
    在参数act指向 sigaction 结构中, sa_handler 是一个函数指针,它执行接受到信号 sig 后被调用的信号处理函数。
    sa_mask 成员指定一个信号集,在调用 sa_handler 所执行的信号处理函数之前,该信号集将被加入到进程的信号屏蔽字中。这是一组将被阻塞且不会传递给该进程的信号。设置信号屏蔽字可以防止信号在它的信号处理函数还未结束时就被接收到的情况。
    若想在二次响应信号时进行默认操作。则必须在 sa_flags 成员中包含值 SA_RESETHAND。不用的话,0 伺候。

  7. 信号集
    头文件 signal.h 定义了类型 sigset_t 和用来处理信号集的函数。sigaction 和其它函数将用这些信号集来修改进程在接受到信号时的行为。

     #include <signal.h> int sigaddset(sigset_t * set,int signo); int sigemptyset(sigset_t* set); int sigfillset(sigset_t * set); int sigdelset(sigset_t * set);

    这些函数如同它的名字一些,分别用于将信号集初始化为空,将信号集初始化为包含所有以定义的信号。最后两个,分别从信号集中添加或删除给定的信号码(sinno)。成功返回 0,失败返回 -1,并设 errno 的值。
    sigismember(sigset_t* set,int signo);用于进程一个给定的信号是否为信号集的成员。
    还有更多的函数,即不在一一说明。
    - 常用信号参考

     SIGALRM        由 alarm 函数设置的定时器产生 SIGINT        中断 SIGKILL        因为这个信号不能被捕捉或忽略,所以一般在shell中用它来强行终止异常进程。 SIGPIPI        如果在向管道中写数据时没有与之对应的读进程,就会产生这个信号
0 0