alin的学习之路(Linux系统编程:五)(目录操作、进程相关概念)
1. 目录操作
1. 查看工作目录 getcwd
char *getcwd(char *buf, size_t size)
功能:
获取当前工作目录(绝对路径)
参数:
buf 保存工作路径的缓冲区
size 指定buf可以存储字节数
返回值:
成功 buf的起始地址
失败 NULL
#include <stdio.h>
#include <unistd.h>
#define SIZE 32
int main()
{
char buf[SIZE] = {0};
char* p = getcwd(buf,SIZE);
if(NULL == p)
{
perror("getcwd");
return 1;
}
printf("%s\n",buf);
return 0;
}
2. 改变工作目录 chdir
int chdir(const char *path);
功能:
改变当前进程工作目录
参数:
path 相对路径或者绝对路径
返回值:
成功 0
失败 -1
#include <stdio.h>
#include <unistd.h>
#define SIZE 32
int main()
{
char buf[SIZE] = {0};
char* p = getcwd(buf,SIZE);
if(NULL == p)
{
perror("getcwd");
return 1;
}
printf("%s\n",buf);
return 0;
}
itcast@ubuntu:~/classcode/day05$ ^C
itcast@ubuntu:~/classcode/day05$ cat 2chdir.c
#include <stdio.h>
#include <unistd.h>
#define SIZE 32
#include <string.h>
int main()
{
char *p = NULL;
char buf[SIZE];
memset(buf, 0, SIZE);
//获取当前进程工作路径
p = getcwd(buf, SIZE);
if (NULL == p)
{
perror("getcwd");
return 1;
}
printf("buf: %s\n", buf);
int ret = chdir("/home/itcast");
if(ret == -1)
{
perror("chdir");
return 1;
}
p = getcwd(buf, SIZE);
if (NULL == p)
{
perror("getcwd");
return 1;
}
printf("buf: %s\n", buf);
return 0;
}
3.打开目录、关闭目录 opendir\closedir
DIR *opendir(const char *name);
功能:
打开目录
参数:
name: 目录绝对路径或者相对路径
返回值:
成功 DIR指针类型
失败 NULL
int closedir(DIR *dirp);
功能:
关闭目录
参数:
dirp opendir函数的返回值
返回值:
成功 0
失败 -1
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main()
{
DIR * d = NULL;
d = opendir("/home/itcast");
if(NULL == d)
{
perror("opendir");
return 1;
}
printf("打开目录成功\n");
closedir(d);
printf("关闭目录成功\n");
return 0;
}
4. 读目录 readdir
struct dirent *readdir(DIR *dirp)
功能:
读目录
参数:
dirp opendir函数的返回值
返回值:
成功: 结构体指针
失败: NULL
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
d_type 文件的类型
d_name 文件名
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main()
{
DIR *dir = NULL;
struct dirent* d;
//打开目录
dir = opendir("/home/itcast");
if (NULL == dir)
{
printf("opendir failed...\n");
return 1;
}
while(1)
{
d = readdir(dir);
if(d == NULL)
break;
printf("type: %u, name:%s\n",d->d_type,d->d_name);
}
closedir(dir);
return 0;
}
2.时间相关函数
time_t time(time_t *tloc);
功能:
获取时间 从1970年1月1日 00:00:00到现在经历多少秒
参数:
tloc time_t类型指针 保存秒数,可以传NULL
返回值:
成功 返回秒数 从1970年1月1日 00:00:00到现在经历多少秒
失败 -1
char *ctime(const time_t *timep);
功能:
时间转换函数 将time_t类型的时间转化为字符串
参数:
timep time_t指针
返回值:
成功 字符串格式的时间
struct tm *localtime(const time_t *timep)
功能:
将time_t类型的时间转化为struct tm*类型的时间
参数:
timep time_t* 类型
返回值:
成功 struct tm指针
失败 (void*)-1
struct tm {
int tm_sec; /* Seconds (0-60) */ 秒
int tm_min; /* Minutes (0-59) */ 分钟
int tm_hour; /* Hours (0-23) */ 时
int tm_mday; /* Day of the month (1-31) */ 日
int tm_mon; /* Month (0-11) */ 月
int tm_year; /* Year - 1900 */ 年
int tm_wday; /* Day of the week (0-6, Sunday = 0) */ 星期
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */ 一年中第多少天
int tm_isdst; /* Daylight saving time */
};
#include <stdio.h>
#include <time.h>
int main()
{
time_t t; //time_t是long int类型
struct tm* tp;
t = time(NULL);
printf("time = %ld\n",t);
printf("ctime = %s\n",ctime(&t));
//转化 将秒数转化为年月日时分秒
tp = localtime(&t);
if ((void*)-1 == tp)
{
perror("localtime");
return 1;
}
printf("tm_sec %d\n", tp->tm_sec );
printf("tm_min %d\n", tp->tm_min );
printf("tm_hour %d\n", tp->tm_hour );
printf("tm_mday %d\n", tp->tm_mday );
printf("tm_mon %d\n", tp->tm_mon + 1 );
printf("tm_year %d\n", tp->tm_year+ 1900 );
printf("tm_wday %d\n", tp->tm_wday );
printf("tm_yday %d\n", tp->tm_yday );
//asctime函数
printf("asctime: %s\n", asctime(tp));
//gmtime函数
//转化 将秒数转化为年月日时分秒
tp = gmtime(&t);
if ((void*)-1 == tp)
{
perror("localtime");
return 1;
}
printf("tm_sec %d\n", tp->tm_sec );
printf("tm_min %d\n", tp->tm_min );
printf("tm_hour %d\n", tp->tm_hour + 8);
printf("tm_mday %d\n", tp->tm_mday );
printf("tm_mon %d\n", tp->tm_mon + 1 );
printf("tm_year %d\n", tp->tm_year+ 1900 );
printf("tm_wday %d\n", tp->tm_wday );
printf("tm_yday %d\n", tp->tm_yday );
return 0;
}
时间相关的函数根据manpage中的函数参数可返回值即可学会它们的使用
3. 进程相关概念及命令
1. 进程概念简述
-
进程和程序:
- 程序是一个可执行文件
- 进程是程序执行的过程
- 进程的状态是变化的,包括就绪态,运行态和阻塞态
- 进程是管理事务的基本单元。
-
单道与多道程序
- 单道程序:一个进程运行时其他进程阻塞等待
- 多道程序:时钟中断多个进程轮流运行
-
并行和并发
- 并行:任意时刻多个进程同时执行
- 并发:任意时刻仅有一个进程执行,多个进程每个进程执行一小段时间,轮流依次执行
-
MMU:
MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。
-
进程控制块PCB
- 进程运行时,内核为进程每个进程分配一个PCB(进程控制块),维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。
- 其内部成员有很多,我们掌握以下部分即可:
- 进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。
- 进程的状态,有就绪、运行、挂起、停止等状态。
- 进程切换时需要保存和恢复的一些CPU寄存器。
- 描述虚拟地址空间的信息。
- 描述控制终端的信息。
- 当前工作目录(Current Working Directory)。
- umask掩码。
- 文件描述符表,包含很多指向file结构体的指针。
- 和信号相关的信息。
- 用户id和组id。
- 会话(Session)和进程组。
- 进程可以使用的资源上限(Resource Limit)。
-
进程的状态
-
在三态模型中,进程状态分为三个基本状态,即运行态,就绪态,阻塞态。
在五态模型中,进程分为新建态、终止态,运行态,就绪态,阻塞态。
-
-
**①TASK_RUNNING:**进程正在被CPU执行。当一个进程刚被创建时会处于TASK_RUNNABLE,表示己经准备就绪,正等待被调度。
②TASK_INTERRUPTIBLE(可中断):进程正在睡眠(也就是说它被阻塞)等待某些条件的达成。一旦这些条件达成,内核就会把进程状态设置为运行。处于此状态的进程也会因为接收到信号而提前被唤醒,比如给一个TASK_INTERRUPTIBLE状态的进程发送SIGKILL信号,这个进程将先被唤醒(进入TASK_RUNNABLE状态),然后再响应SIGKILL信号而退出(变为TASK_ZOMBIE状态),并不会从TASK_INTERRUPTIBLE状态直接退出。
③TASK_UNINTERRUPTIBLE(不可中断):处于等待中的进程,待资源满足时被唤醒,但不可以由其它进程通过信号或中断唤醒。由于不接受外来的任何信号,因此无法用kill杀掉这些处于该状态的进程。而TASK_UNINTERRUPTIBLE状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程,于是原有的流程就被中断了,这可能使某些设备陷入不可控的状态。处于TASK_UNINTERRUPTIBLE状态一般总是非常短暂的,通过ps命令基本上不可能捕捉到。
**④TASK_ZOMBIE(僵死):**表示进程已经结束了,但是其父进程还没有调用wait4或waitpid()来释放进程描述符。为了父进程能够获知它的消息,子进程的进程描述符仍然被保留着。一旦父进程调用了wait4(),进程描述符就会被释放。
**⑤TASK_STOPPED(停止):**进程停止执行。当进程接收到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU等信号的时候。此外,在调试期间接收到任何信号,都会使进程进入这种状态。当接收到SIGCONT信号,会重新回到TASK_RUNNABLE。
-
stat中的参数意义如下:
参数 含义 D 不可中断 Uninterruptible(usually IO) R 正在运行,或在队列中的进程 S(大写) 处于休眠状态 T 停止或被追踪 Z 僵尸进程 W 进入内存交换(从内核2.6开始无效) X 死掉的进程 < 高优先级 N 低优先级 s 包含子进程 + 位于前台的进程组 l 多线种进程(如CLONE_THREAD, NPTL )
2. 进程相关命令
- ps 命令:查看当前系统的进程信息
常用选项:-aux 或 -ef
itcast@ubuntu:~/classcode/day05$ ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 1.3 168668 12808 ? Ss 00:44 0:03 /sbin/in
root 2 0.0 0.0 0 0 ? S 00:44 0:00 [kthread
root 3 0.0 0.0 0 0 ? I< 00:44 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< 00:44 0:00 [rcu_par
root 6 0.0 0.0 0 0 ? I< 00:44 0:00 [kworker
可以搭配管道和grep使用,查看指定信息
itcast@ubuntu:~/classcode/day05$ ps -aux | grep bash
itcast 1500 0.0 0.5 8540 5352 pts/0 Ss+ 00:58 0:00 -bash
itcast 3689 0.0 0.5 8540 5320 pts/1 Ss 01:46 0:00 -bash
itcast 15277 0.0 0.0 6300 740 pts/1 S+ 08:52 0:00 grep --color=auto bash
- top命令:动态查看当前系统的进程信息,类似Windows的任务管理器
itcast@ubuntu:~/classcode/day05$ top
top - 08:55:10 up 8:10, 2 users, load average: 0.00, 0.00, 0.00
Tasks: 190 total, 1 running, 189 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.
MiB Mem : 953.3 total, 119.1 free, 242.8 used, 591.4 buff/cache
MiB Swap: 1906.0 total, 1905.2 free, 0.8 used. 549.8 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+
754 root 0 -20 161148 7012 6036 S 0.3 0.7 0:35.77
3461 root 20 0 0 0 0 I 0.3 0.0 0:43.65
3688 itcast 20 0 13924 5788 4312 S 0.3 0.6 0:00.41
15326 itcast 20 0 9248 3784 3260 R 0.3 0.4 0:00.02
1 root 20 0 168668 12808 8576 S 0.0 1.3 0:03.82
2 root 20 0 0 0 0 S 0.0 0.0 0:00.01
3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00
- kill 命令
- 使用方法
- kill 进程号
- kill -信号编号 进程号
- kill -信号宏 进程号
- 所以使用kill 必须要得知进程编号,进程编号从ps -aux得来
- 使用方法
# sleep second 可以使程序睡眠second秒,用于创建一个进程,并使用kill杀死
# 终端1
itcast@ubuntu:~/classcode/day05$ sleep 1000
Terminated
itcast@ubuntu:~/classcode/day05$ sleep 1000
Killed
itcast@ubuntu:~/classcode/day05$ sleep 1000
Killed
# 终端2
itcast@ubuntu:~/classcode/day05$ ps -aux | grep sleep
itcast 15451 0.0 0.0 5476 592 pts/0 S+ 08:59 0:00 sleep 1000
itcast 15460 0.0 0.0 6432 664 pts/1 S+ 09:00 0:00 grep --color=auto sleep
itcast@ubuntu:~/classcode/day05$ kill 15451
itcast@ubuntu:~/classcode/day05$ ps -aux | grep sleep
itcast 15470 0.0 0.0 5476 596 pts/0 S+ 09:00 0:00 sleep 1000
itcast 15474 0.0 0.0 6432 672 pts/1 S+ 09:00 0:00 grep --color=auto sleep
itcast@ubuntu:~/classcode/day05$ kill -9 15470
itcast@ubuntu:~/classcode/day05$ ps -aux | grep sleep
itcast 15479 0.0 0.0 5476 592 pts/0 S+ 09:00 0:00 sleep 1000
itcast 15481 0.0 0.0 6432 732 pts/1 S+ 09:00 0:00 grep --color=auto sleep
itcast@ubuntu:~/classcode/day05$ kill -SIGKILL 15479
例如:杀死sleep进程的三种方法
1. kill 进程号
2. kill -9 进程号
3. kill -SIGKILL 进程号
- killall 命令 :杀死指定名称的所有进程
# 终端1
itcast@ubuntu:~/classcode/day05$ sleep 1000 &
[1] 15565
itcast@ubuntu:~/classcode/day05$ sleep 1000 &
[2] 15566
itcast@ubuntu:~/classcode/day05$ sleep 1000 &
[3] 15567
# 终端2
itcast@ubuntu:~/classcode/day05$ ps -aux | grep sleep
itcast 15565 0.0 0.0 5476 592 pts/0 S 09:04 0:00 sleep 1000
itcast 15566 0.0 0.0 5476 528 pts/0 S 09:04 0:00 sleep 1000
itcast 15567 0.0 0.0 5476 596 pts/0 S 09:04 0:00 sleep 1000
itcast 15571 0.0 0.0 6432 736 pts/1 S+ 09:04 0:00 grep --color=auto sleep
itcast@ubuntu:~/classcode/day05$ killall sleep
itcast@ubuntu:~/classcode/day05$ ps -aux | grep sleep
itcast 15583 0.0 0.0 6432 724 pts/1 S+ 09:04 0:00 grep --color=auto sleep
4.进程号相关函数
- getpid 获取当前进程进程号
- getppid 获取父进程号
- getpgid 获取进程组号
与fork搭配看效果
5.创建进程 fork
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid = getpid();
printf("创建进程前:\n");
printf("pid = %d,ppid = %d,pgid = %d\n",
getpid(),getppid(),getpgid(pid));
fork();
printf("创建进程后:\n");
printf("pid = %d,ppid = %d,pgid = %d\n",
getpid(),getppid(),getpgid(pid));
return 0;
}
得到的结果因为子进程在结束之前父进程已经结束,从而子进程的父进程变为/bin/bash,同时getgpid返回
- getpid 获取当前进程进程号
- getppid 获取父进程号
- getpgid 获取进程组号
与fork搭配看效果
5.创建进程 fork
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid = getpid();
printf("创建进程前:\n");
printf("pid = %d,ppid = %d,pgid = %d\n",
getpid(),getppid(),getpgid(pid));
fork();
printf("创建进程后:\n");
printf("pid = %d,ppid = %d,pgid = %d\n",
getpid(),getppid(),getpgid(pid));
return 0;
}
得到的结果因为子进程在结束之前父进程已经结束,从而子进程的父进程变为/bin/bash,同时getgpid返回