主题:进程控制编程

2019-07-13 08:16发布

参考书本《高质量嵌入式linux C编程》
一、为何需要多线程,为何需要并发
    首先解释什么是线程:通常在硬盘上的一个可执行文件(例如在windows上的是.exe文件,在Linux上是只要有可执行权限x) 称之为程序。当运行一个程序之后,程序就会加载到内存中,在运行的过程中就称之为线程。因为可以发现,一个程序可以创造多个线程。但为何需要多线程呢?为何需要并发呢?作为一枚单片机爱好者,在写裸机程序的时候,只有一个main函数,此时同一时间内只能执行一个程序。就比如说,一个快餐店只有一个服务员,他既要负责做餐,还要负责卖,还要负责收钱,他是不是很累。如果有了鸣人(火影忍者人物)的分身术,他可创造三个分身,一个专门做餐,一个专门收钱,最后一个专门负责卖餐。这就是多线程的应用。
  1.1 进程
对于单cpu的计算机来说,每个时刻只能执行一条指令代码。而linux是多任务系统,那么linux是如何实现多进程同时执行呢?其实在linux中使用了进程调度算法,首先,为每个进程分配一定的运行时间,这个时间往往很短,然后根据算法,从众多进程中挑选出一个进程进行运行,其他进程都处于停滞状态,当运行时间耗尽之后或者进程执行完毕之后,Linux会重新进行算法调度,挑选下一个进程进行运行。其中,每个进程可以运行时间很短,一般都是毫秒级。因此,从使用者来说,感觉就像多个线程同时运行。需要注意的是,Linux系统中会为每个进程很配进程控制块(PCB),PCB中包含了很多重要的信息供系统和进程本身使用,最主要的有进程ID,也称之为进程标识符,是一个非负整数,它与每一个进程一一对应。
  1.2 进程分类
进程一般分为交互进程,批处理进程,和守护进程
  1.3父子进程
父进程和子进程的关系是管理与被管理,当父进程结束,子进程也随之结束。而子进程结束,父进程任然可以存在。子进程如果结束了,而父进程却没有为其收尸,子进程就会变成僵尸进程,其不占用任何内存空间,仅仅会在进程列表中占有一个位置。太多僵尸进程会造成系统奔溃。即使当父进程在结束之前没有为子进程收尸,系统init进程会自动接收子进程为其收尸,但是父进程一直处于死循环没有结束,子进程的产生的僵尸进程就会一直存在。
如何避免僵尸进程:父进程通过wait和waitpid等函数等待子进程结束,但这会导致父进程挂起。如果父进程很忙,那么可以调用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程就会收到信号,可以在hander中调用wait回收。如果父进程不关心子进程什么时候结束,可以使用singal(SIGCHLD,SIG_IGN),忽略,然后子进程结束后,由系统内核回收。 头文件 #include
#include 定义 pid_t wait(int *status) 函数说明 父进程获取子进程的状态 返回值 返回子进程ID,错误-1
二、 Linux进程管理
   2.1 各种进程命令
        查看进程列表,静态
        ps -aux或者ps -lax         查询进程
        pgrep 参数 程序名 
         -l 列出  程序名和进程ID
        -o 进程起始ID
        -n 进程终止ID
               终止进程
          kill 【信号代码】 进程ID 
           一般来说信号代码 -9,表示强行终止
         killall 通过程序名直接杀死进程       动态监视进程
         top 三、进程创建
   3.1 获取进程
     头文件 #include
            #include 定义 pid_t getpid(void) 函数说明 获取正在执行进程的ID 返回值 返回进程ID 头文件 #include
            #include 定义 pid_t getppid(void) 函数说明 获取父进程的ID 返回值 返回父进程ID    3.2 创建进程
      头文件 #include #include  定义 pid_t fork(void)  函数说明 创建新进程,子进程只是复制父进程的资源,子进程有其自己的task_struct和PID,而且子进程和父进程所停留的代码位置是一样的。父子进程之间不会共享数据空间,堆栈。在子进程中修改变量的数据不会影响父进程。 返回值 在父进程中返回子进程ID,在子进程返回0,错误-1 头文件              #include #include 定义 pid_t vfork(void) 函数说明 创建新进程,子进程共享父进程数据空间,堆栈。在子进程中修改变量的数据会影响父进程。 返回值 在父进程中返回子进程ID,在子进程返回0,错误-1 代码测试: */*********************************************************************** > File Name: my_fork.c > Author:silence > Mail: 1774623251@qq.com > Created Time: 2018年12月19日 星期三 12时38分45秒 ************************************************************************/ #include #include #include #include #include //目的:觀察子進場是否共享父進場資源 int main(void) { pid_t pid; int tmp = 10; pid = fork(); if(pid < 0) { printf("create progress failure "); exit(1); } if(pid == 0) { //child progress printf("-----I am child progress----- "); tmp = 20; printf("tmp = %d ",tmp); } else { //father progress sleep(1); printf("-----I am father progress----- "); tmp = 30; printf("tmp = %d ",tmp); } return 0; } 运行结果: -----I am child progress----- tmp = 20 -----I am father progress----- tmp = 10 结论:fork创建的子进程不共享父进程资源     /************************************************************************* > File Name: my_vfork.c > Author:silence > Mail: 1774623251@qq.com > Created Time: 2018年12月19日 星期三 12时50分45秒 ************************************************************************/ #include #include #include #include #include //目的:vfork()創察子進場是否共享父進場資源 int main(void) { pid_t pid; int tmp = 10; pid = vfork(); if(pid < 0) { printf("create progress failure "); exit(1); } if(pid == 0) { //child progress printf("-----I am child progress----- "); tmp = 20; printf("tmp = %d ",tmp); exit(0); } else { //father progress sleep(1); printf("-----I am father progress----- "); printf("tmp = %d ",tmp); } return 0; } 运行结果: -----I am child progress----- tmp = 20 -----I am father progress----- tmp = 20 my_vfork: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed. Aborted (core dumped) 结论:vfork创建的子进程共享父进程资源  运行程序出现问题:my_vfork: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed.
