Linux进程

目录

一、进程与程序的概念

 二、进程的创建与查看进程信息

 三、进程的退出与回收

 四、守护进程


一、进程与程序的概念

程序
存放在磁盘上的指令和数据的有序集合(文件)
静态的

进程
执行一个程序所分配的资源的总称
动态的

 进程和程序内容区别

Linux进程_第1张图片

BSS段:存放程序中未初始化的全局变量
数据段:已初始化的全局变量
代码段:程序执行代码
堆(heap):malloc等函数分配内存
栈(stack):局部变量,函数参数,函数的返回值
进程控制块(pcb):PID, 进程优先级,文件描述符表

 二、进程的创建与查看进程信息

示例代码:

#include 
#include 
#include
int main(int argc,char **argv)
{
	    pid_t pid;
		printf("before fork\n");		
		pid = fork();
		if(pid>0)
		{
			printf("This is father process\n");	
			printf("pid=%d\n",(int)pid);
			printf("father after fork\n");
			while(1)
			{
				sleep(1);	
				printf("father sleep\n");
			}
		}
		else if(pid==0)
		{
			printf("This is child process\n");	
			printf("pid=%d\n",(int)pid);
			printf("child after fork\n");
			while(1)
			{
				sleep(1);	
				printf("child sleep\n");
			}
			
		}
		else if(pid<0)
		{
			perror("fork");	
			exit(0);
		}
		return 0;
}

运行结果:

Linux进程_第2张图片

 运行逻辑:

首先,在父进程中,程序输出 "before fork",然后调用 fork 函数创建一个子进程。
在 fork 函数之后,父进程和子进程的代码是完全相同的,因为 fork 会复制当前进程的内存空间给子进程。
如果 fork 返回的是正整数,说明当前执行的是父进程。父进程输出 "This is father process" 和父进程的进程ID(pid),然后进入一个无限循环,每秒输出 "father sleep"。
如果 fork 返回的是 0,说明当前执行的是子进程。子进程输出 "This is child process" 和子进程的进程ID(pid),然后进入一个无限循环,每秒输出 "child sleep"。
如果 fork 返回的是负整数,说明 fork 函数调用失败,输出错误信息并退出程序。
由于父进程和子进程是并行运行的,它们的执行顺序可能不确定。在这个例子中,由于父进程和子进程都进入了无限循环,它们会交替执行,但具体哪个进程先运行、哪个先休息会取决于系统的调度策略。

3800的父进程ID(PPID)是2728,而3801的父进程ID(PPID)是3800。

注意:这个打印出的pid=3801,是子进程的pid。

        父进程打印了 "pid=3801",这是由于 printf("pid=%d\n",(int)pid); 这行代码中的 pid 是通过 fork() 函数返回的,它返回的是子进程的进程ID。所以,在父进程中,这个值是子进程的进程ID。父进程的进程ID是3800,而子进程的进程ID是由操作系统动态分配的。

        输出显示 "pid=3801",说明此时正在父进程中执行这行代码。在接下来的输出中看到 "This is father process" 和 "father after fork",这证实了这部分代码是在父进程中执行的。

总结:3801是父进程中 fork() 函数返回的子进程的进程ID,而不是父进程的进程ID。

Linux进程_第3张图片

查看进程信息:

ps     查看系统进程快照
top    查看进程动态信息
/proc  查看进程详细信息
ps 命令详细参数:
-e:显示所有进程
-l:长格式显示更加详细的信息
-f 全部列出,通常和其他选项联用

创建5个子进程:

         如果使用下列代码创建,会出现下图的运行结果,发现不止是父节点在创建子节点,子节点也在创建孙节点............。        

#include 
#include 
#include
int main()
{
	    pid_t pid;
		int i;    
		for(i=0;i<5;i++)
		{
			pid = fork();
			if(pid<0)
			{
				perror("fork");
				return 0;	
			}
			else if(pid==0)
			{
				printf("child process\n");
				sleep(5);
			//	break;	
			}
			else
			{
				printf("Father process\n");
				sleep(5);	
			}	
		}
		sleep(100);
}

Linux进程_第4张图片

 如果加入break就能顺利创建:

Linux进程_第5张图片

