开始一个新进程
     你可以启动一个程序从另一个程序的内部,所以创建一个进程可以使用system库函数。
     #include <stdlib.h>
     int system (const char *string);
     这个system方法启动一个命令并将string传递给它,然后等待它结束。这个命令的执行有点像 “$ sh -c string”被传递给shell。如果shell不能执行这个命令system函数返回127,如果错误则返回 -1,否则system函数返回命令结束代码。
     example:    #include <stdlib.h>
                         #include <stdio.h>
                         int main()
                        {
                           printf(“Running ps with system/n”);
                           system(“ps ax”);
                           printf(“Done./n”);
                           exit(0);
                       }

Replacing a Process Image

这里有一个以exec开头的相关的方法组。它们的不同之处就是他们开始的进程和提供给程序的参数。一个exec方法会代替当前的进程然后产生一个新的指定进程。你可以使用exec方法去传递一个程序的执行到另一个。exec的方法组比system有效的多,因为新程序开始后原程序不再运行了。

#include <unistd.h>

char **environ;

int execl(const char *path, const char *arg0, ...,  (char *)0);

int execlp(const char *file, const char *arg0, ...,  (char *)0);

int execle(const char *path, const char *arg0, ...,  (char *)0, char *const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

这些方法归类为两种,execl,execlp和execle有一些可变数量的参数,以一个null指针结束。execv和execvp以一个字符串作为第二个参数。这两种情况下,新的程序开始时将出现在argv数组中的参数传递给main。这些方法中通常会使用execve。
这些以“p”结尾的方法,他们将查询PATH环境变量去查找新的程序执行文件。如果在这个路径中找不到可执行程序,那么就需要传递文件的绝对路径和描述符给方法。这个全局变量environ可以将值给新程序环境。 在方法execle和execve中的另外一个参数可以传递一个字符串数组给新程序作为环境变量。如果你想要开始ps程序,你可以从这六个exec方法组中选择一个,如下:
       /* Example of an argument list */
       /* Note that we need a program name for argv[0] */
       char *const ps_argv[] = {“ps”, “ax”, 0};
       /* Example environment, not terribly useful */
       char *const ps_envp[] = {“PATH=/bin:/usr/bin”, “TERM=console”, 0};
       /* Possible calls to exec functions */
       execl(“/bin/ps”, “ps”, “ax”, 0);            /* assumes ps is in /bin */
       execlp(“ps”, “ps”, “ax”, 0);                /* assumes /bin is in PATH */
       execle(“/bin/ps”, “ps”, “ax”, 0, ps_envp);  /* passes own environment */
       execv(“/bin/ps”, ps_argv);
       execvp(“ps”, ps_argv);
       execve(“/bin/ps”, ps_argv, ps_envp);

复制一个进程
     你可以通过使用fork来创建一个新线程,这个方法复制当前的进程,在进程表中创建一个新实体并且拥有和当前进程一样的属性。这个进程等价于原进程,执行同样的代码但是有自己的数据空间和环境和文件描述符。

#include <sys/types.h>
      #include <unistd.h>
      pid_t fork(void);
     fork返回一个新的子进程的PID给父进程,也就是调用这个的函数的进程。新进程在原进程的基础上继续执行,但有一个例外,这个子进程调用fork返回的是0。如果fork失败了,就返回-1,这个原因可恩嗯是由于父进程所拥有子进程的个数限制,或者空间内存问题。
      一个关于fork的典型应用:
         pid_t new_pid;
         new_pid = fork();
         switch(new_pid) {
         case -1 :       /* Error */
             break;
         case 0 :        /* We are child */
             break;
         default :       /* We are parent */
             break;
         }
等待进程

#include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *stat_loc);
    这个wait函数引起一个父进程暂停直到其中的一个子进程停止了。这个函数返回子进程的PID,这个子进程可能已经终止了。status信息允许父进程去决定子进程的退出status,这个值从main返回或者之前传递的。如果stat_loc不是一个空指针,这个status信息将会被写到指向的地方。
     你可以进一步了解这些状态信息在sys/wait.h中的宏,如下表:
                WIFEXITED(stat_val)           Nonzero if the child is terminated normally.
                WEXITSTATUS(stat_val)      If WIFEXITED is nonzero, this returns child exit code.
                WIFSIGNALED(stat_val)     Nonzero if the child is terminated on an uncaught signal.
                WTERMSIG(stat_val)      If WIFSIGNALED is nonzero, this returns a signal number.
                WIFSTOPPED(stat_val)      Nonzero if the child has stopped.
                WSTOPSIG(stat_val)      If WIFSTOPPED is nonzero, this returns a signal number.
    例子:    
       child_pid = wait(&stat_val);
       printf(“Child has finished: PID = %d/n”, child_pid);
       if(WIFEXITED(stat_val))
          printf(“Child exited with code %d/n”, WEXITSTATUS(stat_val));
       else
         printf(“Child terminated abnormally/n”);

僵尸进程

使用fork去创建进程是非常有用的,但是你必须和子进程保持联系。一个子进程结束的条件是与之相关的父进程要么也终止了那么就调用了wait。这个子进程加入到进程表所以它不会立刻就被释放。即使它不再活动了,这个子进程仍然存在系统中,因为它的exit代码需要被存储,以为它的父进程调用wait。那么这个子进程就会编程一个僵尸进程。

一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit, 它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它 已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外, 僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进 程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。 但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。(来自于百度百科)

在Linux系统中还有另一个方法我们可以调用去等待子进程,叫做waitpid,你可以使用它来等待一个指定的进程终止。

#include <sys/types.h>
             #include <sys/wait.h>
             pid_t waitpid(pid_t pid, int *stat_loc, int options);

pid参数指向子进程需要等待的那个进程的PID,如果返回-1,waitpid将返回任意子进程的信息。像wait,它会将状态校信息写入到stat_loc中。options参数允许你修改waitpid的行为,最有用的参数就是WNOHANG,它阻止调用进程被挂载起来(如果没有任何已经结束的子进程则马上返回, 不予以等待)。其他参数和wait一样。所以你可以用附近成去检查一个指定的子进程是否结束了,像

waitpid(child_pid, (int *) 0, WNOHANG);    返回0,则说明这个子进程没有终止或结束,返回-1时繁盛错误或设置错误。

信号

信号是一个事件由Linux和UNIX系统应对一些状态产生,由一个进程获取并做出一些动作。我们使用raise去表明产生一个信号,用catch表示接收到一个信号。  信号名字定义在signal.h中,他们以SIG开始,表括在如下表中:

SIGABORT    *Process abort
             SIGALRM          Alarm clock
             SIGFPE         *Floating-point exception
             SIGHUP           Hangup
             SIGILL           *Illegal instruction
             SIGINT           Terminal interrupt
             SIGKILL        Kill (can’t be caught or ignored)
             SIGPIPE         Write on a pipe with no reader
             SIGQUIT        Terminal quit
             SIGSEGV        *Invalid memory segment access
             SIGTERM       Termination
             SIGUSR1       User-defined signal 1
             SIGUSR2       User-defined signal 2
         如果一个进程接收到以上的一个信号并没有去第一时间的catch它,那么这个进程会立刻结束掉。
        一些额外的信号:

SIGCHLD        Child process has stopped or exited.
              SIGCONT          Continue executing, if stopped.
              SIGSTOP            Stop executing. (Can’t be caught or ignored.)
              SIGTSTP            Terminal stop signal.
              SIGTTIN        Background process trying to read.
              SIGTTOU   Background process trying to write.

SIGCHLD可以被用来管理子进程,默认它是忽略的。剩余的信号都会导致进程接收到便停止,除了SIGCONT,SIGCONT造成进程恢复。

程序可以控制信号通过使用库函数。
              #include <signal.h>
              void (*signal(int sig, void (*func)(int)))(int);

需要获取或忽略的信号被做为sig参数,这个func方法发生是在指定的信号被接收到的情况下,这个方法必须有一个单一的信号int参数。signal方法返回一个同类型的方法,由前面提到的控制信号的方法值,或以下两个:

SIG_IGN     Ignore the signal.
             SIG_DFL   Restore default behavior.

例如

#include <signal.h>
               #include <stdio.h>
              #include <unistd.h>
              void ouch(int sig)
              {
                  printf(“OUCH! - I got signal %d/n”, sig);
                  (void) signal(SIGINT, SIG_DFL);
               }

int main()
              {
                    (void) signal(SIGINT, ouch);
                     while(1) {
                          printf(“Hello World!/n”);
                         sleep(1);
                    }
              }

发送信号

一个进程可以发送一个信号给另一个进程包括它自己,通过使用kill函数。这个函数可能失败如果这个程序没有发送这个信号的权限。

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

kill函数发送指定的信号sig给指定pid的进程。返回0则说明成功。发送信号的进程必须有足够的权限,通常这是意味着两个进程必须有一个用户ID。kill函数失败则返回-1,然后设置errno。

Signals提供一个有用的alarm时钟设备。这个alarm方法可以被进程使用并安排一个SIGALRM信号在一段时间后。

#include <unistd.h>
               unsigned int alarm(unsigned int seconds);

这个alarm函数安排SIGALRM信号在几秒后传递。0值会取消掉所有突出的alarm请求。在这个信号接收到之前就取消这个alram的话,这个alarm会被从新安排。每一个进程只可以有一个alarm。alarm函数返回这个alarm将被发送的剩余的秒数,-1则表示失败。

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

static int alarm_fired = 0;
             void ding(int sig)
             {
                alarm_fired = 1;
             }
            int main()
            {
                pid_t pid;
                 printf(“alarm application starting/n”);
                 pid = fork();
                switch(pid) {
                case -1:
                 /* Failure */
                perror(“fork failed”);
                exit(1);
               case 0:
                /* child */
                sleep(5);
                kill(getppid(), SIGALRM);
                exit(0);
                }
             /* if we get here we are the parent process */
           printf(“waiting for alarm to go off/n”);
           (void) signal(SIGALRM, ding);
           pause();      // pause, which simply causes the program to suspend execution until a signal occurs
           if (alarm_fired)
           printf(“Ding!/n”);
           printf(“done/n”);
           exit(0);
        }

一个全面的信号接口

#include <signal.h>
          int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);

