进程基础

来源:互联网 发布:幼儿园美工室制度 编辑:程序博客网 时间:2024/06/10 16:54

学习笔记,小白可以相互学习,大佬看到能告诉咱理解不对的地方就好了。


1.进程相关概念

进程是一个独立的可调读的任务

进程是一个程序执行一次的过程

进程是程序执行和资源管理的最小单位


进程和程序的区别

程序是静态的,它是保存在磁盘上的指令的有序集合,没有任何执行的概念

进程是一动态的概念,它是程序执行的过程,包括创建,调度和消亡。

linux中的进程包含了1.2.3.,程序只有1.2.

进程1.正文段程序1.2.3.2.用户数据段1.2. 3.系统数据段 进程不仅仅包含了程序的指令和数据,而且包含了程序计数器值,CPU的所有寄存器值记忆存储临时数据的进程堆栈。


2.Linux下的进程

2.1结构

主要的进程标识:进程号,(getpid()得到进程号)  父进程号,(getppid()得到父进程号)

PID唯一地标识一个进程

2.2进程类型

交互类型:该类进程由shell控制和运行。交互进程可以在前台或者后台运行。
批处理进程:该类进程不属于某个终端,他被提交到一个队列中以便顺序执行。

守护进程:该类进程在后台运行,他一般在Linux启动时开始运行,系统关闭才结束。

2.3进程运行状态

运行态(R态):此时进程正在运行,或者准备运行。

等待态:此时程序在等待一个事件的发生或某种系统资源。

可中断(S)

不可中断(D)

停止态:此时进程被中止(T)。

死亡态:这是一个已终止的进程,但还在进程向量数组中占有一个task_struct结构


3.调度进程

ps:查看系统中的进程(ps -elf查看优先级)

top:动态显示系统中的进程

nice:按用户指定的优先级运行进程(数字越小,优先级越高,例如nice -5 ./a.out)

renice:改变正在运行进程的优先级

kill:结束进程(包含后台进程)。(kill - l查看kill的信号,9号信号和2号信号都是结束信号)

bg:将挂起的进程在后台执行(ctrl + Z可以把一个进程挂起,bg + 作业号)

fg:把后台运行的进程放到前台运行(fg + 作业号)


4.函数

4.1fork()进程创建

头文件#include<sys/types.h>
#include<unistd.h>函数原型pid_t fork(void);函数返回值0:子进程
子进程PID:父进程
-1:出错






fork()函数用于创建一个子进程,子进程是几乎拷贝了父进程的全部内容。


#include <stdio.h>#include <string.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <unistd.h>int main(){printf("nihao\n");pid_t pid;if (0 > (pid = fork())) {perror("fork");return -1;}else if (pid == 0) {  //子进程printf("child: pid = %d, ppid = %d\n", getpid(), getppid());}else {  //父进程//sleep(1);printf("parent: child_pid = %d, pid = %d\n", pid, getpid());}printf("hello world!\n");return 0;}

4.2 exec函数族

exec函数族提供了一种在进程中启动另外一个程序执行的方法。他可以根据指定的文件名或者目录名找到可执行文件,并用它来取代原调用进程数据段,代码段和堆栈段。在执行完后,原调用进程的内容除了进程号,其他全被替换。

可执行文件可以是二进制文件,也可以是任何Linux下可执行的脚本文件。

头文件#include<unistd.h>原型int execv(const char *path,const *arg, argv[]); int execle(const char *path,const *arg,..., char *const envp[]); int execve(const char *path,char *const argv[],char *const envp ); int execl(const char *path,const *arg, ...); int execlp(const char *file,const *arg, ...); int execvp(const char *file,char *const argv[]);返回值-1;出错

p:系统会自动从环境变量“#PATH”所包含的路径中进行查找。(p为path,代表shell搜索路径)

l: list便是逐个列举的方式

v: vertor表示将所有参数构造成指针数组传递

e: 在envp[]中传递当前进程所使用的环境变量


例子:

execl:

#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(){    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        return -1;;    }    else if( pid == 0 )    {        if( 0 > execl("/bin/ls","ls","-l",NULL))//第一个参数是文件路径        //if( 0 > ("./a.out","./a.out",NULL))        {            perror("excel");            return -1;        }    }    else    {        printf("hello\n");    }    return 0;}

execv:

#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(){    char *arg[] = {"ls","-l",NULL};    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        return -1;;    }    else if( pid == 0 )    {        if( 0 > execv("/bin/ls",arg))//就是把execl中命令的参数写入了数组arg        //if( 0 > ("./a.out","./a.out",NULL))        {            perror("excev");            return -1;        }    }    else    {        printf("hello\n");    }    return 0;}

execle:

#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(int argc,char *argv[],char *envp[])//main函数的第三个参数{    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        return -1;;    }    else if( pid == 0 )    {        if( 0 > execle("/usr/bin/env","env",NULL,envp))//env命令查看当前系统环境,which查看文件路径,例如which 1.c        {            perror("excele");            return -1;        }    }    else    {        printf("hello\n");    }    return 0;}

execve:

#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(int argc,char *argv[],char *envp[]){    char *arg[] = {"env",NULL};    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        return -1;;    }    else if( pid == 0 )    {        if( 0 > execve("/usr/bin/env",arg,envp))        {            perror("exceve");            return -1;        }    }    else    {        printf("hello\n");    }    return 0;}

execlp:

#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(int argc,char *argv[]){    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        return -1;;    }    else if( pid == 0 )    {        if( 0 > execlp("env","env",NULL))//第一个参数不用写全具体路径,只写文件名,他会自动搜索        {            perror("excelp");            return -1;        }    }    else    {        printf("hello\n");    }    return 0;}

execvp:

#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(int argc,char *argv[]){    char *arg[] = {"env",NULL};    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        return -1;;    }    else if( pid == 0 )    {        if( 0 > execvp("env",arg))        {            perror("excevp");            return -1;        }    }    else    {        printf("hello\n");    }    return 0;}


4.3exit和_exit

头文件exit: #include<stdlib.h> _exit: #includ<unistd.h>原型exit: void exit(int status) _exit: void _exit(int status)函数传入值status是一个整型参数,可以利用这个参数
传递进程结束时的状态。通常用0表示正常
结束:其他的数字表示出现了错误,进程非
正常结束。在实际编程中可用wait系统调用
接收子进程的返回值,进行相应处理。

exit和_exit的区别

_exit()函数作用最为简单,直接使进程终止运行,清除其使用的内存空间,并且销毁其在内核的各种数据结构。

exit()会把文件缓冲区的内容写回文件(就是刷新缓存)。

#include<stdlib.h>#include<unistd.h>int main(){printf("hello\n");//exit(0);//只会直接打印hello_exit(0);//会打印hello worldprintf("world\n");return 0;}


4.4 wait 和 waitpid

wait函数,调用该函数使进程阻塞,直到任意一个子进程结束或者是该进程接收到了一个信号为止。如果该进程没有子进程或者子进程已经结束,wait函数会立即返回。

waitpid函数,功能和wait函数类似。可以指定等待某个子进程结束以及等待的方式(非阻塞或者阻塞)。

wait函数头文件#include<sys/types.h>
#include<sys/wait.h>原型pid_t wait(int *status)参数status是一个整型指针,指向的对象用来保存子
进程退出时的状态。status若为空则忽略子进程
退出时的状态,不为空则表示子进程退出时的
状态。子进程结束的状态可用特定的宏测定,
WIFEXITED(status)判断子进程是否正常结束,
WEXIT(status)当子进程退出时,得到子进程的
退出值返回值成功:子进程的进程号
失败:-1


waitpid函数头文件#include<sys/types.h>
#include<sys/wait.h>原型pid_t waitpid(pid_t pid,int *status,int options)参数pidpid>0,只等待进程ID等于pid的子进程,不管其他子进程是否已经结束退出了,
只要指定的子进程还没结束,waitpid就会一直等下去。 pid=-1,等待任意一个子进程结束,此时和wait的作用一样 pid=0,等待其组ID等于调用进程的组ID的任意一子进程 pid<-1,等待其组ID等于pid的绝对值得任意一子进程参数status同wait参数optionsWNOHANG:若由pid指定的子进程并不立即可用,则waitpid不阻塞,此时返回0 WUNTRACED:若某实现支持作业控制,则由pid指定的任意一子进程状态已
暂停,且其状态自暂停以来还未报告过,则返回其状态。 0:同wait,阻塞父进程,等待子进程退出返回值正常:结束的子进程的进程号使用WNOHANG,且没有子进程结束时:0 调用出粗:-1

#include<stdio.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<stdlib.h>int main(){    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        return -1;    }    else if( 0 == pid)    {        printf("child pid = %d\n",getpid());        sleep(5);        printf("child!\n");    }    else    {        printf("parent\n");        pid = wait(NULL);        if(0 > pid)        {            perror("wait");            exit(-1);        }        printf("wait :pid = %d\n",pid);    }    return 0;}


