先写好代码:
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("I'm a process\n");
sleep(2)
}//写一个死循环,没两秒打印一次
return 0;
}
makefile
文件也写好:
mycode:code.c
gcc -o $@ $^ -std=c99
.PHONY:clean
clean
rm -f mycode
ps
是一个常用的 Unix/Linux 命令,用于显示当前系统中运行的进程信息。它的名称来源于 “process status”(进程状态)的缩写。通过 ps
命令,您可以查看正在运行的进程的各种信息,例如进程 ID、CPU 使用情况、内存占用、进程状态等。
常用的 ps
命令选项和参数:
-e
或 -A
:显示所有进程,等同于 -e
。
-f
:显示全格式,包括进程的 UID、PID、PPID、C、STIME、TTY、TIME 和 CMD 等信息。
-l
:以长格式显示进程信息,包括 UID、PID、PPID、C、PRI、NI、ADDR、SZ、WCHAN、TTY、TIME 和 CMD 等。
-u
:以用户格式显示进程信息,包括 USER、PID、%CPU、%MEM、VSZ、RSS、TTY、STAT、START 和 TIME。
-a
:显示所有进程,包括其他用户的进程。
-j
:以作业格式显示进程信息,包括 PID、PPID、PGID、SID、UID、C、STIME、TTY、TIME 和 CMD 等。
-x
:同时显示没有控制终端的进程(守护进程)。
综合来说,ps -ajx
命令会列出当前系统中所有进程的详细信息,包括进程的作业信息以及其他相关信息。
常见的 ps
命令用法包括:
ps
:显示当前用户的正在运行的进程。ps -ajx
:ps -ajx
是一个常用的 Unix/Linux 命令,用于显示当前系统中所有进程的详细信息,并以完整的格式输出ps -ajx | grep process_name
:查找特定进程名的进程。在输出中,第一个进程是你的可执行文件 mycode 的进程,第二个进程是由于你使用了 grep 命令进行字符串匹配而产生的 grep 进程 具体来说:
终止后:
进程的属性都在task_struct 里,而task_struct是操作系统内部的数据,我们想要访问内部的数据只能通过系统调用
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t id =getpid();
while(1)
{
printf("I'm a process,pid:%d\n",id);
sleep(2);
}//写一个死循环,没两秒打印一次
return 0;
}
getgid()
函数是一个系统调用,用于获取调用进程的有效组 ID(Group ID)。在 Linux 中,每个进程都属于一个或多个组,getgid()
函数返回调用进程的有效组 ID。其函数原型定义在 <unistd.h>
头文件中:
#include <unistd.h>
gid_t getgid(void);
其中,gid_t
是一个类型,通常是一个无符号整数类型,用于表示组 ID。
在Linux系统中,/proc文件系统提供了关于运行中进程的详细信息。你可以通过查看/proc文件系统中的特定目录和文件来获取有关进程的信息。下面是一些你可以在/proc文件系统中找到的有关进程的信息:
ls /proc/[PID]
来查看。
cat /proc/[PID]/status
查看。
cat /proc/[PID]/cmdline
查看。
ls -l /proc/[PID]/exe
查看。
ls /proc/[PID]/fd
查看。
在操作系统中,当一个进程(称为父进程)创建另一个新进程(称为子进程)时,父子进程之间建立了一种特殊的关系。这种关系具有以下特点和行为:
fork()
或类似系统调用创建的。几乎所有进程都是由其他进程创建的,因为通常情况下,操作系统启动时会先创建一个初始进程(通常是init进程或systemd),然后其他进程都是由这些初始进程创建的。但是,也有一些特殊情况下的进程,比如内核线程和守护进程,它们可能是由操作系统内核直接创建的,而不是由其他进程创建的。总体而言,大多数进程都是有其父进程创建的。
父进程和子进程之间的关系是一个重要的概念,它们之间的关系可以通过系统调用来获取。在Unix/Linux系统中,可以使用 getpid()
系统调用来获取当前进程的PID,使用 getppid()
系统调用来获取当前进程的父进程的PID。
以下是这两个系统调用的简要说明:
getpid()
:该系统调用返回调用进程的PID,即当前进程的PID。
getppid()
:该系统调用返回调用进程的父进程的PID,即当前进程的父进程的PID。
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = getpid(); // 获取当前进程的PID
pid_t ppid = getppid(); // 获取当前进程的父进程的PID
printf("PID: %d\n", pid);
printf("PPID: %d\n", ppid);
return 0;
}
我们要创建一个进程,那一定涉及到访问操作系统的内部数据,肯定也需要使用系统调用
fork
是一个系统调用(也是库函数),用于创建一个新的进程。它会复制调用进程(父进程)的内存和上下文,包括代码段、数据段、堆栈等,然后将这个副本分配给新创建的进程(子进程)。fork
系统调用的原型通常定义在 <unistd.h>
头文件中。
#include <unistd.h>
pid_t fork(void);
pid_t
是一个类型,用于表示进程 ID,fork
函数返回的是一个pid_t
类型的值。
fork
调用成功时,在父进程中返回子进程的 PID,而在子进程中返回 0。fork
调用失败,则返回一个负数,表示错误。在 fork
调用后,父子进程都会继续执行 fork
调用之后的指令,但是它们会在不同的地址空间中运行,即它们各自拥有独立的内存空间。这意味着,父进程和子进程之间的数据是相互独立的,任何一个进程对内存的修改都不会影响到另一个进程。
父子进程代码共享的原理是采用写时拷贝(copy-on-write)。在 fork
调用后,操作系统并不会立即复制父进程的内存给子进程,而是让父子进程共享同一段内存空间。只有当其中一个进程试图修改共享的内存时,操作系统才会复制该内存页,确保修改不会影响到其他进程。这样做可以节省内存,并提高效率。
fork()
是系统调用也是库函数
fork
在 Unix-like 系统中既是一个系统调用,也是一个库函数,可以这样理解:
fork
系统调用是由操作系统内核实现的,用于创建一个新的进程。当用户程序调用 fork
时,实际上是请求操作系统内核为其创建一个新的进程,这需要通过系统调用来完成。
fork
函数的封装,这意味着用户程序可以通过调用 fork
库函数来发起对 fork
系统调用的请求,而不必直接调用系统调用。
从用户程序的角度来看,fork
可以被视为一个库函数,因为它是通过调用库函数来实现的。
从操作系统内核的角度来看,fork
是一个系统调用,因为它需要通过内核来创建新的进程。
fork
函数在调用后会返回两次,这是因为它是一个复制当前进程的系统调用。下面是对这两个返回值的解释:
fork
返回新创建子进程的进程 ID(PID),这个 PID 是子进程的标识符,父进程通过这个 PID 可以识别并操作子进程。
fork
也会返回一个值,但是返回的是 0。这是因为在 Unix-like 系统中,子进程是通过复制父进程的地址空间而创建的,因此子进程从父进程继承了大部分的内存布局和数据。为了区分父进程和子进程,fork
在子进程中返回 0,表示这是子进程执行的代码路径。
fork
函数在调用后会创建一个新的子进程(在return之前就已经创建好子进程了),新的子进程拥有父进程的副本。因此,fork
在执行时会返回两次:一次在父进程中(返回子进程的 PID),另一次在子进程中(返回 0)。这样做是为了让父进程和子进程可以根据返回值来执行不同的代码路径。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
printf("before fork:I'm a process, pid:%d, ppid:%d\n",getpid(),getppid());
sleep(1);
pid_t id=fork();
if(id<0) return 1;//说明创建失败
else if(id==0)
{
//只有子进程能进这里,我们就可以让子进程做自己的事
printf("after fork:I'm a child process, pid:%d, ppid:%d\n",getpid(),getppid());
sleep(2);
}
else
{
//只有父进程能进这里
printf("after fork:I'm a father process, pid:%d, ppid:%d\n",getpid(),getppid());
}
return 0;
}
我们首先要知道:进程不是一直在运行的。进程放在了CPU上,也不是一直会运行的,可能在等待某种软硬件资源 而且计算机的资源相对来说一直是不够的,那么必然出现进程排队的情况,那么排队是怎么个排法呢?
task_struct
结构体在排队——放到一个队列里在操作系统中,进程的状态可以分为多种,常见的包括运行、阻塞和挂起。每种状态都对应着一个状态队列,用于存储处于相应状态的进程
num
:表示队列中当前的进程数量,即队列长度。q
:一个指针,指向保存了 PCB(Process Control Block,进程控制块)信息的结构体。这个结构体可能是链表或其他数据结构,存储了就绪状态的进程的信息。#include<stdio.h>
int main()
{
int a=0;
scnaf("%d",&a);
printf("%d",a);
return 0;
}
这个过程:
scanf()
函数时,它会尝试从标准输入读取数据。如果标准输入是终端设备(比如键盘),则进程需要等待用户输入数据。此时,操作系统将会将进程状态从运行状态更改为阻塞状态,表示进程暂时无法继续执行,因为它在等待外部事件的发生。
task_struct
将从运行队列中移除,并加入到描述键盘的结构中,以便在键盘输入数据后能够唤醒这个进程。
printf()
函数,并输出用户输入的数据。然后进程执行完成,返回0,最终退出。
一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)进程的状态通常由几个不同的状态标识符表示。以下是一些常见的进程状态及其在内核源代码中的定义:
static const char* const task_state_array[] =
{
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
好啦这次就到这里啦!!!也是才结束51假期,希望51数学建模能拿个不错的奖项吧 感谢大家支持