这个sigaction结构体用于定义接收到指定信号sig后做出的动作,他定义在signal.h中丙炔至少含有以下的成员:

void (*) (int) sa_handler    /*  function, SIG_DFL or SIG_IGN

sigset_t sa_mask             /*  signals to block in sa_handler

int sa_flags                 /*  signal action modifiers

这个sigaction方法设置与信号sig相关的行为。如果oact不为空,sigaction函数则将前面的action写入到指定的地方去。如果act是null,代表着所有的信号都会处理,如果act不为空,那么就处理指定的信号。0,成功,-1,失败。

在结构体sigaction类型的参数act中,sa_handler 指向一个方法当接收到信号的时候调用。和signal函数的方法func参数类似。你可以使用指定的值SIG_IGN和SIG_DFL给sa_handler 表示忽略或者被默认的存储。sa_mask参数指定一个一个信号集,在sa_handler被调用之前加入得到该进程的信号标识中。这些信号会被中断也不用被传递到进程中。它可以阻止它的handler在结束之前结构其他信号。

#include <signal.h>

#include <stdio.h>

#include <unistd.h>

void ouch(int sig)

{

printf(“OUCH! - I got signal %d/n”, sig);

}

int main()

{

struct sigaction act;

act.sa_handler = ouch;

sigemptyset(&act.sa_mask);

act.sa_flags = 0;

sigaction(SIGINT, &act, 0);

while(1) {

printf(“Hello World!/n”);

sleep(1);

}

}

信号集

头文件signal.h定义了sigset_t类型和一些用于管理信号集的函数。这些集合被用在sigaction和其他函数中用来改变进程接收到信号后的行为。

#include <signal.h>

int sigaddset(sigset_t *set, int signo);   //向set中增加一个信号

int sigemptyset(sigset_t *set);              //初始化一个信号集为空

int sigfillset(sigset_t *set);                    //初始化一个信号集去包含所有定义的信号。

int sigdelset(sigset_t *set, int signo);   //从set中删除信号signo

int sigismember(sigset_t *set, int signo);   //查看信号signo是否在set中,返回1表示是,0表示不是,-1则信号不能错,错误。

这些方法返回0表示成功,-1则表示错误。如果指定的信号不能用,唯一的错误是EINVAL。

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

sigprocmask根据how参数有多种方式可以改变进程信号标识。如果set不为空,新的信号标识会被传递到,以前的信号标识会被写入到oset中。,how可以取的值如下:

SIG_BLOCK           The signals in set are added to the signal mask.

SIG_SETMASK       The signal mask is set from set.

SIG_UNBLOCK      The signals in set are removed from the signal mask.

int sigpending(sigset_t *set);     //函数返回在送往进程的时候被阻塞挂起的信号集合

int sigsuspend(const sigset_t *sigmask);  //sigsuspend 函数将进程的信号屏蔽字设置为 sigmask 指向的值。在捕捉到一个信号或发生了一个会终止该进程的信号之前,该进程被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返 回,在返回之前,将进程的信号屏蔽字设置为调用sigsuspend之前的值。

sigaction Flags  //P490

Beginning Linux Programming chapter 11相关推荐

  1. beginning linux programming pdf,Beginning Linux Programming, Second Edition

    摘要: If you've already got Linux up and running on your machine and you really want to exploit its ca ...

  2. 《Beginning Linux Programming》读书笔记(四)

    1, 读写空指针 先看第一种情况,printf试图访问空指针,以打印出字符串,而sprintf试图向空指针写入字符串,这时,linux会在GNU C函数库的帮助下,允许读空指针,但不允许写空指针. 复 ...

  3. 《Beginning Linux Programming》读书笔记(二)

    第2章Shell编程没什么多说的,记录个代码,本章最后的那个简单的CD管理程序 复制代码 #!/bin/sh # Very simple example shell script for managi ...

  4. Part 2 Linux programming:chapter 18:多线程服务器端实现

    内容概要: Web服务器端需要同时向多个用户提供服务,而进程的局限性使得人们开始利用更高效的线程来实现Web服务器端. 18.1 线程的概念 本章主要介绍线程的通用说明,是windows线程的基础. ...

  5. Part 2 Linux programming:chapter 15:套接字和标准I/O

    第十五章:套接字和标准I/O 15.1 标准I/O函数 这里需要熟练掌握一些文件操作时使用的函数(fopen.feof.fgetc.fputs等) 啥是标准I/O函数呢? 下面列出一些常用的 fope ...

  6. 【读书笔记0103】Beginning linux programming-shell programming

    学习体会:本章内容比较多,我这里按照自己的理解重新组织了一下,希望对读过这本书的同学有帮助: shell programming 是linux学习的必经之路,也是必须学好的,经过一段个人体会是: sh ...

  7. CHAPTER 11 Syntactic Parsing

    CHAPTER 11 Syntactic Parsing Speech and Language Processing ed3 读书笔记 Syntactic parsing is the task o ...

  8. Mastering Embedded Linux Programming 学习 (五)在百问网157开发板上,解决网络配置问题

    Mastering Embedded Linux Programming 学习 (五)在百问网157开发板上,解决网络配置问题 思考.参考 搜索发现,需要配置设备树,参考这个链接 修改设备树 找到百问 ...

  9. Mastering Embedded Linux Programming 学习 (一)嵌入式交叉编译工具链的构建,基于crosstool-NG

    Mastering Embedded Linux Programming 学习 (一) 一.ubuntu 软件包下载 sudo apt install autoconf automake bison ...

最新文章

  1. final, finally, finalize 的区别
  2. 吴裕雄 python 神经网络——TensorFlow实现AlexNet模型处理手写数字识别MNIST数据集...
  3. [渝粤教育] 辽宁对外经贸学院 国际集装箱多式联运 参考 资料
  4. oracle sql 执行计划分析_ORACLE数据库查看执行计划
  5. JavaWeb之得到web应用中的资源文件
  6. Atitit 函数式编程与命令行语言的区别与优缺点 目录 1. 常见的函数式语言 2 1.1. 命令行 bat 2 1.2. Sql h5 css 正则表达式 2 1.3. 工作流语言 anno注
  7. 计算机408真题_2019年计算机统考408真题第8题及其解析
  8. 大数(小于10000)N的阶乘准确值(效率)
  9. FFMPEG视频编码 NVIDIA 和 INTEL 硬件加速 x265 8bit 和 10bit
  10. java实现图片文件上传下载_java实现文件的上传和下载
  11. 微信小程序OCR插件使用指南
  12. 跟小静读CLR via C#(06)- 构造器
  13. Matlab绘制二维圆环和三维圆环
  14. 升级Mountain Lion系统后因为 “来自身份不明开发者” 不能打开某些软件的解决方法
  15. Apache与Tomcat关系和区别
  16. iOS YYText的使用笔记一(YYTextView图文编辑器)
  17. 搜狗输入法在idea打不了汉字_好烦啊,IDEA输入中文时输入法候选词框不跟随光标...
  18. 官网下载MySQL数据库
  19. FaceBook 快速获取密匙散列
  20. 华中师范大学新生研讨课有感

热门文章

  1. Linux:打印一个国际象棋的棋盘
  2. 20189312任方园《网络攻防》第十次作业
  3. FastReport VCL Enterprise v6.7.9 (09 Jul 2020) for D7-D10.4 Sydney with Source Code
  4. 【自然语言处理】【文本生成】CRINEG Loss:学习什么语言不建模
  5. wwhhuu (思维
  6. Nature:中科院先进院发现空间扩展生境定殖的进化稳定性策略
  7. 龙芯1B:点亮第一个led灯
  8. Qt之实现录音播放及raw(pcm)转wav格式
  9. 翻译连载 |《你不知道的JS》姊妹篇 |《JavaScript 轻量级函数式编程》- 第 1 章:为什么使用函数式编程?...
  10. 2015 年第七届全国大学生数学竞赛江西赛区获奖名单(数学专业)