#include<stdio.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<stdlib.h>int main(){    int status;    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        return -1;    }    else if( 0 == pid)    {        printf("child pid = %d\n",getpid());        sleep(5);        printf("child!\n");        exit(5);    }    else    {        printf("parent\n");        pid = wait(&status);//得到子进程的结束状态,作用与前面一样pid = waitpid(-1,NULL,0)        if(0 > pid)        {            perror("wait");            exit(-1);        }        printf("wait :pid = %d\n",pid);        if(WIFEXITED(status))//判断子进程是否正常结束        {            printf("child process exit normol\n");            printf("child exit:%d\n",WEXITSTATUS(status));//当子进程正常退出时,得到子进程的退出值        }        else        {                    printf("child process exit unnormol\n");        }    }    return 0;}
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <sys/wait.h>int main(){int status;pid_t pid;if (0 > (pid = fork())) {perror("fork");exit(-1);}else if (pid == 0) { //子进程printf("child: pid = %d\n", getpid());sleep(5);printf("child\n");exit(0);}else { //父进程printf("parent\n");pid = waitpid(-1, NULL, WNOHANG);if (pid < 0) {perror("wait");exit(-1);}else if (pid > 0)printf("waitpid: pid = %d\n", pid);else printf("No child process exit\n");}return 0;}

5. 守护进程

守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。守护进程常常在系统启动时开始运行,在系统关闭时终止。

在Linux中,每一个系统与用户进行交流的界面称为终端。从终端开始运行的进程都会依附于这个终端,当控制终端被关闭时,相应的进程也会被自动关闭,

但是守护进程可以突破这种限制。如果想让某个进程不受终端变化的影响,必须把它变成守护进程。

ps -axj:查看系统中的守护进程

ps -ef | grep ./a.out(进程名):可查看特定进程的信息,可用kill命令结束守护进程

Linux守护进程编写步骤:

  1. 创建子进程,父进程退出(第一步完成,子进程就在形式上做到了与控制终端的脱离)
  2. 在子进程中创建新会话
  3. 改变当前目录为根目录
  4. 重设文件权限掩码
  5. 关闭文件描述符
#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/stat.h>#include<sys/types.h>#include<fcntl.h>#include<string.h>#include<time.h>void init_daemon()//创建守护进程{    /**1.创建子进程,父进程退出******************/    pid_t pid;    if(0 > (pid = fork()))    {        perror("fork");        exit(-1);    }    else if(pid > 0)    {        exit(0);    }    /***2.在子进程中创建会话********************/    if(0 > setsid())    {        perror("setsid");        exit(-1);    }    /***3.改变当前目录为根目录********************/    if( 0 > chdir("/tmp"))    {        perror("chdir");        exit(-1);    }    /****4.重设文件掩码***********************/    umask(0);    /****5.关闭文件描述符**********************/    int num = getdtablesize();    int fd;    for(fd = 0; fd < num; fd++)    {        close(fd);    }}int main(int argc,char *argv[]){    init_daemon();    /**创建一个文件,每隔一秒向其中写入当前时间加行号****/    int fd;    if( 0 > (fd = open(argv[1],O_WRONLY | O_CREAT | O_APPEND,0666)))    {        perror("open");        exit (-1);    }    write(fd,"hello world\n",sizeof("hello world"));    int l = 0,ret;    char a[100];    time_t t;    struct tm *tm;    while(1)    {        char buf[100];        time(&t);        tm = localtime(&t);        sprintf(buf,"%2d, %4d-%2d-%2d %2d:%2d:%2d\n",++l,tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);        write(fd,buf,24);                sleep(1);    }    close(fd);  /*    int l = 0;    char buf[100];    FILE *fp = fopen(argv[1],"ab+");    if(NULL == fp)    {        perror("fopen");        exit (-1);    }    while(NULL != fgets(buf,sizeof(buf),fp))    {        if(buf[strlen(buf)-1] == '\n')        {            l++;        }    }        time_t t;    struct tm *tm;    while(1)    {        time(&t);        tm = localtime(&t);        sprintf(buf,"%2d, %4d-%2d-%2d %2d:%2d:%2d\n",++l,tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);        fputs(buf,fp);        fflush(fp);        sleep(1);    }        fclose(fp);   */         return 0;}



原创粉丝点击