进程是系统资源的最小单元,很重要。
进程相关的还有用户和用户组标识、进程时间、资源利用的函数,参考APUE.
进程运行的状态进程的结构:主要包含数据段、代码段、堆栈段进程模型:用户态和内核态linux启动进程的两种方式: 手动启动:前台启动,最常见的是在终端里输入命令,该命令的执行就是一个进程; 后台启动,用&,不影响终端,在终端后面默默运行。调度启动:制定时间运行,有一些命令,at命令可以在指定时刻执行相关进程;cron命令可以自动周期性的执行相关进程。
常用进程相关命令:
/* 7-1,fork */ #include <stdio.h> // printf #include <stdlib.h> // exit #include <unistd.h> #include <fcntl.h> // open,fcntl #include <sys/types.h> int main(int args, char *argv[]) { pid_t pid_rtn; pid_rtn = fork(); if( pid_rtn == 0 ) { printf("\r\nChild thread, pid %d, ppid %d",getpid(),getppid()); } else if( pid_rtn > 0 ) { sleep(1); // 如果父进程先结束,则子进程会被init进程收养,用getppid时获取的就不是创建他的父进程了 printf("\r\nParent thread, pid %d, child %d",getpid(),pid_rtn); } else { printf("\r\nfork err."); } printf("\r\nfinish.\r\n"); exit(0); }$ ./example Child thread, pid 4087, ppid 4086finish.Parent thread, pid 4086, child 4087finish.如果父进程不睡1s,则运行结果如下:$ ./example Parent thread, pid 4121, child 4122finish.$ Child thread, pid 4122, ppid 2326finish.$ ps -A*2326 ? 00:00:01 upstart // upstart就是ubuntu的init进程,对于父进程已经结束的子进程,会被这个进程“收养”*
/* 7-2,exec */
#include <stdio.h> // printf#include <stdlib.h> // exit#include <unistd.h>#include <fcntl.h> // open,fcntl#include <sys/types.h>
int main(int args, char *argv[]){ pid_t pid_rtn; pid_rtn = fork(); if( pid_rtn == 0 ) { execlp("ps","ps","-A","NULL"); // 第一个ps是文件名,后面是参数,输入参数时,第一个参数是要运行的程序,跟在shell里输入是一样的,注意要用NULL结尾 }
printf("\r\nfinish.\r\n"); exit(0);}
相当于执行了“ps -A”命令,运行结果:
PID TTY STAT TIME COMMAND 1 ? Ss 0:12 /sbin/init splash 2 ? S 0:00 [kthreadd]
......
进程调用exit()和_exit()后不会立即退出,而是进入僵死zombie状态,变成僵尸进程,僵尸进程只在进程列表里保留一个位置,记录该进程的退出状态等供其他进程收集(一般是父进程用wait收集)。
#include <unistd.h> // _exit#include <stdlib.h> // exitvoid exit( int status );void _exit( int status);参数: status 可以返回本进程(调用exit的进程)的退出状态,一般0表示正常,其他数值表示出错,进程非正常结束; 父进程用wait()系统调用接收子进程的返回值。
#include <stdio.h> // printf#include <stdlib.h> // exit#include <unistd.h>
int main(int args, char *argv[]){ printf("Start.\n"); printf("content in buffer."); _exit(0);}
$ ./example // 在缓冲区里就没有了,因为_exit不刷缓冲区Start.
#include <stdio.h> // printf#include <stdlib.h> // exit#include <unistd.h>
int main(int args, char *argv[]){ printf("Start.\n"); printf("content in buffer."); exit(0);}
$ ./example // 在缓冲区里的也刷出来了,exit干的Start.content in buffer.
【注意】
printf遇到“\n”换行符时自动从缓冲区中将记录读出
union wait { int w_status; struct { # if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int __w_termsig:7; /* Terminating signal. */ unsigned int __w_coredump:1; /* Set if dumped core. */ unsigned int __w_retcode:8; /* Return code if exited normally. */ unsigned int:16; # endif } __wait_terminated; // 正常退出和异常终止,格式
struct { # if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int __w_stopval:8; /* W_STOPPED if stopped. */ unsigned int __w_stopsig:8; /* Stopping signal. */ unsigned int:16; # endif } __wait_stopped; // 暂停,stop,格式 };
常用宏: WIFEXITED(status),若为正常退出,则返回真。若为真,可用WEXITSTATUS(status)获取exit返回的状态; WIFSIGNALED(status),若子程序为异常终止,则返回真(被信号终止),可用WTERMSIG(status)获取子进程终止的信号编号;可用WCOREDUMP(status)检查是否产生core文件,产生时为真; WIFSTOPPED(status),如果子程序暂停,则为真,可通过WSTOPSIG(status),获取使子程序暂停的信号编号 WIFCONTINUED(status),若暂停后又继续的子进程返回状态,则为真,仅用于waitpid。#define WIFEXITED(status) (((status) & 0x7f)== 0) #define WIFSIGNALED(status) (((signed char) (((status) & 0x7f) + 1) >> 1) > 0) #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
#define WEXITSTATUS(status)(((status) & 0xff00) >> 8) #define WTERMSIG(status) ((status) & 0x7f) #define WSTOPSIG(status) (((status) & 0xff00) >> 8)
返回值: 成功:已结束运行(被等待的)的子进程的进程号 失败:-1pid_t waitpid( pid_t pid, int *status, int options );参数: pid: >0,等待进程ID=pid的子进程,不管别的; =-1,等待任何一个子进程,与wait()作用一样; = 0,等待“组ID==调用进程组ID”的任一子进程; <-1,等待“组ID==pid绝对值”的任一子进程 status:同wait()函数 options:sya WNOHANG:不阻塞 WUNTRACED:若实现某支持作业控制,则由 pid 指定的任一子进程状态已暂停,且其状态自暂停以来还未报告过,则返回其状态 0:同wait(),阻塞 返回值: 正常:已结束运行的子进程的进程号 使用WNOHANG且没有子进程:0 调用出错:-1【注意】1. 关于几个测试退出状态的特殊的宏
/* 7-4,waitpid */
#include <stdio.h> // printf#include <stdlib.h> // exit#include <unistd.h>#include <sys/types.h> // pid_t#include <sys/wait.h>
int main(int args, char *argv[]){ pid_t pid_fork,pid_wait; int status; pid_fork=fork(); if( pid_fork == 0 ) // child { sleep(5); exit(0); } else if( pid_fork > 0 ) { do { pid_wait = waitpid(pid_fork,&status,WNOHANG); if( pid_wait != pid_fork ) printf("child thread %d is not over\r\n",pid_fork); else printf("child thread %d is over,status 0x%x\r\n",pid_fork,status); sleep(1); } while(pid_wait!=pid_fork); } else { printf("fork err code %d.\r\n",pid_fork); }
exit(0);}
$ ./example
child thread 7575 is not overchild thread 7575 is not overchild thread 7575 is not overchild thread 7575 is not overchild thread 7575 is not overchild thread 7575 is over,status 0x0
$ ./example // 子进程exit(-1)时,waitpid的获取的值是0xff00,高位是exit的返回值,需要用到宏了child thread 7605 is not overchild thread 7605 is not overchild thread 7605 is not overchild thread 7605 is not overchild thread 7605 is not overchild thread 7605 is over,status 0xff00
守护进程,也叫deamon进程,是后台服务进程;系统引导载入时启动,系统关闭时终止,独立于控制终端;常用于周期性的执行某种任务或等待处理某些事件。守护进程已d结尾,例如crond、lpd等。
控制终端:系统与用户进行交流的界面称为终端,每个从此终端开始运行的进程都会依赖这个终端,这个终端就是这些进程的控制终端。控制终端关闭时,相应的进程都会关闭。但是守护进程不受影响。
守护进程不受终端、用户和其他变化的影响,直到系统关闭时才退出。
步骤:
父进程退出后,子进程编程了孤儿进程,被init进程收养。 形式上做到了与控制终端的脱离。
先了解基本概念:进程组、会话组、会话期
进程组:一个或多个进程的集合,每个进程组都有一个组长进程,进程组ID=组长PID
会话组:一个或多个进程组的集合
会话期:通常一个会话开始于用户登录,终止与用户退出,在此期间该用户运行的所有进程都属于这个会话期。
setsid():创建新的会话,并担任该会话组的组长。调用后起到3个作用: 让进程摆脱原会话的控制;让进程摆脱原进程组的控制让进程摆脱原控制终端的控制 总之,跟之前的控制终端、进程组、会话组都没有关系了。使进程完全独立出来,从而摆脱所有其他进程的控制#include <sys/types.h>#include <unistd.h>pid_t setsid( void );返回值: 成功:该进程组ID 出错:-1
通常的做法是将守护进程的当前目录设置为根目录,用chdir()系统调用。
umask(0),基本思路是给最大权限。
父进程那继承来的文件描述符,一般不用,浪费,关闭。 连基本的输入输出都没用了,setsid时已经失去联系了,可以关了。
/* 7-5,deamon */ #include <stdio.h> // printf #include <stdlib.h> // exit #include <unistd.h> #include <sys/types.h> // pid_t #include <fcntl.h> int main(int args, char *argv[]) { pid_t pid_fork; int i; int fd; char buf[32]="The deamon info.\n"; pid_fork = fork(); if( pid_fork < 0 ) { printf("fork err.\r\n"); } else if( pid_fork > 0 ) { exit(0); } // only child enter setsid(); chdir("/"); umask(0); for( i=0;i<getdtablesize();i++ ) // 终端也关了,printf没有效果了,需要用别的调试方法 close(i); if( fd=open("/tmp/log", O_RDWR|O_CREAT,0644) < 0 ) printf("open file err\r\n"); while(1) { write(fd,buf,sizeof(buf)); sleep(1); } exit(0); }
printf不好使,咋办?用linux提供的syslog服务,系统中有syslogd守护进程。不通版本linux的syslog日志文件的位置可能不通。
#include <syslog.h>void openlog( char * ident, int options, int facility );参数: ident:向每个消息加入的字符串,通常为程序的名称; option:LOG_CONS,如果消息无法送到系统日志服务,则直接输出到系统控制终端 LOG_NDELAY:立即打开系统日志服务的连接。在正常情况下,直接发送到第一条消息时才打开连接 LOG_PERROR:将消息也同时送到 stderr 上 LOG_PID:在每条消息中包含进程的 PID facility: 指定程序发送的消息类型 LOG_AUTHPRIV:安全/授权信息 LOG_CRON:时间守护进程(cron 及 at) LOG_DAEMON:其他系统守护进程 LOG_KERN:内核信息 LOG_LOCAL[0~7]:保留 LOG_LPR:行打印机子系统 LOG_MAIL:邮件子系统 LOG_NEWS:新闻子系统 LOG_SYSLOG:syslogd 内部所产生的信息函数传入值 LOG_USER:一般使用者等级信息 LOG_UUCP:UUCP 子系统 void syslog(int priority, char *format, ...) 参数: priority ,指定消息的重要性, LOG_EMERG:系统无法使用 LOG_ALERT:需要立即采取措施 LOG_CRIT:有重要情况发生 LOG_ERR:有错误发生 LOG_WARNING:有警告发生 LOG_NOTICE:正常情况,但也是重要情况 LOG_INFO:信息消息 LOG_DEBUG:调试信息 format,同printfvoid closelog( void )
转载于:https://www.cnblogs.com/liuwanpeng/p/6605985.html