实现一个进程链,父进程->子进程->孙进程->重孙进程->重重孙进程

#include 
#include 
#include 
int main()
{
    pid_t pid;
    printf("I am A,mypid=%d\n",getpid());
    pid=fork();//创建子进程B
    if(pid==0)
	{
        printf("I am B,mypid=%d\n",getpid());
        pid_t pid1=fork();//创建孙子进程C
        if(pid1==0)
		{
            printf("I am C,mypid=%d\n",getpid());
            pid_t pid2=fork();//创建重孙进程D
            if(pid2==0)
			{
                printf("I am D,mypid=%d\n",getpid());
                pid_t pid3=fork();//创建重重孙进程E
                if(pid3==0)
				{
                    printf("I am E,mypid=%d\n",getpid());
                }
            }
        }
    }
	while(1)
	{
		sleep(1);
	}
return 0;
}

 三、进程的退出与回收

进程的回收:

#include 
#include 
#include 
#include 
 
int main(int argc, char** argv)
{	 
	   pid_t pid;
	   pid_t rpid;
	   pid = fork();
	   int status;
	   if(pid<0)
	   {
		   perror("fork");
		   return 0;
	   }
	   else if(pid == 0)
	   {
		   sleep(10);
		   printf("child will exit\n");
		   exit(2);
	   }
	   else if(pid >0)
	   {
		   sleep(20);
		   waitpid(-1,&status,WNOHANG);
		   printf("wait..\n");
		   printf("Get child status=%x\n",WEXITSTATUS(status));
	   }
	   while(1)
	   {
		   sleep(1);
	   }
}
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ vim wait.c
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ gcc -o wait wait.c
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ ./wait
child will exit
wait..
Get child status=2
^C
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ 
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ vim wait.c
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ gcc -o wait wait.c
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ ./wait
child will exit
wait..
Get child status=2
^C
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ cat wait.c
#include 
#include 
#include 
#include 
 
int main(int argc, char** argv)
{	 
	   pid_t pid;
	   pid_t rpid;
	   pid = fork();
	   int status;
	   if(pid<0)
	   {
		   perror("fork");
		   return 0;
	   }
	   else if(pid == 0)
	   {
		   sleep(10);
		   printf("child will exit\n");
		   exit(2);
	   }
	   else if(pid >0)
	   {
		   sleep(10);
		   waitpid(-1,&status,0);
		   printf("wait..\n");
		   printf("Get child status=%x\n",WEXITSTATUS(status));
	   }
	   while(1)
	   {
		   sleep(1);
	   }
}
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ 
waitpid 是一个用于等待子进程状态变化的系统调用函数。它的原型如下:
#include 
pid_t waitpid(pid_t pid, int *status, int options);

pid 参数指定了要等待的子进程的进程ID,可以有以下几种情况:
如果 pid 大于0,则等待进程ID为 pid 的子进程。
如果 pid 等于0,则等待和调用进程属于同一个进程组的任意子进程。
如果 pid 小于-1,则等待进程组ID为 pid 绝对值的任意子进程。
如果 pid 等于-1,则等待任意子进程。

status 参数是一个整数指针,用于存储子进程的退出状态。

options 参数指定一些选项:
0:阻塞等待子进程退出。
WNOHANG:非阻塞,如果没有子进程退出,则立即返回,不等待。
其他选项可以参考 waitpid 函数的文档,例如 WUNTRACED 和 WCONTINUED。
waitpid 函数的返回值是退出的子进程的进程ID,或者出错时返回-1。当使用 WNOHANG 选项时,如果没有子进程退出,返回0。

 进程的退出:

#include  
 pid_t  fork(void);
创建新的进程,失败时返回-1
成功时父进程返回子进程的进程号,子进程返回0
通过fork的返回值区分父进程和子进程


要点:1 子进程只执行fork之后的代码
2. 父子进程执行顺序是操作系统决定的。

子进程继承了父进程的内容
父子进程有独立的地址空间,互不影响
若父进程先结束
 子进程成为孤儿进程,被init进程收养
 子进程变成后台进程

若子进程先结束
 父进程如果没有及时回收,子进程变成僵尸进程
#include  
 #include  
 void  exit(int  status);
 void  _exit(int  status);

