- 系统数据文件信息
/etc/passwd hh: x:1000:1000:,:/home/hh:/bin/bash
struct passwd * getpwuid(uid_t uid);
根据用户号查询用户信息struct passwd * getpwuid(const char* name);
根据用户名获取用户信息
id和用户名的转换,用这个命令/函数实现
// 根据用户号查询用户名代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
int main(int argc, char ** argv)
{
if (argc < 2)
{
fprintf(stderr, "usage");
exit(1);
}
// 传递用户号 uid 使用atoi转换
struct passwd * pwdline = getpwuid(atoi(argv[1]));
puts(pwdline->pw_name);
return 0;
}
/etc/group
- struct group *getgrnam(const char *name); // 通过组名获取组的信息
- struct group *getgrgid(gid_t gid);// 通过组号获取组的信息
/etc/shadow
struct spwd* getspnam(const char *name);
char * crypt(const char *phrase, const char *setting); // 原串,杂字串
注意:ubuntu下gcc的连接过程中是从左到右,该代码中使用了crypt, -lcrypt 写在源文件名右边,链接器就可以在crypt库中找到crypt函数的定义,gcc checkpass.c -lcrypt
sudo ./a.out hh
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <shadow.h>
#include <crypt.h>
int main(int argc, char** argv)
{
if (argc < 2)
{
fprintf(stderr, "usage");
exit(1);
}
// 获取口令原文
// getpass(); // 作为口令输入
char* input_pass = getpass("PassWord:");
struct spwd * shadowline = getspnam(argv[1]);
char * crypted_pass = crypt(input_pass, shadowline->sp_pwdp); // 加密 口令原文,
if (strcmp(shadowline->sp_pwdp, crypted_pass) == 0)
{
puts("ok");
}
else
{
puts("failed");
}
return 0;
}
时间戳 time_t
st_atime
st_mtime
st_ctime
time_t time() || time(time_t);
struct tm* localtime(const time_t * times);
time_t mktime(struct tm * times);
会先进行校验合法,如果溢出,会自动进位size_t strftime(**char *s, size_t max,** const char *format,const struct tm *tm); ``top 2 parametes
共同决定了一块缓冲区;const char *format
提取的格式化,一般使用%Y-%m-%d %h:%M:%s
,const struct tm *tm
源时间,
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 */
};
在out文件中,每隔一秒输出一行当前时间
涉及文件读写,追加读写,时间戳格式化,全缓冲刷新流
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define FEILNAEM "/tmp/out"
#define BUFFSIZE 1024
int main()
{
FILE* fp = fopen(FEILNAEM, "a+"); // 追加读写
if (fp == NULL)
{
perror("fopen()");
exit(1);
}
int count = 0; // 有多少行
char buf[BUFFSIZE];
// 读内容 有多少行
while (fgets(buf, BUFFSIZE, fp) != NULL)
{
count++;
}
time_t stamp;
while (1)
{
time(&stamp); // 获取时戳
struct tm* tm = localtime(&stamp); // 转换
// 文件是全缓冲模式,\n 已经无法进行刷新,需要手动刷新fflush
fprintf(fp, "%-4d%d-%d-%d %d:%d:%d\n", ++count,\
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, \
tm->tm_hour, tm->tm_min, tm->tm_sec); // 写入
// 年从1900开始计算,月从0 开始计算,需要+1900 +1
fflush(fp);
sleep(1);
}
fclose(fp);
// tail -f /tmp/out 实时查看文件
return 0;
}
案例:输出今天的时间和100天后的时间
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define timestrsize 1024
int main()
{
time_t stamp;
stamp = time(NULL);
char timestr[timestrsize];
struct tm *tm;
tm = localtime(&stamp);
strftime(timestr, timestrsize, "Now: %Y-%m-%d", tm);
puts(timestr); // 今天
tm->tm_mday += 100;
(void)mktime(tm); // 使用mktime的副作用,校验合法进位的作用
strftime(timestr, timestrsize, "100days : %Y-%m-%d", tm);
puts(timestr);
return 0; // 给父进程的
}
- 进程环境
- main函数
int main(int argc, char** argv) - 进程的终止
正常终止: 从main函数返回; 调用exit函数;调用_exit()或者_Exit();最后一个线程从其启动例程返回;最后一个线程调用了pthead_exit()函数
异常终止:在程序中调用abort();接到一个信号并终止;最后一个线程对其取消请求做出响应 - 命令行参数的分析
int getopt(argc, argv, const char* optstring);
getopt_long();
ls -l -i -n -a /etc/tmp
// 测试口令 ./mydate -y 4 -m -d 输出当前时间,年月日,4表示年按照4位输出eg:2023
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#define timestrsize 1024
#define FMTSTRSIZE 1024
// -y : year
// -m : month
// -d : day
// -H : hour
// -M : minute
// -S : second
int main(int argc, char** argv)
{
time_t stamp;
stamp = time(NULL);
char timestr[timestrsize];
struct tm *tm;
tm = localtime(&stamp);
int c;
char fmtstr[FMTSTRSIZE]; // 格式化使用参数进行拼接
fmtstr[0] = '\0';
while (1)
{
c = getopt(argc, argv, "H:MSy:md");// 如果有冒号,那么指针就默认指向有冒号的变量 // 删除冒号可实现MSmd的功能
if (c < 0)
{
break; // 失败跳出
}
switch (c)
{
case 'H':
if (strcmp(optarg, "12") == 0)
strncat(fmtstr, "%I(%P) ", FMTSTRSIZE);
else if (strcmp(optarg, "24") == 0)
strncat(fmtstr, "%H ", FMTSTRSIZE);
else
fprintf(stderr, "invalid argv");
break;
case 'M':
strncat(fmtstr, "%M ", FMTSTRSIZE);
break;
case 'S':
strncat(fmtstr, "%S ", FMTSTRSIZE);
break;
case 'y':
if (strcmp(optarg, "2") == 0)
strncat(fmtstr, "%y ", FMTSTRSIZE);
else if (strcmp(optarg, "4") == 0)
strncat(fmtstr, "%Y", FMTSTRSIZE);
else
fprintf(stderr, "invalid argv -y");
break;
case 'm':
strncat(fmtstr, "%m ", FMTSTRSIZE);
break;
case 'd':
strncat(fmtstr, "%d ", FMTSTRSIZE);
break;
default:
break;
}
}
strftime(timestr, timestrsize, fmtstr, tm); // 缓冲区,大小,格式化,源
puts(timestr); // 今天
return 0;
}
测试口令: ./mydate -y 4 -m -d /tmp/out
如果指定文件,将结果输出到文件中,如果未指定文件 ,将结果输出到终端
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#define timestrsize 1024
#define FMTSTRSIZE 1024
// -y : year
// -m : month
// -d : day
// -H : hour
// -M : minute
// -S : second
int main(int argc, char** argv)
{
time_t stamp;
stamp = time(NULL);
char timestr[timestrsize];
struct tm *tm;
tm = localtime(&stamp);
int c;
char fmtstr[FMTSTRSIZE]; // 格式化使用参数进行拼接
fmtstr[0] = '\0';
FILE* fp = stdout;
while (1)
{
c = getopt(argc, argv, "-H:MSy:md");// 如果有冒号,那么指针就默认指向有冒号的变量
if (c < 0)
{
break; // 失败跳出
}
switch (c)
{
case 1:
// 如果给出输出文件,就将结果输出到文件中,否则输出到终端中
if (fp == stdout)
{
fp = fopen(argv[optind - 1], "w"); // man getopt 可以使用与其关联的宏optind optarg
if (fp == NULL)
{
perror("fopen");
fp = stdout;
}
}
break;
case 'H':
if (strcmp(optarg, "12") == 0)
strncat(fmtstr, "%I(%P) ", FMTSTRSIZE);
else if (strcmp(optarg, "24") == 0)
strncat(fmtstr, "%H ", FMTSTRSIZE);
else
fprintf(stderr, "invalid argv");
break;
case 'M':
strncat(fmtstr, "%M ", FMTSTRSIZE);
break;
case 'S':
strncat(fmtstr, "%S ", FMTSTRSIZE);
break;
case 'y':
if (strcmp(optarg, "2") == 0)
strncat(fmtstr, "%y ", FMTSTRSIZE);
else if (strcmp(optarg, "4") == 0)
strncat(fmtstr, "%Y", FMTSTRSIZE);
else
fprintf(stderr, "invalid argv -y");
break;
case 'm':
strncat(fmtstr, "%m ", FMTSTRSIZE);
break;
case 'd':
strncat(fmtstr, "%d ", FMTSTRSIZE);
break;
default:
break;
}
}
strncat(fmtstr, "\n", FMTSTRSIZE);
strftime(timestr, timestrsize, fmtstr, tm); // 缓冲区,大小,格式化,源
fputs(timestr, fp); // 今天
if (fp != stdout)
fclose(fp);
return 0;
}
- 环境变量
本质是key=value 命令export
一般命令在当前路径可以实现 ,因为默认在path环境变量中添加了当前路径
environ
输出环境变量
#include <stdio.h>
#include <stdlib.h>
extern char** environ;
int main()
{
for (int i = 0; environ[i] != NULL; i++)
{
puts(environ[i]);
}
return 0;
}
char *getenv(const char *name); // 获取环境变量值 根据key获取value
#include <stdio.h>
#include <stdlib.h>
int main()
{
puts((getenv("PATH"))); // 获取path的环境变量
return 0;
}
int setenv(const char *name, const char *value, int overwrite);
- c程序的存储空间布局
命令 pmap 进程号 可查看进程空间 - 库
动态库,静态库,手工装载库
#include <dlfcn.h>
void *dlopen(const char *filename, int flags);
int dlclose(void *handle);
dlerror
链接的时候加上-ldl
- 函数跳转
int setjmp(jmp_buf env); 设置跳转点 马上返回的时候,返回0,如果从longjmp返回的时候,返回非0
void longjmp(jmp_buf env, int val); 跳回到跳转点
实现跨函数的安全跳转,切换执行环境
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
static jmp_buf save;
static void d(void)
{
printf("%s():Begin.\n", __FUNCTION__);
printf("%s(): jump now!",__FUNCTION__);
longjmp(save, 6); // 带回的值是6 如果设置为0,那么会强制带回1,因为是跳回,必须是非0
printf("%s():End.\n", __FUNCTION__);
}
static void c(void)
{
printf("%s():Begin.\n", __FUNCTION__);
printf("%s():Call d().\n", __FUNCTION__);
d();
printf("%s():d() returned.\n", __FUNCTION__);
printf("%s():End.\n", __FUNCTION__);
}
static void b(void)
{
printf("%s():Begin.\n", __FUNCTION__);
printf("%s():Call c().\n", __FUNCTION__);
c();
printf("%s():c() returned.\n", __FUNCTION__);
printf("%s():End.\n", __FUNCTION__);
}
static void a(void)
{
printf("%s():Begin.\n", __FUNCTION__);
int ret = setjmp(save);
if (ret == 0)
{
// 设置
printf("%s():Call b().\n", __FUNCTION__);
b();
printf("%s():b() returned.\n", __FUNCTION__);
}
else
{
// 跳回来的
printf("%s():jumped back here with code%d\n", __FUNCTION__, ret);
}
printf("%s():End.\n", __FUNCTION__);
}
int main()
{
// __FUNCTION__ gcc提供 还有__line__ __file__
printf("%s():Begin.\n", __FUNCTION__);
printf("%s():Call a().\n", __FUNCTION__);
a();
printf("%s():a() returned.\n", __FUNCTION__);
printf("%s():End.\n", __FUNCTION__);
return 0;
}
- 资源的获取及控制
命令:ulimit -a
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
struct rlimit {
rlim_t rlim_cur; /* Soft limit */
rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */ // 高限值不可以提高,只能降低(ROOT用户可以升高)
};
atexit(); 钩子函数 register a function to be called at normal process termination 正常终止的时候调用,释放该释放的内容
#include <stdio.h>
#include <stdlib.h>
static void f1(void)
{
puts("f1() is working");
}
static void f2(void)
{
puts("f2 is working");
}
static void f3(void)
{
puts("f3 is working");
}
int main()
{
puts("begin");
// 钩子函数
atexit(f1);
atexit(f2);
atexit(f3); // 先执行
puts("end");
return 0;
}
钩子函数用法
open(f1)
atexit(f1);
open(f2)
atexit(f2);
这意味着当f1 open失败的时候,会调用钩子函数1,当f2 open失败的时候,会先调用f2钩子函数,再调用f1钩子函数