Linux 进程

标题标题什么是程序,什么是进程

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

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

	使用 ps 指令查看,在实际工作中,配合 grep来查找程序中是否存在某一进程
	使用 top  指令查看,类似windows的任务管理器

什么是进程标识符

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

什么是父进程,什么是子进程

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

在这里插入图片描述
正文段: 由CPU执行的机器指令部分
初始化数据段:通常将此段称为数据段包含了程序中需明确地赋初值的变量
非初始化数据段: 通常将此段称为bss(block started by symbol)段。
栈 :自动变量以及每次函数调用时需保存信息都存放在此段中。每次调用函数时,其返回地址以及调用者的环境信息都存放在栈中,写入读取顺序为先进后出,后进先出。
堆 :通常在堆中进行动态存储分配。写入读取顺序为先进先出

使用fork函数创建一个进程

	#include <unistd.h>
	pid_t fork(void)
	返回值: 子程序返回0;父程序返回子程序ID   出错 返回-1;
	fork函数调用成功,返回两次;

由Fork 创建的新进程被称为于进程(child process)。 fork 函数被调用-但返回两次。两次返回的区别是子进程的返回值是 0,而父进程的返回值则是新建子进程的进程 ID。将字进程 D 返回给父进程的理由是:因为一个进程的子进程可以有至个,并且没有个函数使一个进程可以获得其所有子进程的进程 ID。Fork 使子进程得到返回值 。的理由是:一个进程只会有一个父进程,所以子进程总是可以调用 getppid 以获得其父进程的进程 (进程 ID。总是由内核交换进程使用,所以-一个子进程的进程 加D不可能为 0)
子进程和父进程继续执行 Fork 调用之后的令。子进程是父进程的副本。例如,子进程获得父进程数据空间、 堆和栈的副本。注意,这是子进程所拥有的副本。父进程和子进程并不共享这些存储空间部分。父进程和子进程共享正文段。
由于在fork 之后经常跟随着 exec,所以现在的很多实现并不执行一个父进程数据段栈和堆的完全副本。作为替代,使用了写时复制<Copy-on-Write,Cow)技术。这些区域由父进程和子进程共享,而且内核将它们的访问权限改变为只读。如果父进程和子进程中的任二个试图修改这些区城。则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储/系统中的一“页”

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>


int main()
{
	pid_t pid;
	pid_t pid2;

	pid = getpid();
	printf("before fork: pid = %d\n",pid );	

	fork();
	
	pid2 = getpid();
	printf("after fork: pid = %d\n",pid2 );	
	

	if(pid == pid2)
	{
		printf("this is father print\n");
	}
	else{
		printf("this is child print,child pid = %d\n",getpid());
	}

	return 0;
}

进程创建发生了什么

#include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4
  5 int main()
  6 {
  7     pid_t pid;
  8     int data=10;
  9
 10     printf("before fork : pid =%d\n",getpid());
 11
 12     pid = fork();
 13
 14     if(pid > 0)
 15     {
 16         printf("this is father pid = %d\n",getpid());
 17     }
 18     else if(pid == 0)
 19     {
 20         printf("this is child print,child pid = %d:\n",getpid());
 21         data+=100;
 22     }
 23
 24     printf("data : %d\n",data);
 25
 26     return 0;
 27
 28 }

运行结果
在这里插入图片描述

在执行fork函数操作时,子进程拷贝父进程一样的正文段,初始化的数据,为初始化的数据,堆和栈,子进程和父进程的内存内容一样,地址不一样,相互独立。早期的系统代码段是共享的, 代码中的pid_t pid; int data=10; 是数据段,子进程和父进程相互独立,子进程改变了data的值,子进程输出的是改变的值,父进程还是原来的值。
新的fork函数操作是父子进程之间内存的管理可以通过写时拷贝(copy on write),子进程不对数据或其他的不改变时,不去拷贝,去共享父进程中的内存。

fork创建一个子进程的一般目的

	一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的,父进程等待容户端的服务 请求。当这种请求到达时,父进程调用fork,使进程处理此请求。父进程则继续等待下一个服务请求到达。
socket 在收到一个连接请求时,创建子进程对接,
模拟fork函数socket编程
#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;
}

vfork函数创建进程

vfork 函数也可以创建进程,与fork有什么区别

  1. vfork直接使用父进程存储空间,不拷贝
  2. vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
	pid_t pid;
	int cnt;

	pid = vfork();
		
	if(pid > 0)
	{
		while(1){
			printf("this is father print, pid = %d\n",getpid());
			sleep(1);
		}	
	}
	
	else if(pid == 0){
		while(1){
			printf("this is chilid print, pid = %d\n",getpid());
			cnt++;
			sleep(1);
			if(cnt>=3)
			{
				exit(0);
			}
		}	
	}

	return 0;
}

