1什么是进程:进程是一个执行中的程序

执行的程序: 代码->资源->CPU

进程有很多数据维护:进程状态/进程属性


所有进程属性采用的一个树形结构体维护

ps  -a//所有进程


ps -aue //有效进程

进程状态:(man ps)

       D    Uninterruptible sleep (usually IO)

       R    Running or runnable (on run queue)

       S    Interruptible sleep (waiting for an event to complete)

       T    Stopped, either by a job control signal or because it is being traced.

       W    paging (not valid since the 2.6.xx kernel)

       X    dead (should never be seen)

       Z    Defunct ("zombie") process, terminated but not reaped by its parent.


     For BSD formats and when the stat keyword is used, additional characters may be

       displayed:

       <    high-priority (not nice to other users)

       N    low-priority (nice to other users)

       L    has pages locked into memory (for real-time and custom IO)

       s    is a session leader 

       l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) 多进程

       +    is in the foreground process group 前台进程

top
实时查看进程


pstree 查看进程树


kill 向进程发送信号  
kill -s 信号 进程ID


kill -l  //查看所有信号


kill -s 9 224  //关闭224进程

2 创建进程

1  进程有关的创建函数

1.1 system

int system(const char*filename); 


建立一个独立的进程,拥有独立的代码空间


等待新的进程执行完毕,system才返回(阻塞)

使用system调用函数(观察进程ID 观察阻塞)


新进程的返回值和system返回值有关系


1任何进程的返回值不要超过255


2 system中进程的返回值存放在system返回值的8-15位


可以通过printf("%d\n",r>>8 ); 这样来获得返回值


3 linux中有专用的获得返回状态的宏


      WEXITSTATUS(status) //#include<wait.h>  #include <sys/wait.h>

sys.c

  1. #include <stdio.h>
  2. #include <unistd.h>
  3.  
  4. int main()
  5. {
  6. printf("%d\n",getpid());
  7. sleep(2);
  8. return 254;
  9. }
  10.  
  11. sys2.c
  12. #include <stdio.h>
  13. #include <unistd.h>
  14. #include <wait.h>
  15. #include <sys/types.h>
  16. int main()
  17. {
  18. int r;
  19. printf("%d\n",getpid());
  20. r = system("./sleep");
  21. //printf("%d\n",r>>8 );
  22. printf("%d\n",WEXITSTATUS(r));
  23.  
  24. }

gcc sys.c -o sleep


gcc sys2.c 


./a.out

1.2 popen:创建子进程

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

案例:使用popen调用ls -l 并且建立一个管道读取输出

  1. #include <stdio.h>
  2. #include <unistd.h>
  3.  
  4. int main()
  5. {
  6. FILE *fp = popen("cat file","r"); //注意这里是读取输出,不是打开文件 不能直接写文件名
  7. //FILE *fp = popen("ls -l","r");
  8. if(!fp)
  9. {
  10. perror("popen");
  11. return 1;
  12. }
  13.  
  14. int fd = fileno(fp);
  15. int r;
  16. char buf[1024] = {0};
  17. while((r=read(fd,buf,1023)) > 0)
  18. {
  19. buf[r] = 0;
  20. printf("%s",buf);
  21. }
  22. pclose(fp);
  23.  
  24. }

1.3 exec系列函数

int execl(const char *path, const char *arg, ...);

       int execlp(const char *file, const char *arg, ...);

       int execle(const char *path, const char *arg,

                  ..., char * const envp[]);

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

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

作用: 替换当前代码空间中的数据


函数本身不创建新的进程


第一个参数:替换的程序 


第二个参数:
命令行


命令行格式:命令名 选项参数 NULL


命令行结尾必须是空字符串

execl execlp的区别:


execl   只在当前路径搜索


execlp 可以使用系统搜索路径(which能找到)


如果都找不到,可以使用绝对路径

命令锁指向的程序  命令本身 参数列表


如果返回-1  失败

  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5. int r = execl("/bin/ls","ls","-l",0); // 只能调用当前路径
  6. // int r = execlp("ls","ls","-l",0);// 能调用系统路径
  7. printf("调用结束%d\n",r);
  8. return 0;
  9. }

1.4 fork

pid_t fork()

1 创建进程


2 新进程的代码是什么:克隆父进程的挨骂


而且克隆来执行的位置


3 在子进程中不调用fork 所有返回值=0


4 父子进程同时执行

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main()
  4. {
  5. printf("创建进程前\n");
  6. int pid = fork(); //父进程执行的
  7. while(1)
  8. {
  9. if(pid == 0)
  10. {
  11. printf("子进程 %d\n",getpid());
  12. }
  13. else
  14. {
  15. printf("父进程 %d\n",getpid());
  16. }
  17. }
  18. }

3 应用进程

使用fork实现多任务(unix本身是不支持线程的)


1 进程


2 线程


3 信号


4 异步


5 进程池与线程池

4 理解进程

1父子进程的关系


独立的两个进程


互为父子关系


