Linux-C 进程

Linux-C 进程

一、简述

     记--进程的概念,简单的创建并使用进程。进程是资源分配的最小单位。所有的进程都是1号进程的子进程,一个进程至少有一个线程,也可以有多个线程。

     每个进程一般都拥有4G的虚拟内存(真实并没有)。创建一个子进程,子进程也拥有独立的4G虚拟内存,并且复制父进程的运行状态(代码都是一样的,并且代码运行到哪都一样)

     子进程退出后一般进入“僵死态”,由其父进程回收资源,若是其父进程已经结束,则由父进程的父进程回收,以此类推。

二、创建进程

        2.1 fork()函数

功能 创建子进程
头文件  #include
 #include
函数原型 pid_t fork(void);
返回值 成功时,子进程的PID将在父进程中返回,并在子进程中返回0。 失败时,在父项中返回-1,不创建子进程,并正确设置errno。
备注 子进程资源、代码、内存状况都与父进程的一样,互不干扰。子进程就从fork()之后的代码执行。

 

       子进程资源、代码、内存状况都与父进程的一样,子进程从fork()函数之后的代码执行。

      Linux-C 进程_第1张图片

      Linux-C 进程_第2张图片

      虽然父进程与子进程代码相同,但是fork()的返回值不同,在父进程中返回值是子进程的PID,而在子进程中返回值是0。

      测试代码:

#include 
#include 
#include 

int main(int argc, char *argv[])
{
	printf("main\n");
	pid_t pid = fork();
	
	printf("pid=%d\n", pid);
	return 0;
}

      Linux-C 进程_第3张图片

      Linux-C 进程_第4张图片

 

      通过fork的返回值来区分父进程和子进程,从而执行不同的动作

      Linux-C 进程_第5张图片

     Linux-C 进程_第6张图片

三、退出进程

        3.1 exit()函数

功能 终止一个进程
头文件 #include
函数原型 void exit(int status);
参数 退出状态,常用宏EXIT_SUCCESS表示成功, EXIT_FAILURE表示失败
返回值
备注

1、status&0377的值(其实就是status的低8位)返回给父进程,在父进程可用wait()来获取。(0377-->011 111 111)

2、在退出进程前会先执行atexit或者是on_exit所登记的退出处理函数
3、清理IO缓冲区,假如退出程序之前输出缓冲区有数据则会输出。
4、 _exit:直接退出进程,不会进行多余的操作(例如不会输出缓冲区的内容)

      父进程等待子进程结束,并获取子进程的结束状态。使用宏WEXITSTATUS来获取。

     Linux-C 进程_第7张图片

    在退出进程前会先执行atexit或者是on_exit所登记的退出处理函数,

    atexit()函数

功能 注册一个函数,在程序退出前调用
头文件 #include
函数原型 int atexit(void (*function)(void));
参数 一个无返回值,无参数的函数指针
返回值 成功返回0,失败返回非0值

Linux-C 进程_第8张图片

 

on_exit()函数

功能 注册一个函数,在程序退出前调用
头文件 #include
函数原型 int on_exit(void (*function)(int , void *), void *arg);
参数

function是一个待参数的函数指针

arg是function指向函数的参数,以字符串形式

返回值 成功返回0,失败返回非0值
备注 func(int status, void *);一个是退出状态,一个是参数地址

测试代码

 

#include 
#include 
#include 
#include 

void func(int status, void *arg)
{
	printf("status=%d, arg=%s", status, (char *)arg);
}

int main(int argc, char *argv[])
{
	printf("main\n");

	pid_t pid = fork();

	on_exit(func," this is on_exit call\n");
	if(pid==0)
	{
		printf("this is child process!\n");
		exit(5);
	}
	

	printf("this is parent process!\n");
	

	return 0;
}

    Linux-C 进程_第9张图片

 exit退出会清理IO缓冲区,_exit()直接退出,不清理缓冲区

  Linux-C 进程_第10张图片

  Linux-C 进程_第11张图片

三、等待进程

      wait()函数

功能 等待进程改变状态
头文件 #include
#include
函数原型

pid_t wait(int *wstatus);

pid_t waitpid(pid_t pid, int *wstatus, int options);

参数 pid:是要等待的子进程的PID >0:等待进程号为pid的子进程
 =0:等待调用者所在进程组的  任一子进程
  -1:等待任一子进程退出(跟wait一样),可以不同进程组
小于-1:等待组ID的绝对值为pid的进程组 中的任一子进程
wstatus    是子进程的状态 比如子进程的退出状态
options:附加选项 0:正常引用该函数

