PCB的概念:进程控制块
Linux
系统中
在这个路径之下有一个头文件
sched.h其中定义了进程控制块的结构体。
列出当前进程的详细信息的命令:
进程控制块的一些参数:
命令ulimit
什么叫进程可以使用的资源上线呢?举一个例子
在这里普通用户可以打开的文件数目最大是
1024. 这就是资源上限。
使用
ulimit -n 2048 命令修改软限制的时候发现硬限制也修改了,这不是很理想。
关闭中断(
exit)之后,重新打开终端,改动的限制就会重置为默认。
普通用户无法修改 超出
Hard limit 限制的数量,但是root
用户时刻已修改的。
虚拟地址
##重要图解
——重要知识点:
1、操作系统管理内存的单位是“页”(
page)
2、申请的内存的时候尽量是
page的整数倍,1page = 4096 B
3、内核空间在各个进程之间是共享的,为什么可以共享呢?因为进程只能读写用户空间, 而无法访问内核空间,不会发生冲突
4、代码
char str[10] = “hello”;为什么是不可以修改的呢?因为它存在只读空间,操作系
统的
page 被设置为只读的时候,就无法被修改
5
、inter CPU有4个工作级别,linux使用的时候只用到了
0 级别和3级别,0级别是内核 态,
3级别是用户态。当你调用驱动函数的时候CPU进入内核态,处在0级别的CPU
才有资格访问内核空间(
4G-3G),0级别状态可以对物理内存的页面进程读写操作,3 级别状态可以对虚拟地址进行读写操作。
从上往下是
3G--0C标准工作在用户空间。
进程环境
上边使用
environ 指针可以查看所有环境变量字符串,但是不够方便,如果给出name,在环境变量找他的value,可以使用getenv()函数。
NAME
getenv, secure_getenv - get an environment variable
SYNOPSIS
#include
char *getenv(const char *name);
char *secure_getenv(const char *name);
返回值是指向
value的指针,未找到返回NULL。
修改环境变量可以使用函数
setenv()
NAME
setenv - change or add an environment variable
SYNOPSIS
#include
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);
举例:
// function: 设置新的环境变量
#include
#include
int main(void)
{
printf("PATH=%s
", getenv("PATH"));
setenv("PATH", "hello", 1);
printf("PATH=%s
", getenv("PATH"));
return 0;
}
进程状态
被中断的进程信息保存在内核栈上。
##重要图解
进程原语
函数 fork()
NAME
fork - create a child process
SYNOPSIS
#include
pid_t fork(void);
RETURN VALUE
On success, the PID of the child process is returned in the parent, and
0 is returned in the child. On failure, -1 is returned in the parent,
no child process is created, and errno is set appropriately.
如果有子进程,那么子进程的
PID被返回给父进程;而在子进程返回0;
举例创建一个子进程
;
#include #include #include
int main(void)
{
int n = 10;
pid_t pid;
//调用1次返回2次,在父进程返回自进程的PID,在子进程返回0
pid = fork();//父子进程都已经存在
if(pid > 0) {
/*in parent*/
while(1) {
printf("I am parent %d
", n++);
printf("my pid = %d
", getpid());
printf("my parent pid = %d
", getpid());
sleep(1);
}
}
else if (pid == 0) {
while(1) {
printf("I am childi %d
", n++);
printf("my pid = %d
", getpid());
printf("my parent pid = %d
", getpid());
sleep(3);
}
}
else {
perror("fork");
exit(1);
}
return 0;
}
这里说明了 父进程与子进程内存数据不是共享的。
##重要图解
函数
getpid() 返回调用进程的
PID
函数
getppid() 返回调用进程父进程的PID
函数
getuid() 返回实际用户ID
函数
geteuid() 返回有效用户ID
函数
getgid()
函数
getegid()
这的
s为表示为设置用户ID,当我们写一个程序,然后将这个程序放在根目录之下,是这个程序的拥有者成为root,这个时候如果没有设置用户ID这一位的时候,其他用户是不能执行这个文件的。当我们
chmod 04755 filename 的时候设置用户ID,这时候其他用户执行该文件的时候,实际用户ID就是当前的普通用户,但是有效用户ID就是root;这同理与系统的passwd命令。这就是getpid()
和 getppid() 的区别。在上文中的文件权限为也有描述。
粘住位渐渐地已经废弃。
函数exec ()
NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file
SYNOPSIS
#include
extern char **environ;
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
DESCRIPTION
The exec() family of functions replaces the current process image with
a new process image.
函数名中有字母
P的时候,救护在path
环境变量中查找。
——重要知识点
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用失败则返回
-1,所以exec函数只有出错返回值。
——例1:
#include
#include
int main(void)
{
printf("hello
");
execl("/bin/ls", "ls", "-l", NULL);
//用于替换之前正在执行的程序
printf("world
");
return 0;
}
分析:结果是只打印了
exec调用之前的内容;原因就是,当调用到exec函数的时候,exec会覆盖之前的代码段,而且程序将会从exec调用结束,而不再执行其后的代码。
——例2:
程序test 功能是打印命令的内容
#include#include
int main(int argc, char *argv[])
{
int i = 0;
while(i < argc)
printf("%s
",argv[i++]);
return 0;
}
程序 a.out 执行上边的test
#include
#include
int main(void)
{
printf("hello
");
//execl("/bin/ls", "ls", "-l", NULL);
//用于替换之前正在执行的程序
execl("/home/xuxing/program/c_code/test", "./test", "aaa", "11", NULL);
printf("world
");
return 0;
}
——例3:
代码如下截图:
左边是将小写字母转大写。
右边的程序可以调用左边的程序将一个文件中的所有小写字母转换成大写字母。
##重要图解
这张图说明了
exec的工作原理,程序最终的返回是从exec
的调用出返回的。
和
shell工作的进程关系。
——重要知识点
操作系统的
API 一半在man
的第二章,C标准库的函数一半在man 的第三章。
函数wait/waitpid
NAME
wait, waitpid, waitid - wait for process to change state
SYNOPSIS
#include
#include
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop,int options);
The value of pid can be:
< -1 meaning wait for any child process whose process
group ID is equal to the absolute value of pid.回收指定进程组内的任意子进程
-1 meaning wait for any child process. 回收任意子进程,不管出于八个进程组
0 meaning wait for any child process whose process
group ID is equal to that of the calling process.
回收和当前调用waitpid一个组的所有子进程
> 0 meaning wait for the child whose process ID is
equal to the value of pid.回收指定ID的子进程
WNOHANG return immediately if no child has exited.
——例1:
#include#include#include
#include#include
int main(void)
{
int n = 10;
pid_t pid, pid_c;
//调用1次返回2次,在父进程返回自进程的PID,在子进程返回0
pid = fork();//父子进程都已经存在
if(pid > 0) {
/*in parent*/
while(1) {
printf("I am parent %d
", getpid());
pid_c = wait(NULL); //wait() 回收子进程的ID号
printf("wait for child pid = %d
", pid_c);
sleep(1);
}
}
else if (pid == 0) {
printf("I am childi %d
", getpid());
sleep(10);
}
else {
perror("fork");
exit(1);
}
return 0;
}
父进程一直存在,子进程在打印一次之后,进入睡眠状态,这时候子进程处于僵尸态(进程的代码空间
0--3G的空间都已经释放,但是子进程的结束状态还保存在内核的PCB中,为什么要保存呢?供父进程清楚子进程使用),那么父进程就会终结掉子进程。所以之后不在打印。
——重要知识点
1
、 上述代码中的wait(NULL)
属于阻塞回收子进程;
2
、 Kill进程组
kill -9 -23464
父进程创建的所有进程组默认与父进程属于同一进程组。
3、 waitpid
最重要的就是设置非阻塞回收将僵尸进程
僵尸进程:子进程退出,父进程没有回收子进程资源(
PCB)。则子进程编程僵尸进程
孤儿进程:父进程先于子进程结束,则子进程沦为孤儿进程
子进程的父进程称为
1号进程init,称为int进程领养孤儿进程