Linux进程

question?

什么是程序,什么是进程,有什么区别?

程序是静态的概念,gcc xxx.c -o pro
磁盘中生成pro文件,叫做程序。
进程就是程序的一次运行活动,通俗点讲就是程序跑起来,系统中就多了一个进程。

如何查看系统中有哪些进程?

1、使用ps指令查看(比如ps -aux|grep xxx)
2、使用top(类似任务管理器)

什么是进程标识符?

每个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证
Pid=0: 称为交换进程(swapper),作用—进程调度
Pid=1:init进程,作用—系统初始化
编程调用getpid函数获取自身的进程标识符
getppid获取父进程的进程标识符

什么是父进程、什么是子进程?

进程A创建了进程B,那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类中的父子关系。

C程序的存储空间是如何分配的?

在这里插入图片描述

fork

pid_t fork(void);
fork函数调用成功,返回两次
返回值为0, 代表当前进程是子进程
返回值非负数,代表当前进程为父进程
调用失败,返回-1

父子进程写实拷贝,共享正文段,并不共享存储空间部分,父进程返回值为子进程pid,子进程返回值为0

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
 pid_t pid;
 int a=10;
 printf("father id=%d\n",getpid());
 pid = fork(); 
 if(pid>0){
  	printf("this is father pid=%d\n",getpid());
 }else if(pid == 0){
  	printf("this is child pid=%d\n",getpid());
  	a=a+10
 }
 printf("%d\n",a);
 return 0; 
}

fork创建子进程的一般目的

应用:服务端请求

(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。在网络服务中是常见的-----父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则等待下一服务请求到达。demo如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
 	pid_t pid;
 	int data=10;
while(1){
	printf("please input a data\n");
	scanf("%d",&data);
	if(data==1){
		pid=fork();
		if(pid>0)
		{}
		else if(pid==0){
		while(1){
printf("do net request,pid=%d\n",getpid());
sleep(3);
			}
		}
	}
 		else{
 printf("wait do nothing\n");
	 }
 }
 return 0; 
}

(2)一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程返回后立即调用exec(后面讲)

vfork

vfork与fork的区别:
关键区别一:
vfork 直接使用父进程存储空间,不拷贝。
关键区别二:
vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。

#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
         pid_t pid;
         int cnt = 0;
         pid = vfork(); //固定子进程先运行
         if(pid>0){
         printf("cnt=%d\n",cnt);
         printf("this is father print,pid=%d",getpid());
         sleep(1);
         }else if(pid == 0){
                while(1){
                        printf("this is child print,pid=%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                        exit(0);
                }
        }
     }
     return 0;
}

进程退出

正常退出:
1、main函数调用return
2、进程调用exit(),标准C库
3、进程调用_exit()或_Exit(),属于系统调用
补充:
1、进程最后一个线程返回
2、最后一个线程调用pthread_exit
异常退出:
1、调用abort
2、当进程收到某些信号时,如ctrl+C
3、最后一个线程对取消(cancellation)请求作出响应

等待子进程

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

status参数:
是一个整型数指针
非空:子进程退出状态放在它所指向的地址中。
空:不关心退出状态

父进程等待子进程退出,并收集进程的退出状态,若子进程的退出状态不被收集,变成僵尸进程。

若正常终止子进程返回的状态,则为真,执行WEXITSTATUS(staus)取子进程传送给exit、_exit、_Exit参数的低8位。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    pid_t pid;
    int cnt = 0;
    int staus=10;
    pid = fork();
    if(pid > 0){
        wait(&staus);
        //waitpid(pid,&status,WNOHANG);父进程不阻塞,也产生僵尸进程。
        printf("child quit,status=:%d\n",WEXITSTATUS(staus));
        while(1){
            printf("cnt=%d\n",cnt);
            printf("this is father print,pid =%d\n",getpid());
            sleep(1);
        }
    }else if(pid==0){
        while(1){
            printf("this is child print,pid=%d\n",getpid());
            sleep(1);
            cnt++; 
            if(cnt == 3){
                exit(3);
            }
        }
    }
    return 0;
} 

孤儿进程

父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程
Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    pid_t pid;
    int cnt = 0;
    pid = fork();
    if(pid > 0){
            printf("this is father print,pid =%d\n",getpid());
    }else if(pid==0){
        while(1){
  printf("this is child print,pid=%d,my father pid=%d\n",getpid(),getppid());
            sleep(1);
            cnt++; 
            if(cnt == 3){
                exit(3);
            }
        }
    }
    return 0;
} 

exec族函数