使用pstree看到

     ├─gnome-terminal─┬─bash───a.out───a.out //父子关系

     │                ├─bash───pstree

     │                ├─gnome-pty-helpe

     │                └─2*[{gnome-terminal}]

2 问题:

1如果父进程先结束 子进程在么办


子进程将变成孤儿进程,直接依托根进程(init)

孤儿 进程是没有危害的

init─┬─NetworkManager─┬─dhclient


│                └─{NetworkManager}


├─a.out

2 如果子进程先结束 父进程怎么办


子进程先结束,子进程会成为僵尸进程。


僵尸进程的特点: 不占用内存 cpu,但在进程任务管理树上占用一个节点(宝贵)


       实际上僵尸进程会造成进程名额的资源浪费。一定要处理僵尸进程

   ├─gnome-terminal─┬─bash───pstree


  │                ├─bash───a.out───a.out

3 僵尸进程使用wait回收(阻塞函数)


#include <sys/types.h>


#include <sys/wait.h>


pid_t wait(int *status);


pid_t waitpid(pid_t pid, int *status, int options);


wait 阻塞直到任意进程结束,status来接收进程返回值,返回值表示返回的进程号


waitpid 阻塞直到指定进程结束

WEXITSTATUS(status) 解析返回值  status的 8-15位是进程的返回值

4 父进程怎么知道子进程退出?


子进程结束时,通常会向父进程发送一个SIGCHLD信号

5父进程处理子进程的信号


#include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);


signal 的功能 :


向系统注册,只要接收到信号signal,系统停止进程,执行handler函数,


当函数执行完毕,继续原来的进程 (软中断)


5.1实现处理函数


5.2 使用signal绑定信号与函数

只有当子进程退出时才用wait,因为wait是一个阻塞函数。所以wait和signal一起用。

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/wait.h>
  4. #include <sys/types.h>
  5. #include <signal.h>
  6. void deal(int s)
  7. {
  8. printf("回收中\n");
  9. sleep(5);
  10. int status;
  11. wait(&status);
  12. printf("回收完毕%d\n",WEXITSTATUS(status));
  13. }
  14.  
  15. int main()
  16. {
  17. printf("创建进程前\n");
  18. int pid = fork(); //父进程执行的
  19. if(pid == 0)
  20. {
  21. printf("子进程 %d\n",getpid());
  22. sleep(5);
  23. return 88;
  24. }
  25. else
  26. {
  27.  
  28. printf("父进程 %d\n",getpid());
  29. signal(SIGCHLD,deal);
  30. while(1)
  31. {
  32. sleep(1);
  33. printf("parent\n");
  34. }
  35. return 0;
  36. }
  37. }

zhao@ubuntu:~/unix/5$ ./a.out 

创建进程前

父进程 2324

子进程 2325

parent

parent

parent

parent

回收中

回收完毕88

parent

parent

parent

^C

6 父子进程的内存空间

6.1全局变量 局部变量 堆变量 都会被子进程拷贝,但与原先的独立。

注意 堆内存被复制了,需要分别在各个进程中手动释放。

子进程克隆了父进程的全局区和局部区内存,但内存区域指向不同的物理空间。


尽管克隆但内存独立,不能相互访问。


进程间通信(IPC)是大问题。

6.2 内存映射与子进程:

内存映射的属性,决定子进程和父进程是否映射在同一物理空间。


MAP_SHARED: 映射到同一物理空间。 (改一个进程中的,其他进程的也变化)


MAP_PRIVATE:映射到不同的物理空间。

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/mman.h>
  4.  
  5. int main()
  6. {
  7. int *a = mmap(0,4,PROT_READ|PROT_WRITE,
  8. MAP_ANONYMOUS|MAP_SHARED,0,0);
  9. *a = 20;
  10. int pid = fork(); //父进程执行的
  11. if(pid == 0)
  12. {
  13. *a= 90;
  14. printf("parent :a=%d\n",*a);//90
  15. }
  16. else
  17. {
  18. sleep(3);
  19. printf("child :a=%d\n",*a);//90
  20. }
  21.  
  22. }

因为使用的是MAP_SHARED ,所以射到了同一物理空间, 改动会影响其它的.

若使用MAP_PRIVATE 就可以映射到不同物理空间.

6.3 文件描述符的拷贝

每个进程都维护一个文件描述符列表。


父子进程间,拷贝了文件描述符, 相同文件描述符指向的是同一个文件内核对象。


1 各个进程的文件描述符都需要close。


2 对文件的读写会改变文件对象的读写指针位置,对所有进程都有影响。

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/mman.h>
  4. #include <fcntl.h>
  5.  
  6. int main()
  7. {
  8. int fd = open("test.txt",O_RDWR);
  9. int pid = fork(); //父进程执行的
  10. if(pid == 0)
  11. {
  12. printf("parent:\n");
  13. char buf[1024] ={0};
  14. lseek(fd,0,SEEK_SET);
  15. read(fd,buf,1023);
  16. printf("%s\n",buf);
  17. close(fd);
  18. }
  19. else
  20. {
  21. printf("child:\n");
  22. char buf[1024] ={0};
  23. lseek(fd,0,SEEK_SET);
  24. read(fd,buf,1023);
  25. printf("%s\n",buf);
  26. close(fd);
  27. }
  28.  
  29. }