运行结果

orangepi@orangepizero2:~/pro$ ./a.out
this is chilid print, pid = 5283
this is chilid print, pid = 5283
this is chilid print, pid = 5283
this is father print, pid = 5282
this is father print, pid = 5282
this is father print, pid = 5282
this is father print, pid = 5282
this is father print, pid = 5282

进程退出

正常退出

#include  <unistd.h>
void exit(int status);

#include <stdlib.h>
void exit(int status);

#include <stdlib.h>
void Exit(int status);
  1. main函数调用return;
  2. 进程调用exit(),标准c库
  3. 进程调用_exit()或_Exit(),属于系统调用
  4. 进程最后一个线程返回
  5. 最后一个线程调用pthread——exit

异常退出

  1. 主动调用abort函数
  2. 当进程收到某些信号时,如crtl+c
  3. 最后一个线程对取消(cancellation)请求做出响应
WIFEXITED(status)若为正常终止子进程返回状态,则为真。对于这种情况可执行WEXITSTATUS(status),获取子进程传送给exit 或_exit 参数的底8位
WIFSIGNALED(status)若为异常终止子进程返回状态,则为真(接到一个不捕捉的信号)。对于这种情况可执行WTERMSIG(status),获取子进程终止的信号编号。另外,有些实现(非signle,UNIX specifiation)定义宏WCOREDUMP(status),若产生终止进程的core文件,则返回真
WIFSTOPPED(status)若为当前暂停子进程返回状态,则为真。对于这种情况可执行WSTOPSIG(status),获取子进程暂停的信号编号。
WIFCONTINUED(status)若为作业控制暂停后子进程返回状态,则为真(POSIX.1的XSI扩展,仅用于waitpid)

父进程等待子进程退出

	#include <sys/types.h>
	#include <sys/wait.h>
	pid_t wait (int *status)
	pid_t wait (,pid_t pid,int *status,int options)
	int waitid(idtype_t idtype,id _t  id,siginfo_t *infop,int option)

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

	如果其所有子进程都还在运行,则阻塞,
	如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回
	如果它没有任何子进程,则立即出错返回
	wait()函数使用者阻塞,waitpid有一个选项,可以使调用者不阻塞

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

 #include <stdio.h>
  2 #include <stdio.h>
  3 #include <sys/types.h>
  4 #include <unistd.h>
  5 #include <stdlib.h>
  6
  7
  8 int main()
  9 {
 10     pid_t pid;
 11
 12     int cnt = 0;
 13     int status = 10;
 14
 15
 16     pid = fork();
 17
 18     if(pid > 0)
 19     {
 20             printf("this is father print, pid = %d\n",getpid());
 21     }
 22
 23     else if(pid == 0){
 24
 25         while(1){
 26             printf("this is chilid print, pid = %d,my father pid :%d\n",getpid(),getppid());
 27             sleep(1);
 28             cnt++;
 29             if(cnt == 5){
 30                 exit(3);
 31             }
 32         }
 33     }
 34
 35     return 0;
 36 }

exec族函数

大神写的exec族函数博客 Linux 进程exec族函数

system函数

#include <stdlib.h>
int system(const char *command)
函数详解
int system(const char * cmdstring)
{
  pid_t pid;
  int status;

  if(cmdstring == NULL){
      
      return (1);
  }


  if((pid = fork())<0){

        status = -1;
  }
  else if(pid == 0){
    execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
    -exit(127); //子进程正常执行则不会执行此语句
    }
  else{
        while(waitpid(pid, &status, 0) < 0){
          if(errno != EINTER){
            status = -1;
            break;
          }
        }
    }
    return status;
}

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

popen函数

	比 system 在应用中的好处 :可以获取运行的输出结果
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
int main(void)
{
    char ret[1024] = {0};
    FILE *fp;

    fp = popen("ps","r");
    int nread = fread(ret,1,1024,fp);	

    printf("read ret %d byte,\n ret=%s\n",nread,ret);
	    
    return 0;
}

运行结果

orangepi@orangepizero2:~/pro$ ./a.out
read ret 141 byte,
 ret=  PID TTY          TIME CMD
 3840 pts/0    00:00:00 bash
 4241 pts/0    00:00:00 a.out
 4242 pts/0    00:00:00 sh
 4243 pts/0    00:00:00 ps


总结

浅显的梳理一下知识点,发现有不对的,多多交流,相互学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值