进程、进程控制编程

进程的概念:进程是一个具有独立功能的程序的一次运行活动、 资源分配的最小单元。

进程与程序:1、程序是放到磁盘里的可执行文件(静态的、长久的)

                        2、进程是指程序执行的实例(动态的、短暂的)一个状态的变化过程

                        通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序。

创建:父进程(PPID)—>子进程(PID)——>孙进程              启动进程的用户ID(UID

在Linux系统中,进程具有并行性、互不干扰等特点。一个进程发生异常不会影响其他进程。

(1)、“数据段”存放的是全局变量、常数以及动态数据分配的数据空间;

(2)、“代码段”存放的是程序代码的数据。

(3)、“堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。 

进程互斥:是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。

 

1、获取ID:getpid

#include

#include

pid_t getpid(void)        获取本进程ID。

pid_t getppid(void)        获取父进程ID

#include 
#include 
#include 

int main()
{
	printf("pid : %d\n", getpid());
	printf("ppid : %d\n", getppid());

	while (1);
	return 0;
}

【注】:加while死循环是为了让这个进程不结束,才可以获取id

进程、进程控制编程_第1张图片

2、fork

#include

pid_t fork(void)

功能:创建子进程      

fork的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值:

返回值: 0: 子进程 //        子进程ID(大于0):父进程          //   -1: 出错

(1)、父进程创建子进程

#include 
#include 
#include 
#include 

int main()
{
	pid_t pid;
	int a = 0;

	pid = fork();   //创建子进程 
	if (-1 == pid)   //失败返回-1
	{
		perror("fork");
		exit(1);
	}
	else if (0 == pid)    //子进程
	{	
		a++;
		printf("Child a = %d %p\n", a, &a);
		printf("This is ChildProcess pid %d ppid %d!\n", getpid(), getppid());
		printf("%d\n", pid);
	}
	else                  //父进程
	{
		a++;
		printf("Parent a = %d %p\n", a, &a);
		printf("This is ParentProcess %d!\n", getpid());
		printf("%d\n", pid);
	}


	return 0;
}

进程、进程控制编程_第2张图片

值得注意的是fork( )创建子进程是大多数情况是先返回0,再返回子进程的ID。

(2)、父进程写,子进程读  fork实现

​
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void ReadFile()
{	
	char buf[32] = {0};

	int fd = open("file.tmp", O_RDONLY);
	if (-1 == fd)
	{
		perror("open");
		exit(1);
	}

	int ret = read(fd, buf, sizeof(buf));
	if (-1 == ret)
	{
		perror("read");
		exit(1);
	}
	printf("Child read from txt : %s\n", buf);
}

void WriteFile()
{
	char buf[32] = "helloworld";

	int fd = open("file.tmp", O_WRONLY | O_CREAT | O_EXCL, S_IRWXU);
	if (-1 == fd)
	{
		perror("open");
		exit(1);
	}

	int ret = write(fd, buf, strlen(buf));
	if (-1 == ret)
	{
		perror("write");
		exit(1);
	}
}

int main()
{
	pid_t pid;

	pid = fork();
	if (-1 == pid)
	{
		perror("fork");
		exit(1);
	}
	else if (0 == pid)   //子进程读数据
	{
		sleep(1);
		ReadFile();
	}
	else                 //父进程写数据
	{
		WriteFile();
		sleep(2);
	}

	return 0;
}

​

解决方法在wait处。

(3)、第二种方法

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void ReadFile(int fd)
{	
	char buf[32] = {0};

	lseek(fd, 0, SEEK_SET);

	int ret = read(fd, buf, sizeof(buf));
	if (-1 == ret)
	{
		perror("read");
		exit(1);
	}
	printf("Child read from txt : %s\n", buf);
}

void WriteFile(int fd)
{
	char buf[32] = "helloworld";

	int ret = write(fd, buf, strlen(buf));
	if (-1 == ret)
	{
		perror("write");
		exit(1);
	}
}

int main()
{
	pid_t pid;
	int fd;

	fd = open("file.tmp", O_RDWR | O_CREAT | O_EXCL, S_IRWXU);
	if (-1 == fd)
	{
		perror("open");
		exit(1);
	}

	pid = fork();
	if (-1 == pid)
	{
		perror("fork");
		exit(1);
	}
	else if (0 == pid)   //子进程读数据   子进程可以继承父进程已经打开的文件描述符
	{
		sleep(1);
		ReadFile(fd);
	}
	else                 //父进程写数据
	{
		WriteFile(fd);
		sleep(2);
	}

	return 0;
}

进程、进程控制编程_第3张图片

count++被父进程、子进程一共执行了两次,为什么count的第二次输出为什么不为2?

图解:

进程、进程控制编程_第4张图片

子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享。 在子进程中对count进行加1的操作,并没有影响到父进程中的count值,父进程中的count值仍然为0

 

3、vfork (另一种进程创建方式)

#include

#include

pid_t vfork(void)

功能:创建子进程

#include 
#include 
#include 
#include 

int main()
{
	pid_t pid;
	int a = 0;

	pid = vfork();
	if (-1 == pid)
	{
		perror("vfork");
		exit(1);
	}
	else if (0 == pid)   //子进程  子进程先运行
	{
		sleep(2);
		a++;
		printf("Child Process a = %d\n", a);
		exit(1);     //子进程指定退出方式
	}
	else      //父进程
	{
		a++;
		printf("Parent Process a = %d!\n", a);
	}
	return 0;
}

进程、进程控制编程_第5张图片

区别: 1.   fork:子进程拷贝父进程的数据       vfork:子进程与父进程共享数据

             2.   fork:父、子进程的执行次序不确定        vfork:子进程先运行,父进程后运行

值得注意的是vfork创建的子进程必须显示调用exit(0);来结束,否则子进程不能结束!

还有一条:子进程先运行父进程后运行。这点必须和fork区分开。

 

4、exec函数族         用被执行的程序替换调用它的程序。

Int execl(const char *path, const char *arg,….)

path  可执行进程的绝对路径

(1)、execl           execl(“可执行进程的绝对路劲”);

#include 
#include 
#include 
#include 

int main()
{
	pid_t pid;
	//char *argv[] = {"./test", "hello", "world", "123", NULL};

	pid = vfork();
	if (-1 == pid)
	{
		perror("vfork");
		exit(1);
	}
	else if (0 == pid)
	{
		execl("/mnt/hgfs/share/0815/Process/test", "./test", "hello", "world", "123", NULL);
		sleep(2);
	}
	else
	{
		printf("This is Parent Process %d!\n", getpid());
	}
	return 0;
}

进程、进程控制编程_第6张图片

可以看出孤儿进程出现了,这是因为在vfork中子进程遇到execl( ),父进程就不用等子进程结束,遇到execl父进程也开始运行了,至于父和子谁先输出,这就看概率了。

被调用函数

#include 

int main(int argc, char *argv[])
{
	int i;
	printf("%d\n", getpid());
	for (i = 1; i < argc; i++)
	{
		printf("%s\n", argv[i]);
	}
	return 0;
}

(2)、execv  

#include 
#include 
#include 
#include 

int main()
{
	pid_t pid;
	char *argv[] = {"./test", "hello", "world", "123", NULL};

	pid = vfork();
	if (-1 == pid)
	{
		perror("vfork");
		exit(1);
	}
	else if (0 == pid)
	{
		execv("/mnt/hgfs/share/0815/Process/test", argv);
		sleep(2);
	}
	else
	{
		printf("This is Parent Process %d!\n", getpid());
	}
	return 0;
}

进程、进程控制编程_第7张图片

 

5、wait   /    waitpid

(1)、pid_t wait (int * status)

功能:阻塞该进程,直到其某个子进程退出。

#include 
#include
#include                                                  
#include
#include
#include
#include
#include

int main()
{
	pid_t pid;
	  
	pid = fork();    //创建子进程
    if(-1 == pid)    //失败返回-1
    {
        perror("fork");
        exit(1);
    }
	
	else if( 0 == pid)    //子进程
	{	
		sleep(1);
        printf("this is child process!\n");
		exit(100); 
    }
    else      //父进程
    {	
        int status;
        printf("this is Parent process\n");
		wait(&status);          //1、等待子进程结束(阻塞)   2、回收子进程资源
		if(WIFEXITED(status))    //判断子进程是否正常退出
		{
			printf("child exit normally %d !\n", WEXITSTATUS(status));    //获取子进程退出状态
		}
    }
	
	return 0;
}

进程、进程控制编程_第8张图片

(2)、waitpid 

pid_t waitpid (pid_t pid, int * status, int options)  // options一般为0

功能: 会暂时停止目前进程的执行,直到有信号来到或子进程结束

#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	pid_t pid;

	pid = fork();   //创建子进程 
	if (-1 == pid)   //失败返回-1
	{
		perror("fork");
		exit(1);
	}
	else if (0 == pid)    //子进程
	{	
		sleep(1);
		printf("This is ChildProcess pid!\n");
		exit(100);
	}
	else                  //父进程
	{
		int status;
		printf("This is ParentProcess!\n");
		waitpid(pid, &status, 0);
		if (WIFEXITED(status))    //判断子进程是否正常退出
		{
			printf("Child Exit Normally! %d\n", WEXITSTATUS(status));  //获取子进程退出状态
		}
	}


	return 0;
}

