linux系统编程1

=============================================================

系统编程技术点

1、进程的概念、接口函数

2、进程间的通信方式

3、进程间的信号集

4、线程的概念,函数接口,意义

5、线程之间的通信

6、线程池

进程的概念

进程:正在运行的程序
程序:hello.c ==> gcc hello.c -o hello (二进制程序)
进程:./hello (进程)

创建进程

在命令终端执行一个程序就产生一个进程: ./project
创建进程时,操作系统会产生一个结构体 : task_struct{}
产生一个任务管理结构体:结构体包含进程所需要的所有资源,包括当前进程状态,内存资源,进程ID等等。

linux中的进程

查看进程的相关命令:
ps -aux
ps -ef
top :动态显示当前系统中的所有进程
pstree : 树状图显示当前系统中的所有进程
例子:
 systemd─┬─ManagementAgent───6*[{ManagementAgent}]
 ├─ModemManager─┬─{gdbus}
 │ └─{gmain}
 ├─NetworkManager─┬─dhclient
 │ ├─dnsmasq
 │ ├─{gdbus}

注释:所有的进程有一个共同的源头 systemd (在Ubuntu14之前的版本,这个进程叫做init进程:祖进程 所有的进程都由这个进程、
产生。

进程的状态

就绪态、执行态、暂停态、睡眠态、僵尸态、死亡态
在这里插入图片描述
注释:进程是由另一个进程产生,产生这个进程的进程称为这个进程的父进程。

进程的相关函数

函数一:
pid_t fork(void);
函数功能:创建一个子进程
头文件:#include <unistd.h>
例:使用fork函数创建一个子进程,父进程中,每隔1秒,输出一个father,子进程中,每隔1秒,输出一个child

#include <stdio.h>
#include <unistd.h>
int main()
{
    
    
	int i = 10;
	pid_t pid;
	printf("hello world\n");
	//创建一个子进程
	pid = fork();
	//父进程执行部分
	if(pid > 0){
    
    
		i = 11;
		while(1){
    
    
			printf("In father code : i = %d\n", i);
			sleep(1);
		}
	}
	//子进程执行部分
	if(pid == 0){
    
    
		while(1){
    
    
			printf("In child code : i = %d\n", i);
			sleep(1);
		}
	}
	if(pid < 0){
    
    
		perror("fork failed");
	}
	return 0;
}

注意:
(1)fork创建子进程,父子进程相互独立,没有先后关系。
(2)父子进程的变量所占用的空间都是独立的
(3)父进程执行所有的代码,子进程执行fork下面的代码。
在这里插入图片描述
函数二:
pid_t getpid(void):查看自身进程的ID号
pid_t getppid(void):查看父进程的ID号
头文件:
#include <sys/types.h>
#include <unistd.h>
函数三:
1、pid_t wait(int *status):父进程阻塞等待回收子进程的资源
头文件:
#include <sys/types.h>
#include <sys/wait.h>

练习:获取子父进程的pid号

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
    
    
	pid_t pid;
	pid = fork();
	printf("pid = %d\n", pid);
	if(pid > 0)
	{
    
    
		printf("father = %d\n", getpid());
		sleep(1);
	}
	if(pid == 0)
	{
    
    
		printf("pid = %d, ppid =%d\n ", getpid(), getppid());
	
	}
	return 0;
}

2、pid_t waitpid(pid_t pid, int *status, int options);

参数:
	status : int类型的指针,存放进程返回的状态
	pid : 
		<-1 :父进程等待ID为pid的绝对值的进程
		-1 :等待任意一个子进程 (相当于wait)
		0 : 等待进程组中的任意一个进程
		>0 : 等待指定的子进程
	options :	
		WNOHANG :如果没有子进程退出,函数会立即返回(函数不会阻塞)
		WUNTRACED :监听子进程状态,阻塞
		WCONTINUED :阻塞监听子进程的恢复信号

返回值:成功 返回状态改变的子进程的pid	
		WNOHANG :没有进程状态改变,返回0, 失败 -1;

练习:
父进程创建2个子进程,父进程等待两个子进程结束,自己再结束。
子进程输出自己的进程号。进程号小的先结束。(越先创建的进程,进程号越小)

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