execl, execlp, execle, execv, execvp, execvpe
功能
  在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件

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[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

返回值
  exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
  
参数说明
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。

execl、execlp

带l的一类exac函数(l表示list),包括execl、execlp、execle,要求将新程序的每个命令行参数都说明为 一个单独的参数。这种参数表以空指针结尾。

带p的一类exac函数,包括execlp、execvp、execvpe,如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。举个例子,PATH=/bin:/usr/bin

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//int execl(const char *path, const char *arg, ...);
//int execlp(const char *file, const char *arg, ...);
int main()
{
    printf("before execl****\n");
   // if(execl("/bin/date","date",NULL) == -1)
    if(execlp("date","date",NULL) == -1)
    {
        printf("execl failed!\n");
    }
    printf("after execl\n");
    return 0;
}


execvp

带v不带l的一类exac函数,包括execv、execvp、execve,应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//int execvp(const char *file, char *const argv[]);
int main(void)
{
    printf("before execlp\n");
    char *argv[] = {"ps","-l",NULL};
    if(execvp("ps",argv) == -1) 
    {
        printf("execvp failed!\n");     
    }
    printf("after execlp\n");
    return 0;
}

execle

带e的一类exac函数,包括execle、execvpe,可以传递一个指向环境字符串指针数组的指针。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//int execle(const char *path, const char *arg,..., char * const envp[]);
char *env_init[] = {"AA=aa","BB=bb",NULL};
int main(void)
{
    printf("before execle****\n");
        if(execle("./bin/echoenv","echoenv",NULL,env_init) == -1)
        {
                printf("execle failed!\n");
        }       
    printf("after execle*****\n");
    return 0;
}
//文件echoenv.c
#include <stdio.h>
#include <unistd.h>
extern char** environ;
int main(int argc , char *argv[])
{
    int i;
    char **ptr;
    for(ptr = environ;*ptr != 0; ptr++)
        printf("%s\n",*ptr);
    return 0;
}

结果

AA=aa
BB=bb

exec配合fork使用

建一个config.txt
SPEED=3
LENG=0
SCORE=9
LEVEL=5

建一个changeData.c//之前文件那章写过

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char **argv)
{
 	int fdSrc;
 	char *readBuf=NULL;
 	if(argc !=2){
  		printf("error\n");
  		exit(-1);
 	}
 	fdSrc = open(argv[1],O_RDWR);
	int size= lseek(fdSrc,0,SEEK_END);
	lseek(fdSrc,0,SEEK_SET);
 	readBuf=(char *)malloc(sizeof(char)*size +8);
 	int n_read = read(fdSrc,readBuf,size);
 	char *p=strstr(readBuf,"LENG=");
 	if(p==NULL){
		printf("not found\n");
		exit(-1);
	}
	p=p+strlen("LENG=");
	*p='5';
	lseek(fdSrc,0,SEEK_SET);
	int n_write =write(fdSrc,readBuf,strlen(readBuf));
 	close(fdSrc);
 	return 0;
}

gcc changeData.c -o changeData生成可执行文件changeData

#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
 pid_t pid;
 int data=10;
while(1){
	prinyf("please input a data\n");
	scanf("%d",&data);
	if(data==1){
		pid=fork();
		if(pid>0){
			wait(NULL);
		}
		if(pid==0){
			execl("./changeData","changeData","config.txt",NULL);
		}
	}else{
		print("wait,do nothing\n");
		}
}
 return 0; 
}

system函数

int system(const char * string);

system与exec族函数的区别:

1、system()和exec()都可以执行进程外的命令,system是在原进程上开辟了一个新的进程,但是exec是用新进程(命令)覆盖了原有的进程。
2、system()和exec()都有能产生返回值,system的返回值并不影响原有进程,但是exec的返回值影响了原进程。
3、system需要先启动一个shell才能运行指定的命令,调用system函数执行指定命令时,原进程会暂停等待,之后再继续进行;调用exec函数开启新进程后,原进程将被直接关闭。

返回值:
成功,则返回进程的状态值;
当sh不能执行时,返回127;
失败返回-1;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{      //int system(const char * string);
        //execl("/bin/date","date",NULL);
        if(system("date") == -1){
                printf("execl failed!\n");
                perror("why");
        }
        printf("after execl\n");
        return 0;
}

popen函数

FILE popen( const char command, const char* mode )

popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell 以运行命令来开启一个进程。这个进程必须由 pclose() 函数关闭。
比system在应用中的好处:
可以获取运行的输出结果

mode: 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。

返回值:如果调用 失败,或者不能分配内存将返回NULL,否则返回标准 I/O 流。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
        char ret[1024] = {0};
        FILE *fp;
        fp = popen("date","r");
        int nread = fread(ret,1,1024,fp);
        printf("read ret %d byte , ret:%s\n",nread,ret);
        return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值