结束当前的进程并将status返回
exit结束进程时会刷新(流)缓冲区

return 和exit的区别
main函数结束时会隐式地调用exit函数,普通函数return是返回上一级。

 查看进程与修改进程优先级:

nice   按用户指定的优先级运行进程
     nice [-n NI值] 命令
NI 范围是 -20~19。数值越大优先级越低
普通用户调整 NI 值的范围是 0~19,而且只能调整自己的进程。
普通用户只能调高 NI 值,而不能降低。如原本 NI 值为 0,则只能调整为大于 0。
只有 root 用户才能设定进程 NI 值为负值,而且可以调整任何用户的进程。
renice   改变正在运行进程的优先级
	renice [优先级] PID

jobs   查看后台进程
bg     将挂起的进程在后台运行
fg      把后台运行的进程放到前台运行

ctrl+z  把运行的前台进程转为后台并停止。
./test &  把test程序后台运行

wait的使用: 

linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ vim execlp.c
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ gcc -o execlp execlp.c
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ ./execlp
father process
hello,world aa bbb
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ cat execlp.c
#include
#include
#include
int main()
{

	pid_t pid;
	pid = fork();
	if(pid == 0)
	{
		execlp("./test","test.c","aa","bbb","./",NULL);
		exit(0);
	}
	else if(pid > 0)
	{
		printf("father process\n");
		wait(NULL);
	}
}
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ cat test.c
#include
int main(int argc,char** argv)
{
	printf("hello,world %s %s\n",argv[1],argv[2]);
}
linux@linux:~/code_Linux/code_2024_1_23/Linux_day6$ 

为什么需要wait?

wait(NULL) 是一个系统调用,用于等待子进程的结束。
它确保父进程在子进程结束之前不会继续执行后续的代码。
如果没有 wait(NULL),父进程可能在子进程还没有结束时就继续执行,这可能导致意外的行为。

具体来说,wait(NULL) 的作用包括:
阻塞父进程,直到任意一个子进程结束。
获取子进程的终止状态(退出状态),该状态可以通过 WEXITSTATUS 宏来获取。
由于父进程调用了 wait(NULL),它会等待子进程的结束,并且在子进程结束后获取子进程的退出状态。
这确保了父进程在子进程完全结束之前不会继续执行。

如果不关心子进程的退出状态,也可以使用 waitpid 函数,或者简单地使用 wait(NULL)。
这样可以避免父进程在子进程执行期间继续执行。

用exec函数族函数实现 "ls -l -a" 命令

linux@linux:~/code_Linux/code_2024_1_22$ cd Linux_day5
linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ ls
linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ vim exec.c
linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ gcc -o exec exec.c
linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ ./exec
before exec
total 20
drwxrwxr-x 2 linux linux 4096 Jan 22 02:08 .
drwxrwxr-x 4 linux linux 4096 Jan 22 01:57 ..
-rwxrwxr-x 1 linux linux 7366 Jan 22 02:08 exec
-rw-rw-r-- 1 linux linux  183 Jan 22 02:08 exec.c
linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ cat exec.c
#include 
#include 
 
int main(){	 
	   printf("before exec\n");
	   if(execl("/bin/ls","ls","-a","-l","./",NULL)<0)
	   {
			 perror("execl");
	   }
		   
		   
}

 四、守护进程

概念:
守护进程又叫精灵进程(Daemon Process),它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。

特点:
始终在后台运行,独立于任何终端,周期性的执行某种任务或等待处理特定事件。
它是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。

举例:
http 服务的守护进程叫 httpd,mysql 服务的守护进程叫 mysqld。

更简便地创建守护进程: nohup 命令
nohup  xxxx  &

 守护进程创建步骤:

1.创建子进程fork()
2.退出父进程exit(0)
3.创建新会话setsid(),现在子进程脱离了终端的控制
4.改变工作目录,可以不改,只是更安全,chdir("/");将工作目录变更为根目录
5.开启所有权限umask(0)
6.关闭标准的输入输出与错误文件描述符close()
setsid函数:
pid_t setsid(void);
成功:返回调用进程的会话ID;失败:-1,设置errno。
调用了setsid函数的进程,既是新的会长,也是新的组长