int main()
{
    
    
	pid_t pid1, pid2;
	pid1 = fork();
	if(pid1 < 0)
	{
    
    
		perror("fork failed");
		return -1;
	}
	if(pid1 > 0)
	{
    
    
		pid2 = fork();
		if(pid2 > 0)
		{
    
    
		  sleep(5);//延时5秒后退出
		}
		if(pid2 < 0)
		{
    
    
			perror("fork 2 failed");
			return -1;
		}
		if(pid2 == 0)
		{
    
    
			usleep(1000);
			printf("child2 pid:%d\n", getpid());
			usleep(1000);
		}
	}	
	if(pid1 == 0)
	{
    
    
		printf("child1 pid:%d\n", getpid());
	}
	return 0;
}
回收子进程资源

子进程结束,进入僵尸态。此时子进程拥有进程执行需要的资源(内存资源…)
此时需要父进程进行回收。如果父进程没有回收wait,并且父进程没有结束。子进程变成
僵尸进程: 资源未被回收,执行完毕的进程,父进程未结束。
回收方式:
(1)父进程调用wait/waitpid回收资源
(2)父进程结束,子进程由系统守护进程systemd统一回收

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
int main()
{
    
    
	pid_t pid;
	pid = fork();
	if(pid < 0)
	{
    
    
		perror("fork failed");
		return -1;
	}
	//创建子进程
	if(pid > 0)
	{
    
    
		pid_t pid1;
		pid1 = fork();
		if(pid1 < 0)
		{
    
    
			perror("fork failed");
			return -1;
		}
		//父进程
		if(pid1 > 0)
		{
    
    
			//father
			printf("In father\n");
			wait(NULL);
			wait(NULL);
		}
		//子进程2
		if(pid1 == 0)
		{
    
    
			//child 2
			int i;
			for(i=0; i<5; i++)
			{
    
    
				printf("child 2 %d\n", getpid());
				sleep(1);
			}
		}
	}
	//子进程1
	if(pid == 0)
	{
    
    
		int i;
		for(i=0; i<3; i++)
		{
    
    
			printf("child 1 %d helloworld\n", getpid());
			sleep(1);
		}
	}
	return 0;
}

进程退出函数
函数功能:结束进程
#include <unistd.h>

void _exit(int status);		//直接结束进程,不清理输入输出缓冲区

#include <stdlib.h>

void exit(int status);		//结束进程之前,清理输入输出缓冲区
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    
    
	printf("helloworld");
	sleep(5);
	//_exit(1);	//直接结束进程
	exit(1);	//清理缓冲区之后,结束进程
	while(1);
	return 0;
}

进程结束的几种情况:
(1) 在main函数中执行 return
(2) 在任意一个函数中调用 exit()/_exit()
(3) 在主线程中调用 pthread_exit()
(4) 被信号 “杀死”

退出注册函数 (进程退出时执行的函数)
#include <stdlib.h>

int atexit(void (*function)(void));

参数:	
	参数为空,返回值为空的函数指针
返回值:成功

例:注册退出函数,在进程结束时使用。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void exit_fun(void)
{
    
    
	printf("my pid : %d\n", getpid());
}
int main()
{
    
    
	//注册退出执行函数
	printf("main pid : %d\n", getpid());
	int rt = atexit(exit_fun);
	if(rt != 0)
	{
    
    
		printf("atexit failed");
		return -1;
	}
	//exit(1);		//执行退出执行函数
	_exit(1);	//不执行退出执行函数
	printf("atexit success !\n");
	return 0;
}

编程小tips:
printf(“hello world”);与printf(“hello world\n”) 的区别
printf(“hello world”):hello world会存放到缓冲区,不会立即输出到屏幕
printf(“hello world\n”):hello world不会存放到缓冲区,立即输出到屏幕

//无输出
#include <stdio.h>
int main(){
    
    
	printf("hello world");  
	while(1);
}

//有输出
#include <stdio.h>
int main(){
    
    
	printf("hello world\n");  
	while(1);
}

猜你喜欢

转载自blog.csdn.net/weixin_42518229/article/details/103847146