进程的数据交换,基于两种方式:


内存: 有序/无序:mmap


文件:有序/无序:普通文件


基于内核对象:文件/内存/队列

LINUX编程学习笔记(十四) 创建进程与 父子进程内存空间的更多相关文章

  1. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

  2. Linux 编程学习笔记----文档管理系统

    本文从网络上完成的第 Linux在文件系统管理. 1.VFS文件系统概述 linux採用VFS来管理文件系统,并且linux设计的原则之中的一个就是everything is file.因此文件管理系 ...

  3. Linux 编程学习笔记----过程管理和项目发展(在)

    转载请注明出处,http://blog.csdn.net/suool/article/details/38406211,谢谢. Linux进程存储结构和进程结构 可运行文件结构 例如以下图: 能够看出 ...

  4. Linux 编程学习笔记----动笔makefile档

    Befroe Beginning. 在设置暑假的plan ,关于Linux的书籍如今在看的是ALP和Linux高级程序设计(杨宗德)第三版.在计划中的是Linux高级环境编程. 如今開始关于Linux ...

  5. (C/C++学习笔记) 十四. 动态分配

    十四. 动态分配 ● C语言实现动态数组 C语言实现动态数组,克服静态数组大小固定的缺陷 C语言中,数组长度必须在创建数组时指定,并且只能是一个常数,不能是变量.一旦定义了一个数组,系统将为它分配一个 ...

  6. Linux编程学习笔记 -- Process

    进程是一个程序的运行.   在一个程序中执行另一个执程序的方法有两种: 1)system 在shell中执行程序 2)fork + exec 复制一个进程,在进程中用新的程序替换原有的程序   for ...

  7. JavaScript高级程序设计学习笔记第四章--变量、作用域和内存问题

    1.变量可能包含两种不同数据类型的值:基本类型值和引用类型值. 基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象. 2.变量复制 如果从一个变量向另一个变量复制基本类型的值,会在 ...

  8. Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(十四)之Type Information

    Runtime type information (RTTI) allow you to discover and use type information while a program is ru ...

  9. Linux 编程学习笔记----ANSI C 文件I/O管理

    转载请注明出处:http://blog.csdn.net/suool/article/details/38129201 问题引入 文件的种类 依据数据存储的方式不同,能够将文件分为文本文件和二进制文件 ...

随机推荐

  1. 《Javascript权威指南》学习笔记之十二:数组、多维数组和符合数组(哈希映射)

    Array(数组)是JavaScript中较为复杂的数据类型,同Java.C#.VB等程序语言的数组相比.Javascript数组中的元素不必为同样的数据类型,能够在数组每一个元素上混合使用数字.日期 ...

  2. linux命令: tree的c实现

    tree命令的c语言实现. #include<stdio.h> #include<dirent.h> #include<sys/stat.h> #include&l ...

  3. jQuery.mobile.changePage() | jQuery Mobile API Documentation

    jQuery.mobile.changePage() | jQuery Mobile API Documentation <script> $.mobile.changePage( &qu ...

  4. 安装windows7导致Ubuntu启动项消失的问题的解决

    系统原来是Ubuntu14,前两天安装win7后,启动直接是win7.也就是Ubuntu的启动项消失了. 在windows下尝试非常多方法,都以失败告终,最后选择Ubuntu下boot-repair软 ...

  5. Oracle的序列

    Oracle的序列 序列介绍 序列是Oracle提供的用于产生一系列唯一数字的数据库对象. 使用序列能够实现自己主动产生主键值.序列也能够在很多用户并发环境中使用.为所实用户生成不反复的顺序数字,并且 ...

  6. java 基于JDK中的源码总结下String二

    申明:转载请注明出处,如有商用目的请务必知会本人,感谢. 上一篇文章:http://blog.csdn.net/ts1122/article/details/8738336,介绍了String一些易错 ...

  7. javaee加密部署,tomcat使用自己的classloader解密【正解】

    [起因] 公司需要对一个web项目进行加密之后出售, 大家都知道的,class很好反编译, 所以需要对class文件先进行加密, 然后使用自己的classloader进行解密并加载. [步骤] 大概分 ...

  8. Qt生成灰度图(转载)

    Qt生成灰度图(转载)   项目中用到大量基础图像处理知识,其中灰度图的生成是很重要的一环. 先补充一些基础知识: ------------------------------------------ ...

  9. boost::asio async_write也不能保证一次发完所有数据 二

    只有看boost源码才能弄明白发生了什么.首先我是将vector里面写入了数据,然后用boost::asio::buffer将vector构造成了mutable_buffer_1对象. 参考该文档的重 ...

  10. cookie的path和domain參数实例解析

    一句话概括两个參数含义各为: path表示cookie所在的文件夹 domain表示的是cookie所在的域,默觉得请求的地址 首先改动我们的 hosts 文件 我本机内网ip 192.168.1.1 ...