系统调用是什么
在linux中,系统调用是指操作系统提供给用户程序调用的一组特殊接口。系统调用规划了用户访问内核的路径。
Linux C语言 文件部分系统调用
1.open()打开文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path,int flag);
/*
返回值:成功时返回文件描述符,失败时返回-1
参数:
1.path:文件名的字符串地址
2.flag:文件打开模式信息(用位运算'|'连接)
O_CREAT:必要时创建文件
O_TRUNC:删除全部现有数据
O_APPEND:维持现有数据,保存到其后面
O_RDONLY:只读打开
O_WRONLY:只写打开
O_RDWR:读写打开
*/
2.close()关闭文件
#include <unistd.h>
int close(int fd);
/*
返回值:成功时返回0,失败时返回-1
参数:
fd:该文件的文件描述符
*/
3.mkdir()创建目录
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int mkdir(const char *pathname,mode_t mode);
/*
返回值:成功时返回0,失败时返回-1
参数:
1.pathname 目录路径字符串
2.mode_t (一个无符号八进制数)
S_IRUSR 00400 文件所有者具有可读权限
S_IWUSR 00200 文件所有者具有可写权限
S_IXUSR 00100 文件所有者具有可执行权限
S_IRGRP 00040 用户组具可读取权限
S_IWGRP 00020 用户组具可写入权限
S_IXGRP 00010 用户组具可执行权限
S_IROTH 00004 其他用户具可读取权限
S_IWOTH 00002 其他用户具可写入权限
S_IXOTH 00001 其他用户具可执行权限
S_IRWXU 00700 文件所有者具有可读写执行权限
*/
4.access()判断路径是否存在
#include <unistd.h>
int access(const char *pathname, int mode);
/*
access函数用来判断指定的文件或目录是否存在(F_OK),
已存在的文件或目录是否有可读(R_OK)、可写(W_OK)、可执行(X_OK)权限。
F_OK、R_OK、W_OK、X_OK这四种方式通过access函数中的第二个参数mode指定。
如果指定的方式有效,则此函数返回0,否则返回-1。
返回值:存在或者有权限时返回0,否则返回-1
参数:
1.pathname :文件或者目录路径
2.mode
F_OK 是否存在
R_OK 是否可读
W_OK 是否可写
X_OK 是否可执行
*/
5.fcntl()函数
#include fcntl(int fd, int cmd, ... /* int args*/ );
//返回值: 若成功,则依赖于cmd(见下);若出错,则返回-1
fcntl函数有5种功能
- 复制一个已有的文件描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)
- 获取/设置文件描述符标志(cmd=F_GETFD或F_SETFD)
- 获取/设置文件状态标志(cmd=F_GETFL或F_SETFL)
- 获取/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)
- 获取/设置记录锁(cmd=F_GETLK/F_SETLK或F_SETLKW)
Linux C语言 命令系统调用
1.执行命令 system函数
#include <stdlib.h>
int system(const char * command)
//执行 dos(windows系统) 或 shell(Linux/Unix系统) 命令,参数字符串command为命令名。另,在windows系统下参数字符串不区分大小写。
//在windows系统中,system函数直接在控制台调用一个command命令
//在Linux/Unix系统中,system函数会调用fork函数产生子进程,由子进程来执行command命令,命令执行完后随即返回原调用的进程
Linux C语言 线程系统调用
1.创建线程pthread_create函数
#include <pthread.h>
int pthread_create(
pthread_t * restrict thread,
const pthread_attr_t * restrict attr,
void* (* start_routine)(void *),
void * restrict arg
);
/*
返回值:创建线程成功时,返回0;失败时,返回其他值。
参数:
thread:保存新线程ID的变量地址值
attr:用于传递线程属性的参数;传递NULL时,创建默认属性的线程
start_routine:相当于线程main函数的、在单独执行流中执行的函数地址值(函数指针)
arg:传给start_routine函数的参数的变量地址值。
*/
2.退出线程pthread_exit函数
#include <pthread.h>
void pthread_exit(void *retval);
/*
作用:将单个线程退出
参数:retval表示线程退出状态,通常传NULL。
*/
注意点:
1.return的作用是返回到函数的调用点,如果是main函数中的return,则代表该进程结束,并释放进程地址空间,所有线程都终止。对于其它函数的return,则直接返回到函数的调用点。exit和_exit函数会直接终止整个进程,导致所有线程结束。pthread_exit函数则会导致调用该函数的线程结束。所以,多线程环境中,应尽量少用,或者不使用exit函数,取而代之使用pthread_exit函数,将单个线程退出。任何线程里exit导致进程退出,其他线程也结束,主控线程退出时不能return或exit。
2.另注意,pthread_exit或者return返回的指针(线程执行的函数用return或者pthread_exit结束线程时)所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了(已经把线程栈上的内存回收),此时退出的这个线程函数所占据的栈空间可能又会被重新分配出去,因此其他线程再次使用这个返回的地址没有意义。
3.等待线程结束 pthread_join函数
4.获取线程id pthread_self()函数和gettid()函数
#include <pthread.h>
pthread_t pthread_self(void);
/*返回当前线程id*/
5.线程清理处理程序 pthread_cleanup_push/pop
void pthread_cleanup_push(void (*routine)(void*),void *arg);
//通过这个函数注册线程处理函数,即将routine放入栈中;arg为routine的参数;
void pthread_cleanup_pop(int execute);
//该函数的作用是:将pthread_cleanup_push放入栈中的函数指针取出,并让其执行;
但是要注意两种情况,
(1)当execute!=0的时候,线程清理函数一定会执行,因为是从栈中拿数据,所以处理函数的顺序会与pthread_cleanup_push注册的函数的顺序相反.
(2)当execute=0的时候,如果在pthread_cleanup_push和pthread_cleanup_pop之间没有调用pthread_exit()或有取消点的pthread_cancel()的时候,这时候仅仅是将函数指针从栈中取出来,而不会去执行.相反的话,即使execute=0还是会去
6.条件变量 pthread_cond_wait/signal
#include <pthread.h>
struct msg { //结点
struct msg *m_next;
/*结点数据*/
};
struct msg *workq; //消息队列头结点
pthread_cond_t qready = PTHREAD_COND_INITIALIZER; //条件变量
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; //互斥锁
void process_msg() //处理消息队列的消息
{
struct msg *mp;
while(1){
pthread_mutex_lock(&qlock); //上锁
while(workq == NULL) //队列为空
{
pthread_cond_wait(&qready,&qlock); //释放锁,等待条件变量变为true(阻塞);条件变量true后,上锁,条件变量变为false。
}
//取出结点
mp = workq;
workq = workq->m_next;
pthread_mutex_unlock(&qlock);
}
}
void enqueue_msg(struct msg *mp) //消息入队
{
pthread_mutex_lock(&qlock);
mp->m_next = workq;
workq = mp;
pthread_mutex_unlock(&qlock);
pthread_cond_signal(&qready); //队列有消息,条件变量true
}
C文件函数(封装了系统调用)
File 结构体
C语言的stdio.h头文件中,定义了用于文件操作的结构体FILE。这样,我们通过fopen返回一个文件指针(指向FILE结构体的指针)来进行文件操作。可以在stdio.h(位于visual studio安装目录下的include文件夹下)头文件中查看FILE结构体的定义,如下:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
C程序用不同的FILE结构管理每个文件。程序员可以使用文件,但是不需要知道FILE结构的细节。实际上,FILE结构是间接地操作系统的文件控制块 (FCB)来实现对文件的操作的,如下图:
上面图中的_file实际上是一个描述符,作为进入打开文件表索引的整数 (文件描述符)。
文件结束符EOF
- 我们知道,C++ 中可以通过cin,cin.get(),cin.getline(),getline() 等对字符串进行输入(若对这些输入模糊,可以阅读这篇文章:cin, cin.get(), cin.getline(), getline()的区别(直接看总结)),但它们都具有一定的限制,当遇到某种字符时便会结束读取,而有时候我们需要输入的文本中包含这些字符,该如何解决这个问题呢?
- 这时我们就要用到文件结束符 EOF 来控制输入的结束,而在使用模拟 EOF 时则需要用到 cin.fail() 函数。
在 Windows 系统中,我们可以通过 Ctrl + Z + Enter 来模拟 EOF 的输入,在 Unix 系统中可以通过 Ctrl + D + Enter 来模拟。
此时,当我们模拟输入 EOF 后,函数 fail() 便会返回 true,我们就可以利用这一点作为判断条件,通过循环语句来进行输入,且在想要结束输入时模拟 EOF 的输入,即按下 Ctrl + Z +Enter 或 Ctrl + D + Enter 。
如果想要清除 EOF 标记,则可以使用 cin.clear(),使输入能够继续。 - 人们经常误认为 EOF 是从文件中读取的一个字符(牢记)。其实,EOF 不是一个字符,它被定义为是 int 类型的一个负数(比如 -1)。EOF 也不是文件中实际存在的内容。EOF 也不是只表示读文件到了结尾这一状态(这种状态可以用 feof() 来检测),它还能表示 I/O 操作中的读、写错误(通常可以用 ferror() 来检测)以及其它一些关联操作的错误状态
打开文件 fopen
FILE *fopen( const char *filename, const char *mode );
- 第一个参数是指向文件名字符串常量的指针类型;
- 第二个参数指定文件打开的模式
- r: 读取,如果文件不存在,函数调用失败
- w: 为写入操作打开一个空文件。若文件不存在,则创建一个文件;若给定的文件已经存在,那么它的内容将被清空
- a: 为写入操作打开文件。若文件不存在,则首先创建一个文件;若文件存在,那么在该文件结尾添加新数据,在写入数据之前,不会移除已有的EOF标记
- r+:打开文件用于写入操作和读取操作,文件必须存在;
- w+:写入和读取,其他同w;
- a+:打开文件用于读取和添加操作,其他同a。
stdio.h(io头文件)
sprintf 往字符数组输入数据
int sprintf(char *buffer, const char *format, [argument]…)
参数:
(1)buffer:是char类型的指针,指向写入的字符串指针;
(2)format:格式化字符串,即在程序中想要的格式;
(3)argument:可选参数,可以为任意类型的数据;
函数返回值:buffer指向的字符串的长度;