WCONTINUED:报告任一从暂停态出来且从未报告过的子进程的状态

WNOHANG:非阻塞等待

WUNTRACED:报告任一当前处于暂停态且从未报告过的子进程的状态

返回值 成功返回退出的子进程的pid,失败返回-1,返回0则是设置了非阻塞等待,其没有出错但是又没有子进程退出
备注

如果不需要获取子进程的退出状态,stat_loc 可以设置为 NULL

WEXITSTATUS(status);返回子进程的退出状态。 这包括子项在exit(3)或_exit(2)的调用中指定的status参数的最低有效8位,或者作为main()中return语句的参数。 仅当WIFEXITED返回true时才应使用此宏。

Linux-C 进程_第12张图片

四、vfork()与exec()

功能 创建子进程并阻塞父进程
头文件 #include
#include
函数原型 pid_t vfork(void);
返回值 成功时,子进程的PID将在父进程中返回,并在子进程中返回0。 失败时,在父项中返回-1,不创建子进程,并正确设置errno。
备注 相对比于fork有以下特点:
              1、共享父进程的资源,也就是共同操作一份虚拟内存,数据相互影响。
              2、父进程进入睡眠,一直等到子进程结束或者是调用exec系列函数。
              3、专门用来于exec系列函数配合使用。

     资源共享,内存空间一样,共同操作一份数据。父进程进入睡眠,一直等到子进程结束。

    Linux-C 进程_第13张图片

    vfork()与exec()系列函数进行配合使用

功能 在进程中加载新的程序文件或者脚本,覆盖原有代码,重新运行。(调用第三方程序,成功调用原来程序就不继续执行了)
头文件 #include
函数原型

int execl(const char *path, const char *arg, ...); int execv(const char *path, char *const argv[ ]);

int execle(const char *path, const char *arg, ..., char * const envp[ ]);

int execlp(const char *file, const char *arg, ...); int execvp(const char *file, char *const argv[ ]);

int execvpe(const char *file, char *const argv[ ],char *const envp[ ]);

  path 即将被加载执行的 ELF 文件或脚本的路径
  file 即将被加载执行的 ELF 文件或脚本的名字
  arg 以列表方式罗列的 ELF 文件或脚本的参数
  argv 以数组方式组织的 ELF 文件或脚本的参数
  envp 用户自定义的环境变量数组
返回值 调用第三方程序,成功调用原来程序就不继续执行了,返回值就没用了。如果调用失败或出错就返回-1。
备注

1,函数名带字母 l 意味着其参数以列表(list)的方式提供。

2,函数名带字母 v 意味着其参数以矢量(vector)数组的方式提供。

3,函数名带字母 p 意味着会利用环境变量 PATH 来找寻指定的执行文件。

4,函数名带字母 e 意味着用户提供自定义的环境变量。

*在给execl()函数传参时,最后一个参数为NULL,以标识参数结尾。

 

子进程中成功调用了第三方程序,子进程后面的代码就不会执行了。如例子中的printf("in child i=%d\n", i);就不会执行。

Linux-C 进程_第14张图片

可以使用execlp()函数调用系统命令

Linux-C 进程_第15张图片

五、守护进程

    1、守护进程的概念

          类似于服务。通常创建的子进程受限于父进程,受限于组等。比如当某个信号是发送的该组的话,该组所有的进程都将收到影响。比如说在终端运行一个程序a,a程序会占用终端的控制资源,控制终端等程序a结束后才继续。而在控制终端下按下Ctrl+c(向子进程发出挂断信号),或者直接关掉终端,程序a都会被终端并退出。而守护进程就相当于独立出来,不再受组、父进程等影响的一个进程。例如在终端运行的一个程序,关闭终端后,守护进程不受影响,然可继续运行。

      Linux-C 进程_第16张图片

    2、将一个普通进程变为守护进程

          Linux-C 进程_第17张图片

代码:

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