进程、进程控制编程_第9张图片

 

参数:如果不在意结束状态值,则参数status可以设成NULL。

参数pid为欲等待的子进程识别码:

pid<-1 等待进程组识别码为pid绝对值的任何子进程。

pid=-1 等待任何子进程,相当于wait()。

pid=0 等待进程组识别码与目前进程相同的任何子进程。

pid>0 等待任何子进程识别码为pid的子进程。

 

6、进程之间的通信方式 kill

首先写一个死循环:

#include 
 
int main()
{
    printf("%d\n", getpid());
    while(1)
    {   
        ;
    }   
 
    return 0;
}

(1)kill函数

#include 
#include 
#include 

int main()
{
    int id;
    scanf("%d", &id);
    kill(id, SIGKILL);
    return 0;
}

 这里明确一点:目前说的信号 1, 2, 啊之类的不是数字1,2,3,kill的选项:

SIGKILL 即 ‘9’

进程、进程控制编程_第10张图片

执行结果:

(2)进程、进程控制编程_第11张图片

(2)raise 函数

#include 
 
int main()
{
    printf("%d\n", getpid());
    
    raise(9);
    while(1)
    {   
        ;
    }   
 
    return 0;
}

自杀!!! 

 进程、进程控制编程_第12张图片

(3)、signal 函数

 #include
 typedef void (*sighandler_t)(int);
 sighandler_t signal(int  signum, sighandler_t  handler);

 当遇到信号signum就调用函数 handler

(1)、 接调用函数

进程、进程控制编程_第13张图片

此时执行ctrl -c或者是 kill -2 都会继续出现“helloworld”    (13683是 ./signal的PID  || 通过 ps -elf | grep signal查找);

 

(2)、SIG_IGN

#include 
#include 

void print(int num)
{
    printf("heloworld! %d\n", num);

}

int main()
{
    signal(2, SIG_IGN);
    while(1);
    return 0;
}

signal后面不调用函数换成SIG_IGN就能忽略信号

此时按 Ctrl-c是没有用的。

得按 ctrl -z 才可以终结进程 (Ctrl -z  不完全= kill -9

(3)、接 9 / SIGKILL

进程、进程控制编程_第14张图片

不可被忽略

 

 

你可能感兴趣的:(进程、进程控制编程)