Aborted (core dumped)
vfork():同上返回两个值,区别在于vfork()子进程共享父进程的地址空间,即子进程运行在父进程的地址空间上,子进程对数据的修改父进程同样能看到。特别注意,使用vfork() 时子进程中需调用exec 或exit 父进程才可能被调度运行,如果在调用exec 或 exit之前子进程要依赖父进程的的某个行为,则会造成死锁。
a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != ((void *)0)' failed.
Aborted (core dumped)
这种错误的一个原因是由于子进程中没有调用exit 或 exec
在子进程中加入
exit(0);就可以运行正确。 3.3 exec族函数
作用:用来替换调用进程执行的程序。
在上面的例子中,在创建的子程序中任然和父进程有相同的代码空间,而实际中子程序都需要执行另外一个程序,
这种情况下,就可以使用exec族函数完全代替子进程的程序作为新程序。
#include
exterm vooid **environ;
int execl(const char *path,cosnt char *arg,...);
int execlp(const char *file,cosnt char *arg,...);
int execle(const char *path,cosnt char *arg,...,char *const envp[]);
int execv(const char *path,char *cosnt argv[]);
int execvp(const char *file,char *cosnt argv[]);
int execvpe(const char *file,char *cosnt argv[],char *const envp[]); 后缀P:表示使用文件file做参数,如果file中包含"/"视作路径,否则在PATH环境变量所指的各个目录搜索文件。
后缀L:表示使用list形式传递参数,传入新程序的所有参数都是可变的,但是最后是以NULL结尾。
后缀V:传入的参数放入数组中 /************************************************************************* > File Name: my_execl.c > Author: > Mail: > Created Time: 2018年12月19日 星期三 14时43分17秒 ************************************************************************/ //在本文件中创建子进程,然后调用new_progress 可执行文件 #include #include #include #include int main(void) { pid_t pid; pid = fork(); if(pid < 0) { printf("Create progress failure "); exit(1); } if(pid == 0) { //child progress printf("----I am child---- "); execl("./new_progress","new_progress",NULL); exit(2); } else { //father progress printf("----I am father progress---- "); } return 0; } /********************************************************************** > File Name: new_progress.c > Author: > Mail: > Created Time: 2018年12月19日 星期三 14时56分00秒 *********************************************************************/ #include int main(void) { printf("---- new_progress is runing ---- "); return 0; } 运行结果: ----I am father progress---- ----I am child---- ---- new_progress is runing ----