getsid函数
pid_t getsid(pid_t pid)
成功:返回调用进程的会话ID;失败:-1,设置errno
1.pid为0表示察看当前进程session ID
2.ps ajx命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。
3.组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程。

getpid:pid_t getpid(void);       获取进程id
getpgid:pid_t getpgid(pid_t pid);   获取进程组id


#include 
#include 
#include 
#include 
int main(){
  pid_t pid;
  pid = fork();
  if(pid<0){
    perror("fork");
    return 0;
 
  }else if(pid>0){
    exit(0);
  }
  printf("I am a deamon\n");
  printf("sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
 
  if(setsid()<0){
    perror("setsid");
    exit(0);
  }  
 
  printf("after sid=%d,pid=%d,pgid=%d\n",getsid(getpid()),getpid(),getpgid(getpid()));
   
  chdir("/");
 
  if(umask(0)<0){
    perror("unmask");
    exit(0);
  }
   
  close(0);
  close(1);
  close(2);
  printf("after close \n");  
 
 
 
  sleep(100);
 
}

  列举三个GDB调试多进程程序的命令及其含义:

inferiors:
含义:显示当前正在调试的所有进程的列表。
用法:inferiors 或 info inferiors。
示例:在GDB中输入 inferiors 或 info inferiors 可以查看当前调试的所有进程,以及它们的描述、可执行文件路径等信息。

inferior :
含义:切换到指定的进程,其中  是目标进程的编号。
用法:inferior 。
示例:在GDB中输入 inferior 2 可以切换到编号为2的进程进行调试。

set follow-fork-mode :
含义:设置在fork系统调用时GDB应该如何继续调试。
用法:set follow-fork-mode ,其中  可以是 child、parent 或 ask。
示例:
set follow-fork-mode child:在fork后只跟踪子进程。
set follow-fork-mode parent:在fork后只跟踪父进程。
set follow-fork-mode ask:在fork后询问用户如何继续调试。

使用pthread_create实现 10 个子线程,并且让每个子线程打印自己的线程号

(运行结果:)

linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ vim thread_n.c
linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ gcc -o thread_n thread_n.c -lpthread
linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ ./thread_n
This is main Thread,tid = 3076303680
This is main Thread,tid = 3067910976
This is main Thread,tid = 3059518272
This is main Thread,tid = 3051125568
This is main Thread,tid = 3042732864
This is main Thread,tid = 3034340160
This is main Thread,tid = 3025947456
This is main Thread,tid = 3017554752
This is main Thread,tid = 3009162048
This is main Thread,tid = 3000769344
arg = 6
This is a Thread test,pid = 8513,tid = 3025947456
arg = 7
This is a Thread test,pid = 8513,tid = 3017554752
arg = 8
This is a Thread test,pid = 8513,tid = 3009162048
arg = 9
This is a Thread test,pid = 8513,tid = 3000769344
arg = 5
This is a Thread test,pid = 8513,tid = 3034340160
arg = 4
This is a Thread test,pid = 8513,tid = 3042732864
arg = 3
This is a Thread test,pid = 8513,tid = 3051125568
arg = 2
This is a Thread test,pid = 8513,tid = 3059518272
arg = 1
This is a Thread test,pid = 8513,tid = 3067910976
arg = 0
This is a Thread test,pid = 8513,tid = 3076303680
^C
linux@linux:~/code_Linux/code_2024_1_22/Linux_day5$ cat thread_n.c

(示例代码:)

#include
#include
#include
#include

void* testThread(void* arg)
{

	printf("This is a Thread test,pid = %d,tid = %lu\n",getpid(),(unsigned long int)pthread_self());
	printf("arg = %d\n",(int)arg);
	while(1)	
	{
		sleep(1);
	}
}

int main()
{
	pthread_t tid[10];
	int ret = 0;
	int i = 0;
	for(i = 0;i<10;i++)
	{
		ret = pthread_create(&tid[i],NULL,testThread,(void*)i);
		printf("This is main Thread,tid = %lu\n",(unsigned long int)tid[i]);
	}
	while(1)
	{
		sleep(1);
	}
	return 0;

}

Linux进程_第6张图片

你可能感兴趣的:(C语言,linux)