目录
1.字符串函数
2.数据转换函数
3.格式化输入输出函数
4.权限控制函数
5.IO函数
6.进程控制函数
7.文件和目录函数
1.字符串函数
2.数据转换函数
3.格式化输入输出函数
#include<stdarg.h>
void test(const char * format , ...){
va_list ap;
va_start(ap,format);
vprintf(format,ap);
char buffer[5000] = "";
va_start(ap,format);
vsnprintf(buffer,sizeof (buffer) , format , ap );
va_start(ap,format);
vsprintf(buffer , format,ap);
}
int main(){
test("%s %d %f " , "bug" , 20 , 20.145);
}
4.权限控制函数
补充知识:
#chmod
# 语法格式: chmod who [+|-|=] mod 文件名
- who:
- u: user -> 文件所有者
- g: group -> 文件所属组用户
- o: other -> 其他
- a: all, 以上是三类人 u+g+o
- 对权限的操作:
+: 添加权限
-: 去除权限
=: 权限的覆盖
- mod: 权限
r: read, 读
w: write, 写
x: execute, 执行
-: 没有权限
# 将文件所有者权限设置为读和执行, 也就是权限覆盖
robin@OS:~/Linux$ chmod u=rx b.txt
robin@OS:~/Linux$ ll b.txt
-r-xrw-r-- 2 robin robin 2929 Apr 14 18:53 b.txt*
# 给其他人添加写和执行权限
robin@OS:~/Linux$ chmod o+wx b.txt
robin@OS:~/Linux$ ll b.txt
-r-xrw-rwx 2 robin robin 2929 Apr 14 18:53 b.txt*
# 给文件所属组用户去掉读和执行权限
robin@OS:~/Linux$ chmod g-rx b.txt
robin@OS:~/Linux$ ll b.txt
-r-x-w-rwx 2 robin robin 2929 Apr 14 18:53 b.txt*
# 将文件所有者,文件所属组用户,其他人权限设置为读+写+执行
robin@OS:~/Linux$ chmod a=rwx b.txt
robin@OS:~/Linux$ ll b.txt
-rwxrwxrwx 2 robin robin 2929 Apr 14 18:53 b.txt*
# 语法格式: chmod [+|-|=] mod 文件名
- 对权限的操作:
+: 添加权限
-: 去除权限
=: 权限的覆盖, 等号可以不写
- mod: 权限描述, 所有权限都放开是 7
- 4: read, r
- 2: write, w
- 1: execute , x
- 0: 没有权限
# 分解: chmod 0567 a.txt
0 5 6 7
八进制 文件所有者 文件所属组用户 其他人
r + x r + w r+w+x
######################### 举例 #########################
# 查看文件 c.txt 的权限
robin@OS:~/Linux$ ll c.txt
-rwxrwxrwx 2 robin robin 2929 Apr 14 18:53 c.txt*
# 文件所有者去掉执行权限, 所属组用户去掉写权限, 其他人去掉读+写权限
robin@OS:~/Linux$ chmod -123 c.txt
robin@OS:~/Linux$ ll c.txt
-rw-r-xr-- 2 robin robin 2929 Apr 14 18:53 c.txt*
# 文件所有者添加执行权限, 所属组用户和其他人权限不变
robin@OS:~/Linux$ chmod +100 c.txt
robin@OS:~/Linux$ ll c.txt
-rwxr-xr-- 2 robin robin 2929 Apr 14 18:53 c.txt*
# 将文件所有者,文件所属组用户,其他人权限设置为读+写, 没有执行权限
robin@OS:~/Linux$ chmod 666 c.txt
robin@OS:~/Linux$ ll c.txt
-rw-rw-rw- 2 robin robin 2929 Apr 14 18:53 c.txt
# 语法1-只修改所有者:
$ sudo chown 新的所有者 文件名
# 语法2-同时修改所有者和所属组:
$ sudo chown 新的所有者:新的组名 文件名
# 查看文件所有者:b.txt 属于 robin 用户
robin@OS:~/Linux$ ll b.txt
-rw-rw-rw- 2 robin robin 2929 Apr 14 18:53 b.txt
# 将 b.txt 的所有者修改为 luffy
robin@OS:~/Linux$ sudo chown luffy b.txt
[sudo] password for robin:
robin@OS:~/Linux$ ll b.txt
-rw-rw-rw- 2 luffy robin 2929 Apr 14 18:53 b.txt
# 修改文件所有者和文件所属组
# 查看文件所有者和所属组
robin@OS:~/Linux$ ll b.txt
-rw-rw-rw- 2 luffy robin 2929 Apr 14 18:53 b.txt
# 同时修改文件所有者和文件所属组
robin@OS:~/Linux$ sudo chown robin:luffy b.txt
robin@OS:~/Linux$ ll b.txt
-rw-rw-rw- 2 robin luffy 2929 Apr 14 18:53 b.txt
# 只修改文件所属的组, 普通用户没有这个权限, 借助管理员的权限
# 语法: sudo chgrp 新的组 文件名
# 查看文件所属组信息
robin@OS:~/Linux$ ll b.txt
-rw-rw-rw- 2 robin luffy 2929 Apr 14 18:53 b.txt
# 修改文件所属的组
robin@OS:~/Linux$ sudo chgrp robin b.txt
robin@OS:~/Linux$ ll b.txt
-rw-rw-rw- 2 robin robin 2929 Apr 14 18:53 b.txt
下面说一下文件设置用户ID位,这个ID仅仅是一个二进制的bit位,在文件stat结构的st_mode成员中,对于一般的文件,该位是置为无效的,只有可执行文件的该位是置为有效的。
改变三个用户ID的方法
2. 什么时候用到设置用户id和设置组id?
小实验
此时给上文件用户设置ID和文件设置组ID的权限s,此时执行时
特权用户使用这几个函数的时候,都是直接用参数的值来设置实际用户ID或者有效用户ID,这些值都可以是任意的。
例如:setreuid(ruid, euid), 如果是特权用户,则直接设置实际用户ID为ruid,有效用户ID为euid。
但非特权用户就不行了,非特权用户用setuid(),seteuid()则只能将有效有户ID设置为实际用户ID或保存的设置用户ID,如果不是这两个数,设置失败!
此时我们改变一下文件的用户和用户组为zhz
创建会话ID:
pid_t setsid(void) 守护进程的关键调用函数
1.用户和组要有足够的权限2.当前进程只能是子进程才能调用成功
5.IO函数
头文件
<sys/types.h>
<sys/stat.h>
<fcntl.h>
打开文件
int open(const char *pathname, int flags, mode_t mode)
<fcntl.h>
测试代码:
#include <cstdio>
#include<iostream>
#include<unistd.h> //头文件
#include<fcntl.h>
#include<sys/file.h>
#include<error.h> //errno
#include<string.h> //strerror
void f() {
//int fd = open("/home/zhz/text.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
int fd = open("/home/zhz/text.txt", O_RDWR | O_CREAT, 0755);
//文件锁是一个建议性锁,此时是无效上锁
/*
if (fd >= 0) {
printf("write:%d\n", write(fd, "hello", 5));
printf("flock:%d\n", flock(fd, LOCK_EX));
sleep(6);
flock(fd, LOCK_UN);
close(fd);
}
*/
if (fd >= 0) {
printf("flock:%d\n", flock(fd, LOCK_EX)); //先上锁在写(你认为这有锁才起效果)
printf("write:%d\n", write(fd, "hello", 5));
sleep(6);
flock(fd, LOCK_UN);
close(fd);
}
else {
std::cout << strerror(errno) << std::endl;
}
}
int main()
{
f();
return 0;
}
6.进程控制函数
exec函数族提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。
在Linux中使用exec函数族主要有以下两种情况
1.当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。
2.如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。
1参数传递方式:exec函数族的参数传递有两种方式,一种是逐个列举(l)的方式,而另一种则是将所有参数整体构造成指针数组(v)进行传递。
在这里参数传递方式是以函数名的第5位字母来区分的,字母为“l”(list)的表示逐个列举的方式,字母为“v”(vertor)的表示将所有参数整体构造成指针数组传递,然后将该数组的首地址当做参数传给它,数组中的最后一个指针要求是NULL。读者可以观察execl、execle、execlp的语法与execv、execve、execvp的区别。
2.环境变量:exec函数族使用了系统默认的环境变量,也可以传入指定的环境变量。这里以“e”(environment)结尾的两个函数execle、execve就可以在envp[]中指定当前进程所使用的环境变量替换掉该进程继承的环境变量。
3.PATH环境变量说明
PATH环境变量包含了一张目录表,系统通过PATH环境变量定义的路径搜索执行码,PATH环境变量定义时目录之间需用用“:”分隔,以“.”号表示结束。PATH环境变量定义在用户的.profile或.bash_profile中,下面是PATH环境变量定义的样例,此PATH变量指定在“/bin”、“/usr/bin”和当前目录三个目录进行搜索执行码。
PATH=/bin:/usr/bin:.
export $PATH
4.进程中的环境变量说明
在Linux中,Shell进程是所有执行码的父进程。当一个执行码执行时,Shell进程会fork子进程然后调用exec函数去执行执行码。Shell进程堆栈中存放着该用户下的所有环境变量,使用execl、execv、execlp、execvp函数使执行码重生时,Shell进程会将所有环境变量复制给生成的新进程;而使用execle、execve时新进程不继承任何Shell进程的环境变量,而由envp[]数组自行设置环境变量。
void f() {
pid_t pid = fork();
if (pid > 0) {
//这是父进程
sleep(5);
std::cout << "parent!!!!" << std::endl;
}
else {
execl("/bin/ls", "ls", "-l", NULL);
}
}
进程的数量是有限的
#include<assert.h>
C/C++ assert()函数用法总结 - 白菜菜白 - 博客园 (cnblogs.com)
(9条消息) 多进程之进程退出函数:exit,_exit,atexit详解_c语言 exit() atexit_谢永奇1的博客-CSDN博客
(9条消息) on_exit()函数使用说明_Ahren.zhao的博客-CSDN博客
#include <cstdio>
#include<iostream>
#include<unistd.h> //头文件
#include<fcntl.h>
#include<sys/file.h>
#include<error.h> //errno
#include<string.h> //strerror
#include<assert.h>
void fa() {
printf("father is over\n");
}
void son(int status , void * str) {
printf("%d %s\n", status , (char *)str);
}
void pra() {
pid_t pid = fork();
if (pid > 0) {
atexit(fa);
std::cout << "parent!!!!" << std::endl;
exit(0);
}
else {
char s[10] = "i am son";
char* str = &s[0];
on_exit(son, str );
exit(0);
}
}
int main()
{
pra();
return 0;
}
#include <cstdio>
#include<iostream>
#include<unistd.h> //头文件
#include<fcntl.h>
#include<sys/file.h>
#include<error.h> //errno
#include<string.h> //strerror
#include<assert.h>
#include<setjmp.h>
#include<signal.h>
jmp_buf jmpbuf; //用来存储寄存器的信息
void test002() {
//to do somethings
longjmp(jmpbuf, 1); //此时程序发生错误1
}
void test001() {
//to do somethings
//...
test002();
}
void signal_deal(int sig) { //注册信号处理函数
if (sig == SIGSEGV) {
longjmp(jmpbuf, SIGSEGV);
}
}
void pra() {
signal(SIGSEGV,signal_deal); //异常捕获
int ret = setjmp(jmpbuf); //保存所有寄存器的值
if (ret == 0) { //c语言处理异常的一种机制
test001();
*(int*)(NULL) = 0; //段错误
}
else if (ret == 1) {
printf("error 1\n");
}
else if (ret == SIGSEGV) {
printf("error SIGSEGV\n");
}
}
int main()
{
pra();
return 0;
}
当注释掉test001()时,此时引发段错误信号,进入异常捕获处理函数。
知识补充:
信号是进程在运行过程中,由自身产生或由进程外部发过来的消息。
信号是硬件中断的软件模拟(软中断)。每个信号用一个整型常量宏表示,以SIG开头,比如SIGCHLD、SIGINT等,它们在系统头文件中<signal.h>定义。
由进程的某个操作产生的信号称为同步信号(synchronous signals),例如除0;由用户击键之类的进程外部事件产生的信号叫做异步信号。(asynchronous signals)。
signal()
C 库函数 void (*signal(int sig, void (*func)(int)))(int) 设置一个函数来处理信号,即带有 sig 参数的信号处理程序。
进程接收到信号以后,可以有如下3种选择进行处理:
1. 接收默认处理:接收默认处理的进程通常会导致进程本身消亡。例如连接到终端的进程,用户按下CTRL+c,将导致内核向进程发送一个SIGINT的信号,进程如果不对该信号做特殊的处理,系统将采用默认的方式处理该信号,即终止进程的执行;
2.忽略信号:进程可以通过代码,显示地忽略某个信号的处理,例如:signal(SIGINT,SIGDEF);但是某些信号是不能被忽略的,
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void sighandler(int);
int main()
{
signal(SIGINT, sighandler);
while(1)
{
printf("开始休眠一秒钟...\n");
sleep(1);
}
return(0);
}
void sighandler(int signum)
{
printf("捕获信号 %d,跳出...\n", signum);
exit(1);
}
让我们编译并运行上面的程序,这将产生以下结果,且程序会进入无限循环,需使用 CTRL + C 键跳出程序。
3.捕捉信号并处理:进程可以事先注册信号处理函数,当接收到信号时,由信号处理函数自动捕捉并且处理信号。
有两个信号既不能被忽略也不能被捕捉,它们是SIGKILL和SIGSTOP。即进程接收到这两个信号后,只能接受系统的默认处理,即终止线程。
which的取值根据who来选择,一般关注进程优先级
PRIO_PROCESS PRIO_PGRP PRIO_USER
#include <cstdio>
#include<iostream>
#include<unistd.h> //头文件
#include<fcntl.h>
#include<sys/file.h>
#include<error.h> //errno
#include<string.h> //strerror
#include<assert.h>
#include<setjmp.h>
#include<signal.h>
#include<stdlib.h>
void pra(){
int ret = system("ls -l");
printf("system return : %d\n", ret);
ret = system("mkdir test");
printf("system return : %d\n", ret);
char buffer[512] = "";
snprintf(buffer, sizeof buffer, "echo \"%s\">test/test.txt", __FUNCTION__);
//组合使用可以进行批处理
printf("%s\n", buffer);
ret = system(buffer);
printf("system return : %d\n", ret);
}
int main() {
pra();
return 0;
}
补充知识:
7.文件和目录函数
总结open与fopen的区别 - NickyYe - 博客园 (cnblogs.com)
实现文件输出重定向
void pra1() {
FILE* pFile = fopen("./test.txt", "r");
if (pFile != NULL) {
//char buffer[4096] 栈上申请的buffer不要超过64k
char* buffer = new char[1024*10]; //单词内存分配最好不要超过2G
memset(buffer, 0 , 1024 * 10);
size_t ret = fread(buffer, 1, 1024, pFile);
printf("num : %d <%s> \n", ret,buffer);
fclose(pFile);
}
else {
printf("open error\n");
}
}
void pra() {
FILE* pFile = fopen("./test.txt", "r");
if (pFile != NULL) {
char* buffer = new char[1024*10]; //单词内存分配最好不要超过2G
printf("%s(%d):%s [%c]\n", __FILE__, __LINE__, __FUNCTION__,fgetc(pFile));
printf("%s(%d):%s [%s]\n", __FILE__, __LINE__, __FUNCTION__, fgets(buffer,1024*10 ,pFile));
printf("[%d]\n", fgetc(pFile));
fclose(pFile);
}
else {
printf("open error\n");
}
}
每次读写都会改变文件中位值的指针记录
while(!feof(pFile)){
bzero(buffer,sizeof buffer);
fgets(buffer,sizeof buffer,pFile);
}
int ret = 0;
ret = mkdir( "he" , 755);
std::cout << ret << std::endl;
int ret = 0;
//ret = mkdir( "he" , 755);
remove("he");
std::cout << ret << std::endl;
递归遍历目录