int main(int argc, char *argv[])
{
	pid_t pid;
	int max_fd, i;
	
	//1 忽略挂断信号
	signal(SIGHUP, SIG_IGN);
	
	//2  创建子进程,并结束父进程
	pid = fork();
	if(pid>0)
		exit(0);
	
	//3 设置新的会话
	setsid();
	
	//4 创建子进程,并结束父进程
	pid = fork();
	if(pid>0)
		exit(0);
	
	//5 设置新的组
	setpgrp();
	
	//6 关闭所有描述符,(输出信息可以记录在系统日志,/var/log/syslog)
	max_fd = sysconf(_SC_OPEN_MAX);
	for(i=0;i

结果:

  可以使用命令:kill -s 9 进程ID  或   killall 进程名称          来结束进程

  由于关闭了所有打开的文件描述符(包括标准输入0,标准输出1、标准出错2),所以需要输出信息的的话,可以使用系统日志文件。
    void openlog(const char *ident, int option, int facility);//打开日志
    void syslog(int priority, const char *format, ...);//写入日志
    void closelog(void);//关闭日志

openlog()函数
功能 为程序打开到系统记录器的连接。(打开系统日志文件)
头文件 #include
原型 void openlog(const char *ident, int option, int facility);
参数

ident:添加到每条消息的前面,用来标识消息,通常设置为程序的名称。

option:指定控制openlog()操作和后续syslog()调用的标志。可以是下面一个或多个值的“或”

            LOG_CONS    如果系统日志服务器不能用,写入控制台

            LOG_NDELAY    立即打开连接,正常情况下,直到发送第一条消息才打开连接

            LOG_PERROR    打印输出到stderr(标准出错,通常是显示屏,不缓冲输出)

            LOG_PID        每条消息中包含进程 PID

facility:指定程序发送消息的类型。

            LOG_AUTHPRIV    安全授权消息

            LOG_CRON    时钟守护进程:cron和at

            LOG_DAEMON    其他系统守护进程

            LOG_KERN    内核消息

            LOG_LPR        打印机子系统

            LOG_MAIL    邮件子系统

            LOG_USER    默认

返回值
备注  
syslog()函数
功能 生成一条日志消息(将信息写入日志文件)
头文件 #include
原型 void syslog(int priority, const char *format, ...);
参数 priority:priority指定消息的重要性。

               LOG_EMERG    系统不能使用

               LOG_ALERT    立即采取措施

              LOG_CRIT    紧急事件

              LOG_ERR        出错条件

              LOG_WARNING    警告条件

              LOG_NOTICE    正常但重大事件

              LOG_INFO    信息消息

              LOG_DEBUG    调试信息

format:类似于printf函数,指定格式的字符串

返回值
备注 syslog在首次使用的时候自动打开日志文件,也就是说可以不调用openlog()函数。
closesys()函数
功能 关闭系统日志文件
头文件 #include
原型 void closelog(void);

linux系统上日志文件通常是/var/log/syslog

测试代码:将信息写到系统日志文件中

liang@ubuntu:~$ ./system
liang@ubuntu:~$ vi /var/log/syslog 
liang@ubuntu:~$ cat system.c
#include 
#include 
#include 
#include 
#include 

int daemon_init(void);

int main(void)
{
	int i = 10;
	
	//1、创建守护进程
	daemon_init();	
	
	//2、打开系统日志
	openlog("daemon_test", LOG_CONS | LOG_PID, LOG_DAEMON);
	
	//3、将信息写入系统日志
	while(i--)
	{
		syslog(LOG_DAEMON, "this is syslog test! %d\n", i);
		printf("printf test\n");//因为在创建守护进程时关闭了所有打开的描述符(包括标准输入输出、标准出错),所以这句话是没有打印的
		sleep(2);
	}
	
	
	//4、关闭系统日志
	closelog();

	return 0;
}

int daemon_init(void)
{
	int i, max_fd;
	pid_t pid;

	// 1 创建子进程
	pid = fork();
	
	if(pid<0)//创建子进程a失败
	{
		perror("fork faild!");
		exit(1);
	}
	else if(pid != 0)//退出a的父进程
	{
		exit(0);
	}

	//以下是a子进程部分
	
	// 2 忽略挂断信号
	signal(SIGHUP, SIG_IGN);

	// 3 设置会话id
	if(setsid() < 0)
	{
		exit(1);
	}

	// 4 子进程a再创建子进程aa
	if((pid = fork()) < 0)
	{
		perror("fork faild!");
		exit(1);
	}
	else if(pid != 0)//退出aa的父进程a
	{
		exit(0);
	}
	
	// 5 设置组id
	setpgrp();

	// 6 关闭所有打开的文件描述符
	max_fd = sysconf(_SC_OPEN_MAX);//获取到打开中最大的文件描述符
	for (i = max_fd; i>=0; i--)//逐个关闭
	{
		close(i);
	}

	// 7 设置掩码
	umask(0);

	// 8 改变工作路径
	chdir("/");

	return 0;
}

运行结果:

Linux-C 进程_第18张图片

Linux-C 进程_第19张图片

六、进程间通信

     1、管道

     2、信号

     3、套接字

     4、system V-IPC

等等。。。。。

你可能感兴趣的:(Linux)