Linux 多进程/多线程管理

进程状态

TASK_RUNNING 可执行状态 0
TASK_INTERRUPTIBLE 可中断的睡眠状态 1
TASK_UNINTERRUPTIBLE 不可中断的睡眠状态 2
TASK_STOPPED 暂停状态 4
TASK_ZOMBIE 退出状态 8

可中断的睡眠状态

  1. 处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒

不可中断的睡眠状态

  1. 进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号

暂停状态

  1. 向进程发送一个SIGSTOP信号,它就会因响应该信号而进入TASK_STOPPED状态(除非该进程本身处于TASK_UNINTERRUPTIBLE状态而不响应信号)。(SIGSTOPSIGKILL信号一样,是非常强制的。不允许用户进程通过signal系列的系统调用重新设置对应的信号处理函数。)
  2. 向进程发送一个SIGCONT信号,可以让其从TASK_STOPPED状态恢复到TASK_RUNNING状态

退出状态

  1. 进程在退出的过程中,处于TASK_DEAD状态
  2. 在这个退出过程中,进程占有的所有资源将被回收,除了task_struct结构(以及少数资源)以外。于是进程就只剩下task_struct这么个空壳,故称为僵尸

进程基本属性

进程号 PID

  • getpid() 获取当前自己的进程号
  • getppig() 获取当前自己的父进程号
  • getpgid() 获取当前自己的组进程号

用户号

  • setuid() 把当前文件 临时提升文件拥有者的权限

    • UID 当前进程的权限
    • EUID 当前进程有效权限
    • S 权限 就是 该文件有setuid的属性

会话号 SID

  • getsid() 返回调用进程的会话ID
  • setsid() 创建新的会话

控制终端

  • tty (终端设备的统称)
  • pty (虚拟终端)
  • ttyn ( 控制台终端 )
  • /dev/pts/n (虚拟终端 )

进程

fork() 新起一个进程

函数通过系统调用创建一个与原来进程几乎完全相同的进程

返回值: 大于0 为父进程 等于0为子进程 -1错误

wait(NULL); 可以让父进程等待子进程退出

  • 也会对 IOFILE 中的缓存进行拷贝
  • 复制的 是 _IO_FILE_plus 的文件结构体

    • _IO_FILE_plus 的文件结构体 中保存的是 fd对应的文件
    • ((struct _IO_FILE_plus )&IO_2_1_stdin) 在gdb中用p可以查看

vfork() 等于 线程

创建的子进程与父进程共享数据段,而且由vfork()创建的子进程将先于父进程运行

  • 在必要的时候才新申请内存 需要栈是才申请栈,需要堆在申请堆

守护进程

脱离组进程和父进程

控制终端

  1. **一个会话可以有一个控制终端**
  • 控制进程就是打开终端的进程
  • 前台终端:会占用当前控制台
  • 后台终端:不占用当前控制台

终端发送的信号,只有前台进程组的进程会收到,后台进程不会收到

tcgetpgrp() 获取当前前台进程组的进程号

tcpgetsid() 获取终端首进程的会话ID

进程用户

  • uid 创建进程真是用户号

    • 0 为root 用户
    • 0 ~ 1000 系统保留用户
    • 1001以上 是用户自己添加的用户
  • euid 当前进程有效用户号

    • se tuid() 把当前文件 临时提升文件拥有者的权限

进程的代码执行

启动新的程序,用新程序的代码段来覆盖当前程序的代码段

头文件 #include

  • l 的是一类

    • 是输入文件路径
  • p 的是一类

    • 是环境变量 所指向的文件路径

execl()

是使用文件路径名字

execlp ()

使用文件名 和 参数表

execve() 经常使用

  • 使用指针数组

execle()

  • 可变长度参数列表 在参数后面添加arg 的参数

回收进程资源

  • 使用 exit() 函数退出 比较干净 会刷新缓冲区
  • return 退出 只是单单退出
  • on_exit() 接收退出码
  1. on_exit()
  2. 参数1:收到退出码执行的函数
  3. 参数2:函数的第2个参数,第一个参数是退出码
  4. //第一个参数是退出码
  5. void test_exit(int status,void * arg);
  6. on_exit(test_exit,"buf");

waitpid() 等待线程返回

  • waitpid分为阻塞状态和非阻塞状态
  • 参数1:pid号

    • pid > 0(表示等代次pid进程结束)
    • pid=-1(表示等待任意子进程结束)
    • pid= 0(表示等待与当前进程组中的进程结束)
    • pid<-1(表示等待pid绝对值的pgid组中的任意进程结束)
  • 参数2:传递一个 int 用与该函数的修改
  • 参数3:0为阻塞等待,1为非阻塞等待
  • 返回值:结束的pid

WIFEXITED() 解析 waitpid第2个参数的值

WEXITSTATUS() 解析 waitpid第2个参数的值

设置守护进程

  1. **得到一个相对干净的进程**

守护进程:使用父进程的子进程的子进程

  1. void creat_daemon()
  2. {
  3. int i,fd;
  4. pid_t id;
  5. struct sigaction sa;
  6. umask(0);//第一步:调用umask将文件模式创建屏蔽字设置为0.
  7. if((id = fork()) < 0){
  8. printf("fork error!\n");
  9. return;
  10. }else if(id != 0){
  11. exit(0);//第二步:调用fork,父进程退出。保证子进程不是话首进程,从而保证后续不和其他终端关联。
  12. }
  13. setsid();//第三步:设置新会话。
  14. sa.sa_handler = SIG_IGN;
  15. sigemptyset(&sa.sa_mask);
  16. sa.sa_flags = 0;
  17. if(sigaction(SIGCHLD,&sa,NULL) < 0){
  18. //注册子进程退出忽略信号
  19. return;
  20. }
  21. if((id = fork()) < 0){
  22. printf("fork 2 error!\n");
  23. return;
  24. }else if(id != 0){
  25. exit(0);
  26. }
  27. // 守护进程空间
  28. if(chdir("/") < 0){
  29. //第四步:更改工作目录到根目录
  30. printf("child dir error\n");
  31. return;
  32. }
  33. close(0);
  34. fd = open("/dev/null",O_RDWR);//关闭标准输入,重定向所有标准(输入输出错误)到/dev/NULL
  35. dup2(fd,1);
  36. dup2(fd,2);
  37. }

设置系统开启启动进程

  • 安装 xinetd
  • 在 /etc/xinetd.d/ 目录中新建个配置文件如:

    • vi /etc/xinetd.d/date_maine
  • 输入:
  1. service date_mine
  2. {
  3. disable = no
  4. type = UNLISTED
  5. socket_type = stream
  6. protocol = tcp
  7. user = root
  8. wait = no
  9. port = 8888
  10. bind = 0.0.0.0
  11. server = /bin/date //启动程序路径
  12. }
  • service xinetd start 启动
  • service xinetd restart 重新启动